bdljsn.txt

@PURPOSE: Provide a value-semantic JSON type and supporting utilities

@MNEMONIC: Basic Development Library JSoN (bdljsn)

@DESCRIPTION: The 'bdljsn' package provides the 'bdljsn::Json' type, found in
the 'bdljsn_json' component, which is a value-semantic representation of JSON
data.  This package also provides facilities for reading and writing
'bdljsn::Json' objects to JSON documents.  'bdljsn::Json' has close structural
similarities to the JSON grammar itself, which looks like the following, at a
high level:
..
  JSON ::= Object
         | Array
         | String
         | Number
         | Boolean
         | null
..
Noting that the 'Object' and 'Array' alternatives can recursively contain
'JSON'.  Just like this grammar, the value of a 'bdljsn::Json' object can be an
object, array, string, number, boolean, or the null value.  Objects and Arrays
are represented by the 'bdljsn::JsonObject' and 'bdljsn::JsonArray' types,
respectively, and are also provided by the 'bdljsn_json' component.
'bdljsn::JsonObject' is an associative container from strings to 'bdljsn::Json'
objects, and 'bdljsn::JsonArray' is a sequence container with 'bdljsn::Json'
elements.  Strings and booleans are represented with the standard 'bsl::string'
and 'bool' types.  The singular "null" value is represented by the
'bdljsn::JsonNull' type, which has a single value like 'std::monostate'.
Numbers are represented by the 'bdljsn::JsonNumber' type, which has facilities
for storing any number that satisfies the JSON number grammar with arbitrary
precision.  It also provides operations for converting these
arbitrary-precision numbers to common finite-precision number vocabulary types
like 'int', 'double', and 'bdldfp::Decimal64', and detecting where overflow,
underflow, and/or truncation would occur during conversion.

Though this package provides several types representing different kinds of JSON
values, in general the 'bdljsn::Json' interface is rich enough to be the
primary vocabulary type for working with JSON.  For example, if the value
stored in a 'bdljsn::Json' holds a JSON object, then you can use much of the
associative container interface on the 'bdljsn::Json' object directly, e.g.,
..
  void getName(bsl::string *name, const bdljsn::Json& json)
      // Load to the specified 'name' the "name" member of the specified
      // 'json'.  The behavior is undefined unless 'json' is a JSON object and
      // has a "name" member that is a string.
  {
      // First, verify that the 'json' is a JSON object, and not another
      // kind of value.
      assert(json.isObject());

      // Then, we can use the subscript operator with string keys, just like a
      // map.
      *name = json["name"].theString();
  }
..
Alternatively, if a 'bdljsn::Json' holds a JSON array, then we can use it like
a sequence container,
..
  bool findWaldo(const bdljsn::Json& json)
      // Return 'true' if 'json' is an array that contains the string "waldo",
      // and return 'false' otherwise.
  {
      if (!json.isArray()) {
          return false;                                               // RETURN
      }

      for (bsl::size_t i = 0; i != json.size(); ++i) {
          const bdljsn::Json& element = json[i];

          if (!element.isString()) {
              continue;                                             // CONTINUE
          }

          if (element.theString() == "waldo") {
              return true;                                            // RETURN
          }
      }

      return false;
  }
..
For an example of constructing 'bdljsn::Json' objects, consider the use case
where we have a simple representation of an organization chart, which we would
like to convert to JSON for printing to standard output:
..
  struct Employee {
      // PUBLIC DATA
      int         d_id;
      bsl::string d_firstName;
      bsl::string d_lastName;
  };

  struct Team {
      // PUBLIC DATA
      Employee              d_manager;
      bsl::vector<Employee> d_members;
  };
