# JSON for Modern C++ (nlohmann/json) — User Guide

**JSON for Modern C++** is a header-only C++ library that brings JSON into your code as a first-class data type. Its design goal is *intuitive syntax*: a JSON value behaves like an STL container, and converting between JSON and your own C++ types is a one-liner. If you can write `std::vector` or `std::map`, you already know most of this library.

```cpp
#include <nlohmann/json.hpp>
using json = nlohmann::json;

json j;
j["pi"] = 3.141;
j["happy"] = true;
j["list"] = {1, 0, 2};
```

This guide shows you how to add the library to your project and use every part of its public API — building values, parsing and serializing, accessing elements, converting your own types, working with JSON Pointer / JSON Patch, and reading and writing binary formats (CBOR, MessagePack, UBJSON, BSON, BJData). Architecture, the full macro reference, and build-system internals live in the companion **Admin Guide**.

> [!NOTE]
> This guide documents library version **3.12.0**. The single header you include is `nlohmann/json.hpp`, and the JSON value type is `nlohmann::json` (conventionally aliased to `json`). The full online API reference lives at [json.nlohmann.me](https://json.nlohmann.me/).

---

## Table of Contents

1. [Quick Start](#1-quick-start)
2. [Installation](#2-installation)
3. [Creating JSON Values](#3-creating-json-values)
4. [Parsing and Serializing](#4-parsing-and-serializing)
5. [Accessing Elements](#5-accessing-elements)
6. [Iterating](#6-iterating)
7. [Modifying Values](#7-modifying-values)
8. [Converting To and From Your Own Types](#8-converting-to-and-from-your-own-types)
9. [Type Inspection and Conversion](#9-type-inspection-and-conversion)
10. [JSON Pointer, JSON Patch, and Merge Patch](#10-json-pointer-json-patch-and-merge-patch)
11. [Binary Formats](#11-binary-formats)
12. [Parsing Extras: Comments, Trailing Commas, JSON Lines](#12-parsing-extras-comments-trailing-commas-json-lines)
13. [Compile-Time Options (Macros)](#13-compile-time-options-macros)
14. [Common Tasks (Recipes)](#14-common-tasks-recipes)
15. [Troubleshooting](#15-troubleshooting)
16. [FAQ](#16-faq)
17. [Quick Reference](#17-quick-reference)

---

## 1. Quick Start

Here is a complete program that builds a JSON object, modifies it, and pretty-prints it. Every line below uses only the public API.

```cpp
#include <iostream>
#include <iomanip>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    // create a JSON object
    json j =
    {
        {"pi", 3.141},
        {"happy", true},
        {"name", "Niels"},
        {"nothing", nullptr},
        {"answer", {{"everything", 42}}},
        {"list", {1, 0, 2}},
        {"object", {{"currency", "USD"}, {"value", 42.99}}}
    };

    // add new values
    j["new"]["key"]["value"] = {"another", "list"};

    // count elements
    auto s = j.size();
    j["size"] = s;

    // pretty print with indent of 4 spaces
    std::cout << std::setw(4) << j << '\n';
}
```

```text
{
    "answer": {
        "everything": 42
    },
    "happy": true,
    "list": [
        1,
        0,
        2
    ],
    "name": "Niels",
    "new": {
        "key": {
            "value": [
                "another",
                "list"
            ]
        }
    },
    "nothing": null,
    "object": {
        "currency": "USD",
        "value": 42.99
    },
    "size": 8
}
```

> [!TIP]
> The `std::setw(4)` stream manipulator sets the indentation when you print a `json` to an `std::ostream`. Without it, the output is the compact single-line form. For programmatic control, use `j.dump(4)` instead (see [§4](#4-parsing-and-serializing)).

> [!NOTE]
> Object keys are stored **sorted** by default (note `answer` appears before `happy` above). If you need to preserve insertion order, use `nlohmann::ordered_json` — see [§16, "How do I keep object keys in insertion order?"](#16-faq).

---

## 2. Installation

The library is **header-only**: at minimum you only need to make `single_include/nlohmann/json.hpp` reachable on your include path and compile with **C++11 or newer**. There is nothing to build or link.

### Requirements

| Requirement | Detail |
|---|---|
| Language standard | C++11 minimum; C++14/17/20/23 unlock extra features (see [§13](#13-compile-time-options-macros)) |
| Compiler | Any reasonably modern GCC, Clang, MSVC, Intel, or compatible compiler |
| Dependencies | **None** for using the library (test/build tooling is separate) |
| Header | `#include <nlohmann/json.hpp>` |

### Choosing an integration method

=== "CMake — FetchContent (recommended)"

    No manual download. CMake fetches the library at configure time:

    ```cmake
    cmake_minimum_required(VERSION 3.15)
    project(json_example)

    include(FetchContent)
    FetchContent_Declare(
      nlohmann_json
      URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz
    )
    FetchContent_MakeAvailable(nlohmann_json)

    add_executable(json_example example.cpp)
    target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json)
    ```

=== "CMake — find_package (system / package manager)"

    If the library is already installed (via vcpkg, Conan, Homebrew, your distro, etc.):

    ```cmake
    cmake_minimum_required(VERSION 3.15)
    project(json_example)

    find_package(nlohmann_json 3.12.0 REQUIRED)

    add_executable(json_example example.cpp)
    target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json)
    ```

=== "CPM.cmake"

    ```cmake
    cmake_minimum_required(VERSION 3.15)
    project(json_example)

    include(${CMAKE_SOURCE_DIR}/cmake/CPM.cmake)
    CPMAddPackage("gh:nlohmann/json@3.12.0")

    add_executable(json_example example.cpp)
    target_link_libraries(json_example PRIVATE nlohmann_json::nlohmann_json)
    ```

=== "Single header (no build system)"

    Download `json.hpp` from the [release page](https://github.com/nlohmann/json/releases) and drop it into your project:

    ```bash
    # compile directly, pointing the compiler at the header's directory
    g++ -std=c++11 -I path/to/single_include example.cpp -o example
    ```

> [!IMPORTANT]
> Always link against the imported target **`nlohmann_json::nlohmann_json`**, not a raw include path. The target carries the correct include directories and (for `find_package`) the version compatibility check. The CMake package name is `nlohmann_json` (with the underscore).

### Package managers

The library is widely packaged. Pick whichever matches your toolchain, then use the `find_package` snippet above.

| Manager | Install command |
|---|---|
| vcpkg | `vcpkg install nlohmann-json` |
| Conan | add `nlohmann_json/3.12.0` to your `conanfile` |
| Homebrew | `brew install nlohmann-json` |
| Meson | `dependency('nlohmann_json')` |
| Spack | `spack install nlohmann-json` |
| Swift Package Manager | depend on the package; product is `json` (C++11) |

> [!TIP]
> Several install snippets in the project's own documentation pass `CONFIG` to `find_package` (e.g. `find_package(nlohmann_json CONFIG REQUIRED)`). This forces CMake to use the package's config file rather than a find-module, which is the correct behavior for Homebrew, vcpkg, and macports installs.

---

## 3. Creating JSON Values

A `json` value can hold any JSON type: object, array, string, number, boolean, binary, or `null`. You build values with ordinary C++ syntax.

### From C++ literals

```cpp
json j_null;                       // null (default-constructed)
json j_bool   = true;              // boolean
json j_int    = 42;                // number (integer)
json j_float  = 3.141;             // number (floating-point)
json j_string = "Hello, world!";   // string
json j_array  = {1, 2, 3};         // array
json j_object = {{"key", "value"}};// object
```

### Nested objects and arrays with initializer lists

Braced initializer lists build nested structures. A list of `{key, value}` pairs becomes an **object**; any other list becomes an **array**.

```cpp
json j =
{
    {"name", "Niels"},
    {"age", 30},
    {"hobbies", {"coding", "reading"}},   // array
    {"address", {                          // nested object
        {"city", "Munich"},
        {"zip", "80331"}
    }}
};
```

> [!WARNING]
> An empty `{}` is ambiguous — the library treats it as an **empty object**, not an empty array. To force an empty array, use `json::array()`. To be explicit either way, use the named constructors below.

### Forcing array vs. object

When initializer-list deduction is ambiguous (for example, a list that *looks* like key/value pairs but should stay an array), use the explicit factory functions:

```cpp
// always an array, even though it could be read as an object
json arr = json::array({{"one", 1}, {"two", 2}});

// always an object
json obj = json::object({{"one", 1}, {"two", 2}});

json empty_array  = json::array();   // []
json empty_object = json::object();  // {}
```

### From a string literal with `_json`

The `_json` user-defined literal parses a string into a `json` at the point of use:

```cpp
using namespace nlohmann::literals;

json j = R"({"happy": true, "pi": 3.141})"_json;
```

> [!TIP]
> Prefer raw string literals (`R"( ... )"`) for embedded JSON so you don't have to escape every `"`. Bring the literal into scope with `using namespace nlohmann::literals;`.

---

## 4. Parsing and Serializing

### Parsing (text → `json`)

`json::parse()` accepts a string, a `char` range, an `std::istream`, an iterator pair, or any contiguous container of bytes.

```cpp
// from a string
json j = json::parse(R"({"happy": true, "pi": 3.141})");

// from a stream (e.g. a file)
std::ifstream f("data.json");
json j2 = json::parse(f);

// from an iterator pair
std::vector<std::uint8_t> bytes = {'{', '}'};
json j3 = json::parse(bytes.begin(), bytes.end());
```

If the input is malformed, `parse()` throws a `json::parse_error` (see [Troubleshooting](#15-troubleshooting)). To check validity *without* building a value or throwing, use `accept()`:

```cpp
bool ok = json::accept(R"({"valid": true})");   // true
```

### Serializing (`json` → text)

`dump()` returns a `std::string`. With no argument it produces compact output; pass an indentation width for pretty-printing.

```cpp
json j = {{"pi", 3.141}, {"happy", true}};

std::string compact = j.dump();      // {"happy":true,"pi":3.141}
std::string pretty  = j.dump(4);     // indented with 4 spaces
```

`dump()` has four parameters, all optional:

| Parameter | Type | Default | Purpose |
|---|---|---|---|
| `indent` | `int` | `-1` | Spaces per level; `-1` means compact (no newlines) |
| `indent_char` | `char` | `' '` | Character used for indentation (e.g. `'\t'`) |
| `ensure_ascii` | `bool` | `false` | If `true`, escape non-ASCII as `\uXXXX` |
| `error_handler` | `error_handler_t` | `strict` | How to handle invalid UTF-8 (see below) |

```cpp
// tab-indented, ASCII-safe output
std::string s = j.dump(1, '\t', true);
```

### Stream operators

You can read and write JSON directly with `<<` and `>>`:

```cpp
std::cout << j;                 // compact
std::cout << std::setw(4) << j; // pretty (indent = 4)

json j2;
std::cin >> j2;                 // parse from stdin
```

> [!NOTE]
> When dumping, the `error_handler` controls what happens if the value contains a string that is not valid UTF-8. `error_handler_t::strict` (default) throws a `type_error`; `replace` substitutes U+FFFD (`�`); `ignore` drops the offending bytes.

---

## 5. Accessing Elements

The library offers four access styles. Choosing the right one is mostly about whether a missing key is a bug (throw) or expected (return a default).

| Style | Syntax | On missing key/index | Use when |
|---|---|---|---|
| Unchecked | `j["key"]` / `j[0]` | **Inserts** a null (objects) / UB (arrays) | You know it exists, or you are assigning |
| Checked | `j.at("key")` / `j.at(0)` | Throws `out_of_range` | A missing key is an error |
| Default | `j.value("key", fallback)` | Returns `fallback` | A missing key is acceptable |
| Pointer | `j.get_ptr<T*>()` | Returns `nullptr` | You want to test without throwing |

### `operator[]` — unchecked, may insert

```cpp
json j;
j["name"] = "Niels";   // creates the key
auto n = j["name"];    // reads it
```

> [!CAUTION]
> Reading a **non-existent** key with `operator[]` on a *non-const* object **silently inserts** a null value at that key. This is a frequent source of bugs (objects that grow unexpectedly). Use `at()` or `contains()` when you only mean to read.

### `at()` — checked, throws on miss

```cpp
json j = {{"name", "Niels"}};
std::string n = j.at("name");   // ok
// j.at("missing");             // throws json::out_of_range (#403)
// j.at(10) on an array;        // throws json::out_of_range (#401)
```

### `value()` — read with a fallback

```cpp
json j = json::parse(R"({"uint64": 18446744073709551615})");

std::cout << j.value("uint64", 0) << '\n';                    // narrows to int
std::cout << j.value("uint64", std::uint64_t(0)) << '\n';     // keeps full width
std::cout << j.value<std::uint64_t>("uint64", 0) << '\n';     // explicit return type
```

> [!TIP]
> The return type of `value()` is deduced from the *default* argument. If you read a large integer with an `int` default, the value is narrowed. Pass a default of the right type (or use the explicit `value<T>(...)` form) to avoid surprises.

### `contains()`, `find()`, `count()`

```cpp
json j = {{"name", "Niels"}, {"age", 30}};

if (j.contains("name")) { /* ... */ }   // true, no insertion

auto it = j.find("age");
if (it != j.end()) { int age = *it; }

std::size_t n = j.count("name");        // 0 or 1 for objects
```

---

## 6. Iterating

A `json` array or object exposes STL-style iterators, so range-based `for` works directly.

```cpp
json j = {1, 2, 3, 4};
for (auto& element : j) {
    std::cout << element << '\n';
}
```

For objects, range-based `for` over the value alone gives you only the values. To get **keys and values**, use `items()`:

```cpp
json j = {{"one", 1}, {"two", 2}};

for (auto& [key, value] : j.items()) {        // C++17 structured bindings
    std::cout << key << " : " << value << '\n';
}

// pre-C++17
for (auto it = j.begin(); it != j.end(); ++it) {
    std::cout << it.key() << " : " << it.value() << '\n';
}
```

> [!NOTE]
> `it.key()` is only valid when iterating an **object**. Calling it while iterating an array throws an `invalid_iterator` (#207). Reverse iteration is available via `rbegin()`/`rend()` and the const variants `cbegin()`/`cend()`/`crbegin()`/`crend()`.

---

## 7. Modifying Values

### Arrays

```cpp
json j = {1, 2, 3};

j.push_back(4);                 // [1,2,3,4]
j += 5;                         // [1,2,3,4,5]  (operator+= alias)
j.emplace_back(6);              // construct in place

j.erase(0);                     // remove by index → [2,3,4,5,6]
```

### Objects

```cpp
json j = {{"a", 1}};

j["b"] = 2;                          // insert/overwrite
j.emplace("c", 3);                   // insert if absent
j.erase("a");                        // remove by key

// merge another object's keys in
json extra = {{"d", 4}, {"e", 5}};
j.update(extra);                     // adds d and e
```

> [!TIP]
> `update()` is the idiomatic "merge these keys into me" operation for objects (similar to Python's `dict.update`). By default it merges only the top level; pass `true` as the second argument to merge recursively.

### `insert()`, `clear()`, `swap()`

```cpp
json arr = {1, 2, 3};
arr.insert(arr.begin() + 1, 99);     // [1,99,2,3]

json a = {1, 2}, b = {3, 4};
a.swap(b);                            // a == [3,4], b == [1,2]

arr.clear();                          // becomes []  (type preserved, emptied)
```

---

## 8. Converting To and From Your Own Types

This is the library's headline feature: any type can be made JSON-serializable, and conversion is implicit.

### Standard library types just work

`std::vector`, `std::map`, `std::array`, `std::set`, `std::optional` (C++17), and friends convert automatically:

```cpp
std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
json j = m;                                  // {"a":1,"b":2}
auto m2 = j.get<std::map<std::string, int>>();
```

### Your own structs — the macros

For your own types, the simplest path is one of the `NLOHMANN_DEFINE_TYPE_*` macros. Place the macro inside (intrusive) or next to (non-intrusive) your type, listing the members to serialize.

**Non-intrusive** (use when you cannot change the type, or want to keep it a plain aggregate):

```cpp
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace nlohmann::literals;

namespace ns {
struct person {
    std::string name;
    std::string address;
    int age;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
}

int main()
{
    ns::person p = {"Ned Flanders", "744 Evergreen Terrace", 60};

    json j = p;                                       // serialize
    std::cout << j << '\n';
    // {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

    json j2 = R"({"name":"Homer","address":"742 Evergreen Terrace","age":40})"_json;
    auto p2 = j2.get<ns::person>();                   // deserialize
}
```

**Intrusive with defaults** — when JSON may be missing fields, use the `_WITH_DEFAULT` variant so absent keys fall back to the member's default-constructed value instead of throwing:

```cpp
namespace ns {
class person {
    std::string name = "John Doe";
    std::string address = "123 Fake St";
    int age = -1;
  public:
    person() = default;
    person(std::string n, std::string a, int g)
        : name(std::move(n)), address(std::move(a)), age(g) {}

    NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(person, name, address, age)
};
}

// JSON missing "age" → age stays -1 instead of throwing
auto p = R"({"address":"742 Evergreen Terrace","name":"Maggie Simpson"})"_json
             .get<ns::person>();
```

> [!IMPORTANT]
> The plain (non-`WITH_DEFAULT`) macros throw a `type_error`/`out_of_range` if a listed key is **missing** from the JSON during deserialization. If your inputs may omit fields, use the `_WITH_DEFAULT` family. There is also an `_ONLY_SERIALIZE` family for types that should be written but never read back, and `_WITH_NAMES` variants that let the JSON key differ from the C++ member name. The full macro matrix is in the **Admin Guide**.

### Enums

Map enum values to JSON (usually strings) with `NLOHMANN_JSON_SERIALIZE_ENUM`:

```cpp
namespace ns {
enum class Color { red, green, blue, unknown };

NLOHMANN_JSON_SERIALIZE_ENUM(Color, {
    { Color::unknown, "unknown" },
    { Color::red,     "red" },
    { Color::green,   "green" },
    { Color::blue,    "blue" },
})
}

json j = ns::Color::red;                 // "red"
auto c = j.get<ns::Color>();             // Color::red
```

> [!WARNING]
> The **first** pair listed is the default: deserializing a JSON value that doesn't match any mapping yields that first enum value (here `Color::unknown`). This surprised enough users (issue #3992) that a stricter macro, `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT`, now exists — it throws on an unmatched value instead of silently defaulting.

### Manual `to_json` / `from_json`

For full control, define free functions `to_json` and `from_json` in your type's namespace (ADL finds them):

```cpp
namespace ns {
void to_json(json& j, const person& p) {
    j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
}
void from_json(const json& j, person& p) {
    j.at("name").get_to(p.name);
    j.at("address").get_to(p.address);
    j.at("age").get_to(p.age);
}
}
```

> [!TIP]
> `get_to(target)` reads a value into an existing variable and deduces the type from `target` — cleaner than `target = j.get<T>()` inside `from_json`.

---

## 9. Type Inspection and Conversion

### Asking what a value is

```cpp
json j = 42;

j.is_number();          // true
j.is_number_integer();  // true
j.is_string();          // false
j.type();               // json::value_t::number_integer
j.type_name();          // "number"
```

The full predicate set: `is_null`, `is_boolean`, `is_number`, `is_number_integer`, `is_number_unsigned`, `is_number_float`, `is_object`, `is_array`, `is_string`, `is_binary`, `is_primitive`, `is_structured`, `is_discarded`.

### Extracting a C++ value

```cpp
json j = json::parse(R"({"name":"Niels","age":30})");

std::string name = j["name"].get<std::string>();
int age          = j.at("age").get<int>();

// get_to writes into an existing variable
std::string n2;
j["name"].get_to(n2);

// implicit conversion (enabled by default)
std::string n3 = j["name"];
```

> [!NOTE]
> Implicit conversions (`std::string n = j["name"];`) are convenient but can be ambiguous in generic code. You can disable them project-wide by defining `JSON_USE_IMPLICIT_CONVERSIONS=0`, which forces the explicit `get<T>()` / `get_to()` forms everywhere (see [§13](#13-compile-time-options-macros)).

### `get_ref` / `get_ptr` — zero-copy access

When you need a reference to the underlying storage without a copy:

```cpp
json j = "hello";
auto& s = j.get_ref<std::string&>();   // reference, no copy
auto* p = j.get_ptr<std::string*>();   // pointer, or nullptr if wrong type
```

---

## 10. JSON Pointer, JSON Patch, and Merge Patch

### JSON Pointer (RFC 6901) — addressing into a document

A `json::json_pointer` is a path like `/foo/bar/0` that drills into a nested document.

```cpp
json j = R"({"foo": {"bar": [10, 20, 30]}})"_json;

json::json_pointer ptr("/foo/bar/1");
std::cout << j[ptr] << '\n';      // 20
std::cout << j.at(ptr) << '\n';   // 20 (checked)
```

Pointer rules: every reference token starts with `/`; `~1` means a literal `/` inside a key, and `~0` means a literal `~`; the token `-` refers to the (nonexistent) element past the end of an array, used for appends.

```cpp
json::json_pointer p1("");          // whole document
json::json_pointer p2("/foo/bar");  // nested
json::json_pointer p3("/foo/-");    // "append" position for arrays
// json::json_pointer bad("foo");   // throws parse_error: must start with '/'
```

### JSON Patch (RFC 6902) — a list of edits

A JSON Patch is itself a JSON array of operations (`add`, `remove`, `replace`, `move`, `copy`, `test`). Apply it with `patch()`:

```cpp
json doc = R"({"baz": "qux", "foo": "bar"})"_json;

json patch = R"([
    { "op": "replace", "path": "/baz", "value": "boo" },
    { "op": "add",     "path": "/hello", "value": ["world"] },
    { "op": "remove",  "path": "/foo" }
])"_json;

json result = doc.patch(patch);
// {"baz":"boo","hello":["world"]}
```

You can also **compute** the patch that turns one document into another with `json::diff()`:

```cpp
json patch = json::diff(source, target);
json roundtrip = source.patch(patch);   // == target
```

### JSON Merge Patch (RFC 7386) — a simpler merge

Merge Patch describes changes as a partial document. `null` values mean "delete this key"; everything else is overlaid.

```cpp
json document = R"({
    "title": "Goodbye!",
    "author": {"givenName": "John", "familyName": "Doe"},
    "tags": ["example", "sample"]
})"_json;

json patch = R"({
    "title": "Hello!",
    "phoneNumber": "+01-123-456-7890",
    "author": {"familyName": null},
    "tags": ["example"]
})"_json;

document.merge_patch(patch);
// title updated, phoneNumber added, author.familyName removed, tags replaced
```

> [!TIP]
> Use **JSON Patch** when you need precise, ordered, testable operations (e.g. an audit trail of edits). Use **Merge Patch** when you just want to overlay a partial update and don't care about per-operation control.

### Flatten / unflatten

`flatten()` collapses a nested document into a flat object keyed by JSON Pointers; `unflatten()` reverses it.

```cpp
json j = R"({"a": {"b": 1}, "c": [2, 3]})"_json;
json flat = j.flatten();
// {"/a/b":1, "/c/0":2, "/c/1":3}
json back = flat.unflatten();   // == j
```

---

## 11. Binary Formats

The library reads and writes five binary JSON-like formats. Each has a `to_*` (serialize) and `from_*` (parse) pair.

| Format | Serialize | Parse | Notes |
|---|---|---|---|
| CBOR | `json::to_cbor(j)` | `json::from_cbor(bytes)` | RFC 8949 |
| MessagePack | `json::to_msgpack(j)` | `json::from_msgpack(bytes)` | |
| UBJSON | `json::to_ubjson(j)` | `json::from_ubjson(bytes)` | |
| BSON | `json::to_bson(j)` | `json::from_bson(bytes)` | Top-level value must be an object |
| BJData | `json::to_bjdata(j)` | `json::from_bjdata(bytes)` | UBJSON derivative |

```cpp
json j = R"({"compact": true, "schema": 0})"_json;

// JSON → CBOR (a std::vector<std::uint8_t>)
std::vector<std::uint8_t> v = json::to_cbor(j);

// CBOR → JSON
json j2 = json::from_cbor(v);   // round-trips back to j
```

> [!NOTE]
> Binary formats carry a true binary type that plain JSON lacks. Build one with `json::binary(bytes)` (optionally with a subtype), and inspect it with `is_binary()` / `get_binary()`. CBOR and BSON preserve subtypes; the others approximate.

> [!CAUTION]
> `to_bson()` requires the **top-level** value to be an object — BSON has no notion of a bare array or scalar document. Passing an array throws a `type_error`.

---

## 12. Parsing Extras: Comments, Trailing Commas, JSON Lines

Standard JSON forbids comments and trailing commas, but the parser can be told to tolerate them.

### Comments

```cpp
std::string s = R"({
    // line comment
    "planets": ["Mercury", "Venus", "Earth" /*, "Pluto" */]
})";

// 4th argument enables comment skipping
json j = json::parse(s,
                     /* callback        */ nullptr,
                     /* allow_exceptions*/ true,
                     /* ignore_comments */ true);
```

> [!WARNING]
> Without `ignore_comments = true`, the comment above causes a `parse_error`. Comment support is an extension; do not rely on it when interoperating with strict JSON consumers.

### Trailing commas

Trailing-comma tolerance is controlled the same way (via the parse options). A document like `{"a": 1,}` parses cleanly when enabled and throws otherwise.

### JSON Lines

To process a stream of one-JSON-value-per-line (JSON Lines / NDJSON), read and parse line by line:

```cpp
std::ifstream f("data.jsonl");
std::string line;
while (std::getline(f, line)) {
    if (line.empty()) continue;
    json j = json::parse(line);
    // handle j
}
```

### Parser callbacks — filtering during parse

A `parser_callback_t` lets you accept, reject, or transform elements as they are parsed — useful for dropping large subtrees you don't need.

```cpp
json::parser_callback_t cb =
    [](int depth, json::parse_event_t event, json& parsed)
{
    // skip any object member whose key is "Thumbnail"
    if (event == json::parse_event_t::key && parsed == json("Thumbnail"))
        return false;   // discard
    return true;        // keep
};

json filtered = json::parse(text, cb);
```

For very large inputs where you don't want to build a DOM at all, use the **SAX interface** (`json::sax_parse`) — see the Admin Guide.

---

## 13. Compile-Time Options (Macros)

Because this is a header-only library with no runtime configuration, its "settings" are **preprocessor macros** you define before including the header (or via your build system, e.g. `target_compile_definitions`). These are the ones you are most likely to touch as a user; the complete macro reference is in the Admin Guide.

| Macro | Default | Effect |
|---|---|---|
| `JSON_USE_IMPLICIT_CONVERSIONS` | `1` | Set to `0` to disable implicit `json → T` conversions and require `get<T>()`/`get_to()` |
| `JSON_DIAGNOSTICS` | `0` | Set to `1` to include the JSON Pointer path of the offending value in exception messages |
| `JSON_NOEXCEPTION` | off | Disable all exceptions (calls abort on error instead); for `-fno-exceptions` builds |
| `JSON_NO_IO` | off | Drop all `<iostream>` dependencies (no stream operators, no `parse(istream)`) — for freestanding/embedded |
| `JSON_USE_GLOBAL_UDLS` | `1` | Set to `0` to keep the `_json` / `_json_pointer` literals out of the global namespace |

```cmake
# enable richer exception messages for everything linking this target
target_compile_definitions(my_app PRIVATE JSON_DIAGNOSTICS=1)
```

> [!IMPORTANT]
> `JSON_DIAGNOSTICS` changes the library's ABI. If you set it, set it **consistently** across every translation unit and every library that exchanges `json` objects, or you will hit subtle linker/ODR issues (the cause of issue #3106). The recommended way with CMake is to define it on the imported target so it propagates everywhere.

---

## 14. Common Tasks (Recipes)

### Read a JSON file into a value

```cpp
std::ifstream f("config.json");
json config = json::parse(f);
```

### Write a value to a file (pretty)

```cpp
std::ofstream out("config.json");
out << std::setw(4) << config << '\n';
```

### Safely read an optional setting with a default

```cpp
int timeout = config.value("timeout_ms", 5000);   // 5000 if absent
```

### Round-trip a struct through a file

```cpp
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Settings, host, port, retries)

Settings s = json::parse(std::ifstream("settings.json")).get<Settings>();
std::ofstream("settings.json") << std::setw(2) << json(s);
```

### Convert JSON to MessagePack for the wire

```cpp
std::vector<std::uint8_t> wire = json::to_msgpack(payload);
// ... send wire ...
json received = json::from_msgpack(wire);
```

### Validate input without throwing

```cpp
if (!json::accept(untrusted_text)) {
    // reject early, no exception, no DOM built
}
```

### Pull library/build metadata at runtime

```cpp
json info = json::meta();
std::cout << info["version"]["string"] << '\n';   // "3.12.0"
std::cout << info["platform"] << ' ' << info["compiler"]["family"] << '\n';
```

---

## 15. Troubleshooting

The library reports errors as typed exceptions derived from `json::exception`. Each carries a stable numeric **id** and a human-readable message. Catch the base `json::exception` to handle any of them, or a specific subtype.

| Exception type | ID range | Meaning |
|---|---|---|
| `json::parse_error` | 1xx | Input text/bytes were not valid JSON |
| `json::invalid_iterator` | 2xx | Misused an iterator |
| `json::type_error` | 3xx | Operation not valid for the value's type |
| `json::out_of_range` | 4xx | Index or key does not exist |
| `json::other_error` | 5xx | Everything else (e.g. a failed `test` patch op) |

### Error: `[json.exception.parse_error.101] parse error ...`

**Cause.** The input passed to `json::parse()` is not well-formed JSON (a stray character, unterminated string, or — commonly — a comment or trailing comma in strict mode).
**Solution.** Fix the input, or enable the relevant tolerance: pass `ignore_comments = true` to `parse()` for comments. To check first without throwing, use `json::accept()`.
**Prevention.** Validate untrusted input with `accept()` before parsing, and prefer raw string literals to avoid escaping mistakes.

### Error: `[json.exception.type_error.302] type must be string, but is ...`

**Cause.** You called `get<T>()` or `get_to()` with a `T` that doesn't match the value's actual JSON type (e.g. reading a number as `std::string`).
**Solution.** Check `j.type()` / `is_string()` first, or read into the correct type. Use `value("key", default)` when the field may be absent or of the wrong type.
**Prevention.** Inspect types defensively when consuming external JSON; lean on the `NLOHMANN_DEFINE_TYPE_*_WITH_DEFAULT` macros so missing fields don't surprise you.

### Error: `[json.exception.out_of_range.403] key 'X' not found`

**Cause.** `at("X")` was called for a key that does not exist.
**Solution.** Use `contains("X")` to test first, `value("X", fallback)` to supply a default, or `find("X")` to get an iterator you can compare against `end()`.
**Prevention.** Reserve `at()` for cases where a missing key really is a programming error; use `value()` for optional fields.

### Error: `[json.exception.out_of_range.401] array index N is out of range`

**Cause.** `at(N)` indexed past the end of an array.
**Solution.** Check `j.size()` before indexing, or iterate instead of indexing.

### Problem: an object grew keys I never set

**Cause.** Reading a missing key with `operator[]` on a non-const `json` **inserts** a null at that key.
**Solution.** Use `at()` or `contains()` for read-only access. Mark the value `const` where possible — `operator[]` on a const object does *not* insert (it asserts/UB on a missing key, surfacing the bug at the access site).
**Prevention.** Treat `operator[]` as "create-or-assign"; treat `at()`/`value()` as "read".

### Problem: deserializing my struct throws on valid-looking JSON

**Cause.** A plain `NLOHMANN_DEFINE_TYPE_*` macro requires *every* listed member to be present in the JSON.
**Solution.** Switch to the `_WITH_DEFAULT` variant so absent fields fall back to defaults, or supply the missing keys.

### Problem: `std::optional` members aren't being picked up

**Cause.** `std::optional` support requires C++17, and it is not auto-detected in every configuration (issue #4864).
**Solution.** Compile with `-std=c++17` (or later) and ensure the C++17 feature path is active. If you rely on optional members, verify with a small round-trip test.

### Problem: ODR / linker errors when mixing diagnostic settings

**Cause.** `JSON_DIAGNOSTICS` (and other ABI-affecting macros) were defined differently across translation units or libraries (issue #3106).
**Solution.** Define ABI-affecting macros uniformly for the whole build — ideally on the imported CMake target so they propagate to every consumer.

> [!TIP]
> Set `JSON_DIAGNOSTICS=1` while debugging: exception messages then include the JSON Pointer to the exact element that triggered the error (e.g. `(/author/familyName)`), which turns "type error somewhere" into a precise location.

---

## 16. FAQ

**Q: Is this library really header-only? Do I need to build or link anything?**
No build, no link. Make `nlohmann/json.hpp` reachable and compile with C++11+. The CMake target `nlohmann_json::nlohmann_json` only carries include directories and compile options.

**Q: What C++ standard do I need?**
C++11 is the minimum. C++14, 17, 20, and 23 progressively enable extra conveniences (transparent comparators, `std::optional` and `std::string_view` support, the spaceship `operator<=>`, C++20 modules, and ranges).

**Q: How do I keep object keys in insertion order instead of sorted?**
Use `nlohmann::ordered_json` (a drop-in alias that stores objects in an insertion-ordered map) instead of `nlohmann::json`. The API is identical.

**Q: Why does reading a key sometimes add it to my object?**
`operator[]` inserts on a missing key for non-const objects. Use `at()`, `value()`, or `contains()` for pure reads. See [§5](#5-accessing-elements).

**Q: How do I serialize my own class with minimal code?**
Add `NLOHMANN_DEFINE_TYPE_INTRUSIVE(YourType, member1, member2, ...)` inside the class (it can see private members) or `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(YourType, ...)` in the same namespace. Use the `_WITH_DEFAULT` variants if fields may be missing in the JSON.

**Q: What's the difference between `dump()` and the stream operator?**
`dump(indent)` returns a `std::string` and lets you control indentation, indent character, ASCII escaping, and error handling. `std::cout << j` writes to a stream; combine with `std::setw(n)` to pretty-print.

**Q: Can I parse JSON with comments or trailing commas?**
Yes, but only by opting in per-parse (`ignore_comments = true`, and the matching trailing-comma option). They are extensions to standard JSON — don't emit them for strict consumers.

**Q: How do I read or write CBOR / MessagePack / BSON?**
Use the `to_<format>` / `from_<format>` static functions (§11). They convert between a `json` value and a `std::vector<std::uint8_t>`.

**Q: How do I produce richer error messages?**
Compile with `JSON_DIAGNOSTICS=1` so exceptions include the JSON Pointer path to the offending value — but set it uniformly across your whole build.

**Q: Is the library thread-safe?**
Reading a `json` value from multiple threads is safe; concurrent reads and writes of the *same* value are not (standard C++ container rules apply). Give each thread its own values, or synchronize externally.

**Q: How do I check the version at runtime?**
`json::meta()["version"]["string"]` returns e.g. `"3.12.0"`, along with platform and compiler details.

---

## 17. Quick Reference

### Construction

| Goal | Code |
|---|---|
| Null | `json j;` |
| From literal text | `auto j = R"({"a":1})"_json;` |
| Empty array / object | `json::array()` / `json::object()` |
| Parse string/stream | `json::parse(s)` |

### Access

| Goal | Code |
|---|---|
| Unchecked (may insert) | `j["k"]`, `j[0]` |
| Checked (throws) | `j.at("k")`, `j.at(0)` |
| With default | `j.value("k", fallback)` |
| Test existence | `j.contains("k")`, `j.find("k")` |
| By JSON Pointer | `j[json::json_pointer("/a/b")]` |

### Serialize

| Goal | Code |
|---|---|
| Compact string | `j.dump()` |
| Pretty (4 spaces) | `j.dump(4)` |
| To stream (pretty) | `out << std::setw(4) << j;` |
| To CBOR / MessagePack | `json::to_cbor(j)` / `json::to_msgpack(j)` |

### Convert your types

| Goal | Code |
|---|---|
| Struct (non-intrusive) | `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(T, a, b)` |
| Struct, tolerate missing | `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(T, a, b)` |
| Enum ↔ string | `NLOHMANN_JSON_SERIALIZE_ENUM(E, {{E::x,"x"}, ...})` |
| Extract value | `j.get<T>()`, `j.get_to(var)` |

### Patch & pointer

| Goal | Code |
|---|---|
| Apply JSON Patch | `doc.patch(patchArray)` |
| Compute diff | `json::diff(src, dst)` |
| Merge Patch | `doc.merge_patch(partial)` |
| Flatten / unflatten | `j.flatten()` / `flat.unflatten()` |

---

_Generated June 2026 from nlohmann/json at d10879b. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
