One value type, six JSON types.
json::Document is the complete public value model. A document carries a json::Type tag plus deliberately ordinary C++ storage for null, booleans, numbers, strings, arrays and objects. You can use the convenience helpers where they fit, or access the underlying fields directly after checking the type.
The six JSON types
| JSON value | json::Type | Check | Read / inspect |
|---|---|---|---|
null | Type::Null | value.is_null() | No separate payload |
| boolean | Type::Boolean | value.is_bool() | value.boolean |
| number | Type::Number | value.is_number() | value.num as double; as_int() convenience helper |
| string | Type::String | value.is_string() | value.string; as_string() convenience helper |
| array | Type::Array | value.is_array() | value.array, index with value[index] |
| object | Type::Object | value.is_object() | value.object, keys with value["key"] |
Parsing
Document::parse parses one complete JSON value. It returns true on success. On failure it returns false and writes a human-readable error containing line and column information.
std::string source = R"({"name":"Jsonic++","enabled":true})";
json::Document value;
std::string error;
if (!json::Document::parse(source, value, error)) {
std::cerr << error << '\n';
return 1;
} The parser expects the entire input to be JSON. Extra non-whitespace characters after the value are rejected rather than silently ignored.
Objects
Objects are stored as std::vector<std::pair<std::string, json::Document>>. For ordinary access, use has() and the string-key operator[].
if (value.is_object() && value.has("name")) {
const json::Document &name = value["name"];
if (name.is_string()) {
std::cout << name.string << '\n';
}
}
if (value.has("enabled") && value["enabled"].is_bool()) {
bool enabled = value["enabled"].boolean;
} Object access has intentionally useful mutation semantics. On a non-const Document, indexing a null value by key turns it into an object, and a missing key is inserted with a null value. On a const object, a missing key throws std::out_of_range. Indexing a non-object throws std::runtime_error.
json::Document config; // null
config["name"] = "example"; // becomes an object
config["enabled"] = true;
config["retries"] = 3;
for (const auto &entry : config.object) {
const std::string &key = entry.first;
const json::Document &item = entry.second;
// inspect item.type or use item.is_*()
} Arrays
Arrays are ordinary std::vector<json::Document> values. You can use the public array field directly, index through operator[], or append with push_back().
if (value.has("items") && value["items"].is_array()) {
const json::Document &items = value["items"];
for (const json::Document &item : items.array) {
// inspect each item
}
if (!items.array.empty()) {
const json::Document &first = items[0];
}
} Array indexing uses bounds-checked std::vector::at(), so an invalid index throws std::out_of_range. push_back() turns a null document into an array automatically; calling it on another existing type throws std::runtime_error.
json::Document tags;
tags.push_back("fast");
tags.push_back("small");
tags.push_back(true); Strings
After is_string(), read the decoded UTF-8/string payload from .string. If you specifically want fallback behavior instead of a type branch, as_string(fallback) returns the stored string only when the value is a string.
if (value["name"].is_string()) {
std::string name = value["name"].string;
}
std::string label = value["label"].as_string("untitled"); Numbers
JSON numbers are stored in .num as double. Use is_number() before reading it. as_int(fallback) is a small convenience helper that casts the stored number to int; it is not a general numeric conversion system.
if (value["ratio"].is_number()) {
double ratio = value["ratio"].num;
}
int retries = value["retries"].as_int(3); Booleans and null
Booleans live in .boolean. Null has no separate payload: the type itself is the value.
if (value["enabled"].is_bool()) {
bool enabled = value["enabled"].boolean;
}
if (value["optional"].is_null()) {
// explicitly null
} Constructing values
Document has constructors for null, bool, int, double, C strings and std::string. Arrays and objects can be created explicitly with make_array() and make_object(), although null-to-container promotion means many small builders do not need them.
json::Document nothing(nullptr);
json::Document active(true);
json::Document count(42);
json::Document ratio(0.75);
json::Document name("Jsonic++");
json::Document list = json::Document::make_array();
list.push_back("one");
list.push_back("two");
json::Document object = json::Document::make_object();
object["name"] = name;
object["items"] = list; Inspecting the type directly
The is_*() helpers are usually clearest, but type is public when a switch is more convenient.
switch (value.type) {
case json::Type::Null: break;
case json::Type::Boolean: std::cout << value.boolean; break;
case json::Type::Number: std::cout << value.num; break;
case json::Type::String: std::cout << value.string; break;
case json::Type::Array: std::cout << value.array.size(); break;
case json::Type::Object: std::cout << value.object.size(); break;
} Serialization
dump(indent) serializes the complete value. An indent of 0 produces compact JSON; positive values pretty-print with that many spaces per level.
std::string compact = value.dump(0);
std::string pretty = value.dump(2); For custom streaming writers, append_escaped_string() appends an escaped JSON string payload without surrounding quotes, reusing Jsonic++'s own escaping rules.
std::string out = "\"name\":\"";
json::Document::append_escaped_string(out, name);
out += '\"'; Large named arrays without a full array DOM
for_each_array_item is the one deliberately specialized parsing path. It expects a root JSON object and streams the items from one named root-array member through a callback, so the complete target array does not need to exist in memory at once. Other root members are still parsed and validated.
std::string error;
bool ok = json::Document::for_each_array_item(
source,
"pages",
[](json::Document &&page) {
if (page.is_object() && page.has("title")) {
std::cout << page["title"].as_string() << '\n';
}
return true; // false stops iteration and reports rejection
},
error
); This is useful for very large generated metadata arrays—the reason it exists in Jsonic++—without turning the rest of the library into a streaming API.
Exceptions and parse errors
Parsing malformed JSON does not throw through the public parse() or for_each_array_item() APIs; they report failure through their boolean result and error string. Value-access misuse is different: wrong-type object/array access throws std::runtime_error, missing const object keys and out-of-range array indices throw std::out_of_range. That distinction keeps syntax errors explicit while still making programmer mistakes difficult to ignore.
Public surface at a glance
| Area | API |
|---|---|
| Type | type, Type::{Null, Boolean, Number, String, Array, Object} |
| Payloads | num, boolean, string, array, object |
| Checks | is_null(), is_bool(), is_number(), is_string(), is_array(), is_object() |
| Object access | has(key), operator[](key) |
| Array access | operator[](index), push_back(value) |
| Fallback helpers | as_string(fallback), as_int(fallback) |
| Creation | scalar constructors, make_array(), make_object() |
| Parsing | parse(), for_each_array_item() |
| Output | dump(indent), append_escaped_string() |