..
We can define utility functions for converting these types to JSON:
..
  struct Utility {
      // CLASS METHODS
      static void toJson(bdljsn::Json *json, const Employee& employee)
          // Load to the specified 'json' the value of the specified 'employee'
          // converted to a JSON value.
      {
          bdljsn::Json& result = *json;

          result.makeObject();

          result["id"]        = employee.d_id;
          result["firstName"] = employee.d_firstName;
          result["lastName"]  = employee.d_lastName;
      }

      static void toJson(bdljsn::Json *json, const Team& team)
          // Load to the specified 'json' the value of the specified 'team'
          // converted to a JSON value.
      {
          bdljsn::Json& result = *json;
          result.makeObject();

          bdljsn::Json manager;
          toJson(&manager, team.d_manager);
          result["manager"] = bsl::move(manager);

          bdljsn::Json members;
          members.makeArray();
          for (bsl::size_t i = 0; i != team.d_members.size(); ++i) {
              bdljsn::Json member;
              toJson(&member, team.d_members[i]);
              members[i] = bsl::move(member);
          }
          result["members"] = bsl::move(members);
      }
  };
..
And then we can create a sample 'Team' and print it to standard output using
our 'toJson' functions,
..
  void example()
  {
      Employee manager = { 1, "Michael", "Bloomberg" };
      Employee employee0 = { 2, "Peter", "Grauer" };
      Employee employee1 = { 3, "Tom", "Secunda" };

      Team team;
      team.d_manager = manager;
      team.d_members.push_back(employee0);
      team.d_members.push_back(employee1);

      bdljsn::Json teamAsJson;
      Utility::toJson(&teamAsJson, team);

      // The following set of options to 'write' specify that we would prefer
      // the output to be pretty-printed.
      bdljsn::WriteOptions options;
      options.setStyle(bdljsn::WriteStyle::e_PRETTY);

      int rc = bdljsn::JsonUtil::write(bsl::cout, teamAsJson, options);
      assert(0 == rc);
  }
..
Then, we can observe the following printed to standard output:
..
  {
      "manager": {
          "id": 1,
          "firstName": "Michael",
          "lastName": "Bloomberg"
      },
      "members": [
          {
              "id": 2,
              "firstName": "Peter",
              "lastName": "Grauer"
          },
          {
              "id": 3,
              "firstName": "Tom",
              "lastName": "Secunda"
          }
      ]
  }
..

/Hierarchical Synopsis
/---------------------
 The 'bdljsn' package currently has 14 components having 5 levels of physical
 dependency.  The list below shows the hierarchical ordering of the components.
 The order of components within each level is not architecturally significant,
 just alphabetical.
..
  5. bdljsn_jsonliterals

  4. bdljsn_jsonutil

  3. bdljsn_json

  2. bdljsn_error
     bdljsn_jsonnumber
     bdljsn_writeoptions

  1. bdljsn_jsonnull
     bdljsn_jsontype
     bdljsn_location
     bdljsn_numberutil
     bdljsn_readoptions
     bdljsn_stringutil
     bdljsn_tokenizer
     bdljsn_writestyle
..

/Component Synopsis
/------------------
: 'bdljsn_error':
:      Provide a description of an error processing a document.
:
: 'bdljsn_json':
:      Provide an in-memory representation of a JSON document.
:
: 'bdljsn_jsonliterals':
:      Provide user-defined literals for 'bdljsn::Json' objects.
:
: 'bdljsn_jsonnull':
:      Provide a type that represents the JSON 'null' value.
:
: 'bdljsn_jsonnumber':
:      Provide a value-semantic type representing a JSON number.
:
: 'bdljsn_jsontype':
:      Enumerate the set of JSON value types.
:
: 'bdljsn_jsonutil':
:      Provide common non-primitive operations on 'Json' objects.
:
: 'bdljsn_location':
:      Provide a value-semantic type for location in a JSON document.
:
: 'bdljsn_numberutil':
:      Provide utilities converting between JSON text and numeric types.
:
: 'bdljsn_readoptions':
:      Provide options for reading a JSON document.
:
: 'bdljsn_stringutil':
:      Provide a utility functions for JSON strings.
:
: 'bdljsn_tokenizer':
:      Provide a tokenizer for extracting JSON data from a 'streambuf'.
:
: 'bdljsn_writeoptions':
:      Provide options for writing a JSON document.
:
: 'bdljsn_writestyle':
:      Enumerate the formatting styles for a writing a JSON document.