# JSON for Modern C++ (nlohmann/json) — Admin & Integrator Guide

This guide is for the engineer who **integrates, builds, ships, or extends** JSON for Modern C++ — not the day-to-day API consumer (that audience is served by the companion **User Guide**). Here you'll find the library's internal architecture, the complete compile-time configuration surface, the single-header amalgamation and ABI machinery, the build and test system, security considerations, and guidance for extending or contributing.

The library is **header-only**, so "deployment" means *integration into a downstream build* and *control of compile-time behavior* rather than running a service. That reframing drives the structure below.

> [!NOTE]
> Documented version: **3.12.0** (source commit `d10879b`). The library has no runtime configuration and no server component. Its "operational surface" is the preprocessor macro set, the CMake package, and the amalgamation tooling. The canonical online reference is [json.nlohmann.me](https://json.nlohmann.me/).

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Architecture Overview](#2-architecture-overview)
3. [Source Layout](#3-source-layout)
4. [The `basic_json` Value Model](#4-the-basic_json-value-model)
5. [Pipelines: Parsing, Serialization, Binary Formats](#5-pipelines-parsing-serialization-binary-formats)
6. [The Serialization Customization System (ADL)](#6-the-serialization-customization-system-adl)
7. [Compile-Time Configuration: Full Macro Reference](#7-compile-time-configuration-full-macro-reference)
8. [Build & Integration](#8-build--integration)
9. [The Amalgamation & ABI Machinery](#9-the-amalgamation--abi-machinery)
10. [Test & Quality Infrastructure](#10-test--quality-infrastructure)
11. [Tooling](#11-tooling)
12. [Security Analysis](#12-security-analysis)
13. [Code Quality & Conventions](#13-code-quality--conventions)
14. [Extending the Library](#14-extending-the-library)
15. [Known Issues & Improvement Areas](#15-known-issues--improvement-areas)
16. [Quick Reference](#16-quick-reference)

---

## 1. Executive Summary

**What it is.** A single-class-centric C++ library that models a JSON document as the class template `basic_json`, with the alias `nlohmann::json` providing the common instantiation. It emphasizes *intuitive syntax* over raw speed, treating JSON values like STL containers and supporting bidirectional conversion to arbitrary C++ types.

**How it ships.** Two consumable forms generated from the same `include/nlohmann/` source tree:

- **Multi-header** (`include/nlohmann/json.hpp` + the `detail/` tree) — for developers who want only the parts they use and faster incremental rebuilds.
- **Single-header** (`single_include/nlohmann/json.hpp`) — one amalgamated file for drop-in use, generated by the `tools/amalgamate` pipeline.

**Key architectural properties for an integrator:**

| Property | Value |
|---|---|
| Distribution | Header-only; no compiled library, no link step |
| Minimum standard | C++11 (features scale up through C++23) |
| Runtime dependencies | None |
| Configuration mechanism | Preprocessor macros (compile-time only) |
| ABI surface | Controlled by an inline namespace encoding version + config flags |
| Primary build/test driver | CMake (3.13+ for the test suite) |
| Packaging | vcpkg, Conan, Homebrew, Meson, Spack, Swift PM, and more |
| License | MIT |

---

## 2. Architecture Overview

The public surface is small — essentially one class template plus a few helpers — but it sits on a layered internal stack under `detail/`.

```
                    Public API (include/nlohmann/*.hpp)
   ┌───────────────────────────────────────────────────────────────┐
   │  basic_json<...>    json (alias)    ordered_json (alias)        │
   │  json_pointer       json_sax        adl_serializer             │
   │  byte_container_with_subtype        ordered_map                 │
   └───────────────────────────────────────────────────────────────┘
                                  │  uses
   ┌───────────────────────────────────────────────────────────────┐
   │                    detail/  (internal implementation)          │
   │                                                                 │
   │  input/    lexer → parser → json_sax_dom_(callback_)parser      │
   │            binary_reader (CBOR/MsgPack/UBJSON/BSON/BJData)       │
   │            input_adapters (string / stream / iterator / array)  │
   │                                                                 │
   │  output/   serializer (text dump, UTF-8 handling)               │
   │            binary_writer (binary formats)                       │
   │            output_adapters (string / stream / vector)           │
   │                                                                 │
   │  conversions/  from_json.hpp  to_json.hpp  (ADL hooks)          │
   │                                                                 │
   │  iterators/  iter_impl  json_reverse_iterator                   │
   │              iteration_proxy  primitive_iterator                │
   │                                                                 │
   │  meta/    type_traits  cpp_future  detected  call_std           │
   │           (SFINAE, feature detection, C++ version shims)        │
   │                                                                 │
   │  value_t.hpp   exceptions.hpp   hash.hpp   json_pointer.hpp     │
   │  json_ref.hpp  macro_scope.hpp  abi_macros.hpp  string_*.hpp    │
   └───────────────────────────────────────────────────────────────┘
```

Two design decisions dominate everything:

1. **`basic_json` is a template, `json` is a typedef.** Almost every type in the library — the object map, array vector, string, number types, allocator, and even the serializer — is a template parameter with a sensible default. `nlohmann::json` is the default instantiation; `nlohmann::ordered_json` swaps the object container for an insertion-ordered map.
2. **Customization happens through ADL (Argument-Dependent Lookup), not inheritance.** To make a type serializable you provide free `to_json`/`from_json` functions (directly or via macros); the library finds them through the `adl_serializer` indirection. No base class, no virtual dispatch.

---

## 3. Source Layout

The triage of the repository (HIGH = source/docs that define behavior; LOW = leaf detail headers and per-integration fixtures) maps to these areas:

| Path | Role |
|---|---|
| `include/nlohmann/` | The library. `json.hpp` is the umbrella header; `json_fwd.hpp` is the forward declaration header. |
| `include/nlohmann/detail/` | All implementation: `input/`, `output/`, `conversions/`, `iterators/`, `meta/`, plus value type, exceptions, hashing, JSON Pointer, macros. |
| `single_include/nlohmann/` | The amalgamated single-header build, generated from the above. |
| `docs/mkdocs/` | The MkDocs-Material documentation site (`mkdocs.yml` nav is the authoritative API index). |
| `docs/mkdocs/docs/examples/` | 227 standalone, **compile-tested** example `.cpp` files — one per API entry. The CI builds and diffs their output. |
| `docs/docset/` | Dash/Zeal docset generation (`docSet.sql` is a complete API symbol index). |
| `tests/` | The doctest-based unit suite (`tests/src/unit-*.cpp`), ABI tests, CMake-integration tests, fuzzers, benchmarks. |
| `tools/` | Maintenance tooling: `amalgamate`, `astyle`, `serve_header`, `macro_builder`, `generate_natvis`, `gdb_pretty_printer`. |
| `cmake/` | CMake package config, compiler/library detection, test helpers. |

> [!NOTE]
> By raw character count the repository is dominated by `tests/src` (~2 MB of unit tests across 79 files) and the example corpus. The *library* itself is the comparatively small `include/nlohmann/` tree. When reasoning about behavior, the headers are ground truth; the tests are the executable specification.

---

## 4. The `basic_json` Value Model

`basic_json` stores exactly one JSON value at a time using a tagged union.

### Type tag — `value_t`

The discriminant is the enum `detail::value_t` (exposed as `json::value_t`):

`null`, `object`, `array`, `string`, `boolean`, `number_integer`, `number_unsigned`, `number_float`, `binary`, `discarded`.

The ordering matters: it defines the comparison order between values of different types. `discarded` is a special internal state produced when a parser callback rejects a subtree.

### Storage — the `json_value` union

The payload is a union. Variable-length types are heap-allocated (pointer in the union) so the union itself stays small — at most 64 bits with the default type parameters:

| JSON type | `value_t` | Stored as |
|---|---|---|
| object | `object` | `object_t*` (pointer) |
| array | `array` | `array_t*` (pointer) |
| string | `string` | `string_t*` (pointer) |
| binary | `binary` | `binary_t*` (pointer) |
| boolean | `boolean` | `boolean_t` (inline) |
| number (int) | `number_integer` | `number_integer_t` (inline) |
| number (unsigned) | `number_unsigned` | `number_unsigned_t` (inline) |
| number (float) | `number_float` | `number_float_t` (inline) |
| null | `null` | *no storage* |

The default type parameters are: object = `std::map`, array = `std::vector`, string = `std::string`, integer = `std::int64_t`, unsigned = `std::uint64_t`, float = `double`, boolean = `bool`. All are template arguments — `ordered_json` differs only by substituting an insertion-ordered map for the object type.

### Invariants and iterative destruction

The class enforces an invariant — checked by `assert_invariant()` at the end of every mutating operation — that the type tag and the active union member always agree (a non-null object/array/string/binary pointer when the tag says so).

> [!IMPORTANT]
> Destruction of nested containers is **iterative, not recursive**. `json_value::destroy()` flattens the value into a heap-allocated stack and tears it down in a loop. This is a deliberate defense against stack overflow when destroying very deeply nested documents (e.g. adversarial input) — a recursive destructor would blow the stack. Keep this in mind if you ever fork the storage layer.

### Diagnostics: parent pointers

When `JSON_DIAGNOSTICS=1`, each value additionally tracks an `m_parent` pointer maintained by `set_parents()` / `set_parent()`. This lets exceptions report the **JSON Pointer path** to the offending element. Because it adds a member and changes layout, it is an **ABI-affecting** flag (see [§7](#7-compile-time-configuration-full-macro-reference) and [§9](#9-the-amalgamation--abi-machinery)).

---

## 5. Pipelines: Parsing, Serialization, Binary Formats

### Input path (text → value)

```
input source ──▶ input_adapter ──▶ lexer ──▶ parser ──▶ json_sax_dom_parser ──▶ basic_json
   (string,         (uniform           (tokens)   (grammar)   (builds DOM)
    stream,          byte feed)
    iterator,
    bytes)
```

- **input_adapters** normalize every input kind (string, `std::istream`, iterator pair, contiguous byte container) into a uniform byte feed.
- The **lexer** turns bytes into tokens, with optional comment skipping.
- The **parser** drives the grammar and emits SAX events, optionally invoking the user `parser_callback_t` to accept/reject/transform elements (rejected subtrees become `discarded`).
- **`json_sax_dom_parser`** (or the callback variant) consumes the events and materializes the `basic_json`.

`accept()` runs the same front end but discards the result, returning only validity. `sax_parse()` exposes the SAX layer directly so you can consume events without ever building a DOM — essential for streaming very large inputs.

### Output path (value → text)

The `serializer` walks the value and writes to an `output_adapter` (string, stream, or vector). It owns indentation, the indent character, `ensure_ascii` escaping, and UTF-8 error handling via `error_handler_t` (`strict` throws, `replace` emits U+FFFD, `ignore` drops bad bytes).

### Binary formats

`binary_reader` and `binary_writer` implement five formats behind the symmetric `from_<fmt>` / `to_<fmt>` static functions: **CBOR**, **MessagePack**, **UBJSON**, **BSON**, **BJData**. They reuse the same SAX/DOM plumbing as text parsing. The `binary_t` type (a `byte_container_with_subtype<BinaryType>`) carries an optional integer **subtype** so formats that support tagged binary (CBOR, BSON) can round-trip it.

> [!NOTE]
> The SAX interface (`json_sax<basic_json>`) is a first-class extension point. Implement its callbacks (`null`, `boolean`, `number_integer`, `string`, `start_object`, `key`, `end_object`, `start_array`, `end_array`, `binary`, `parse_error`) to build your own consumer — for example, validating or streaming-transforming a multi-gigabyte document without materializing it.

---

## 6. The Serialization Customization System (ADL)

The mechanism that lets `json j = my_struct;` compile is worth understanding because it is where most integration effort goes.

1. `basic_json`'s converting constructor and `get<T>()` delegate to `JSONSerializer<T>` — by default `adl_serializer<T>`.
2. `adl_serializer<T>::to_json` / `from_json` simply call unqualified `to_json` / `from_json`, triggering **ADL** in `T`'s namespace.
3. The library ships `to_json`/`from_json` overloads for all standard types (arithmetic, `std::string`, `std::vector`, `std::map`, `std::array`, `std::optional` (C++17), tuples, etc.) in `detail/conversions/`.
4. For your own types you provide the two free functions — by hand, or generated by a macro.

### The macro families

The `NLOHMANN_DEFINE_TYPE_*` and `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros generate those two functions for you. They form a matrix along three axes:

| Axis | Options |
|---|---|
| **Placement** | `INTRUSIVE` (inside the class — can read private members) vs `NON_INTRUSIVE` (in the enclosing namespace) |
| **Missing-key policy** | default (throw on missing key) vs `WITH_DEFAULT` (fall back to the default-constructed member) vs `ONLY_SERIALIZE` (generate `to_json` only) |
| **Key naming** | member-name keys vs `WITH_NAMES` (explicit JSON key strings, decoupled from member names) |

`NLOHMANN_DEFINE_DERIVED_TYPE_*` variants additionally serialize a base class's members. `NLOHMANN_JSON_SERIALIZE_ENUM` (and its `_STRICT` form) generate the enum ↔ JSON mapping.

> [!TIP]
> Choose the variant by your input contract: `WITH_DEFAULT` for tolerant config-style inputs, the plain variant for strict schemas where a missing field is a hard error, `ONLY_SERIALIZE` for write-only DTOs (logs, telemetry), and `WITH_NAMES` when the wire format's keys differ from your C++ identifiers (e.g. `"first-name"` ↔ `firstName`).

---

## 7. Compile-Time Configuration: Full Macro Reference

Because the library has no runtime configuration, **macros are the entire configuration surface**. Define them before including the header or, preferably, on the CMake target so they propagate consistently.

### Behavior toggles

| Macro | Default | Effect | ABI-affecting? |
|---|---|---|---|
| `JSON_USE_IMPLICIT_CONVERSIONS` | `1` | `0` disables implicit `json → T` conversions; forces `get<T>()`/`get_to()` | No |
| `JSON_DIAGNOSTICS` | `0` | `1` adds JSON-Pointer paths to exception messages (adds parent tracking) | **Yes** |
| `JSON_DIAGNOSTIC_POSITIONS` | `0` | `1` records source byte positions (`start_pos()`/`end_pos()`) of parsed values | **Yes** |
| `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON` | `0` | `1` restores pre-3.x comparison semantics for discarded values | **Yes** |
| `JSON_NOEXCEPTION` | off | Disable all exceptions; errors call `abort()`. For `-fno-exceptions` builds | n/a |
| `JSON_NO_IO` | off | Remove all `<iostream>` use (no stream operators, no `parse(istream)`). For freestanding/embedded | No |
| `JSON_USE_GLOBAL_UDLS` | `1` | `0` keeps `_json` / `_json_pointer` literals out of the global namespace | No |
| `JSON_DISABLE_ENUM_SERIALIZATION` | off | Disable the default enum-as-integer conversion (avoids ambiguity with custom enum handling) | No |

### Feature-detection macros (read-only)

These are *set by the library* based on the active standard, but you can override the unsupported-compiler/version checks:

| Macro | Purpose |
|---|---|
| `JSON_HAS_CPP_11/14/17/20` | Detected language level; gates features like `std::optional`, `string_view`, `operator<=>` |
| `JSON_HAS_FILESYSTEM` / `JSON_HAS_EXPERIMENTAL_FILESYSTEM` | Whether `std::filesystem` conversions are available |
| `JSON_HAS_RANGES` | Whether C++20 ranges integration is active |
| `JSON_HAS_THREE_WAY_COMPARISON` | Whether `operator<=>` is provided |
| `JSON_HAS_STATIC_RTTI` | Whether `<any>`-based paths are enabled |
| `JSON_SKIP_LIBRARY_VERSION_CHECK` | Suppress the multi-header version-mismatch check |
| `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK` | Build on a compiler the library flags as unsupported |

### Advanced hooks

| Macro | Purpose |
|---|---|
| `JSON_ASSERT(x)` | Override the assertion macro (default `assert`); used at every invariant check |
| `JSON_TRY_USER` / `JSON_CATCH_USER` / `JSON_THROW_USER` | Replace the try/catch/throw mechanism (e.g. for custom error handling under `-fno-exceptions`) |
| `NLOHMANN_JSON_NAMESPACE_BEGIN` / `_END` / `NLOHMANN_JSON_NAMESPACE` | The version-and-config-encoding inline namespace (see §9) |
| `NLOHMANN_JSON_NAMESPACE_NO_VERSION` | Omit the version component from the inline namespace |
| `NLOHMANN_JSON_VERSION_MAJOR/MINOR/PATCH` | Compile-time version constants (currently 3 / 12 / 0) |

> [!CAUTION]
> The four **ABI-affecting** macros change `basic_json`'s memory layout or symbol names. If two translation units — or your code and a prebuilt dependency — disagree on any of them, you get One-Definition-Rule violations that manifest as link errors or, worse, silent memory corruption. The inline-namespace scheme (§9) turns *some* of these into clean link-time errors instead of runtime UB, but the safe rule is: **define ABI macros once, on the target, for the whole build graph.** Issue #3106 is the canonical report of getting this wrong via `find_package`.

---

## 8. Build & Integration

### Consuming the library (downstream)

Always link the imported target; never hand-roll include paths.

```cmake
find_package(nlohmann_json 3.12.0 REQUIRED)
target_link_libraries(my_app PRIVATE nlohmann_json::nlohmann_json)
```

Equivalent acquisition methods — `FetchContent`, `CPM`, `add_subdirectory`, single-header drop-in, and the system package managers — all surface the same `nlohmann_json::nlohmann_json` target. (Full snippets are in the User Guide §2.) Other build systems are first-class too: Meson (`dependency('nlohmann_json')`), Bazel, Buildroot/Buildsystem fixtures under `docs/mkdocs/docs/integration/`, and Swift Package Manager (`Package.swift` targets `single_include/nlohmann`, C++11).

### Building the library's own test/dev tree

```bash
mkdir build && cd build
cmake .. -DJSON_BuildTests=ON
cmake --build . -j
ctest -j
```

Relevant CMake options (from `tests/CMakeLists.txt` and the project options):

| Option | Default | Purpose |
|---|---|---|
| `JSON_BuildTests` | ON when top-level | Build the unit test suite |
| `JSON_Install` | ON when top-level | Generate/install the CMake package config |
| `JSON_MultipleHeaders` | — | Test against the multi-header layout vs the amalgamation |
| `JSON_Valgrind` | OFF | Run the suite under Valgrind |
| `JSON_FastTests` | OFF | Skip expensive/slow tests (used by some CI lanes) |
| `JSON_32bitTest` | AUTO | Enable the 32-bit unit test (ON/OFF/AUTO/ONLY) |
| `JSON_TestStandards` | "" | Restrict which C++ standards to test (default tests 11/14/17/20/23) |

> [!NOTE]
> The test build compiles each unit file against **every supported C++ standard** the toolchain provides (11, 14, 17, 20, 23), guarded by per-compiler capability checks in `tests/CMakeLists.txt` (e.g. Clang < 5.0 has no C++17, GCC < 9.0 has flaky C++20). This is why a full test run is large — the matrix is files × standards.

### C++20 modules

The library can be consumed as a C++20 module (`import nlohmann.json;`), exercised by `tests/module_cpp20`. There are known rough edges on some compilers (GCC redefinition warnings are suppressed via pragmas in `json.hpp`; MSVC C++23-module issues are tracked in #5002). Treat module support as available-but-evolving and pin compiler versions if you depend on it.

---

## 9. The Amalgamation & ABI Machinery

### Single-header generation

`single_include/nlohmann/json.hpp` is **generated**, never hand-edited. `tools/amalgamate/amalgamate.py` walks the multi-header include graph, inlines each `#include <nlohmann/...>` exactly once (tracking already-included files), and emits one self-contained header. The `Amalgamation`/`TranslationUnit` classes drive this; `tools/serve_header` watches the source tree and re-amalgamates on change for local docs/dev.

> [!IMPORTANT]
> If you patch the library in place, edit files under `include/nlohmann/` and **regenerate** the amalgamation — do not edit `single_include/` directly, or your change will be silently overwritten on the next build and will not match the multi-header form that `find_package` may install.

### The inline-namespace ABI guard

The real symbols live not in `nlohmann` but in a nested **inline namespace** whose name encodes the library version *and* the active ABI-affecting flags — produced by `NLOHMANN_JSON_NAMESPACE_BEGIN`/`_END`. The effect: a `json` compiled with `JSON_DIAGNOSTICS=1` and a `json` compiled with it off are *different types with different mangled names*. Linking incompatible objects then fails at link time (a clear error) rather than corrupting memory at runtime.

The `tests/abi/` tree exists solely to verify this: it links translation units built with mismatched `JSON_DIAGNOSTICS` settings (`tests/abi/diag/`) and old-vs-new inline-namespace builds (`tests/abi/inline_ns/`, e.g. `use_v3_10_5.cpp` linked with `use_current.cpp`) and asserts they coexist safely.

> [!TIP]
> If you intentionally need to mix versions in one binary (rare — e.g. a transitive dependency pins an older release), the inline-namespace scheme makes that possible *because* the versions are distinct types. `NLOHMANN_JSON_NAMESPACE_NO_VERSION` opts out of version encoding when you specifically want cross-version source compatibility.

---

## 10. Test & Quality Infrastructure

| Layer | Location | What it covers |
|---|---|---|
| Unit tests | `tests/src/unit-*.cpp` (doctest) | The full API, per C++ standard; the executable spec |
| ABI tests | `tests/abi/` | Inline-namespace and diagnostic-flag link compatibility |
| CMake-integration tests | `tests/cmake_*` | `find_package`, `add_subdirectory`, `FetchContent`, `import`, `target_include_directories` all resolve `nlohmann_json::nlohmann_json` |
| Documentation tests | `docs/Makefile` (`check_output`) | Every `docs/mkdocs/docs/examples/*.cpp` is compiled and its output diffed against the committed `.output` |
| Fuzzers | `tests/Makefile` (OSS-Fuzz) | `parse_{json,bson,cbor,msgpack,ubjson,bjdata}_fuzzer` feed adversarial bytes to each parser |
| Benchmarks | `tests/benchmarks/` | Parse/serialize throughput tracking |
| 32-bit test | `tests/src/unit-32bit.cpp` | Size-type and pointer behavior on 32-bit targets |

> [!NOTE]
> The example corpus doubles as documentation **and** a regression test: `docs/Makefile`'s `check_output_portable` target compiles each example with `-DJSON_USE_GLOBAL_UDLS=0` and diffs stdout, excluding a few platform-dependent ones (`meta`, `max_size`, `std_hash`, `basic_json__CompatibleType`). This is why the examples are trustworthy to copy verbatim — CI fails if they drift.

The project participates in OSS-Fuzz (continuous fuzzing of all six parsers), runs the suite under Valgrind, and uses `astyle` (`tools/astyle`) for enforced formatting.

---

## 11. Tooling

| Tool | Language | Purpose |
|---|---|---|
| `tools/amalgamate` | Python | Generate `single_include/nlohmann/json.hpp` from the multi-header tree |
| `tools/serve_header` | Python | Watch the source tree, re-amalgamate on change, serve headers for local dev (a `watchdog`-based file watcher + threaded HTTP server) |
| `tools/macro_builder` | C++ | Generate the variadic `NLOHMANN_JSON_PASTE` macro expansion ladder (supports up to 64 members in the `DEFINE_TYPE` macros) |
| `tools/generate_natvis` | Python (Jinja2) | Produce a Visual Studio `.natvis` debugger visualizer for the chosen version |
| `tools/gdb_pretty_printer` | Python | GDB pretty-printer so `json` values render readably in the debugger |
| `tools/astyle` | config | Code-style enforcement |

> [!TIP]
> For day-to-day debugging, install the GDB pretty-printer (`tools/gdb_pretty_printer/nlohmann-json.py`) or the generated `.natvis` file. Without them, a `basic_json` shows as its raw union/tag members; with them you see the actual JSON value.

---

## 12. Security Analysis

This is a parsing library that routinely processes **untrusted input**, so its security posture matters to any integrator.

| Concern | Posture |
|---|---|
| Memory safety on deep nesting | Container destruction is **iterative** (§4), preventing stack-overflow on adversarial deeply-nested documents. |
| Parser hardening | All six parsers (JSON + 5 binary) are continuously fuzzed via OSS-Fuzz; the `tests/reports/` directory archives historical fuzzing findings. |
| Error signaling | Malformed input yields typed `parse_error` exceptions (or `abort` under `JSON_NOEXCEPTION`), never silent acceptance. Validate with `accept()` to reject before allocating a DOM. |
| Integer handling | Distinct signed/unsigned/float number types avoid silent sign confusion; very large integers are preserved (see `value()` width caveat in the User Guide). |
| No code execution / no I/O by default | The library does no network or filesystem I/O of its own; with `JSON_NO_IO` it drops even `<iostream>`. There is no `eval`-like surface. |
| Locale sensitivity | Number lexing has had locale-related edge cases (issue #5198 reports a TOCTOU between lexer construction and locale changes affecting float parsing). Avoid mutating the global C locale concurrently with parsing. |

> [!WARNING]
> When parsing untrusted input at scale, prefer `accept()` (or `sax_parse()` with your own bounded consumer) over building a full DOM. A maliciously large or deeply nested document still consumes memory proportional to its size when fully materialized. Impose your own input-size limits upstream; the library does not cap document size for you.

> [!CAUTION]
> `JSON_NOEXCEPTION` converts every error into `abort()`. In a service that must not crash, do **not** enable it without also supplying `JSON_THROW_USER`/`JSON_TRY_USER`/`JSON_CATCH_USER` hooks that route errors to your own recovery path — otherwise a single malformed request can take down the process.

---

## 13. Code Quality & Conventions

- **Single-class architecture.** Nearly all behavior hangs off `basic_json`; the public surface is intentionally narrow and STL-mirroring (`begin/end`, `size`, `at`, `insert`, `erase`, `swap`, comparison operators) so it composes with standard algorithms.
- **Template-everything with defaults.** Every storage type is a parameter, enabling `ordered_json` and fully custom instantiations without touching the core.
- **SFINAE-heavy metaprogramming.** `detail/meta/` provides the type-trait and feature-detection layer (`enable_if_t`, `is_compatible_type`, `detected`, C++-version shims) that makes the implicit conversions and macro generators work across C++11–23.
- **Doxygen `@sa` links.** Every public member carries an `@sa https://json.nlohmann.me/api/...` link tying source to the rendered reference — the in-repo docs and the headers are kept in lockstep.
- **Hedley** macros provide portable compiler-attribute wrappers (`JSON_HEDLEY_*`) for warnings, likely/unlikely, and unused-result hints.
- **Exception design.** A single `exception` base with five typed subclasses, each error carrying a **stable numeric id** (101+/2xx/3xx/4xx/5xx) — ids are a public contract and don't change meaning across releases.

---

## 14. Extending the Library

| Extension point | How |
|---|---|
| Serialize a custom type | Free `to_json`/`from_json` in the type's namespace, or a `NLOHMANN_DEFINE_TYPE_*` macro (§6) |
| Custom enum mapping | `NLOHMANN_JSON_SERIALIZE_ENUM` / `_STRICT` |
| Alternate object ordering | Use `ordered_json`, or instantiate `basic_json` with your own ordered map type |
| Fully custom value types | Instantiate `basic_json<...>` with your own object/array/string/number/allocator parameters |
| Custom serializer | Supply your own `JSONSerializer` template parameter instead of `adl_serializer` |
| Streaming consumption | Implement `json_sax<basic_json>` and feed it via `sax_parse()` |
| Inject a base class | The `CustomBaseClass` parameter (`json_base_class_t`) lets every value carry extra state/behavior |

> [!TIP]
> The lightest-weight way to add per-value metadata (e.g. source positions, annotations) without forking the library is the `CustomBaseClass` template parameter — `basic_json` publicly derives from it. This is how `JSON_DIAGNOSTIC_POSITIONS` layers in `start_pos()`/`end_pos()` without bloating the default type.

---

## 15. Known Issues & Improvement Areas

Derived from the project's open issue tracker and source observations. These are *integration-relevant* caveats, not a changelog.

| Area | Issue | Notes for integrators |
|---|---|---|
| C++17 `std::optional` | #4864 | Optional-member support requires C++17 and isn't auto-enabled in every configuration; verify with a round-trip test if you depend on it. |
| Diagnostics via `find_package` | #3106 | Enabling `JSON_DIAGNOSTICS` only on the consumer (not propagated through the target) causes ABI mismatch. Define it on the target. |
| Locale / float parsing | #5198 | A TOCTOU between lexer construction and a concurrent locale change can truncate floats. Don't mutate the global locale during parsing. |
| Destructor `noexcept` | #5135 | The `basic_json` destructor's iterative teardown allocates, which technically conflicts with strict `noexcept` semantics under memory pressure. |
| MSVC C++23 modules | #5002, #5197 | C++23-module + MSVC has open compatibility gaps (including `u8string` storage). Pin compilers for module builds. |
| Binary `<discarded>` encoding | #4714 | Discarded values inside arrays/objects can produce invalid binary-format encodings; avoid serializing partially-discarded documents. |
| Enum default surprise | #3992 | `NLOHMANN_JSON_SERIALIZE_ENUM`'s first entry is the silent fallback. Use `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` to fail loudly instead. |
| `std::format` support | #4261 | No `std::formatter` specialization yet; use `dump()` to feed formatters. |

> [!NOTE]
> None of the above blocks production use — the library is mature and extremely widely deployed. They are edge cases an integrator should know before enabling modules, `JSON_NOEXCEPTION`, or aggressive diagnostic flags in a large multi-library build.

---

## 16. Quick Reference

### Integration (CMake)

```cmake
find_package(nlohmann_json 3.12.0 REQUIRED)
target_link_libraries(app PRIVATE nlohmann_json::nlohmann_json)
# enable richer diagnostics for the whole target graph:
target_compile_definitions(app PRIVATE JSON_DIAGNOSTICS=1)
```

### ABI-affecting macros (define once, build-wide)

`JSON_DIAGNOSTICS` · `JSON_DIAGNOSTIC_POSITIONS` · `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON` · the inline-namespace version encoding.

### Build & test the source tree

```bash
cmake -B build -DJSON_BuildTests=ON && cmake --build build -j && ctest --test-dir build -j
```

### Regenerate the single header

```bash
# after editing include/nlohmann/** :
python3 tools/amalgamate/amalgamate.py -c <config> -s include  # then commit single_include/
```

### Internal layer map

| Layer | Directory | Key types |
|---|---|---|
| Public | `include/nlohmann/` | `basic_json`, `json_pointer`, `json_sax`, `adl_serializer`, `ordered_map` |
| Input | `detail/input/` | `lexer`, `parser`, `binary_reader`, `input_adapters` |
| Output | `detail/output/` | `serializer`, `binary_writer`, `output_adapters` |
| Conversions | `detail/conversions/` | `to_json`, `from_json` (ADL hooks) |
| Meta | `detail/meta/` | type traits, feature detection, C++-version shims |

### Exception id ranges

| Type | IDs | Cause |
|---|---|---|
| `parse_error` | 1xx | Malformed input |
| `invalid_iterator` | 2xx | Iterator misuse |
| `type_error` | 3xx | Wrong type for the operation |
| `out_of_range` | 4xx | Missing key/index |
| `other_error` | 5xx | e.g. failed `test` patch op |

---

_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._
