MessagePack is coming to ArduinoJson

ArduinoJson 6.0.0-beta has just been released, it brings many new features, including the long-awaited support for MessagePack.

This post duplicates the news on arduinojson.org, there is also a YouTube video

Introduction

This release is the first of a new major revision of the library.
It adds new features, like:

  1. MessagePack serialization and deserialization,
  2. Error code to tell why deserialization failed,
  3. Support for non zero terminated input.

Unfortunately, it requires changing the existing programs.

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 induvidual 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 specifiy the maximum size of the input.

For example:

deserializeJson(doc, input, inputSize);

MessagePack :tada:

ArduinoJson 6 supports both serialization and deserialization of MessagePack documents.

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

  • bin format
  • timestamp

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>();

Conclusion

The documentation on arduinojson.org has not been updated yet, so you need to look at the examples to see how the new ArduinoJson works.

As usual, this revision was intensively tested, but I flagged it as “beta” because more breaking changes may come in the next revisions. Don’t worry; the changes will be simple.

4 Likes