Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

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

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.

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.


Table of Contents

  1. Executive Summary
  2. Architecture Overview
  3. Source Layout
  4. The basic_json Value Model
  5. Pipelines: Parsing, Serialization, Binary Formats
  6. The Serialization Customization System (ADL)
  7. Compile-Time Configuration: Full Macro Reference
  8. Build & Integration
  9. The Amalgamation & ABI Machinery
  10. Test & Quality Infrastructure
  11. Tooling
  12. Security Analysis
  13. Code Quality & Conventions
  14. Extending the Library
  15. Known Issues & Improvement Areas
  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 and §9).


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.

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

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)

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

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

Regenerate the single header

# 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