How to reuse a JsonDocument
?
JsonDocument
s are designed to be throw-away objects.
They are meant to be used during short periods when your program performs the serialization.
The best way to use ArduinoJson
To illustrate the idea of a throw-away object, let’s see the canonical way to use ArduinoJson:
struct SensorData {
char name[32];
int time;
float value;
};
void writeSensorData(const SensorData& data, Stream& output)
{
const size_t capacity = JSON_OBJECT_SIZE(3);
StaticJsonDocument<capacity> doc;
doc["name"] = data.name;
doc["time"] = data.time;
doc["value"] = data.value;
serializeJson(doc, output);
}
void readSensorData(SensorData& data, Stream& input)
{
const size_t capacity = JSON_OBJECT_SIZE(3) + 32;
StaticJsonDocument<capacity> doc;
deserializeJson(doc, input);
strlcpy(data.name, doc["name"] | "N/A", sizeof(data.name));
data.time = doc["time"];
data.value = doc["value"];
}
As you can see, this program uses JsonDocument
only in the serialization functions, and instances only live for a very short period of time.
Also, notice that I used the custom data structure SensorData
to persist the information in the rest of the program.
This approach has the following benefits:
- The memory overhead due to
JsonDocument
is temporary. - The coupling with ArduinoJson is limited to two functions.
- The
JsonDocument
doesn’t leak (see below).
Why is it wrong to reuse a JsonDocument
?
JsonDocument
contains a monotonic allocator: a fast and lightweight allocator that cannot release the memory.
Because it cannot free memory, each time you remove or replace a value in a JsonDocument
, the old value remains in memory.
To release memory, you must either clear or destroy the entire JsonDocument
.
ArduinoJson uses this kind of allocator because it provides the best performance with the smallest code.
Most users don’t notice the problem, but you can run into it if you reuse the same JsonDocument
without destroying or clearing it.
Remember that ArduinoJson is a serialization library. As such, it is designed to serialize and deserialize JSON documents, not to store the state of an application.
How to reuse a JsonDocument
?
The best way to use a JsonDocument
is not to reuse it: destroy it and create a new one.
Don’t worry; it won’t affect the performance: destroying and creating a StaticJsonDocument
requires only a bunch of instructions, and with DynamicJsonDocument
, it only requires a call to free()
and malloc()
.
clear()
The second best way to reuse a JsonDocument
is to clear it between each use.
You can explicitly clear a JsonDocument
by calling JsonDocument::clear()
, but this function is implicitly called by:
deserializeJson()
deserializeMsgPack()
JsonDocument::to<T>()
JsonDocument::set()
JsonDocument::operator=
Also, you must understand that every reference acquired before clearing the JsonDocument
is invalidated.
In other words, you must not use a JsonArray
, JsonObject
, or JsonVariant
created before calling JsonDocument::clear()
.
Here is an example:
// BAD EXAMPLE: don't do that!!!
// Acquire a reference to an object inside the JsonDocument
JsonObject config = doc["config"];
// Reuse the document
doc.clear();
doc["user"] = "benoit";
// Use the old reference 💀
int port = config["port"];
Because the config
reference was created before clearing the document, it is now dangling, so the last line returns an incorrect result.
garbageCollect()
JsonDocument::clear()
discards the content of the document.
If this is a problem for you, you can call JsonDocument::garbageCollect()
, which reclaims the lost memory blocks.
Internally, this function duplicates the JsonDocument
to create a clean copy.
As a result, this operation is slow and consumes a lot of memory.
As with clear, every previously acquired reference (JsonArray
, JsonObject
…) is now dangling.
See also
- I found a memory leak in the library!
- JsonConfigFile.ino illustrates the best way to use the library
- The case study “Configuration on SPIFFS” in Mastering ArduinoJson applies this technique to a complex configuration structure.
- Why must I create a separate config object? Why can’t I use JsonDocument directly?