ArduinoJson has changed a lot since version 5, so the upgrade process is quite complicated. I recommend that you read the whole document before starting and then proceed step by step.

ArduinoJson 7 is significantly bigger ⚠️

ArduinoJson 5 had a strong focus on code size because 8-bit microcontrollers were largely dominant at the time. ArduinoJson 7 loosened the code size constraint to focus on ease of use. As a result, version 7 is significantly bigger than version 5.

If your program targets 8-bit microcontrollers, I recommend upgrading to version 6 instead, which size is comparable to version 5.

References

In ArduinoJson 5, JsonArray and JsonObject were always returned by reference to emphasize that they reside in the JsonBuffer.
In ArduinoJson 7, JsonArray, JsonObject, and JsonVariant are smart pointers, so they are returned by value.

- JsonObject& obj = ...
+ JsonObject obj = ...
- JsonArray& arr = ...
+ JsonArray arr = ...

Don’t be fooled by the fact that they are returned by value: they don’t contain a copy of the array/object but a pointer to the original one located in the JsonDocument (see next item).

JsonDocument

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

ArduinoJson 7 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. Also, there is no need to specify the capacity anymore.

- StaticJsonBuffer<200> jsonBuffer;
+ JsonDocument doc;
- DynamicJsonBuffer jsonBuffer(200);
+ JsonDocument doc;

Automatic conversion

In ArduinoJson 5, you had to call either JsonBuffer::createArray() or JsonBuffer::createObject() to create an empty array or object.
In ArduinoJson 7, you don’t need to choose because the JsonDocument automatically switches to the right type (array or object) according to the way you use it.

For example, in the following snippet, the JsonDocument implicitly converts to an object:

JsonDocument doc;
doc["hello"] = "world";
// doc contains {"hello":"world"}

Whereas in the next snippet, it converts to an array:

JsonDocument doc;
doc.add("hello");
doc.add("world");
// doc contains ["hello","world"]

This automatic conversion occurs when the JsonDocument is empty, but you can also force a conversion by calling to<T>():

JsonDocument doc;
JsonObject obj = doc.to<JsonObject>();
obj["hello"] = "world";

This can be useful when you need to create an empty object or array.

deserializeJson()

In ArduinoJson 5, you invoked the JSON parser by calling JsonBuffer::parseObject() or JsonBuffer::parseArray().
In ArduinoJson 7, you call the function deserializeJson() and pass the JsonDocument and input as arguments.

- JsonObject& obj = jb.parseObject(input);
+ deserializeJson(doc, input);

Each time you call deserializeJson(), it clears the JsonDocument, so you can reuse a JsonDocument several times, which was not possible with JsonBuffer.

DeserializationError

In ArduinoJson 5, you used JsonObject::success() or JsonArray::success() to check whether the parsing succeeded, and you had no information on what went wrong.
In ArduinoJson 7, 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().

