With ArduinoJson 5, JsonArray
and JsonObject
were always returned by reference, to emphasize the fact that they reside in the JsonBuffer
.
// ArduinoJson 5
JsonObject& obj = ...
ArduinoJson 6 simplifies that by returning wrapper classes: JsonArray
, JsonObject
, and JsonVariant
. Be careful though, the memory for these objects is still held somewhere else, as we’ll see.
// ArduinoJson 6
JsonObject obj = ...
As you can see, you just need to remove the ampersand (&
).
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 complicated 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
Lastly, you can assign a JsonDocument
to another to get a deep copy:
DynamicJsonDocument doc2 = doc1; // makes a copy
Most of the time, you can skip the call to JsonDocument::to<T>()
because the JsonDocument
automatically converts to the right type on the first call.
In the following example, the JsonDocument
implicitly converts to an object:
doc["hello"] = "world";
// The above line is equivalent to:
JsonObject root = doc.to<JsonObject>();
root["hello"] = "world";
In the following example, the JsonDocument
implicitly converts to an array:
doc.add("hello");
// The above line is equivalent to:
JsonArray root = doc.to<JsonArray>();
root.add("hello");
Of course, the automatic conversion only occurs when the JsonDocument
is empty.
StaticJsonDocument
and DynamicJsonDocument
As the JsonBuffer
, there are two versions of the JsonDocument
.
The first is StaticJsonDocument
, which is the equivalent of StaticJsonBuffer
:
// ArduinoJson 5
StaticJsonBuffer<256> jb;
// ArduinoJson 6
StaticJsonDocument<256> doc;
The second is DynamicJsonDocument
, which is the equivalent of DynamicJsonBuffer
:
// ArduinoJson 5
DynamicJsonBuffer jb;
// ArduinoJson 6
DynamicJsonDocument doc(1024);
DynamicJsonDocument
has a fixed capacity that you must specify to the constructor. Unlike the DynamicJsonBuffer
, DynamicJsonDocument
doesn’t automatically expand.
With ArduinoJson 5, you invoked the JSON parser by calling JsonBuffer::parseObject()
or JsonBuffer::parseArray()
.
// ArduinoJson 5
JsonObject& obj = jb.parseObject(input);
With ArduinoJson 6, you call the function deserializeJson()
and pass the JsonDocument
and the input as arguments.
// ArduinoJson 6
deserializeJson(doc, input);
Each time you call deserializeJson()
, it clears the JsonDocument
. This feature allows reusing 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.
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.
// ArduinoJson 5
JsonObject& obj = jb.parseObject(input);
if (!obj.success()) {
Serial.println("parseObject() failed");
return;
}
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:
// ArduinoJson 6
auto error = deserializeJson(doc, input);
if (error) {
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
return;
}
With ArduinoJson 5, when you wanted to serialize a JsonArray
or a JsonObject
to a JSON document, you called JsonArray::printTo()
or JsonObject::printTo()
.
// ArduinoJson 5
obj.printTo(Serial);
With ArduinoJson 6, you call the function serializeJson()
and pass the JsonArray
, JsonObject
, or the JsonDocument
.
// ArduinoJson 6
serializeJson(doc, Serial);
Similarly, you can call serializeJsonPretty()
to produce a prettified JSON document.
With ArduinoJson 5, you could compute the length of the serialized document by calling JsonArray::measureLength()
or JsonObject::measureLength()
.
// ArduinoJson 5
size_t len = obj.measureLength();
With ArduinoJson 6, you call measureJson()
to to that.
// ArduinoJson `6`
size_t len = measureJson(doc);
Similarly, measureJsonPretty()
replaces JsonArray::measurePrettyLength()
and JsonObject::measureJsonPretty()
.
ArduinoJson 5 didn’t impose that the input was zero-terminated, but it was strongly recommended to prevent buffer overruns.
With ArduinoJson 6, you can pass an extra argument to deserializeJson()
to specify the maximum size of the input.
For example:
// ArduinoJson 6
deserializeJson(doc, input, inputSize);
With ArduinoJson 5, you could change the nesting limit by passing an optional argument to JsonBuffer::parseArray()
or JsonBuffer::parseObject()
.
// ArduinoJson 5
JsonObject& obj = jb.parseObject(input, 20);
With ArduinoJson 6, you must pass this value to deserializeJson()
and cast it to DeserializationOption::NestingLimit
:
// ArduinoJson 6
deserializeJson(doc, input, DeserializationOption::NestingLimit(20));
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()
.
// ArduinoJson 6
serializeMsgPack(doc, Serial);
Similarly, to deserialize a MessagePack document, you proceed as for a JSON document, except that you call deserializeMsgPack()
instead of deserializeJson()
. For example:
// ArduinoJson 6
deserializeMsgPack(doc, input);
With ArduinoJson 5, when you wanted to insert a preformatted piece of JSON, you called RawJson()
.
// ArduinoJson 5
obj["raw"] = RawJson("[1,2,3]");
With ArduinoJson 6, you call serialized()
which does exactly the same thing, except that it also supports MessagePack and non-zero-terminated strings.
// ArduinoJson 6
doc["raw"] = serialized("[1,2,3]");
With ArduinoJson 5, when you enumerated the member in a JsonObject
, you received a JsonPair
with two member variables key
and value
. The first was a const char*
and the second a JsonVariant
.
// ArduinoJson 5
for (JsonPair p : obj) {
const char* key = p.key;
JsonVariant value = p.value;
...
}
In ArduinoJson 6, JsonPair::key
and JsonPair::value
are member function. Also, key
doesn’t return a const char*
but a JsonString
. You must call JsonString::c_str()
to get the pointer.
// ArduinoJson 6
for (JsonPair p : obj) {
const char* key = p.key().c_str();
JsonVariant value = p.value();
...
}
With ArduinoJson 5, you could easily copy values between a JsonArray
and a regular array using JsonArray::copyFrom()
and JsonArray::copyTo()
.
int values[] = {1,2,3};
// ArduinoJson 5
arr.copyFrom(values);
arr.copyTo(values);
With version 6.9, you must call copyArray()
instead. There is only one function for both operations. The first argument is the source, and the second is the destination.
int values[] = {1,2,3};
// ArduinoJson 6
copyArray(values, arr);
copyArray(arr, values);
With ArduinoJson 5, JsonVariant
had value semantic. It was possible to create a instance without a JsonBuffer
.
// ArduinoJson 5
JsonVariant var = 42;
With ArduinoJson 6, JsonVariant
has reference semantics, like JsonArray
and JsonObject
. You need a JsonDocument
to initialize a JsonVariant
.
// ArduinoJson 6
JsonVariant var = doc.to<JsonVariant>();
var.set(42);
With ArduinoJson 5, you checked if an array or an object was valid by calling success()
:
// ArduinoJson 5
if (!obj.success()) ...
With ArduinoJson 6, you use isNull()
instead:
// ArduinoJson 6
if (obj.isNull()) ...
isNull()
is not the exact opposite of success()
: when the value is defined but is null
, both isNull()
and success()
return true
.
Also, note that, in ArduinoJson 5, is<const char*>()
returned true
if the value was null
; it’s no longer the case in version 6.
// ArduinoJson 5
DynamicJsonBuffer jb;
JsonObject& obj = jb.parseObject(json);
if (!obj.success())
return;
int value = obj["value"];
// ArduinoJson 6
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, json);
if (error)
return;
int value = doc["value"];
// ArduinoJson 5
DynamicJsonBuffer jb;
JsonObject& obj = jb.createObject();
obj["key"] = "value";
obj["raw"] = RawJson("[1,2,3]");
obj.printTo(Serial);
// ArduinoJson 6
DynamicJsonDocument doc(1024);
doc["key"] = "value";
doc["raw"] = serialized("[1,2,3]");
serializeJson(doc, Serial);