References

With ArduinoJson 5, JsonArray and JsonObject were always returned by reference, to emphasize the fact that they reside in the JsonBuffer.

ArduinoJson 6 simplifies that by returning everything by values: JsonArray, JsonObject and JsonVariant. Be careful though, the memory for these objects is still held somewhere else, see below.

JsonDocument

With ArduinoJson 5, it was very difficult to use a JsonObject or a JsonArray as a class member because you had to make sure that the JsonBuffer stayed in memory too. The trick was to add the JsonBuffer as a class member too, but it was more complex than it should be.

ArduinoJson 6 replaces the concept of JsonBuffer with the concept of JsonDocument. The JsonDocument owns the memory and contains the root of the object tree. You can see a JsonDocument as a combination of JsonBuffer and JsonVariant.

Since a JsonDocument can contain any kind of value, you need to cast it to read the content. For example:

JsonObject root = doc.as<JsonObject>(); // get the root object

Similarly, a JsonDocument can be repurposed to hold any kind of values. That is done via JsonDocument::to<T>(). For example, you can reset a JsonDocument to hold a JsonObject like that:

JsonObject root = doc.to<JsonObject>(); // clear and replace with a new JsonObject

As the JsonBuffer, there are two versions of the JsonDocument:

  1. StaticJsonDocument, which lives on the stack,
  2. DynamicJsonDocument, which lives in the heap.

deserializeJson()

With ArduinoJson 5, you invoked the JSON parser by calling JsonBuffer::parseObject() or JsonBuffer::parseArray().

Now, with ArduinoJson 6, you call the function deserializeJson() and pass the JsonDocument and the input as argument. There is only one function to support arrays, objects and variants, so you need to extract the JsonObject from the JsonDocument.

Here is an example:

StaticJsonDocument<200> doc;
deserializeJson(doc, input);
JsonObject root = doc.as<JsonObject>();

Each time you call deserializeJson(), it clears the JsonDocument. This feature allows to reuse the same JsonDocument several times, which was not possible with the JsonBuffer. Please do not see that as an invitation to use a global JsonDocument as it’s an inelegant and inefficient solution.

DeserializationError

With ArduinoJson 5, you used JsonObject::success() or JsonArray::success() to check whether the parsing succeeded or not, and you had no information on what went wrong.

Now, with ArduinoJson 6, you can look at the DeserializationError returned by deserializeJson(). You can test individual values like DeserializationError::InvalidInput or DeserializationError::NoMemory, or you can simply convert the error to a string by calling .c_str().

Here is an example:

auto err = deserializeJson(doc, input);
if (err) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.c_str());
    return;
}

serializeJson() and serializeJsonPretty()

With ArduinoJson 5, when you wanted to serialize a JsonArray or a JsonObject to a JSON document, you called JsonArray::printTo() or JsonObject::printTo().

Now, with ArduinoJson 6, you call the function serializeJson() and pass the JsonArray or JsonObject (or the JsonDocument).

Here is an example:

StaticJsonDocument<200> doc;
JsonObject root = doc.to<JsonObject>();
root["hello"] = "world";
serializeJson(root, Serial);

Similarly, you can call serializeJsonPretty() to produce a prettified JSON document.

Lastly, JsonArray::measureLength() and JsonObject::measureLength() are now replaced with measureJson() and measureJsonPretty().

Nesting limit

With ArduinoJson 5, you could change the nesting limit by passing an optional argument to JsonBuffer::parseArray() or JsonBuffer::parseObject().

But with ArduinoJson 6, the optional argument of deserializeJson() is used to specify the size of the input (see below), so instead, if you need to change the nesting limit, you must change the member nestingLimit of the JsonDocument.

For example:

doc.nestingLimit = 20;
deserializeJson(doc, input);

Non zero-terminated inputs

ArduinoJson 5 didn’t impose that the input was zero-terminated, but it was strongly recommended to prevent buffer overruns.

Now, with ArduinoJson 6, you can pass an extra argument to deserializeJson() to specify the maximum size of the input.

For example:

deserializeJson(doc, input, inputSize);

MessagePack

ArduinoJson 6 supports both serialization and deserialization of MessagePack documents.

However, it currently doesn’t support the following features of MessagePack:

To create a MessagePack document, you use the same technique as for a JSON document, except that you call serializeMsgPack() instead of serializeJson(). For example:

StaticJsonDocument<200> doc;
JsonObject root = doc.to<JsonObject>();
root["hello"] = "world";
serializeMsgPack(root, Serial);

Similarly, to deserialize a MessagePack document, you proceed as for a JSON document, except that you call deserializeMsgPack() instead of deserializeJson(). For example:

StaticJsonDocument<200> doc;
deserializeMsgPack(doc, input);
JsonObject root = doc.as<JsonObject>();

serialized()

With ArduinoJson 5, when you wanted to insert a preformatted piece of JSON, you call RawJson().

Now, with ArduinoJson 6, you call serialized() which does exactly the same thing, except that it also supports MessagePack and non zero-terminated strings.

Summary

Deserialization

Old code:

DynamicJsonBuffer jb;
JsonObject& obj = jb.parseObject(json);
if (obj.success()) {

}

New code:

DynamicJsonDocument doc;
DeserializationError error = deserializeJson(doc, json);
if (error) {

}
JsonObject obj = doc.as<JsonObject>();

Serialization

Old code:

DynamicJsonBuffer jb;
JsonObject& obj = jb.createObject();
obj["key"] = "value";
obj["raw"] = RawJson("[1,2,3]");
obj.printTo(Serial);

New code:

DynamicJsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["key"] = "value";
obj["raw"] = serialized("[1,2,3]");
serializeJson(doc, Serial);