Component of the Week #29: bdljsn_json

Summary:
  • A value-semantic type providing an in-memory representation of a JSON document, maintaining the invariant that all objects represent valid JSON.

The bdljsn::Json component provides a complete in-memory representation of a JSON document. The design closely mirrors the JSON grammar itself, supporting all JSON types: objects, arrays, strings, numbers, booleans, and null values. The key design principle is that every bdljsn::Json object represents a valid JSON document - there is no “invalid” state.

bdljsn::Json is a very rich data structure with a lot of functionality. It would not be possible to cover everything in a short article. For more detail, please see the article Introducing bdljsn::Json, or see the component documentation.

In summary, the component provides three main classes:

  • bdljsn::Json - The main variant type representing any JSON value

  • bdljsn::JsonObject - Represents JSON objects (key-value maps)

  • bdljsn::JsonArray - Represents JSON arrays (ordered sequences)

Additionally, bdljsn::JsonNumber encapsulates JSON numbers, preserving their exact textual representation for precision. These numbers can then be converted to various numeric types as needed.

Basic JSON Document Creation

Creating JSON documents is straightforward:

#include <bdljsn_json.h>
#include <bdldfp_decimal.h>
#include <bsl_iostream.h>
#include <assert.h>

using namespace BloombergLP;
using namespace bdldfp::DecimalLiterals;

int main()
{
    bdljsn::Json json;

    // Create a JSON object: {"name": "Alice", "age": 30, "active": true}
    json.makeObject();
    json["name"] = "Alice";
    json["age"] = 30;
    json["active"] = true;

    // Access values with type checking
    assert(json["name"].isString());
    assert(json["name"].theString() == "Alice");
    assert(json["age"].theNumber() == bdljsn::JsonNumber(30));
    assert(json["active"].theBoolean() == true);

    // Print the JSON structure
    bsl::cout << json << bsl::endl;

    return 0;
}

Reading and Writing JSON Text with bdljsn_jsonutil

While bdljsn::Json provides the in-memory representation, bdljsn_jsonutil handles parsing JSON text and writing JSON objects back to text format:

#include <bdljsn_json.h>
#include <bdljsn_jsonutil.h>
#include <bsl_iostream.h>
#include <bsl_sstream.h>
#include <bsl_string.h>
#include <assert.h>

using namespace BloombergLP;

int main()
{
    // JSON text to parse
    const char *jsonText = R"({
        "user": {
            "name": "Bob",
            "age": 25,
            "preferences": ["coffee", "books", "coding"]
        },
        "timestamp": "2025-09-01T10:30:00Z",
        "active": true
    })";

    // Parse JSON text into bdljsn::Json object
    bdljsn::Json  json;
    bdljsn::Error error;

    int rc = bdljsn::JsonUtil::read(&json, &error, jsonText);
    if (0 != rc) {
        bsl::cout << "Parse error: " << error << bsl::endl;
        return 1;
    }

    // Access parsed data
    assert(json["user"]["name"].theString() == "Bob");
    assert(json["user"]["age"].theNumber() == bdljsn::JsonNumber(25));
    assert(json["user"]["preferences"].theArray().size() == 3);
    assert(json["active"].theBoolean() == true);

    return 0;
 }

Convenient JSON Literals

For even more concise JSON creation, bdljsn_jsonliterals provides user-defined literals that parse JSON text directly into bdljsn::Json objects:

#include <bdljsn_json.h>
#include <bdljsn_jsonliterals.h>
#include <bsl_iostream.h>
#include <assert.h>

using namespace BloombergLP;
using namespace bdljsn::JsonLiterals;  // Enable _json suffix

int main()
{
    // Create JSON objects directly using (string) literals.
    bdljsn::Json config = R"({
        "server": {
            "host": "localhost",
            "port": 8080,
            "ssl": true
        },
        "database": {
            "type": "postgresql",
            "connections": 10
        },
        "features": ["auth", "logging", "metrics"]
    })"_json;

    // Access the parsed data
    assert(config["server"]["port"].theNumber() == bdljsn::JsonNumber(8080));
    assert(config["server"]["ssl"].theBoolean() == true);
    assert(config["features"].theArray().size() == 3);

    // Literals work with simple values too
    bdljsn::Json simpleArray = "[1, 2, 3, \"hello\"]"_json;
    bdljsn::Json number = "42.5"_json;
    bdljsn::Json boolean = "true"_json;

    return 0;
}

Key Features and Design Principles

bdljsn::Json has several important characteristics:

  • Always Valid: Every bdljsn::Json object represents a valid JSON document

  • Precise Numbers: JsonNumber preserves the exact textual representation, which can be converted to various numeric types, like int, double, or bdldfp::Decimal64

  • UTF-8 Strings: All string values are UTF-8

  • Standard Interface: Similar to STL containers with iterators, size(), etc.

The component pairs naturally with bdljsn_jsonutil for reading and writing JSON text, making it easy to work with JSON data in network protocols, configuration files, and APIs.

For more details and advanced usage, see: