[Arduino] JSON library 4.0

Arduino JSON is an elegant and efficient JSON library for embedded systems. Today I’m releasing a new major revision with a lot of cool stuff.

A bit of history

When I originally wrote this library in early 2014, I only needed a JSON parser and I thought that sprintf would be enough to create JSON strings.

Then, with version 2.0, I renamed the library from “Arduino JSON parser” to just “Arduino JSON” after adding support for JSON encoding. It was much more convenient than sprintf and was also surprisingly smaller.

Finally, with version 3.0, I polished the API to make it even easier to use.

Through all these evolutions, the library grew way beyond my initial expectations and the original API was not designed for that. A redesigned was needed.

What was wrong?

The main problem was introduced in version 2.0. When I added the encoding feature, I didn’t want to change the decoding part of the library, so I wrote new classes in a different namespace.

There were two namespaces, ArduinoJson::Generator and ArduinoJson::Parser, making a clear distinction between the two sides of the library. Classes within the two namespaces had similar names so that you feel like you were using the same classes.

But that approach led to several issues.

First, it introduced a C++ namespace which is not something that Arduino users are familiar with.

Second, it was a pain when you wanted to use both namespaces in the same file.

And finally, it was impossible to reuse objects from one namespace to the other. For instance, if you wanted to parse a JSON string, alter the object and then encode it back, it was simply impossible.

What changed?

Version 4.0 is almost a complete reboot of the library. Here is a summary of what changed.

Only one namespace

The parser and the generator parts of the library have been merged together. No more duplicate classes, no more headache when you work with both.

New memory model

The new API contains an allocator (see StaticJsonBuffer in the examples below) for the JSON tokens, preventing problems we had in the past when people were referencing local variable outside of their scope (issues #10 and #17)

Replaced jsmn

I knew that the library would be a little bigger, so I needed to save as much bytes as I could. I replaced the jsmn tokenizer with one of my own, saving all the conversion between the different structures.

Removed Printable

To reduced memory consumption, I needed to remove the vtable of objects that didn’t really need it. In particular JsonArray and JsonObject are not Printable anymore.

This means you can’t write

Serial.print(jsonObject);

but instead, you must write

jsonObject.printTo(Serial);

Smaller for most usages

If you used only the parser or only the generator of the older version, then you’ll see that version 4.0 is a little bigger. But if you used both (which is very likely), the total size is actually a little smaller.

New folder structure

Since I realized that many people were using the library outside of the Arduino world, I changed the layout to match the traditional folder structure for C/C++ libraries:

include/
src/
test/
...

Starting with version 1.5.x, Arduino IDE allows libraries to have a more flexible folder structure. This was also backported to IDE 1.0.6 two months ago, perfect timing ;-)

CMake

I now use CMake instead of relying only on Visual Studio, so that it’s much easier for contributors working in other environments. The library compiles with no warning in Visual Studio, GCC and Clang.

New test framework

Older versions of the library used Microsoft C++ Test Framework. This was bad because you could only build tests with Visual Studio. I switched all the tests to Google Test. There are nearly 300 tests at the time writing this article.

Continuous integration

I now use Travis to build the library and run the tests each time a change is pushed to GitHub. You can see the “build passing” badge on the project page.

What stays the same?

While the API is different from the previous one, it should not be a shock. Most of it will look very familiar.

Arduino JSON version 4.0 still relies on the features that made the library so popular:

  1. Fixed memory allocation (no malloc)
  2. Easy and elegant API
  3. Small footprint
  4. Optional indented output

Examples

Enough with the talking! Show me some code!

Alright, alright… here is how you encode JSON:

Arduino JSON 3.0:

JsonArray<2> array;
array.add<6>(48.756080); // 6 is the number of decimals to print
array.add<6>(2.302038);  // if not specified, 2 digits are printed

JsonObject<3> root; 
root["sensor"] = "gps";
root["time"] = 1351824120;
root["data"] = array;

Serial.print(root); // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

Arduino JSON 4.0:

StaticJsonBuffer<200> jsonBuffer;

JsonObject& root = jsonBuffer.createObject();
root["sensor"] = "gps";
root["time"] = 1351824120;

JsonArray& data = root.createNestedArray("data");
data.add(48.756080, 6);  // 6 is the number of decimals to print
data.add(2.302038, 6);   // if not specified, 2 digits are printed

root.printTo(Serial); // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}

And now, here is how to decode a JSON string:

Arduino JSON 3.0:

JsonParser<16> parser;
char json [] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

JsonObject root = parser.parse(json);
if (!root.success())
{
    Serial.println("JsonParser.parse() failed");
    return;
}

char*  sensor    = root["sensor"];
long   time      = root["time"];
double latitude  = root["data"][0];
double longitude = root["data"][1];

Arduino JSON 4.0:

StaticJsonBuffer<200> jsonBuffer;
char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";

JsonObject& root = jsonBuffer.parseObject(json);
if (!root.success()) {
  Serial.println("parseObject() failed");
  return;
}

const char* sensor = root["sensor"];
long time = root["time"];
double latitude = root["data"][0];
double longitude = root["data"][1];

Conclusion

That’s it for version 4.0. I invite you to check out the documentation for more detail.

Oh… I almost forgot… here is the link to the GitHub project!

Happy coding!