logo
podcast Podcast
get help Get Unstuck

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 👇🏼
email
No spam. Only real-world advice you can use.