BDE 4.14.0 Production release
|
Provide common non-primitive operations on Json
objects.
Json
objectsThis component provides a namespace bdljsn::JsonUtil
containing utility functions that operate on Json
objects.
The following methods are provided by JsonUtil
:
read
populate a Json
object from a JSON text document.write
populate a JSON text document from a Json
object.There are a number of options to configure the output format produced by write
:
sortMembers
: sort the members of any Object elements in the output JSON (default: false
)style
: the style of the resulting outpute_COMPACT
(default): render with no added white spacee_ONELINE
: render a human readable single-line format (e.g., for logging)e_PRETTY
: render a multi-line human readable formatspacesPerLevel
: for the e_PRETTY
style, the number of spaces added for each additional nesting level (default 4)initialIndentationLevel
: for the e_PRETTY
style, the number of sets of spacesPerLevel
spaces added to every line of the output, including the first and last lines (default 0)The example below shows the various write styles:
For more information, see the bdljsn_writeoptions and bdljsn_writestyle components.
bdljsn::JsonObject
represents a JSON Object having unique keys. If an Object with duplicate keys is found in a JSON document, read
will preserve the value associated with the FIRST instance of that key.
Per the JSON RFC (https://www.rfc-editor.org/rfc/rfc8259#section-4):
That is, the expectation is that a JSON document should have unique keys, and JSON documents with duplicate keys are not an interoperable representation. JSON parsing implementations vary on how duplicate keys are handled (though many represent the object in-memory with unique keys). Note that preserving the value of the first key is consistent with the behavior of the existing baljsn::DatumUtil
component.
By default, bdljsn::JsonUtil::read
will report an error for input where a valid JSON document is followed by additional text unless the trailing text consists solely of white space characters. This behavior is configured by the bdljsn::ReadOptions
attribute, "allowTrailingText" (which defaults to false
).
If "allowTrailingText" is true
, then bdljsn::JsonUtil::read
will return success where a valid JSON document is followed by additional text as long as that text is separated from the valid JSON by a delimiter character (i.e., either the JSON text ends in a delimiter, or the text that follows starts with a delimiter). Here, delimiters are white-space characters, [
,]
,{
,}
,,
, or "</tt>. Per RFC 8259, white space characters are
Space (0x20), Horizontal tab (0x09), New Line (0x0A), and Carriage Return
(0x0D).
The table below shows some examples:
@code
* "ATT" = "allowTrailingText"
* Document is only valid where the result is SUCCESS
+-----------+------------------------+-------------+-----------+
| Input | ATT = false (default) | ATT = true | Document |
+===========+========================+=============+===========+
| '[]' | SUCCESS | SUCCESS | [] |
| '[] ' | SUCCESS | SUCCESS | [] |
| '[],' | ERROR | SUCCESS | [] |
| '[]a' | ERROR | SUCCESS | [] |
| 'false ' | SUCCESS | SUCCESS | false |
| 'false,' | ERROR | SUCCESS | false |
| 'falsea' | ERROR | ERROR | |
| '"a"x' | ERROR | SUCCESS | "a" |
+-----------+------------------------+-------------+-----------+
@endcode
@subsection bdljsn_jsonutil-usage Usage
This section illustrates the intended use of this component.
@subsubsection bdljsn_jsonutil-example-1-reading-and-writing-json-data Example 1: Reading and Writing JSON Data
This component provides methods for reading and writing JSON data to/from
<tt>Json</tt> objects.
First, we define the JSON data we plan to read:
@code
const char *INPUT_JSON = R"JSON({ "a boolean": true, "a date": "1970-01-01", "a number": 2.1, "an integer": 10, "array of values": [ -1, 0, 2.718281828459045, 3.1415926535979, "abc", true ], "event": { "date": "1969-07-16", "description": "Apollo 11 Moon Landing", "passengers": [ "Neil Armstrong", "Buzz Aldrin" ], "success": true } }})JSON";
@endcode
Next, we read the JSON data into a <tt>Json</tt> object:
@code
bdljsn::Json result;
bdljsn::Error error;
int rc = bdljsn::JsonUtil::read(&result, &error, INPUT_JSON);
assert(0 == rc);
if (0 != rc) {
bsl::cout << "Error message: "" << error.message() << """
<< bsl::endl;
}
@endcode
Then, we check the values of a few selected fields:
@code
assert(result.type() == JsonType::e_OBJECT);
assert(result["array of values"][2].theNumber().asDouble()
== 2.718281828459045);
assert(result["event"]["date"].theString() == "1969-07-16");
assert(result["event"]["passengers"][1].theString() == "Buzz Aldrin");
@endcode
Finally, we'll <tt>write</tt> the <tt>result</tt> back into another string and make sure
we got the same value back, by using the correct <tt>WriteOptions</tt> to match
the input format:
@code
bsl::string resultString;
// Set the WriteOptions to match the initial style:
WriteOptions writeOptions;
writeOptions.setStyle(bdljsn::WriteStyle::e_PRETTY);
writeOptions.setInitialIndentLevel(0);
writeOptions.setSpacesPerLevel(2);
writeOptions.setSortMembers(true);
bdljsn::JsonUtil::write(&resultString, result, writeOptions);
assert(resultString == INPUT_JSON);
@endcode
@subsubsection bdljsn_jsonutil-example-2-the-effect-of-options-on-write Example 2: The Effect of options on write
By populating a <tt>WriteOptions</tt> object and passing it to <tt>write</tt>, the format
of the resulting JSON can be controlled.
First, let's populate a <tt>Json</tt> object named <tt>json</tt> from an input string
using <tt>read</tt>, and create an empty <tt>options</tt> (see <tt>bdljsn::WriteOptions</tt>):
@code
const bsl::string JSON = R"JSON( { "a" : 1, "b" : [] } )JSON";
bdljsn::Json json;
bdljsn::WriteOptions options;
int rc = bdljsn::JsonUtil::read(&json, JSON);
assert(0 == rc);
@endcode
There are 4 options, which can be broken down into 2 unrelated sets.
The first set consists of the <tt>sortMembers</tt> option, which controls whether
members of objects are printed in lexicographical order.
The second set consists of the <tt>style</tt>, <tt>initialIndentLevel</tt>, and
<tt>spacesPerLevel</tt> options - <tt>style</tt> controls the format used to render a
<tt>Json</tt>, and, if <tt>bdljsn::WriteStyle::e_PRETTY == options.style()</tt>, the
<tt>spacesPerLevel</tt> and <tt>initialIndentLevel</tt> options are used to control the
indentation of the output. For any other value of <tt>options.style()</tt>, the
<tt>spacesPerLevel</tt> and <tt>initialIndentLevel</tt> options have no effect.
@paragraph bdljsn_jsonutil-sortmembers sortMembers
If <tt>sortMembers</tt> is true, then the members of an object output by <tt>write</tt>
will be in sorted order. Otherwise, the elements are written in an
(implementation defined) order (that may change).
The <tt>sortMembers</tt> option defaults to <tt>false</tt> for performance reasons, but
applications that rely on stable output text should set <tt>sortMembers</tt> to
<tt>true</tt> (e.g., in a test where the resulting JSON text is compared for
equality) .
Here, we set <tt>sortMembers</tt> to <tt>true</tt>, and verify the resulting JSON text
matches the expected text:
@code
options.setSortMembers(true);
bsl::string output;
rc = bdljsn::JsonUtil::write(&output, json, options);
assert(0 == rc);
assert(R"JSON({"a":1,"b":[]})JSON" == output);
@endcode
Had we not specified <tt>setSortMembers(true)</tt>, the order of the "a" and "b"
members in the <tt>output</tt> string would be unpredictable.
@paragraph bdljsn_jsonutil-style-and-style-related-options style And style-related options
There are 3 options for <tt>style</tt> (see <tt>bdljsn::WriteStyle</tt>):
* bdljsn::WriteStyle::e_COMPACT
* bdljsn::WriteStyle::e_ONELINE
* bdljsn::WriteStyle::e_PRETTY
Next, we write <tt>json</tt> using the style <tt>e_COMPACT</tt> (the default), a single
line presentation with no added spaces after <tt>:</tt> and <tt>,</tt> elements.
@code
rc = bdljsn::JsonUtil::write(&output, json, options);
assert(0 == rc);
// Using 'e_COMPACT' style:
assert(R"JSON({"a":1,"b":[]})JSON" == output);
@endcode
Next, we write <tt>json</tt> using the <tt>e_ONELINE</tt> style, another single line
format, which adds single characters after <tt>:</tt> and <tt>,</tt> elements for
readability.
@code
options.setStyle(bdljsn::WriteStyle::e_ONELINE);
rc = bdljsn::JsonUtil::write(&output, json, options);
assert(0 == rc);
// Using 'e_ONELINE' style:
assert(R"JSON({"a": 1, "b": []})JSON" == output);
@endcode
Next, we write <tt>json</tt> using the <tt>e_PRETTY</tt> style, a multiline format where
newlines are introduced after each (non-terminal) <tt>{</tt>, <tt>[</tt>, <tt>,</tt>, <tt>]</tt>, and
<tt>}</tt> character. Furthermore, the indentation of JSON rendered in the
<tt>e_PRETTY</tt> style is controlled by the other 2 attributes, <tt>spacesPerLevel</tt>
and <tt>initialIndentLevel</tt>.
<tt>e_PRETTY</tt> styling does not add a newline to the end of the output.
<tt>spacesPerLevel</tt> controls the number of spaces added for each successive
indentation level - e.g., if <tt>spacesPerLevel</tt> is 2, then each nesting level
of the rendered JSON is indented 2 spaces.
<tt>initialIndentLevel</tt> controls how much the entire JSON output is indented.
It defaults to 0 - if it's a positive value, then the entire JSON is
indented by <tt>initialIndentLevel * spacesPerLevel</tt> spaces.
@code
options.setStyle(bdljsn::WriteStyle::e_PRETTY);
options.setSpacesPerLevel(4); // the default
options.setInitialIndentLevel(0); // the default
rc = bdljsn::JsonUtil::write(&output, json, options);
assert(0 == rc);
// Using 'e_PRETTY' style:
assert(R"JSON({ "a": 1, "b": [] })JSON" == output);
@endcode
Finally, if we set <tt>initialIndentLevel</tt> to 1, then an extra set of 4 spaces
is prepended to each line, where 4 is the value of <tt>spacesPerLevel</tt>:
@code
options.setInitialIndentLevel(1);
rc = bdljsn::JsonUtil::write(&output, json, options);
assert(0 == rc);
// Using 'e_PRETTY' style (with 'initialIndentLevel' as 1):
assert(R"JSON({ "a": 1, "b": [] })JSON" == output);