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.