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

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
JsonDocument 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() returns 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:

JsonDocument doc;
doc["answer"] = 42;
// the doc contains {"answer":42}

Here is a JsonDocument that implicitly becomes an array:

JsonDocument doc;
// 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>():

JsonDocument doc;
JsonObject obj =<JsonObject>();
// the doc contains {}

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.


By default, JsonDocument allocates memory through the standard malloc() and free() functions. You can customize this behavior by providing a custom allocator to the constructor.

You create an allocator class by inheriting from ArduinoJson::Allocator and implementing the pure virtual functions:

namespace ArduinoJson {
class Allocator {
  virtual void* allocate(size_t size) = 0;
  virtual void deallocate(void* pointer) = 0;
  virtual void* reallocate(void* pointer, size_t new_size) = 0;

For example, here is an allocator that uses ESP32’s capabilities-based heap memory allocator:

struct SpiRamAllocator : ArduinoJson::Allocator {
  void* allocate(size_t size) override {
    return heap_caps_malloc(size, MALLOC_CAP_SPIRAM);

  void deallocate(void* pointer) override {

  void* reallocate(void* ptr, size_t new_size) override {
    return heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM);

Then you must pass an instance of this class to the constructor of JsonDocument:

SpiRamAllocator allocator;
JsonDocument doc(&allocator);

Member functions

  • as<T>() casts the root to the specified type (e.g. JsonArray or JsonObject)
  • add() adds elements to the root array
  • clear() empties the document and resets the memory pool
  • containsKey() tests if the root object contains the specified key
  • operator[] gets or sets values in the document
  • overflowed() tells if the memory pool was large enough 🆕
  • is<T>() tests the type of the root
  • isNull() tells if the document is null or empty
  • nesting() returns the number of nesting layers in the document
  • remove() removes an element (or member) at the specified index (or key)
  • set() replaces the root with the specified value
  • shrinkToFit() release overallocated memory.
  • size() returns the number of elements (or members) that the root array (or object) contains
  • to<T>() clears the document and converts it to the specified type (e.g. JsonArray or JsonObject)


JsonDocument doc;

doc["hello"] = "world";

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

See also