JSON

JSONWriter

The JSONWriter object creates JSON objects and arrays. While you can create JSON objects using things like sprintf the JSONWriter has a number of advantages:

Measure sprintf JSONWriter JsonParserGeneratorRK
Code Size Small Fairly Small Medium
Easy to Use Not really Yes Yes
Escapes Strings No Yes Yes
Converts UTF-8 No No Yes

Using sprintf is tried-and-true, but the escaping of double quotes can get messy:

int a = 123;
bool b = true;
const char *c = "testing";

snprintf(buf, sizeof(buf), "{\"a\":%d,\"b\":%s,\"c\":\"%s\"}", 
    a, 
    b ? "true" : "false",
    c);

That generates the string in buf: {"a":123,"b":true,"c":"testing"}.


Using JSONWriter the code is much easier to read:

JSONBufferWriter writer(buf, sizeof(buf));
writer.beginObject();
    writer.name("a").value(a);
    writer.name("b").value(b);
    writer.name("c").value(c);
writer.endObject();

The real place where this becomes important is for strings that contain special characters including:

  • Double quote
  • Backslash
  • Control characters (tab, CR, LF, and others)
  • Unicode characters

If the testing string above contained a double quote the sprintf version would generate invalid JSON, but the JSONWriter version correctly escapes a double quote in a string.

Note: JSONWriter does not handle Unicode characters. If you need to handle characters other than 7-bit ASCII, you should use JsonParserGeneratorRK which handles UTF-8 to JSON Unicode (hex escaped UTF-16) conversion.

You will not create a JSONWriter directly, as it's an abstract base class. Instead use JSONBufferWriter or JSONStreamWriter, or your own custom subclass.

The sprintf-style code above created a 12,212 byte binary. The JSONWriter created a 12,452 byte binary, a difference of 240 bytes. However, as the data you are trying to encode becomes more complicated, the difference will likely become smaller.

JSONWriter::beginArray()

Creates an array. This can be a top-level array, or an array as a value with an object at a specific key.

// PROTOTYPE
JSONWriter& beginArray();

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginArray();
writer.value(1);
writer.value(2);
writer.value(3);       
writer.endArray();

// RESULT
[1,2,3]

You can chain JSONWriter methods, fluent-style, if you prefer that style:

writer.beginArray()
  .value(1)
  .value(2)
  .value(3)       
  .endArray();

Example of using an array within a key/value pair of an object:

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginObject();
    writer.name("a").value(123);
    writer.name("b").beginArray();
        writer.value(1);
        writer.value(2);
        writer.value(3);       
    writer.endArray();
writer.endObject();

// RESULT
{"a":123,"b":[1,2,3]}

JSONWriter::endArray()

// PROTOTYPE
JSONWriter& endArray();

Closes an array. You must always balance beginArray() with an endArray().

JSONWriter::beginObject()

// PROTOTYPE
JSONWriter& beginObject();

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginObject();
writer.name("a").value(123);
writer.endObject();

// RESULT
{"a":123}

Begins a new object. The outermost object is not created automatically, so you almost always will start by using beginObject(), which must always be balanced with an endObject().


JSONWriter::endObject()

// PROTOTYPE
JSONWriter& endObject();

Closes an object. You must always balance beginObject() with endObject().


JSONWriter::name(const char *name)

// PROTOTYPE
JSONWriter& name(const char *name);

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

bool v1 = false;
bool v2 = true;

writer.beginObject();
writer.name("v1").value(v1);
writer.name("v2").value(v2);
writer.endObject();

// RESULT
{"v":false,"v2":true}

When adding key/value pairs to an object, you call name() to add the key, then call value() to add the value.

This overload takes a const char *, a c-string, null terminated, that is not modified.

The name is escaped so it can contain double quote, backslash, and other special JSON characters, but it must be 7-bit ASCII (not Unicode).


JSONWriter::name(const char *name, size_t size)

// PROTOTYPE
JSONWriter& name(const char *name, size_t size);

When adding key/value pairs to an object, you call name() to add the key, then call value() to add the value.

This overload takes a pointer to a string and a length, allowing it to be used with unterminated strings.

The name is escaped so it can contain double quote, backslash, and other special JSON characters, but it must be 7-bit ASCII (not Unicode).


JSONWriter::name(const String &name)

// PROTOTYPE
JSONWriter& name(const String &name);

Sets the name of a key/value pair from a String object.


JsonWriter::value(bool val)

// PROTOTYPE
JSONWriter& value(bool val);

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

bool v1 = false;
bool v2 = true;

writer.beginObject();
writer.name("v1").value(v1);
writer.name("v2").value(v2);
writer.endObject();

// RESULT
{"v":false,"v2":true}

Adds a boolean value to an object or array.

When adding to an object, you call beginObject() then pairs of name() and value() for each key/value pair, followed by endObject().

When adding to an array, you call beginArray() then call value() for each value, followed by endArray(). You can mix different types of values within an array (int, string, double, bool, etc.).


JsonWriter::value(int val)

// PROTOTYPE
JSONWriter& value(int val);

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginObject();
writer.name("a").value(123);
writer.endObject();

// RESULT
{"a":123}

Adds a signed 32-bit integer value to an object or array. Since both int and long are 32-bits you can cast a long to an int and use this method.


JsonWriter::value(unsigned val)

// PROTOTYPE
JSONWriter& value(unsigned val);

Adds an unsigned 32-bit integer value to an object or array.

JsonWriter::value(double val, int precision)

Since 1.5.0:

// PROTOTYPE
JSONWriter& value(double val, int precision);

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginObject();
writer.name("d").value(-5.3333333, 3);
writer.endObject();

// RESULT
{"d":-5.333}

Adds a double or float value with a specific number of decimal points. Internally, this uses sprintf with the %.*lf formatting specifier. The precision option was added in Device OS 1.5.0.


JsonWriter::value(double val)

// PROTOTYPE
JSONWriter& value(double val);

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginObject();
writer.name("d").value(-5.3333333);
writer.endObject();

// RESULT
{"d":-5.33333}

Adds a double or float value. Internally, this uses sprintf with the %g formatting specifier. The %g specifier uses the shorter of %e (Scientific notation, mantissa and exponent, using e character) and %f, decimal floating point.

The default precision is 5 decimal places.


JsonWriter::value(const char *val)

// PROTOTYPE
JSONWriter& value(const char *val);

This overload add a const char *, a c-string, null terminated, that is not modified.

The value is escaped so it can contain double quote, backslash, and other special JSON characters, but it must be 7-bit ASCII (not Unicode).

JsonWriter::value(const char *val, size_t size)

// PROTOTYPE
JSONWriter& value(const char *val, size_t size);

This overload takes a pointer to a string and a length, allowing it to be used with unterminated strings.

The value is escaped so it can contain double quote, backslash, and other special JSON characters, but it must be 7-bit ASCII (not Unicode).

JsonWriter::value(const String &val)

// PROTOTYPE
JSONWriter& value(const String &val);

This overload takes a reference to a String object.

The value is escaped so it can contain double quote, backslash, and other special JSON characters, but it must be 7-bit ASCII (not Unicode).

JsonWriter::nullValue()

// PROTOTYPE
JSONWriter& nullValue();

// EXAMPLE
memset(buf, 0, sizeof(buf));
JSONBufferWriter writer(buf, sizeof(buf) - 1);

writer.beginObject();
writer.name("n").nullValue();
writer.endObject();

// RESULT
{"n":null}

Adds a null value to an object. This is a special JSON value, and is not the same as 0 or false.