I just published version 6.16.0 of the library. As we’ll see, this revision reduces memory consumption significantly with minimal impact on speed. Let’s review the changes together.

String deduplication

As you should already know, when you add a string to a JsonDocument, ArduinoJson stores it either by pointer or by copy. It uses a pointer when the string has the type const char*, and it duplicates the string in all other cases (char[], String, Flash string…). Similarly, when you call deserializeJson(), it copies the strings into the JsonDocument (except, of course, if you use the zero-copy mode).

Previously, ArduinoJson blindly duplicated a string even if it was already present in the JsonDocument. Now, with ArduinoJson 6.16, it stores only one copy of each string.

For example, imagine we want to deserialize the following input :

{
  "list": [
    {"temperature":21.2,"humidity":68.9,"weather":"overcast clouds"},
    {"temperature":19.7,"humidity":62.1,"weather":"clear sky"},
    {"temperature":18.6,"humidity":59.8,"weather":"clear sky"}
   ] 
}

Let’s write a program to see the memory consumption:

StaticJsonDocument<512> doc;
deserialize(doc, input);
Serial.println(doc.memoryUsage());

If you run this program on ArduinoJson 6.15 and 6.16, you’d get these results:

Processor v6.15.2 v6.16.0 Difference
8-bit 232 B 164 B -29%
32-bit 336 B 268 B -20%

To deduplicate strings, ArduinoJson has to scan the list of existing strings, which slightly slows down the process:

Processor v6.15.2 v6.16.0 Difference
AtMega328@16MHz 1924 µs 1980 µs +3%
ESP8266@80MHz 405 µs 413 µs +2%
ESP32@160MHz 238 µs 248 µs +4%

The scan duration increases with the number of unique strings in the JsonDocument, so your mileage may vary. If you want to see the result on your board, here is the program I used. You can disable this feature by setting ARDUINOJSON_ENABLE_STRING_DEDUPLICATION to 0.

Currently, the ArduinoJson Assistant doesn’t know about this feature. I plan to keep it that way until the majority of users had the opportunity to install the new version.

This awesome feature is largely due to the contribution of Ewald Comhaire. Thanks, Ewald!

Comparison operators

In previous versions, you could compare a JsonVariant with an integer, a double, or a string, but you could not compare a JsonVariant with another JsonVariant. This is possible with ArduinoJson 6.16, for example you can now write:

if (request["power"] <= config["maxPower"])

The result of the comparison depends on the type of both operands, so it requires a bit more work than just comparing two numbers. If you worry about speed or code size, prefer casting explicitly like so:

if ((double)request["power"] <= (double)config["maxPower"])

New default for ARDUINOJSON_DECODE_UNICODE

Introduced in ArduinoJson 6.9, ARDUINOJSON_DECODE_UNICODE allows you to enable the decoding of Unicode escape sequence in deserializeJson().

When set to 1, deserializeJson() converts the Unicode escape sequences to UTF-8 characters; for example, \u00EE becomes î.

When set to 0, deserializeJson() returns NotSupported if the input contains a Unicode escape sequence.

What changed in ArduinoJson 6.16 is the default value: it used to be 0, and now it’s 1. I managed to reduce the size of the parser (again), that’s why I thought it was acceptable to enable it by default. In the end, the size of the parser on AVR grew only by a hundred bytes and is still smaller than version 6.14.

Evolution of the size of ArduinoJson on AVR

Improved copyArray()

copyArray() is a utility function that allows you to copy values between a C array and a JsonArray, and it works in both ways. For example:

int[] values = [1,2,3];
copyArray(values, doc);
serializeJson(doc, Serial); // prints [1,2,3].

copyArray() already existed; what changed is that you can now use it with an ElementProxy (the class returned by doc[0]) and MemberProxy (the class returned by doc["key"]). This feature allows you to do stuffs like this:

int[] values = [1,2,3];
copyArray(values, doc["config"]["values"]);
serializeJson(doc, Serial);  // prints {"config":{"values":[1,2,3]}}

Reduced stack usage

Several users reported weird issues when building in debug mode on ESP8266 and ESP32. It took us some time to realize that this problem was due to an excessive stack usage in the recursive part of deserializeJson().

The problem only appeared with the optimization flag -Og. This flag produced a stack usage 5 times bigger than -Os.

After a long investigation, I discovered that the -Og behaves like this when you return a struct by value. Indeed, I tend to use this pattern a lot; for example, the implementation of deserializeJson() contains many functions that return a DeserializationError.

By refactoring the code, I managed to remove about a hundred return-struct-by-value, and the stack usage in debug mode is now back to normal. While working on that, I found several optimizations that allowed me to reduce the stack usage in release mode as well.

This fix wouldn’t have been possible without the help of Adam Iredale. Thanks, Adam!

Conclusion

As usual, I encourage you to download the new revision and to report any problem to the GitHub issue board.

Remember that you can support the development by purchasing my book Mastering ArduinoJson. Not only you’ll encourage the development of more releases like this one, but you’ll also learn how to get the best out of the library.

Have a beautiful summer!

Stay informed!

...or subscribe to the RSS feed