Dealing with messy JSON

Sometimes API do unspeakable things and we have to work around it in our app

Joel Clermont
Joel Clermont
2023-11-06

I was recently consuming data from a FileMaker oData API, and it was doing some very unexpected things.

Here's a simplified version of the payload it was trying to return:

{
  "result": {
    "code": 0,
    "data": {
      "name": "Bob"
    }
  }
}

But for some reason, it encoded that inner data property to a JSON string:

{
  "result": {
    "code": 0,
    "data": "{\"name\":\"Bob\"}"
  }
}

On top of that, it was encoding whitespace characters into that inner output, so I actually saw this:

{
  "result": {
    "code": 0,
    "data": "{\r  \"name\": \"Bob\"\r}"
  }
}

In my Laravel app, when I tried to json_decode that payload, it failed:

json_decode($messyPayload, true, flags: JSON_THROW_ON_ERROR);

// throws JSONException "Control character error, possibly incorrectly encoded"

Initially, I thought those visible \r characters were what it was complaining about, but in actuality those encoded carriage returns were not the issue. The real culprit was the un-encoded raw tab characters that weren't visible. Fun!

Looking at the JSON spec, any ASCII characters below 32 are not allowed. So I had to strip those out before decoding the JSON. And then I had to do an in-place second json_decode for that inner value which was double-encoded:

// strip control characters asci decimal codes 0 - 31
$cleanedPayload = preg_replace('/[\x00-\x1F]/', '', $messyPayload);

// first pass at decoding
$result = json_decode($cleanedPayload, true, flags: JSON_THROW_ON_ERROR);

// now do an in-place second decode of that inner property
$result['result']['data'] = json_decode($result['result']['data'], true, flags: JSON_THROW_ON_ERROR);

Success!

Here to help,

Joel

P.S. One thing that helped me figure this all out was Tinkerwell. Being able to interactively poke at the data and try different things was a huge time-saver.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼

Level up your Laravel skills!

Each 2-minute email has real-world advice you can use.