Description

The function deserializeJson() parses a JSON input and puts the result in a JsonDocument.

Before reading the input, this function resets the document, so you don’t need to call JsonDocument::clear().

This function behaves differently depending on the type of input:

  • For a read-only input, it duplicates the strings in the input document. This duplication consumes additional space in the JsonDocument.
  • For a writeable input, it stores pointers to the strings in the input buffer. This is the zero-copy mode. In this mode, the JsonDocument can be smaller, but ArduinoJson needs to modify the input buffer to insert null-terminators and replace escaped characters. Because the JsonDocument stores pointers to the input buffer, you must ensure that this buffer remains in memory.

Prefer the first mode when the input comes from a Stream; prefer the second when the input is in a buffer.

If the input is a char*, ensure the input buffer stays in memory until the JsonDocument is destructed.

Signatures

// writable input => zero-copy
DeserializationError deserializeJson(JsonDocument& doc, char* input);
DeserializationError deserializeJson(JsonDocument& doc, char* input, size_t inputSize);

// read-only input => duplication
DeserializationError deserializeJson(JsonDocument& doc, const char* input);
DeserializationError deserializeJson(JsonDocument& doc, const char* input, size_t inputSize);
DeserializationError deserializeJson(JsonDocument& doc, const __FlashStringHelper* input);
DeserializationError deserializeJson(JsonDocument& doc, const __FlashStringHelper* input, size_t inputSize);
DeserializationError deserializeJson(JsonDocument& doc, const String& input);
DeserializationError deserializeJson(JsonDocument& doc, const std::string& input);
DeserializationError deserializeJson(JsonDocument& doc, Stream& input);
DeserializationError deserializeJson(JsonDocument& doc, std::istream& input);
DeserializationError deserializeJson(JsonDocument& doc, JsonVariantConst input);
DeserializationError deserializeJson(JsonDocument& doc, std::string_view input);

template<typename Reader> // custom reader class (see below)
DeserializationError deserializeJson(JsonDocument& doc, Reader& input);

// all overloads also accept optional parameters (see below)

Support for JsonVariantConst as input was added in ArduinoJson 6.15.2

Arguments

doc: the JsonDocument that will store the memory representation of the JSON document.

input: the JSON document to parse:

inputSize: the maximum number of bytes to read from input

This function supports two optional parameters:

  • a parameter of type DeserializationOption::NestingLimit to change the maximum number of nesting levels that the parser will accept (see “Nesting Limit” below);
  • a parameter of type DeserializationOption::Filter to filter the input document and keep only the fields you need (see “Filtering” below).

Return value

deserializeJson() returns a DeserializationError.

Nesting limit

The ArduinoJson’s parser contains a recursive function that is called each time an opening brace ({) or opening bracket ([) appears in the input. In other words, each object/array nesting level causes a recursive call.

This recursive call is a security risk because an attacker could craft a JSON input with many opening brackets to cause a stack overflow.

To protect against this security risk, ArduinoJson limits the number of nesting levels. The macro ARDUINOJSON_DEFAULT_NESTING_LIMIT sets the default value.

If your JSON input contains more nesting levels than allowed, you can pass an extra parameter of type DeserializationOption::NestingLimit to deserializeJson(). See the example below.

Filtering

When the input document contains many fields that are not relevant to your application, you can ask deserializeJson() to ignore them and save a lot of space in the JsonDocument.

To use this feature, create an ancillary JsonDocument that contains the value true as a placeholder for every field you want to keep in the final document. For arrays, only create one element in the filter document, it will serve as a filter for all elements of the original array. Wrap this document in a DeserializationOption::Filter before passing it to deserializeJson().

See JsonFilterExample.ino for an example.

When filtering, deserializeJson() needs some extra room in the JsonDocument to store object keys temporarily.
Make sure to increase the capacity a bit, or you may get a NoMemory error.

This feature was added in ArduinoJson 6.15.0

Configuration

deserializeJson() can be configured with the following settings:

How to view the JSON input?

When you pass a Stream to deserializeJson(), it consumes the input but doesn’t print anything to the serial, which makes troubleshooting difficult.

If you want to see what deserializeJson() consumed, use ReadLoggingStream from the StreamUtils library. See the example below.

Performance

When you pass a Stream to deserializeJson(), it consumes bytes one by one, which can be slow depending on the input you use. For example, if you read from a SPIFFS file, you can read twenty times faster by reading chunks of 64 bytes.

To read the stream in chunks, you can use ReadBufferingStream from the StreamUtils library. See the example below.

Custom reader

If none of the supported input types is suitable for you, you can implement a custom reader class. This class must implement two member functions, as shown below:

struct CustomReader {
  // Reads one byte, or returns -1
  int read();
  // Reads several bytes, returns the number of bytes read.
  size_t readBytes(char* buffer, size_t length);
};

Then, pass a reference to an instance of this class as the second argument of deserializeJson().

deserializeJson() doesn’t actually use readBytes(), so this function is not strictly necessary.
However, it is used by deserializeMsgPack(), and could also be used in future versions of deserializeJson().

Example

Read-only input

const char* json = "{\"hello\":\"world\"}";
StaticJsonDocument<200> doc;
deserializeJson(doc, json);
const char* world = doc["hello"];

Zero-copy mode

char json[] = "{\"hello\":\"world\"}";
StaticJsonDocument<200> doc;
deserializeJson(doc, json);
const char* world = doc["hello"];

Raise the nesting limit

char json[] = "[[[[[[[[[[[[[[[42]]]]]]]]]]]]]]]";
StaticJsonDocument<200> doc;
deserializeJson(doc, json, DeserializationOption::NestingLimit(15));;
int answer = doc[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0];

Filtering

StaticJsonDocument<200> filter;
filter["list"][0]["dt"] = true;
filter["list"][0]["main"]["temp"] = true;

deserializeJson(doc, input, DeserializationOption::Filter(filter));

Show the JSON input stream

This example requires the StreamUtils library.

Suppose your program is:

deserializeJson(doc, wifiClient);

If you want to see what deserializeJson() consumed, replace this line with:

ReadLoggingStream loggingStream(wifiClient, Serial);
deserializeJson(doc, loggingStream);

The first line creates a new Stream on top of wifiClient that writes everything it reads to Serial.

Improve read performance

This example requires the StreamUtils library.

Suppose your program is:

deserializeJson(doc, file);

If you want to make deserializeJson() read chunks instead of reading bytes one by one, replace this line with:

ReadBufferingStream bufferingStream(file, 64);
deserializeJson(doc, bufferingStream);

The first line creates a new Stream that reads blocks of 64 bytes from file.

Deserialize an object member

Suppose we have the following input:

{"config":"{\"user\":\"toto\"}"}

If you want to deserialize the JSON document in the "config" member, you can do this:

StaticJsonDocument<256> doc1, doc2;
deserializeJson(doc1, input);
deserializeJson(doc2, doc1["config"]);

After executing these lines, doc2 contains:

{"user":"toto"}

This code requires ArduinoJson 6.15.0

See also

Global warming stripes by Professor Ed Hawkins (University of Reading)