Migrating from version 5 to 6
References
With ArduinoJson 5, JsonArray
and JsonObject
were always returned by reference to emphasize 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 (&
).
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 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 value. 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
Automatic conversion
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.
deserializeJson()
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.
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.
// 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;
}
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()
.
// 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.
measureJson()
and measureJsonPretty()
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 do that.
// ArduinoJson 6
size_t len = measureJson(doc);
Similarly, measureJsonPretty()
replaces JsonArray::measurePrettyLength()
and JsonObject::measureJsonPretty()
.
Non-zero-terminated inputs
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);
Nesting limit
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));
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()
.
// 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);
serialized()
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]");
JsonPair
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 functions. 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();
...
}
copyArray()
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);
JsonVariant
is a reference
With ArduinoJson 5, JsonVariant
had value semantic. It was possible to create an 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);
isNull()
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.
as<char>()
and is<char>()
Support for naked char
(i.e., neither signed
nor unsigned
) was deprecated in 6.18 and removed in 6.20.
You can replace them with either signed char
, unsigned char
, int8_t
, uint8_t
, or any other integral type.
For example:
// ArduinoJson 5
char age = doc["age"];
auto height = doc["height"].as<char>();
// ArduinoJson 6
int8_t age = doc["age"];
auto height = doc["height"].as<int8_t>();
as<char*>()
and is<char*>()
Similarly, as<char*>()
and is<char*>()
were removed in 6.20. Instead, you must replace char*
with const char*
.
For example:
// ArduinoJson 5
Serial.println(doc["msg"].as<char*>());
// ArduinoJson 6
Serial.println(doc["msg"].as<const char*>());