- JsonObject& obj = jb.parseObject(input);
+ DeserializationError error = deserializeJson(doc, input);
- if (!obj.success()) {
+ if (error) {
-   Serial.println("parseObject() failed");
+   Serial.print("deserializeJson() returned ");
+   Serial.println(error.c_str());
    return;
  }

createNestedArray() and createNestedObject()

In ArduinoJson 5, createNestedArray() and createNestedObject() allowed adding a new array or object in an existing array or object.
In ArduinoJson 7, these functions are replaced with add<T>() and to<T>().

For example, to create an array of objects, you could write:

- JsonObject& phineas = root.createNestedObject();
+ JsonObject phineas = doc.add<JsonObject>();
  phineas["first"] = "Phineas";
  phineas["last"] = "Flynn";
- JsonObject& ferb = root.createNestedObject();
+ JsonObject ferb = doc.add<JsonObject>();
  ferb["first"] = "Ferb";
  ferb["last"] = "Fletcher";

And to create an array in an object, you could write:

- JsonArray& ports = doc.createNestedArray("ports");
+ JsonArray ports = doc["ports"].to<JsonArray>();
  ports.add(80);
  ports.add(443);

containsKey()

In ArduinoJson 5, containsKey() checked if an object contained a specific key.
In ArduinoJson 7, you must use operator[] followed by is<T>().

- if (obj.containsKey("key")) {
+ if (obj["key"].is<int>()) {
    int value = obj["key"];
    // ...
  }

This syntax not only checks that the key exists but also that the value is of the expected type. It was already available in ArduinoJson 5.

serializeJson() and serializeJsonPretty()

In ArduinoJson 5, when you wanted to serialize a JsonArray or a JsonObject, you called JsonArray::printTo() or JsonObject::printTo().
In ArduinoJson 7, you call the function serializeJson() and pass it the JsonDocument.

- obj.printTo(Serial);
+ 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().
With ArduinoJson 7, you call measureJson() to do that.

- size_t len = obj.measureLength();
+ size_t len = measureJson(doc);

Similarly, measureJsonPretty() replaces JsonArray::measurePrettyLength() and JsonObject::measureJsonPretty().

Nesting limit

In ArduinoJson 5, you could change the nesting limit by passing an optional argument to JsonBuffer::parseArray() or JsonBuffer::parseObject().
In ArduinoJson 7, you must pass this value to deserializeJson() and cast it to DeserializationOption::NestingLimit:

- JsonObject& obj = jb.parseObject(input, 20);
+ deserializeJson(doc, input, DeserializationOption::NestingLimit(20));

serialized()

In ArduinoJson 5, when you wanted to insert a preformatted piece of JSON, you called RawJson().
In ArduinoJson 7, you call serialized():

- obj["raw"] = RawJson("[1,2,3]");
+ doc["raw"] = serialized("[1,2,3]");

JsonPair

In ArduinoJson 5, when you enumerated the members of a JsonObject, you received a JsonPair with two member variables: key and value. The first was a const char* and the second a JsonVariant.

In ArduinoJson 7, JsonPair::key() and JsonPair::value() are member functions. Also, key doesn’t return a const char* but a JsonString, so you must call JsonString::c_str() to get the pointer.

  for (JsonPair p : obj) {
  for (JsonPair p : obj) {
-   const char* key = p.key;
+   const char* key = p.key().c_str();
-   JsonVariant value = p.value;
+   JsonVariant value = p.value();
    ...
  }

copyArray()

In ArduinoJson 5, you could easily copy values between a JsonArray and a C array using JsonArray::copyFrom() and JsonArray::copyTo().
In ArduinoJson 7, 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};

- arr.copyFrom(values);
+ copyArray(values, arr);

- arr.copyTo(values);
+ copyArray(arr, values);

JsonVariant is a reference

In ArduinoJson 5, JsonVariant had value semantic, and you could create an instance without a JsonBuffer.
In ArduinoJson 7, JsonVariant has reference semantics, like JsonArray and JsonObject, and you need a JsonDocument to create one.

- JsonVariant var = 42;
+ JsonDocument doc;
+ JsonVariant var = doc.to<JsonVariant>();
+ var.set(42);

But you could also set the value directly on the JsonDocument:

- JsonVariant var = 42;
+ JsonDocument doc;
+ doc.set(42);

isNull()

In ArduinoJson 5, you checked if an array or an object was valid by calling success().
In ArduinoJson 7, you use isNull() instead:

- if (!obj.success()) ...
+ 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 7.

Lastly, there is also a conversion to boolean, which returns false if the value of the JsonArray, JsonObject, or JsonVariant is null or false. So, in most cases, you can simply write:

- if (!obj.success()) ...
+ if (!obj) ...

Naked char

ArduinoJson 7 no longer supports the char type, so you must replace it with either signed char, unsigned char, int8_t, uint8_t, or any other integral type.

- char age = obj["age"];
+ int8_t age = doc["age"];
- auto height = obj["height"].as<char>();
+ auto height = doc["height"].as<int8_t>();

Non-const char*

ArduinoJson 7 doesn’t support non-const char*, so you must always use const char*.

- Serial.println(obj["msg"].as<char*>());
+ Serial.println(doc["msg"].as<const char*>());

Summary

Deserialization

// ArduinoJson 5
DynamicJsonBuffer jb;
JsonObject& obj = jb.parseObject(json);
if (!obj.success()) 
  return;
int value = obj["value"];
// ArduinoJson 7
JsonDocument doc;
DeserializationError error = deserializeJson(doc, json);
if (error)
  return;
int value = doc["value"];

Serialization

// ArduinoJson 5
DynamicJsonBuffer jb;
JsonObject& obj = jb.createObject();
obj["key"] = "value";
obj["raw"] = RawJson("[1,2,3]");
obj.printTo(Serial);
// ArduinoJson 7
JsonDocument doc;
doc["key"] = "value";
doc["raw"] = serialized("[1,2,3]");
serializeJson(doc, Serial);

See also

Global warming stripes by Professor Ed Hawkins (University of Reading)