BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdljsn_jsonutil

Detailed Description

Outline

Purpose

Provide common non-primitive operations on Json objects.

Classes

Description

This component provides a namespace bdljsn::JsonUtil containing utility functions that operate on Json objects.

The following methods are provided by JsonUtil:

Configuring the Output Format

There are a number of options to configure the output format produced by write:

The example below shows the various write styles:

Compact:
{"a":1,"b":[]}
One-line:
{"a": 1, "b": []}
Pretty:
{
"a": 1,
"b": []
}

For more information, see the bdljsn_writeoptions and bdljsn_writestyle components.

Handling of Duplicate Keys

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):

"The names within an object SHOULD be unique."

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.

Allowing Trailing Text

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);