This page is for you if deserializeJson() returned DeserializationError::Ok, but your program fails to extract any value from the JsonDocument.

From my experience, this happens for two reasons:

  1. Confusion between arrays and objects
  2. Document serialized twice

Let’s see each case in detail.

Case 1: confusion between arrays and objects

In this case, the problem is in the way you extract the values from the JsonDocument.

This often happens when the JSON document contains [{ or :[.

Example 1: an array of object

[{"hello":"world"}]
Bad
const char* world = doc["hello"];
Good
const char* world = doc[0]["hello"];

Example 2: an array in an object

{"hello":["world"]}
Bad
const char* world = doc["hello"];
Good
const char* world = doc["hello"][0];

Example 3: an object in an array in an object

{"hello":[{"new":"world"}]}
Bad
const char* world = doc["hello"]["new"];
Good
const char* world = doc["hello"][0]["new"];

To avoid this kind of problem, use the sample program generated by the ArduinoJson Assistant as a reference.

Case 2: document serialized twice

What’s the issue?

In this second case, the problem is not in the Arduino code, but in the code that generates the JSON document, for example, in the backend server.

You can quickly diagnose the problem by printing the JSON document to the serial port. If the document is correctly serialized to JSON, you should see something like this:

Good
{"hello":"world"}

If instead, you see something like this:

Bad
"{\"hello\":\"world\"}"

…then it means that the document was serialized twice.

For example, if this JSON document comes from a Node.js server, it means that the server somehow performs a double JSON conversion, like so:

JSON.stringify(JSON.stringify({hello:'world'}))

Of course, you won’t find a line like this in the code; you’ll only see one call to JSON.stringify(), the other call is performed implicitly by the underlying framework. For example, in Express.js, res.json() calls JSON.stringify() implicitly.

How to fix this issue?

To fix this problem, your best option is to correct the JSON document. I cannot tell you exactly how to do that since the details depend on the code that produces the JSON document.

In the unfortunate case where you couldn’t change the input, you could deserialize the inner JSON document by calling deserializeJson() twice like so:

StaticJsonDocument<512> doc1, doc2;
deserializeJson(doc1, input);
deserializeJson(doc2, doc1.as<const char*>());
String value = doc2["hello"];

If you were to use this in a real project, you probably want to discard doc1 after extracting doc2, since it’s not needed anymore. You could do that in an helper function:

DeserilizationError deserializeJsonTwice(JsonDocument& doc, Stream& input) {
  StaticJsonDocument<512> tmp;
  DeserializationError err = deserializeJson(doc1, input);
  if (err) return err;
  return deserializeJson(doc, tmp.as<const char*>());
}

Lastly, if you were to use the filtering feature, you would need to pass DeserializationOption::Filter to the second call to deserializeJson().