The first parsing succeeds, why do the next ones fail?
You wrote a program that works fine at the beginning but then fails after some time? Here is why.
Cause 1: Reuse of the JsonBuffer
First, this can happen if you reuse the same JsonBuffer
, for example:
Bad: declare the JsonBuffer outside of the loop
StaticJsonBuffer<200> jsonBuffer; for (int i=0; i<10; i++) { char json[256]; readJsonFromSomewhere(json, sizeof(json)); JsonObject& root = jsonBuffer.parse(json); if (root.success()) { Serial.println("parseObject() succeeded"); } else { Serial.println("parseObject() failed!"); } }
The solution is simply to NOT reuse the JsonBuffer
, like this:
Good: the JsonBuffer is declared inside the loop
for (int i=0; i<10; i++) { char json[256]; readJsonFromSomewhere(json, sizeof(json)); StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.parse(json); if (root.success()) { Serial.println("parseObject() succeeded"); } else { Serial.println("parseObject() failed!"); } }
Note that, contrary to common belief, moving a StaticJsonBuffer
inside of a loop has no negative impact on performance.
See also:
Cause 2: Reuse of JSON input
This second case only concerns the “zero-copy” mode.
This mode is selected when the input is writable (char[]
or a char*
).
In this mode, ArduinoJson modifies the input string in place: it inserts null terminators and unescapes special characters.
If you call parseObject()
twice with the same input buffer, the first will work, but the second will fail because the input buffer doesn’t contain a valid JSON document anymore.
Here is a program that has this problem:
Bad: the same input is parsed several times
char json[256]; readJsonFromSomewhere(json, sizeof(json)); for (int i=0; i<10; i++) { StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.parse(json); if (root.success()) { Serial.println("parseObject() succeeded"); } else { Serial.println("parseObject() failed!"); } }
The solution is simply to parse the input only once, or get a fresh input at each iteration:
Good: a fresh input is used for each iteration
for (int i=0; i<10; i++) { char json[256]; readJsonFromSomewhere(json, sizeof(json)); StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.parse(json); if (root.success()) { Serial.println("parseObject() succeeded"); } else { Serial.println("parseObject() failed!"); } }
If you don’t want ArduinoJson to modify the input buffer, e.e. if you want to disable the zero-copy mode, you need to pass the input as a read-only type, like const char*
:
Good too: let the parser duplicate input
char json[256]; readJsonFromSomewhere(json, sizeof(json)); for (int i=0; i<10; i++) { StaticJsonBuffer<400> jsonBuffer; JsonObject& root = jsonBuffer.parse((const char*)json); if (root.success()) { Serial.println("parseObject() succeeded"); } else { Serial.println("parseObject() failed!"); } }
In this case, you also need to increase the capacity of the JsonBuffer
because it will have to hold a partial copy of the input. As usual, use the Assistant to compute the optimal capacity.
See also
Cause 3: Memory Leak
If there is a memory leak in the program, the parsing will fail sooner or later.
The memory leak can come from anyplace in the program, not necessarily ArduinoJson. Your program may be using ArduinoJson correctly, if memory leaks from somewhere else, the parsing will fail.
Cause 4: heap fragmentation
In rare cases, the fragmentation of the heap can have the same effect as a memory leak.
Indeed, when the heap is highly fragmented, it’s not possible to allocate any significant chunk of memory. Even worse, for devices that don’t limit the size of the stack, it’s possible that the heap and the stack overlap (see chapter 2: The missing C++ course of Mastering ArduinoJson).
This problem is likely to occur on devices with very limited RAM, like the Arduino UNO
.
If you use such device, you should avoid using the heap entirely. That means: no more String
and no more DynamicJsonBuffer
, instead use only char*
and StaticJsonBuffer
.
Bad: only heap memory
String json = "{\"hello\":\"world\"}"; DynamicJsonBuffer jsonBuffer; jsonBuffer.parseObject(json);
Good: only stack memory
char[] json = "{\"hello\":\"world\"}"; StaticJsonBuffer<200> jsonBuffer; jsonBuffer.parseObject(json);