Description

JsonDocument stores a JSON document in memory. It owns the memory referenced by JsonArray, JsonObject, and JsonVariant.

JsonDocument contains a fixed-size memory pool, with a monotonic allocator. This design allows ArduinoJson to be very efficient but requires some discipline on your side:

  1. Because the size is fixed, you need to specify the size when you create the JsonDocument
  2. Because the allocator is monotonic, it cannot release memory when you call JsonObject::remove() for example.

I strongly recommend against using a global JsonDocument because you would be troubled by the monotonic allocator, and would make inefficient use of your RAM.

On the contrary, I recommend declaring a short-lived JsonDocument that you use only in your serialization functions.

StaticJsonDocument vs DynamicJsonDocument

You can choose to store your JsonDocument in the stack or in the heap:

  • Use a StaticJsonDocument to store in the stack (recommended for documents smaller than 1KB)
  • Use a DynamicJsonDocument to store in the heap (recommended for documents larger than 1KB)

You must specify the capacity of a StaticJsonDocument in a template parameter, like that:

StaticJsonDocument<256> doc;

For a DynamicJsonDocument, however, you must use a constructor argument:

DynamicJsonDocument doc(2048);

JsonDocument vs JsonVariant

JsonDocument shares many features with JsonVariant; however, there is one big difference: JsonDocument has value semantics, whereas JsonVariant has reference semantics.

On the one hand, because JsonDocument owns the data, if you copy a JsonDocument, you get a complete clone.

// make a clone of the JsonDocument
DynamicJsonDocument doc2 = doc1;

On the other hand, because JsonVariant is a reference, if you copy a JsonVariant, you only clone the reference:

// make a new reference to the same variant
JsonVariant var2 = var1;

Using a JsonDocument

When you create a JsonDocument, it is initially empty. At this stage, it’s neither an object, nor an array, and JsonDocument::isNull() return true.

When you insert the first value in the JsonDocument, it automatically changes its type to match the call. If you use the JsonDocument like an array, it becomes an array; if you use the JsonDocument as an object, it becomes an object.

Here is a JsonDocument that implicitly becomes an object:

DynamicJsonDocument doc(1024);
doc["answer"] = 42;
// the doc contains {"answer":42}

Here is a JsonDocument that implicitly becomes an array:

DynamicJsonDocument doc(1024);
doc.add(42);
// the doc contains [42]

Sometimes, however, you’ll need to explicitly convert the JsonDocument without adding a value; for example, because you want to create an empty object. In this case you can call JsonDocument::to<T>():

DynamicJsonDocument doc(1024);
JsonObject obj = doc.to<JsonObject>();

JsonDocument::to<T>() clears the document and converts it to the specified type. Don’t confuse this function with JsonDocument::as<T>() that returns a reference only if the requested type matches the one in the document.

History

In older versions, DynamicJsonDocument was able to grow if needed. Starting with version 6.7.0, DynamicJsonDocument has a fixed capacity, just like StaticJsonDocument. This change allows better performance, smaller code, and no heap fragmentation.

Arduino 6.6.0 contained a full-blown allocator (i.e., non-monotonic) and was able to compact the memory inside the JsonDocument. This feature was reverted in version 6.7.0 because the overhead was unacceptable.

Member functions

Example

Here is a program that deserializes a JSON document and stores it in the stack:

StaticJsonDocument<200> doc; // <- a little more than 200 bytes in the stack

char json[] = "{\"hello\":\"world\"}";
deserializeJson(doc, json);

const char* world = doc["hello"];

Here is a program that serializes a JSON document stored in the heap:

DynamicJsonDocument doc(200); // <- 200 bytes in the heap

doc["hello"] = "world";

serializeJson(doc, Serial); // {"hello":"world"}

See also