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
- Executive Summary
- Architecture Overview
- Source Layout
- The
basic_jsonValue Model - Pipelines: Parsing, Serialization, Binary Formats
- The Serialization Customization System (ADL)
- Compile-Time Configuration: Full Macro Reference
- Build & Integration
- The Amalgamation & ABI Machinery
- Test & Quality Infrastructure
- Tooling
- Security Analysis
- Code Quality & Conventions
- Extending the Library
- Known Issues & Improvement Areas
- 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+ thedetail/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 thetools/amalgamatepipeline.
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:
basic_jsonis a template,jsonis 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::jsonis the default instantiation;nlohmann::ordered_jsonswaps the object container for an insertion-ordered map.- Customization happens through ADL (Argument-Dependent Lookup), not inheritance. To make a type serializable you provide free
to_json/from_jsonfunctions (directly or via macros); the library finds them through theadl_serializerindirection. 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 smallinclude/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_tto accept/reject/transform elements (rejected subtrees becomediscarded). json_sax_dom_parser(or the callback variant) consumes the events and materializes thebasic_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.
basic_json's converting constructor andget<T>()delegate toJSONSerializer<T>— by defaultadl_serializer<T>.adl_serializer<T>::to_json/from_jsonsimply call unqualifiedto_json/from_json, triggering ADL inT's namespace.- The library ships
to_json/from_jsonoverloads for all standard types (arithmetic,std::string,std::vector,std::map,std::array,std::optional(C++17), tuples, etc.) indetail/conversions/. - 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_DEFAULTfor tolerant config-style inputs, the plain variant for strict schemas where a missing field is a hard error,ONLY_SERIALIZEfor write-only DTOs (logs, telemetry), andWITH_NAMESwhen 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 viafind_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 editsingle_include/directly, or your change will be silently overwritten on the next build and will not match the multi-header form thatfind_packagemay 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_VERSIONopts 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'scheck_output_portabletarget compiles each example with-DJSON_USE_GLOBAL_UDLS=0and 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.natvisfile. Without them, abasic_jsonshows 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()(orsax_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_NOEXCEPTIONconverts every error intoabort(). In a service that must not crash, do not enable it without also supplyingJSON_THROW_USER/JSON_TRY_USER/JSON_CATCH_USERhooks 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_jsonand 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
@salinks. 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
exceptionbase 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
CustomBaseClasstemplate parameter —basic_jsonpublicly derives from it. This is howJSON_DIAGNOSTIC_POSITIONSlayers instart_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 |