summaryrefslogtreecommitdiff
path: root/docs/handbook/json4cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docs/handbook/json4cpp')
-rw-r--r--docs/handbook/json4cpp/architecture.md613
-rw-r--r--docs/handbook/json4cpp/basic-usage.md601
-rw-r--r--docs/handbook/json4cpp/binary-formats.md411
-rw-r--r--docs/handbook/json4cpp/building.md430
-rw-r--r--docs/handbook/json4cpp/code-style.md209
-rw-r--r--docs/handbook/json4cpp/custom-types.md465
-rw-r--r--docs/handbook/json4cpp/element-access.md581
-rw-r--r--docs/handbook/json4cpp/exception-handling.md368
-rw-r--r--docs/handbook/json4cpp/iteration.md339
-rw-r--r--docs/handbook/json4cpp/json-patch.md341
-rw-r--r--docs/handbook/json4cpp/json-pointer.md361
-rw-r--r--docs/handbook/json4cpp/overview.md330
-rw-r--r--docs/handbook/json4cpp/parsing-internals.md493
-rw-r--r--docs/handbook/json4cpp/performance.md275
-rw-r--r--docs/handbook/json4cpp/sax-interface.md337
-rw-r--r--docs/handbook/json4cpp/serialization.md528
-rw-r--r--docs/handbook/json4cpp/testing.md190
-rw-r--r--docs/handbook/json4cpp/value-types.md474
18 files changed, 7346 insertions, 0 deletions
diff --git a/docs/handbook/json4cpp/architecture.md b/docs/handbook/json4cpp/architecture.md
new file mode 100644
index 0000000000..d0140b8bbf
--- /dev/null
+++ b/docs/handbook/json4cpp/architecture.md
@@ -0,0 +1,613 @@
+# json4cpp — Architecture
+
+## Overview
+
+The json4cpp library (nlohmann/json 3.12.0) is organized as a heavily
+templatized, header-only C++ library. The architecture revolves around a single
+class template, `basic_json`, whose template parameters allow customization of
+every underlying storage type. This document describes the internal structure,
+class hierarchy, memory layout, and key design patterns.
+
+## The `basic_json` Class Template
+
+### Template Declaration
+
+The full template declaration in `include/nlohmann/json_fwd.hpp`:
+
+```cpp
+template<
+ template<typename U, typename V, typename... Args> class ObjectType = std::map,
+ template<typename U, typename... Args> class ArrayType = std::vector,
+ class StringType = std::string,
+ class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename U> class AllocatorType = std::allocator,
+ template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer,
+ class BinaryType = std::vector<std::uint8_t>,
+ class CustomBaseClass = void
+>
+class basic_json;
+```
+
+Each parameter controls a specific aspect:
+
+| Parameter | Purpose | Default |
+|---|---|---|
+| `ObjectType` | Map template for JSON objects | `std::map` |
+| `ArrayType` | Sequential container for JSON arrays | `std::vector` |
+| `StringType` | String type for keys and string values | `std::string` |
+| `BooleanType` | Boolean storage | `bool` |
+| `NumberIntegerType` | Signed integer type | `std::int64_t` |
+| `NumberUnsignedType` | Unsigned integer type | `std::uint64_t` |
+| `NumberFloatType` | Floating-point type | `double` |
+| `AllocatorType` | Allocator template | `std::allocator` |
+| `JSONSerializer` | Serializer template for custom types | `adl_serializer` |
+| `BinaryType` | Container for binary data | `std::vector<std::uint8_t>` |
+| `CustomBaseClass` | Optional base class for extension | `void` |
+
+### Default Type Aliases
+
+Two default specializations are defined:
+
+```cpp
+using json = basic_json<>;
+using ordered_json = basic_json<nlohmann::ordered_map>;
+```
+
+The `ordered_json` type preserves insertion order by using `ordered_map`
+instead of `std::map`.
+
+### Derived Type Aliases
+
+Within `basic_json`, the following public type aliases expose the actual
+types used for JSON value storage:
+
+```cpp
+using object_t = ObjectType<StringType, basic_json,
+ default_object_comparator_t,
+ AllocatorType<std::pair<const StringType, basic_json>>>;
+using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+using string_t = StringType;
+using boolean_t = BooleanType;
+using number_integer_t = NumberIntegerType;
+using number_unsigned_t = NumberUnsignedType;
+using number_float_t = NumberFloatType;
+using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
+using object_comparator_t = detail::actual_object_comparator_t<basic_json>;
+```
+
+The `default_object_comparator_t` depends on the C++ standard level:
+- C++14 and above: `std::less<>` (transparent comparator)
+- C++11: `std::less<StringType>`
+
+## Inheritance Structure
+
+### Base Class: `json_base_class`
+
+`basic_json` inherits from `detail::json_base_class<CustomBaseClass>`:
+
+```cpp
+class basic_json
+ : public ::nlohmann::detail::json_base_class<CustomBaseClass>
+```
+
+When `CustomBaseClass` is `void` (the default), this is an empty base class
+that adds no overhead. When a user-provided type is specified, it becomes
+the base, enabling extension without modifying the library.
+
+### Friend Declarations
+
+The class declares friendship with its internal collaborators:
+
+```cpp
+template<detail::value_t> friend struct detail::external_constructor;
+template<typename> friend class ::nlohmann::json_pointer;
+template<typename BasicJsonType, typename InputType>
+ friend class ::nlohmann::detail::parser;
+friend ::nlohmann::detail::serializer<basic_json>;
+template<typename BasicJsonType>
+ friend class ::nlohmann::detail::iter_impl;
+template<typename BasicJsonType, typename CharType>
+ friend class ::nlohmann::detail::binary_writer;
+template<typename BasicJsonType, typename InputType, typename SAX>
+ friend class ::nlohmann::detail::binary_reader;
+template<typename BasicJsonType, typename InputAdapterType>
+ friend class ::nlohmann::detail::json_sax_dom_parser;
+template<typename BasicJsonType, typename InputAdapterType>
+ friend class ::nlohmann::detail::json_sax_dom_callback_parser;
+friend class ::nlohmann::detail::exception;
+```
+
+## Memory Layout: `json_value` Union
+
+### The `json_value` Union
+
+The core storage is a union that keeps the `basic_json` object at minimum
+size:
+
+```cpp
+union json_value
+{
+ object_t* object; // pointer — 8 bytes
+ array_t* array; // pointer — 8 bytes
+ string_t* string; // pointer — 8 bytes
+ binary_t* binary; // pointer — 8 bytes
+ boolean_t boolean; // typically 1 byte
+ number_integer_t number_integer; // 8 bytes
+ number_unsigned_t number_unsigned; // 8 bytes
+ number_float_t number_float; // 8 bytes
+
+ json_value() = default;
+ json_value(boolean_t v) noexcept;
+ json_value(number_integer_t v) noexcept;
+ json_value(number_unsigned_t v) noexcept;
+ json_value(number_float_t v) noexcept;
+ json_value(value_t t); // creates empty container for compound types
+
+ void destroy(value_t t); // type-aware destructor
+};
+```
+
+**Key design decisions:**
+
+1. **Pointers for variable-length types.** Objects, arrays, strings, and binaries
+ are stored as pointers. This keeps the union at 8 bytes on 64-bit systems
+ and avoids calling constructors/destructors for the union members of
+ non-active types.
+
+2. **Value semantics for scalars.** Booleans, integers, and floats are stored
+ directly in the union without indirection.
+
+3. **Heap allocation via `create<T>()`.** The private static method
+ `basic_json::create<T>(Args...)` uses the `AllocatorType` to allocate
+ and construct heap objects.
+
+### The `data` Struct
+
+The union is wrapped in a `data` struct that pairs it with the type tag:
+
+```cpp
+struct data
+{
+ value_t m_type = value_t::null;
+ json_value m_value = {};
+
+ data(const value_t v);
+ data(size_type cnt, const basic_json& val);
+ data() noexcept = default;
+ data(data&&) noexcept = default;
+
+ ~data() noexcept { m_value.destroy(m_type); }
+};
+```
+
+The instance lives in `basic_json` as `data m_data`:
+
+```cpp
+data m_data = {}; // the type + value
+
+#if JSON_DIAGNOSTICS
+basic_json* m_parent = nullptr; // parent pointer for diagnostics
+#endif
+
+#if JSON_DIAGNOSTIC_POSITIONS
+std::size_t start_position = std::string::npos;
+std::size_t end_position = std::string::npos;
+#endif
+```
+
+### Destruction Strategy
+
+The `json_value::destroy(value_t)` method handles recursive destruction
+without stack overflow. For arrays and objects, it uses an iterative
+approach with a `std::vector<basic_json>` stack:
+
+```cpp
+void destroy(value_t t) {
+ // For arrays/objects: flatten children onto a heap-allocated stack
+ if (t == value_t::array || t == value_t::object) {
+ std::vector<basic_json> stack;
+ // Move children to stack
+ while (!stack.empty()) {
+ basic_json current_item(std::move(stack.back()));
+ stack.pop_back();
+ // Move current_item's children to stack
+ // current_item safely destructed here (no children)
+ }
+ }
+ // Deallocate the container itself
+ switch (t) {
+ case value_t::object: /* deallocate object */ break;
+ case value_t::array: /* deallocate array */ break;
+ case value_t::string: /* deallocate string */ break;
+ case value_t::binary: /* deallocate binary */ break;
+ default: break;
+ }
+}
+```
+
+This prevents stack overflow when destroying deeply nested JSON structures.
+
+## The `value_t` Enumeration
+
+Defined in `detail/value_t.hpp`:
+
+```cpp
+enum class value_t : std::uint8_t
+{
+ null, // null value
+ object, // unordered set of name/value pairs
+ array, // ordered collection of values
+ string, // string value
+ boolean, // boolean value
+ number_integer, // signed integer
+ number_unsigned, // unsigned integer
+ number_float, // floating-point
+ binary, // binary array
+ discarded // discarded by parser callback
+};
+```
+
+A comparison operator defines a Python-like ordering:
+`null < boolean < number < object < array < string < binary`
+
+With C++20, this uses `std::partial_ordering` via the spaceship operator.
+
+## Class Invariant
+
+The `assert_invariant()` method (called at the end of every constructor)
+enforces the following:
+
+```cpp
+void assert_invariant(bool check_parents = true) const noexcept
+{
+ JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr);
+ JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr);
+ JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr);
+ JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr);
+}
+```
+
+When `JSON_DIAGNOSTICS` is enabled, it additionally checks that all children
+have their `m_parent` pointer set to `this`.
+
+## Internal Component Architecture
+
+### Input Pipeline
+
+```
+Input Source → Input Adapter → Lexer → Parser → DOM / SAX Events
+```
+
+1. **Input Adapters** (`detail/input/input_adapters.hpp`)
+ - `file_input_adapter` — wraps `std::FILE*`
+ - `input_stream_adapter` — wraps `std::istream`
+ - `iterator_input_adapter` — wraps iterator pairs
+ - `contiguous_input_adapter` — optimized for contiguous memory
+
+2. **Lexer** (`detail/input/lexer.hpp`)
+ - `lexer_base<BasicJsonType>` — defines `token_type` enumeration
+ - `lexer<BasicJsonType, InputAdapterType>` — the tokenizer
+ - Token types: `literal_true`, `literal_false`, `literal_null`,
+ `value_string`, `value_unsigned`, `value_integer`, `value_float`,
+ `begin_array`, `begin_object`, `end_array`, `end_object`,
+ `name_separator`, `value_separator`, `parse_error`, `end_of_input`
+
+3. **Parser** (`detail/input/parser.hpp`)
+ - `parser<BasicJsonType, InputAdapterType>` — recursive descent parser
+ - Supports callback-based filtering via `parser_callback_t`
+ - Supports both DOM parsing and SAX event dispatch
+
+4. **SAX Interface** (`detail/input/json_sax.hpp`)
+ - `json_sax<BasicJsonType>` — abstract base with virtual methods
+ - `json_sax_dom_parser` — builds a DOM tree from SAX events
+ - `json_sax_dom_callback_parser` — DOM builder with filtering
+
+### Output Pipeline
+
+```
+basic_json → Serializer → Output Adapter → Destination
+```
+
+1. **Serializer** (`detail/output/serializer.hpp`)
+ - `serializer<BasicJsonType>` — converts JSON to text
+ - Handles indentation, UTF-8 validation, number formatting
+ - `error_handler_t`: `strict`, `replace`, `ignore` for invalid UTF-8
+
+2. **Binary Writer** (`detail/output/binary_writer.hpp`)
+ - `binary_writer<BasicJsonType, CharType>` — writes CBOR, MessagePack,
+ UBJSON, BJData, BSON
+
+3. **Output Adapters** (`detail/output/output_adapters.hpp`)
+ - `output_vector_adapter` — writes to `std::vector<CharType>`
+ - `output_stream_adapter` — writes to `std::ostream`
+ - `output_string_adapter` — writes to a string type
+
+### Iterator System
+
+```
+basic_json::iterator → iter_impl<basic_json>
+ → internal_iterator (union of object/array/primitive iterators)
+```
+
+- `iter_impl<BasicJsonType>` — the main iterator class
+- `internal_iterator<BasicJsonType>` — holds the active iterator:
+ - `typename object_t::iterator object_iterator` for objects
+ - `typename array_t::iterator array_iterator` for arrays
+ - `primitive_iterator_t` for scalars (0 = begin, 1 = end)
+- `json_reverse_iterator<Base>` — reverse iterator adapter
+- `iteration_proxy<IteratorType>` — returned by `items()`, exposes
+ `key()` and `value()` methods
+
+### Conversion System
+
+The ADL (Argument-Dependent Lookup) design enables seamless integration of
+user-defined types:
+
+```
+User Type → to_json(json&, const T&) → json value
+json value → from_json(const json&, T&) → User Type
+```
+
+- `adl_serializer<T>` — default serializer that delegates via ADL
+- `detail/conversions/to_json.hpp` — built-in `to_json()` overloads
+ for standard types (arithmetic, strings, containers, pairs, tuples)
+- `detail/conversions/from_json.hpp` — built-in `from_json()` overloads
+
+### JSON Pointer and Patch
+
+- `json_pointer<RefStringType>` — implements RFC 6901, stores parsed
+ reference tokens as `std::vector<string_t>`
+- Patch operations implemented directly in `basic_json::patch_inplace()`
+ as an inline method operating on the `basic_json` itself
+
+## The `ordered_map` Container
+
+Defined in `include/nlohmann/ordered_map.hpp`:
+
+```cpp
+template<class Key, class T, class IgnoredLess = std::less<Key>,
+ class Allocator = std::allocator<std::pair<const Key, T>>>
+struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
+{
+ using key_type = Key;
+ using mapped_type = T;
+ using Container = std::vector<std::pair<const Key, T>, Allocator>;
+
+ std::pair<iterator, bool> emplace(const key_type& key, T&& t);
+ T& operator[](const key_type& key);
+ T& at(const key_type& key);
+ size_type erase(const key_type& key);
+ size_type count(const key_type& key) const;
+ iterator find(const key_type& key);
+ // ...
+};
+```
+
+It inherits from `std::vector` and implements map-like operations with
+linear search. The `IgnoredLess` parameter exists for API compatibility
+with `std::map` but is not used — instead, `std::equal_to<>` (C++14) or
+`std::equal_to<Key>` (C++11) is used for key comparison.
+
+## The `byte_container_with_subtype` Class
+
+Wraps binary data with an optional subtype tag for binary formats
+(MsgPack ext types, CBOR tags, BSON binary subtypes):
+
+```cpp
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+public:
+ using container_type = BinaryType;
+ using subtype_type = std::uint64_t;
+
+ void set_subtype(subtype_type subtype_) noexcept;
+ constexpr subtype_type subtype() const noexcept;
+ constexpr bool has_subtype() const noexcept;
+ void clear_subtype() noexcept;
+
+private:
+ subtype_type m_subtype = 0;
+ bool m_has_subtype = false;
+};
+```
+
+## Namespace Organization
+
+The library uses inline namespaces for ABI versioning:
+
+```cpp
+NLOHMANN_JSON_NAMESPACE_BEGIN // expands to: namespace nlohmann { inline namespace ... {
+// ...
+NLOHMANN_JSON_NAMESPACE_END // expands to: } }
+```
+
+The inner inline namespace name encodes configuration flags to prevent
+ABI mismatches when different translation units are compiled with
+different macro settings. The `detail` sub-namespace is not part of the
+public API.
+
+## Template Metaprogramming Techniques
+
+### SFINAE and Type Traits
+
+Located in `detail/meta/type_traits.hpp`, these traits control overload
+resolution:
+
+- `is_basic_json<T>` — checks if T is a `basic_json` specialization
+- `is_compatible_type<BasicJsonType, T>` — checks if T can be stored
+- `is_getable<BasicJsonType, T>` — checks if `get<T>()` works
+- `has_from_json<BasicJsonType, T>` — checks for `from_json()` overload
+- `has_non_default_from_json<BasicJsonType, T>` — non-void return version
+- `is_usable_as_key_type<Comparator, KeyType, T>` — for heterogeneous lookup
+- `is_comparable<Comparator, A, B>` — checks comparability
+
+### Priority Tags
+
+The `get_impl()` method uses priority tags (`detail::priority_tag<N>`)
+to control overload resolution order:
+
+```cpp
+template<typename ValueType>
+ValueType get_impl(detail::priority_tag<0>) const; // standard from_json
+template<typename ValueType>
+ValueType get_impl(detail::priority_tag<1>) const; // non-default from_json
+template<typename BasicJsonType>
+BasicJsonType get_impl(detail::priority_tag<2>) const; // cross-json conversion
+template<typename BasicJsonType>
+basic_json get_impl(detail::priority_tag<3>) const; // identity
+template<typename PointerType>
+auto get_impl(detail::priority_tag<4>) const; // pointer access
+```
+
+Higher priority tags are preferred during overload resolution.
+
+### External Constructors
+
+The `detail::external_constructor<value_t>` template specializations
+handle constructing `json_value` instances for specific types:
+
+```cpp
+template<> struct external_constructor<value_t::string>;
+template<> struct external_constructor<value_t::number_float>;
+template<> struct external_constructor<value_t::number_unsigned>;
+template<> struct external_constructor<value_t::number_integer>;
+template<> struct external_constructor<value_t::array>;
+template<> struct external_constructor<value_t::object>;
+template<> struct external_constructor<value_t::boolean>;
+template<> struct external_constructor<value_t::binary>;
+```
+
+## Diagnostics Architecture
+
+### `JSON_DIAGNOSTICS` Mode
+
+When enabled, each `basic_json` node stores a `m_parent` pointer:
+
+```cpp
+#if JSON_DIAGNOSTICS
+basic_json* m_parent = nullptr;
+#endif
+```
+
+The `set_parents()` and `set_parent()` methods maintain these links.
+On errors, `exception::diagnostics()` walks the parent chain to build
+a JSON Pointer path showing where in the document the error occurred:
+
+```
+[json.exception.type_error.302] (/config/debug) type must be boolean, but is string
+```
+
+### `JSON_DIAGNOSTIC_POSITIONS` Mode
+
+When enabled, byte offsets from parsing are stored:
+
+```cpp
+#if JSON_DIAGNOSTIC_POSITIONS
+std::size_t start_position = std::string::npos;
+std::size_t end_position = std::string::npos;
+#endif
+```
+
+Error messages then include `(bytes N-M)` indicating the exact input range.
+
+## Copy and Move Semantics
+
+### Copy Constructor
+
+Deep-copies the value based on type. For compound types (object, array,
+string, binary), the heap-allocated data is cloned:
+
+```cpp
+basic_json(const basic_json& other)
+ : json_base_class_t(other)
+{
+ m_data.m_type = other.m_data.m_type;
+ switch (m_data.m_type) {
+ case value_t::object: m_data.m_value = *other.m_data.m_value.object; break;
+ case value_t::array: m_data.m_value = *other.m_data.m_value.array; break;
+ case value_t::string: m_data.m_value = *other.m_data.m_value.string; break;
+ // ... scalar types are copied directly
+ }
+ set_parents();
+}
+```
+
+### Move Constructor
+
+Transfers ownership and invalidates the source:
+
+```cpp
+basic_json(basic_json&& other) noexcept
+ : json_base_class_t(std::forward<json_base_class_t>(other)),
+ m_data(std::move(other.m_data))
+{
+ other.m_data.m_type = value_t::null;
+ other.m_data.m_value = {};
+ set_parents();
+}
+```
+
+### Copy-and-Swap Assignment
+
+Uses the copy-and-swap idiom for exception safety:
+
+```cpp
+basic_json& operator=(basic_json other) noexcept {
+ using std::swap;
+ swap(m_data.m_type, other.m_data.m_type);
+ swap(m_data.m_value, other.m_data.m_value);
+ json_base_class_t::operator=(std::move(other));
+ set_parents();
+ return *this;
+}
+```
+
+## Comparison Architecture
+
+### C++20 Path (Three-Way Comparison)
+
+When `JSON_HAS_THREE_WAY_COMPARISON` is true:
+
+```cpp
+bool operator==(const_reference rhs) const noexcept;
+bool operator!=(const_reference rhs) const noexcept;
+std::partial_ordering operator<=>(const_reference rhs) const noexcept;
+```
+
+### Pre-C++20 Path
+
+Individual comparison operators are defined as `friend` functions:
+
+```cpp
+friend bool operator==(const_reference lhs, const_reference rhs) noexcept;
+friend bool operator!=(const_reference lhs, const_reference rhs) noexcept;
+friend bool operator<(const_reference lhs, const_reference rhs) noexcept;
+friend bool operator<=(const_reference lhs, const_reference rhs) noexcept;
+friend bool operator>(const_reference lhs, const_reference rhs) noexcept;
+friend bool operator>=(const_reference lhs, const_reference rhs) noexcept;
+```
+
+Both paths use the `JSON_IMPLEMENT_OPERATOR` macro internally, which handles:
+1. Same-type comparison (delegates to underlying type's operator)
+2. Cross-numeric-type comparison (int vs float, signed vs unsigned)
+3. Unordered comparison (NaN, discarded values)
+4. Different-type comparison (compares `value_t` ordering)
+
+## `std` Namespace Specializations
+
+The library provides:
+
+```cpp
+namespace std {
+ template<> struct hash<nlohmann::json> { ... };
+ template<> struct less<nlohmann::detail::value_t> { ... };
+ void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept; // pre-C++20 only
+}
+```
+
+The hash function delegates to `nlohmann::detail::hash()` which recursively
+hashes the JSON value based on its type.
diff --git a/docs/handbook/json4cpp/basic-usage.md b/docs/handbook/json4cpp/basic-usage.md
new file mode 100644
index 0000000000..80b9b3a176
--- /dev/null
+++ b/docs/handbook/json4cpp/basic-usage.md
@@ -0,0 +1,601 @@
+# json4cpp — Basic Usage
+
+## Including the Library
+
+```cpp
+#include <nlohmann/json.hpp>
+
+// Convenience alias
+using json = nlohmann::json;
+```
+
+Or with the forward declaration header (for header files):
+
+```cpp
+#include <nlohmann/json_fwd.hpp> // declares json, ordered_json, json_pointer
+```
+
+## Creating JSON Values
+
+### Null
+
+```cpp
+json j; // default constructor → null
+json j = nullptr; // explicit null
+json j(nullptr); // explicit null
+json j(json::value_t::null); // from value_t enum
+```
+
+### Boolean
+
+```cpp
+json j = true;
+json j = false;
+json j(json::value_t::boolean); // false (default-initialized)
+```
+
+### Numbers
+
+```cpp
+// Integer (stored as number_integer_t = std::int64_t)
+json j = 42;
+json j = -100;
+
+// Unsigned (stored as number_unsigned_t = std::uint64_t)
+json j = 42u;
+json j = static_cast<std::uint64_t>(100);
+
+// Floating-point (stored as number_float_t = double)
+json j = 3.14;
+json j = 1.0e10;
+```
+
+### String
+
+```cpp
+json j = "hello world";
+json j = std::string("hello");
+
+// With C++17 string_view:
+json j = std::string_view("hello");
+```
+
+### Array
+
+```cpp
+// From initializer list
+json j = {1, 2, 3, 4, 5};
+
+// Explicit array factory
+json j = json::array(); // empty array
+json j = json::array({1, 2, 3}); // pre-populated
+
+// From value_t enum
+json j(json::value_t::array); // empty array
+
+// From count and value
+json j(5, "x"); // ["x", "x", "x", "x", "x"]
+```
+
+### Object
+
+```cpp
+// From initializer list of key-value pairs
+json j = {
+ {"name", "Alice"},
+ {"age", 30},
+ {"active", true}
+};
+
+// Explicit object factory
+json j = json::object();
+json j = json::object({{"key", "value"}});
+
+// From value_t enum
+json j(json::value_t::object);
+
+// The library auto-detects objects vs arrays in initializer lists:
+// All elements are [string, value] pairs → object
+// Otherwise → array
+json obj = {{"a", 1}, {"b", 2}}; // → object
+json arr = {1, 2, 3}; // → array
+json arr2 = {{1, 2}, {3, 4}}; // → array of arrays
+```
+
+### Binary
+
+```cpp
+// Binary data without subtype
+json j = json::binary({0x01, 0x02, 0x03});
+
+// Binary data with subtype (used by MessagePack ext, CBOR tags, etc.)
+json j = json::binary({0x01, 0x02}, 42);
+
+// From std::vector<std::uint8_t>
+std::vector<std::uint8_t> data = {0xCA, 0xFE};
+json j = json::binary(data);
+json j = json::binary(std::move(data)); // move semantics
+```
+
+### From Existing Types
+
+The `basic_json` constructor template accepts any "compatible type" —
+any type for which a `to_json()` overload exists:
+
+```cpp
+// Standard containers
+std::vector<int> v = {1, 2, 3};
+json j = v; // [1, 2, 3]
+
+std::map<std::string, int> m = {{"a", 1}, {"b", 2}};
+json j = m; // {"a": 1, "b": 2}
+
+// Pairs and tuples (C++11)
+std::pair<std::string, int> p = {"key", 42};
+json j = p; // ["key", 42]
+
+// Enum types (unless JSON_DISABLE_ENUM_SERIALIZATION is set)
+enum Color { Red, Green, Blue };
+json j = Green; // 1
+```
+
+## Parsing JSON
+
+### From String
+
+```cpp
+// Static parse method
+json j = json::parse(R"({"key": "value", "number": 42})");
+
+// From std::string
+std::string input = R"([1, 2, 3])";
+json j = json::parse(input);
+
+// User-defined literal (requires JSON_USE_GLOBAL_UDLS or using namespace)
+auto j = R"({"key": "value"})"_json;
+```
+
+### From Stream
+
+```cpp
+#include <fstream>
+
+std::ifstream file("data.json");
+json j = json::parse(file);
+
+// Or with stream extraction operator:
+json j;
+file >> j;
+```
+
+### From Iterator Pair
+
+```cpp
+std::string input = R"({"key": "value"})";
+json j = json::parse(input.begin(), input.end());
+
+// Works with any input iterator
+std::vector<char> data = ...;
+json j = json::parse(data.begin(), data.end());
+```
+
+### Parse Options
+
+```cpp
+json j = json::parse(
+ input,
+ nullptr, // callback (nullptr = no callback)
+ true, // allow_exceptions (true = throw on error)
+ false, // ignore_comments (false = comments are errors)
+ false // ignore_trailing_commas (false = trailing commas are errors)
+);
+```
+
+### Error Handling During Parsing
+
+```cpp
+// Option 1: Exceptions (default)
+try {
+ json j = json::parse("invalid json");
+} catch (json::parse_error& e) {
+ std::cerr << e.what() << "\n";
+ // [json.exception.parse_error.101] parse error at line 1, column 1:
+ // syntax error while parsing value - invalid literal; ...
+}
+
+// Option 2: No exceptions
+json j = json::parse("invalid json", nullptr, false);
+if (j.is_discarded()) {
+ // parsing failed
+}
+```
+
+### Validation Without Parsing
+
+```cpp
+bool valid = json::accept(R"({"key": "value"})"); // true
+bool invalid = json::accept("not json"); // false
+
+// With options
+bool valid = json::accept(input, true, true); // ignore comments, trailing commas
+```
+
+### Parser Callbacks
+
+Filter or modify values during parsing:
+
+```cpp
+json j = json::parse(input, [](int depth, json::parse_event_t event, json& parsed) {
+ // event: object_start, object_end, array_start, array_end, key, value
+ // Return false to discard the value
+ if (event == json::parse_event_t::key && parsed == json("password")) {
+ return false; // discard objects with "password" key
+ }
+ return true;
+});
+```
+
+## Serialization
+
+### To String
+
+```cpp
+json j = {{"name", "Alice"}, {"age", 30}};
+
+// Compact (no indentation)
+std::string s = j.dump();
+// {"age":30,"name":"Alice"}
+
+// Pretty-printed (4-space indent)
+std::string s = j.dump(4);
+// {
+// "age": 30,
+// "name": "Alice"
+// }
+
+// Custom indent character
+std::string s = j.dump(1, '\t');
+
+// Force ASCII output
+std::string s = j.dump(-1, ' ', true);
+// Non-ASCII chars are escaped as \uXXXX
+```
+
+### `dump()` Method Signature
+
+```cpp
+string_t dump(
+ const int indent = -1,
+ const char indent_char = ' ',
+ const bool ensure_ascii = false,
+ const error_handler_t error_handler = error_handler_t::strict
+) const;
+```
+
+The `error_handler` controls how invalid UTF-8 in strings is handled:
+
+| Value | Behavior |
+|---|---|
+| `error_handler_t::strict` | Throw `type_error::316` |
+| `error_handler_t::replace` | Replace invalid bytes with U+FFFD |
+| `error_handler_t::ignore` | Skip invalid bytes |
+
+### To Stream
+
+```cpp
+std::cout << j << std::endl; // compact
+std::cout << std::setw(4) << j << "\n"; // pretty
+
+// To file
+std::ofstream file("output.json");
+file << std::setw(4) << j;
+```
+
+## Type Inspection
+
+### Type Query Methods
+
+```cpp
+json j = 42;
+
+j.type() // value_t::number_integer
+j.type_name() // "number"
+
+j.is_null() // false
+j.is_boolean() // false
+j.is_number() // true
+j.is_number_integer() // true
+j.is_number_unsigned() // false
+j.is_number_float() // false
+j.is_object() // false
+j.is_array() // false
+j.is_string() // false
+j.is_binary() // false
+j.is_discarded() // false
+
+j.is_primitive() // true (null, string, boolean, number, binary)
+j.is_structured() // false (object or array)
+```
+
+### Explicit Type Conversion
+
+```cpp
+json j = 42;
+
+// Using get<T>()
+int i = j.get<int>();
+double d = j.get<double>();
+std::string s = j.get<std::string>(); // throws type_error::302
+
+// Using get_to()
+int i;
+j.get_to(i);
+
+// Using get_ref<T&>()
+json j = "hello";
+const std::string& ref = j.get_ref<const std::string&>();
+
+// Using get_ptr<T*>()
+json j = "hello";
+const std::string* ptr = j.get_ptr<const std::string*>();
+if (ptr != nullptr) {
+ // use *ptr
+}
+```
+
+### Implicit Type Conversion
+
+When `JSON_USE_IMPLICIT_CONVERSIONS` is enabled (default):
+
+```cpp
+json j = 42;
+int i = j; // implicit conversion
+
+json j = "hello";
+std::string s = j; // implicit conversion
+
+json j = {1, 2, 3};
+std::vector<int> v = j; // implicit conversion
+```
+
+### Cast to `value_t`
+
+```cpp
+json j = 42;
+json::value_t t = j; // implicit cast via operator value_t()
+if (t == json::value_t::number_integer) { ... }
+```
+
+## Working with Objects
+
+### Creating and Modifying
+
+```cpp
+json j; // null
+
+// operator[] implicitly converts null to object/array
+j["name"] = "Alice"; // null → object, then insert
+j["age"] = 30;
+j["scores"] = {95, 87, 92};
+
+// Nested objects
+j["address"]["city"] = "Springfield";
+j["address"]["state"] = "IL";
+```
+
+### Checking Keys
+
+```cpp
+if (j.contains("name")) { ... }
+if (j.count("name") > 0) { ... }
+if (j.find("name") != j.end()) { ... }
+```
+
+### Removing Keys
+
+```cpp
+j.erase("name"); // by key
+j.erase(j.find("age")); // by iterator
+```
+
+### Getting with Default Value
+
+```cpp
+std::string name = j.value("name", "unknown");
+int port = j.value("port", 8080);
+```
+
+## Working with Arrays
+
+### Creating and Modifying
+
+```cpp
+json arr = json::array();
+arr.push_back(1);
+arr.push_back("hello");
+arr += 3.14; // operator+=
+
+arr.emplace_back("world"); // in-place construction
+
+// Insert at position
+arr.insert(arr.begin(), 0);
+arr.insert(arr.begin() + 2, {10, 20});
+```
+
+### Accessing Elements
+
+```cpp
+int first = arr[0];
+int second = arr.at(1); // bounds-checked
+int last = arr.back();
+int first2 = arr.front();
+```
+
+### Modifying
+
+```cpp
+arr.erase(arr.begin()); // remove first element
+arr.erase(2); // remove element at index 2
+arr.clear(); // remove all elements
+```
+
+### Size and Capacity
+
+```cpp
+arr.size(); // number of elements
+arr.empty(); // true if no elements
+arr.max_size(); // maximum possible elements
+```
+
+## Ordered JSON
+
+For insertion-order preservation:
+
+```cpp
+nlohmann::ordered_json j;
+j["z"] = 1;
+j["a"] = 2;
+j["m"] = 3;
+
+// Iteration preserves insertion order: z, a, m
+for (auto& [key, val] : j.items()) {
+ std::cout << key << ": " << val << "\n";
+}
+```
+
+The `ordered_json` type uses `nlohmann::ordered_map` (a `std::vector`-based
+map) instead of `std::map`. Lookups are O(n) instead of O(log n).
+
+## Copy and Comparison
+
+### Copying
+
+```cpp
+json j1 = {{"key", "value"}};
+json j2 = j1; // deep copy
+json j3(j1); // deep copy
+json j4 = std::move(j1); // move (j1 becomes null)
+```
+
+### Comparison
+
+```cpp
+json a = {1, 2, 3};
+json b = {1, 2, 3};
+
+a == b; // true
+a != b; // false
+a < b; // false (same type, same value)
+
+// Cross-type numeric comparison
+json(1) == json(1.0); // true
+json(1) < json(1.5); // true
+```
+
+## Structured Bindings (C++17)
+
+```cpp
+json j = {{"name", "Alice"}, {"age", 30}};
+
+for (auto& [key, val] : j.items()) {
+ std::cout << key << " = " << val << "\n";
+}
+```
+
+## Common Patterns
+
+### Configuration File Loading
+
+```cpp
+json load_config(const std::string& path) {
+ std::ifstream file(path);
+ if (!file.is_open()) {
+ return json::object();
+ }
+ return json::parse(file, nullptr, true, true); // allow comments
+}
+
+auto config = load_config("config.json");
+int port = config.value("port", 8080);
+std::string host = config.value("host", "localhost");
+```
+
+### Safe Value Extraction
+
+```cpp
+template<typename T>
+std::optional<T> safe_get(const json& j, const std::string& key) {
+ if (j.contains(key)) {
+ try {
+ return j.at(key).get<T>();
+ } catch (const json::type_error&) {
+ return std::nullopt;
+ }
+ }
+ return std::nullopt;
+}
+```
+
+### Building JSON Programmatically
+
+```cpp
+json build_response(int status, const std::string& message) {
+ return {
+ {"status", status},
+ {"message", message},
+ {"timestamp", std::time(nullptr)},
+ {"data", json::object()}
+ };
+}
+```
+
+### Merging Objects
+
+```cpp
+json defaults = {{"color", "blue"}, {"size", 10}, {"visible", true}};
+json user_prefs = {{"color", "red"}, {"opacity", 0.8}};
+
+defaults.update(user_prefs);
+// defaults = {"color": "red", "size": 10, "visible": true, "opacity": 0.8}
+
+// Deep merge with merge_objects=true
+defaults.update(user_prefs, true);
+```
+
+### Flattening and Unflattening
+
+```cpp
+json nested = {
+ {"a", {{"b", {{"c", 42}}}}}
+};
+
+json flat = nested.flatten();
+// {"/a/b/c": 42}
+
+json restored = flat.unflatten();
+// {"a": {"b": {"c": 42}}}
+```
+
+## Error Handling Summary
+
+| Exception | When Thrown |
+|---|---|
+| `json::parse_error` | Invalid JSON input |
+| `json::type_error` | Wrong type access (e.g., `string` on a number) |
+| `json::out_of_range` | Index/key not found with `at()` |
+| `json::invalid_iterator` | Invalid iterator operation |
+| `json::other_error` | Miscellaneous errors |
+
+```cpp
+try {
+ json j = json::parse("...");
+ int val = j.at("missing_key").get<int>();
+} catch (json::parse_error& e) {
+ // e.id: 101, 102, 103, 104, 105
+ // e.byte: position in input
+} catch (json::out_of_range& e) {
+ // e.id: 401, 402, 403, 404, 405
+} catch (json::type_error& e) {
+ // e.id: 301, 302, 303, 304, 305, 306, 307, 308, ...
+}
+```
diff --git a/docs/handbook/json4cpp/binary-formats.md b/docs/handbook/json4cpp/binary-formats.md
new file mode 100644
index 0000000000..9cb9f666f2
--- /dev/null
+++ b/docs/handbook/json4cpp/binary-formats.md
@@ -0,0 +1,411 @@
+# json4cpp — Binary Formats
+
+## Overview
+
+The library supports five binary serialization formats in addition to JSON
+text. All are available as static methods on `basic_json`:
+
+| Format | To | From | RFC/Spec |
+|---|---|---|---|
+| CBOR | `to_cbor()` | `from_cbor()` | RFC 7049 |
+| MessagePack | `to_msgpack()` | `from_msgpack()` | MessagePack spec |
+| UBJSON | `to_ubjson()` | `from_ubjson()` | UBJSON spec |
+| BSON | `to_bson()` | `from_bson()` | BSON spec |
+| BJData | `to_bjdata()` | `from_bjdata()` | BJData spec |
+
+Binary serialization is useful for:
+- Smaller payload sizes
+- Faster parsing
+- Native binary data support
+- Type-rich encodings (timestamps, binary subtypes)
+
+## CBOR (Concise Binary Object Representation)
+
+### Serialization
+
+```cpp
+// To vector<uint8_t>
+static std::vector<std::uint8_t> to_cbor(const basic_json& j);
+
+// To output adapter (stream, string, vector)
+static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o);
+static void to_cbor(const basic_json& j, detail::output_adapter<char> o);
+```
+
+```cpp
+json j = {{"compact", true}, {"schema", 0}};
+
+// Serialize to byte vector
+auto cbor = json::to_cbor(j);
+
+// Serialize to stream
+std::ofstream out("data.cbor", std::ios::binary);
+json::to_cbor(j, out);
+```
+
+### Deserialization
+
+```cpp
+template<typename InputType>
+static basic_json from_cbor(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
+```
+
+Parameters:
+- `strict` — if `true`, requires that all bytes are consumed
+- `allow_exceptions` — if `false`, returns discarded value on error
+- `tag_handler` — how to handle CBOR tags
+
+```cpp
+auto j = json::from_cbor(cbor);
+```
+
+### CBOR Tag Handling
+
+```cpp
+enum class cbor_tag_handler_t
+{
+ error, ///< throw parse_error on any tag
+ ignore, ///< ignore tags
+ store ///< store tags as binary subtype
+};
+```
+
+```cpp
+// Ignore CBOR tags
+auto j = json::from_cbor(data, true, true, json::cbor_tag_handler_t::ignore);
+
+// Store CBOR tags as subtypes in binary values
+auto j = json::from_cbor(data, true, true, json::cbor_tag_handler_t::store);
+```
+
+### CBOR Type Mapping
+
+| JSON Type | CBOR Type |
+|---|---|
+| null | null (0xF6) |
+| boolean | true/false (0xF5/0xF4) |
+| number_integer | negative/unsigned integer |
+| number_unsigned | unsigned integer |
+| number_float | IEEE 754 double (0xFB) or half-precision (0xF9) |
+| string | text string (major type 3) |
+| array | array (major type 4) |
+| object | map (major type 5) |
+| binary | byte string (major type 2) |
+
+## MessagePack
+
+### Serialization
+
+```cpp
+static std::vector<std::uint8_t> to_msgpack(const basic_json& j);
+static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o);
+static void to_msgpack(const basic_json& j, detail::output_adapter<char> o);
+```
+
+```cpp
+json j = {{"array", {1, 2, 3}}, {"null", nullptr}};
+auto msgpack = json::to_msgpack(j);
+```
+
+### Deserialization
+
+```cpp
+template<typename InputType>
+static basic_json from_msgpack(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true);
+```
+
+```cpp
+auto j = json::from_msgpack(msgpack);
+```
+
+### MessagePack Type Mapping
+
+| JSON Type | MessagePack Type |
+|---|---|
+| null | nil (0xC0) |
+| boolean | true/false (0xC3/0xC2) |
+| number_integer | int 8/16/32/64 or negative fixint |
+| number_unsigned | uint 8/16/32/64 or positive fixint |
+| number_float | float 32 or float 64 |
+| string | fixstr / str 8/16/32 |
+| array | fixarray / array 16/32 |
+| object | fixmap / map 16/32 |
+| binary | bin 8/16/32 |
+| binary with subtype | ext 8/16/32 / fixext 1/2/4/8/16 |
+
+The library chooses the **smallest** encoding that fits the value.
+
+### Ext Types
+
+MessagePack extension types carry a type byte. The library maps this to the
+binary subtype:
+
+```cpp
+json j = json::binary({0x01, 0x02, 0x03}, 42); // subtype 42
+auto mp = json::to_msgpack(j);
+// Encoded as ext with type byte 42
+
+auto j2 = json::from_msgpack(mp);
+assert(j2.get_binary().subtype() == 42);
+```
+
+## UBJSON (Universal Binary JSON)
+
+### Serialization
+
+```cpp
+static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false);
+```
+
+Parameters:
+- `use_size` — write container size markers (enables optimized containers)
+- `use_type` — write type markers for homogeneous containers (requires `use_size`)
+
+```cpp
+json j = {1, 2, 3, 4, 5};
+
+// Without optimization
+auto ub1 = json::to_ubjson(j);
+
+// With size optimization
+auto ub2 = json::to_ubjson(j, true);
+
+// With size+type optimization (smallest for homogeneous arrays)
+auto ub3 = json::to_ubjson(j, true, true);
+```
+
+### Deserialization
+
+```cpp
+template<typename InputType>
+static basic_json from_ubjson(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true);
+```
+
+### UBJSON Type Markers
+
+| Marker | Type |
+|---|---|
+| `Z` | null |
+| `T` / `F` | true / false |
+| `i` | int8 |
+| `U` | uint8 |
+| `I` | int16 |
+| `l` | int32 |
+| `L` | int64 |
+| `d` | float32 |
+| `D` | float64 |
+| `C` | char |
+| `S` | string |
+| `[` / `]` | array begin / end |
+| `{` / `}` | object begin / end |
+| `H` | high-precision number (string representation) |
+
+## BSON (Binary JSON)
+
+### Serialization
+
+```cpp
+static std::vector<std::uint8_t> to_bson(const basic_json& j);
+static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o);
+static void to_bson(const basic_json& j, detail::output_adapter<char> o);
+```
+
+**Important:** BSON requires the top-level value to be an **object**:
+
+```cpp
+json j = {{"key", "value"}, {"num", 42}};
+auto bson = json::to_bson(j);
+
+// json j = {1, 2, 3};
+// json::to_bson(j); // throws type_error::317 — not an object
+```
+
+### Deserialization
+
+```cpp
+template<typename InputType>
+static basic_json from_bson(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true);
+```
+
+### BSON Type Mapping
+
+| JSON Type | BSON Type |
+|---|---|
+| null | 0x0A (Null) |
+| boolean | 0x08 (Boolean) |
+| number_integer | 0x10 (int32) or 0x12 (int64) |
+| number_unsigned | 0x10 or 0x12 (depends on value) |
+| number_float | 0x01 (double) |
+| string | 0x02 (String) |
+| array | 0x04 (Array) — encoded as object with "0", "1", ... keys |
+| object | 0x03 (Document) |
+| binary | 0x05 (Binary) |
+
+### BSON Binary Subtypes
+
+```cpp
+json j;
+j["data"] = json::binary({0x01, 0x02}, 0x80); // subtype 0x80
+auto bson = json::to_bson(j);
+// Binary encoded with subtype byte 0x80
+```
+
+## BJData (Binary JData)
+
+BJData extends UBJSON with additional types for N-dimensional arrays and
+optimized integer types.
+
+### Serialization
+
+```cpp
+static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false);
+```
+
+### Deserialization
+
+```cpp
+template<typename InputType>
+static basic_json from_bjdata(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true);
+```
+
+### Additional BJData Types
+
+Beyond UBJSON types, BJData adds:
+
+| Marker | Type |
+|---|---|
+| `u` | uint16 |
+| `m` | uint32 |
+| `M` | uint64 |
+| `h` | float16 (half-precision) |
+
+## Roundtrip Between Formats
+
+Binary formats can preserve the same data as JSON text, but with some
+differences:
+
+```cpp
+json original = {
+ {"name", "test"},
+ {"values", {1, 2, 3}},
+ {"data", json::binary({0xFF, 0xFE})}
+};
+
+// JSON text cannot represent binary
+std::string text = original.dump();
+// "data" field would cause issues in text form
+
+// Binary formats can represent binary natively
+auto cbor = json::to_cbor(original);
+auto restored = json::from_cbor(cbor);
+assert(original == restored); // exact roundtrip
+
+// Cross-format conversion
+auto mp = json::to_msgpack(original);
+auto from_mp = json::from_msgpack(mp);
+assert(original == from_mp);
+```
+
+## Size Comparison
+
+Typical size savings over JSON text:
+
+| Data | JSON | CBOR | MessagePack | UBJSON | BSON |
+|---|---|---|---|---|---|
+| `{"a":1}` | 7 bytes | 5 bytes | 4 bytes | 6 bytes | 18 bytes |
+| `[1,2,3]` | 7 bytes | 4 bytes | 4 bytes | 6 bytes | N/A (top-level array) |
+| `true` | 4 bytes | 1 byte | 1 byte | 1 byte | N/A (top-level bool) |
+
+BSON has the most overhead due to its document-structure requirements.
+MessagePack and CBOR are generally the most compact.
+
+## Stream-Based Serialization
+
+All binary formats support streaming to/from `std::ostream` / `std::istream`:
+
+```cpp
+// Write CBOR to file
+std::ofstream out("data.cbor", std::ios::binary);
+json::to_cbor(j, out);
+
+// Read CBOR from file
+std::ifstream in("data.cbor", std::ios::binary);
+json j = json::from_cbor(in);
+```
+
+## Strict vs. Non-Strict Parsing
+
+All `from_*` functions accept a `strict` parameter:
+
+- `strict = true` (default): all input bytes must be consumed. Extra
+ trailing data causes a parse error.
+- `strict = false`: parsing stops after the first valid value. Remaining
+ input is ignored.
+
+```cpp
+std::vector<uint8_t> data = /* two CBOR values concatenated */;
+
+// Strict: fails because of trailing data
+// json::from_cbor(data, true);
+
+// Non-strict: parses only the first value
+json j = json::from_cbor(data, false);
+```
+
+## Binary Reader / Writer Architecture
+
+The binary I/O is implemented by two internal classes:
+
+### `binary_reader`
+
+Located in `include/nlohmann/detail/input/binary_reader.hpp`:
+
+```cpp
+template<typename BasicJsonType, typename InputAdapterType, typename SAX>
+class binary_reader
+{
+ bool parse_cbor_internal(bool get_char, int tag_handler);
+ bool parse_msgpack_internal();
+ bool parse_ubjson_internal(bool get_char = true);
+ bool parse_bson_internal();
+ bool parse_bjdata_internal();
+ // ...
+};
+```
+
+Uses the SAX interface internally — each decoded value is reported to a
+SAX handler (typically `json_sax_dom_parser`) which builds the JSON tree.
+
+### `binary_writer`
+
+Located in `include/nlohmann/detail/output/binary_writer.hpp`:
+
+```cpp
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+ void write_cbor(const BasicJsonType& j);
+ void write_msgpack(const BasicJsonType& j);
+ void write_ubjson(const BasicJsonType& j, ...);
+ void write_bson(const BasicJsonType& j);
+ void write_bjdata(const BasicJsonType& j, ...);
+ // ...
+};
+```
+
+Directly writes encoded bytes to an `output_adapter`.
diff --git a/docs/handbook/json4cpp/building.md b/docs/handbook/json4cpp/building.md
new file mode 100644
index 0000000000..73e29a65fc
--- /dev/null
+++ b/docs/handbook/json4cpp/building.md
@@ -0,0 +1,430 @@
+# json4cpp — Building and Integration
+
+## Header-Only Usage
+
+json4cpp (nlohmann/json 3.12.0) is a header-only library. The simplest way
+to use it is to copy the single amalgamated header and include it:
+
+```cpp
+#include "nlohmann/json.hpp"
+
+using json = nlohmann::json;
+```
+
+### Single Header vs. Multi-Header
+
+The library ships in two forms:
+
+| Form | Location | Use Case |
+|---|---|---|
+| Single header | `single_include/nlohmann/json.hpp` | Simplest integration |
+| Multi-header | `include/nlohmann/json.hpp` + `include/nlohmann/detail/` | Better IDE navigation, faster incremental builds |
+
+The single header (`json.hpp`, ~25,000 lines) is generated by amalgamating
+all the multi-header files. It also ships `json_fwd.hpp` for forward
+declarations without pulling in the full implementation.
+
+### Forward Declaration Header
+
+```cpp
+#include <nlohmann/json_fwd.hpp>
+
+// Now you can declare functions accepting json parameters
+void process(const nlohmann::json& data);
+```
+
+The forward header declares `basic_json`, `json`, `ordered_json`,
+`json_pointer`, `ordered_map`, and `adl_serializer` without including
+any implementation.
+
+## CMake Integration
+
+### As a Subdirectory
+
+```cmake
+add_subdirectory(json4cpp) # or wherever the library lives
+
+target_link_libraries(my_target PRIVATE nlohmann_json::nlohmann_json)
+```
+
+### Via `FetchContent`
+
+```cmake
+include(FetchContent)
+FetchContent_Declare(
+ json
+ SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/json4cpp
+)
+FetchContent_MakeAvailable(json)
+
+target_link_libraries(my_target PRIVATE nlohmann_json::nlohmann_json)
+```
+
+### Via `find_package` (After Install)
+
+```cmake
+find_package(nlohmann_json 3.12.0 REQUIRED)
+target_link_libraries(my_target PRIVATE nlohmann_json::nlohmann_json)
+```
+
+### Target Include Directories
+
+For the simplest possible integration without CMake targets:
+
+```cmake
+target_include_directories(my_target PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/json4cpp/single_include
+)
+```
+
+Or for multi-header:
+
+```cmake
+target_include_directories(my_target PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/json4cpp/include
+)
+```
+
+## CMake Options Reference
+
+The top-level `CMakeLists.txt` defines these options:
+
+```cmake
+cmake_minimum_required(VERSION 3.5...4.0)
+project(nlohmann_json VERSION 3.12.0 LANGUAGES CXX)
+```
+
+### Build Options
+
+| Option | Default | Description |
+|---|---|---|
+| `JSON_BuildTests` | `ON` (main project) | Build the test suite |
+| `JSON_CI` | `OFF` | Enable CI build targets |
+| `JSON_Diagnostics` | `OFF` | Extended diagnostic messages |
+| `JSON_Diagnostic_Positions` | `OFF` | Track byte positions |
+| `JSON_GlobalUDLs` | `ON` | Place UDLs in global namespace |
+| `JSON_ImplicitConversions` | `ON` | Enable implicit `operator T()` |
+| `JSON_DisableEnumSerialization` | `OFF` | Disable automatic enum conversion |
+| `JSON_LegacyDiscardedValueComparison` | `OFF` | Legacy comparison behavior |
+| `JSON_Install` | `ON` (main project) | Install CMake targets |
+| `JSON_MultipleHeaders` | `ON` | Use multi-header tree |
+| `JSON_SystemInclude` | `OFF` | Include as system headers |
+
+### Configuration Variables
+
+```cmake
+NLOHMANN_JSON_TARGET_NAME # Override target name (default: nlohmann_json)
+NLOHMANN_JSON_CONFIG_INSTALL_DIR # CMake config install dir
+NLOHMANN_JSON_INCLUDE_INSTALL_DIR # Header install dir
+```
+
+### Header Selection Logic
+
+```cmake
+if (JSON_MultipleHeaders)
+ set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/include/")
+else()
+ set(NLOHMANN_JSON_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/single_include/")
+endif()
+```
+
+### Compile Definitions Set by CMake
+
+When options are toggled, CMake sets preprocessor definitions on the target:
+
+```cmake
+if (JSON_Diagnostics)
+ target_compile_definitions(nlohmann_json INTERFACE JSON_DIAGNOSTICS=1)
+endif()
+
+if (NOT JSON_ImplicitConversions)
+ target_compile_definitions(nlohmann_json INTERFACE JSON_USE_IMPLICIT_CONVERSIONS=0)
+endif()
+
+if (JSON_DisableEnumSerialization)
+ target_compile_definitions(nlohmann_json INTERFACE JSON_DISABLE_ENUM_SERIALIZATION=1)
+endif()
+
+if (JSON_Diagnostic_Positions)
+ target_compile_definitions(nlohmann_json INTERFACE JSON_DIAGNOSTIC_POSITIONS=1)
+endif()
+
+if (NOT JSON_GlobalUDLs)
+ target_compile_definitions(nlohmann_json INTERFACE JSON_USE_GLOBAL_UDLS=0)
+endif()
+
+if (JSON_LegacyDiscardedValueComparison)
+ target_compile_definitions(nlohmann_json INTERFACE JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1)
+endif()
+```
+
+## Other Build Systems
+
+### Bazel
+
+```python
+# BUILD.bazel is provided at the top level
+cc_library(
+ name = "json",
+ hdrs = glob(["include/**/*.hpp"]),
+ includes = ["include"],
+)
+```
+
+A `MODULE.bazel` file is also provided for Bzlmod support.
+
+### Meson
+
+```meson
+# meson.build is provided at the top level
+nlohmann_json_dep = dependency('nlohmann_json', fallback: ['nlohmann_json', 'nlohmann_json_dep'])
+```
+
+### Swift Package Manager
+
+```swift
+// Package.swift is provided
+.package(path: "json4cpp")
+```
+
+### pkg-config
+
+After installation, a `nlohmann_json.pc` file is generated from
+`cmake/pkg-config.pc.in`:
+
+```
+pkg-config --cflags nlohmann_json
+```
+
+## Preprocessor Configuration Macros
+
+These macros can be defined before including the header or via compiler
+flags to control library behavior:
+
+### Core Behavior
+
+| Macro | Values | Effect |
+|---|---|---|
+| `JSON_DIAGNOSTICS` | `0`/`1` | Extended error messages with parent-chain paths |
+| `JSON_DIAGNOSTIC_POSITIONS` | `0`/`1` | Track byte positions in parsed values |
+| `JSON_USE_IMPLICIT_CONVERSIONS` | `0`/`1` | Enable/disable implicit `operator T()` |
+| `JSON_DISABLE_ENUM_SERIALIZATION` | `0`/`1` | Disable enum-to-integer serialization |
+| `JSON_USE_GLOBAL_UDLS` | `0`/`1` | Place `_json` / `_json_pointer` UDLs in global scope |
+| `JSON_NO_IO` | defined/undefined | Disable all stream-based I/O |
+| `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON` | `0`/`1` | Legacy discarded value comparison |
+
+### Assertion Customization
+
+```cpp
+// Override the assertion macro (default: assert())
+#define JSON_ASSERT(x) my_assert(x)
+```
+
+### Exception Customization
+
+```cpp
+// Override throw behavior
+#define JSON_THROW(exception) throw exception
+#define JSON_TRY try
+#define JSON_CATCH(exception) catch(exception)
+#define JSON_INTERNAL_CATCH(exception) catch(exception)
+```
+
+To disable exceptions entirely:
+
+```cpp
+#define JSON_THROW(exception) std::abort()
+#define JSON_TRY if(true)
+#define JSON_CATCH(exception) if(false)
+#define JSON_INTERNAL_CATCH(exception) if(false)
+```
+
+### Version Macros
+
+```cpp
+NLOHMANN_JSON_VERSION_MAJOR // 3
+NLOHMANN_JSON_VERSION_MINOR // 12
+NLOHMANN_JSON_VERSION_PATCH // 0
+```
+
+### Standard Detection Macros
+
+Set automatically based on the compiler:
+
+```cpp
+JSON_HAS_CPP_11 // always 1
+JSON_HAS_CPP_14 // 1 if C++14 or higher
+JSON_HAS_CPP_17 // 1 if C++17 or higher
+JSON_HAS_CPP_20 // 1 if C++20 or higher
+```
+
+### RTTI Detection
+
+```cpp
+JSON_HAS_STATIC_RTTI // 1 if RTTI is available
+```
+
+### Three-Way Comparison Detection
+
+```cpp
+JSON_HAS_THREE_WAY_COMPARISON // 1 if <=> is available
+```
+
+## C++20 Module Support
+
+The library includes experimental C++20 module support:
+
+```cmake
+option(NLOHMANN_JSON_BUILD_MODULES "Build C++ modules support" OFF)
+```
+
+When enabled and CMake >= 3.28 is available, the module is built from
+`src/modules/`. Usage:
+
+```cpp
+import nlohmann.json;
+```
+
+## Compiler-Specific Notes
+
+### GCC
+
+The `cmake/gcc_flags.cmake` file configures GCC-specific warning flags.
+GCC 4.8 support requires workarounds (user-defined literal spacing).
+
+### Clang
+
+`cmake/clang_flags.cmake` handles Clang warning configuration. The
+`-Wweak-vtables` warning is suppressed in `detail/exceptions.hpp`
+since header-only libraries cannot have out-of-line vtables.
+
+### MSVC
+
+MSVC receives specific warning suppressions. The `nlohmann_json.natvis`
+file provides Visual Studio debugger visualization:
+
+```xml
+<!-- nlohmann_json.natvis provides structured views in the VS debugger -->
+```
+
+## Installation
+
+### Default Installation Layout
+
+```bash
+cmake -B build -DCMAKE_INSTALL_PREFIX=/usr/local
+cmake --build build
+cmake --install build
+```
+
+This installs:
+
+```
+/usr/local/include/nlohmann/ # Headers
+/usr/local/share/cmake/nlohmann_json/ # CMake config files
+/usr/local/share/pkgconfig/ # pkg-config file
+```
+
+### Controlling Installation
+
+```cmake
+set(JSON_Install OFF) # Disable installation entirely
+```
+
+### Version Compatibility
+
+The installed `nlohmann_jsonConfigVersion.cmake` file supports version
+range checking, allowing consumers to request minimum versions:
+
+```cmake
+find_package(nlohmann_json 3.11.0 REQUIRED) # any 3.x >= 3.11.0
+```
+
+## Integration Patterns
+
+### Pattern 1: Copy Single Header
+
+```bash
+cp json4cpp/single_include/nlohmann/json.hpp my_project/third_party/
+```
+
+```cpp
+#include "third_party/json.hpp"
+```
+
+### Pattern 2: Git Submodule + CMake
+
+```bash
+git submodule add <url> third_party/json
+```
+
+```cmake
+add_subdirectory(third_party/json)
+target_link_libraries(my_target PRIVATE nlohmann_json::nlohmann_json)
+```
+
+### Pattern 3: System Package
+
+Most Linux distributions package nlohmann/json:
+
+```bash
+# Debian/Ubuntu
+apt install nlohmann-json3-dev
+
+# Fedora
+dnf install json-devel
+
+# Arch
+pacman -S nlohmann-json
+
+# macOS
+brew install nlohmann-json
+```
+
+### Pattern 4: Header-Only with Forward Declarations
+
+For faster compilation, use the forward declaration header in headers
+and the full header only in implementation files:
+
+```cpp
+// my_class.hpp
+#include <nlohmann/json_fwd.hpp>
+class MyClass {
+ void process(const nlohmann::json& j);
+};
+
+// my_class.cpp
+#include <nlohmann/json.hpp>
+#include "my_class.hpp"
+void MyClass::process(const nlohmann::json& j) { ... }
+```
+
+## Compilation Speed Tips
+
+1. **Use `json_fwd.hpp`** in headers to avoid pulling the full
+ implementation into every translation unit.
+
+2. **Precompiled headers** — add `nlohmann/json.hpp` to your PCH:
+ ```cmake
+ target_precompile_headers(my_target PRIVATE <nlohmann/json.hpp>)
+ ```
+
+3. **Unity builds** work naturally since the library is header-only.
+
+4. **Multi-header mode** with `JSON_MultipleHeaders=ON` can improve
+ incremental rebuild times since changes to one detail header don't
+ invalidate the entire amalgamated file.
+
+5. **`JSON_NO_IO`** — define this if you don't need stream operators,
+ reducing the include chain.
+
+## Minimum Requirements
+
+| Requirement | Minimum |
+|---|---|
+| C++ Standard | C++11 |
+| CMake | 3.5 (3.28 for modules) |
+| GCC | 4.8 |
+| Clang | 3.4 |
+| MSVC | 2015 (19.0) |
+| Intel C++ | 2017 |
diff --git a/docs/handbook/json4cpp/code-style.md b/docs/handbook/json4cpp/code-style.md
new file mode 100644
index 0000000000..05fb76f4dd
--- /dev/null
+++ b/docs/handbook/json4cpp/code-style.md
@@ -0,0 +1,209 @@
+# json4cpp — Code Style & Conventions
+
+## Source Organisation
+
+### Directory Layout
+
+```
+json4cpp/
+├── include/nlohmann/ # Multi-header installation
+│ ├── json.hpp # Main header (includes everything)
+│ ├── json_fwd.hpp # Forward declarations only
+│ ├── adl_serializer.hpp # ADL-based serializer
+│ ├── byte_container_with_subtype.hpp
+│ ├── ordered_map.hpp # Insertion-order map
+│ └── detail/ # Internal implementation
+│ ├── exceptions.hpp # Exception hierarchy
+│ ├── hash.hpp # std::hash specialization
+│ ├── json_pointer.hpp # RFC 6901 implementation
+│ ├── json_ref.hpp # Internal reference wrapper
+│ ├── macro_scope.hpp # Macro definitions
+│ ├── macro_unscope.hpp # Macro undefinitions
+│ ├── string_concat.hpp # String concatenation helper
+│ ├── string_escape.hpp # String escaping utilities
+│ ├── value_t.hpp # value_t enum
+│ ├── abi_macros.hpp # ABI versioning macros
+│ ├── conversions/ # Type conversion traits
+│ ├── input/ # Parsing pipeline
+│ ├── iterators/ # Iterator implementations
+│ ├── meta/ # Type traits & SFINAE
+│ └── output/ # Serialization pipeline
+├── single_include/nlohmann/ # Single-header (amalgamated)
+│ └── json.hpp # Complete library in one file
+├── tests/ # Test suite (doctest)
+│ ├── CMakeLists.txt
+│ └── src/
+│ └── unit-*.cpp # One file per feature area
+└── CMakeLists.txt # Build configuration
+```
+
+### Public vs. Internal API
+
+- `include/nlohmann/*.hpp` — public API, included by users
+- `include/nlohmann/detail/` — internal, not for direct inclusion
+- `single_include/` — generated amalgamation, mirrors the public API
+
+Users should only include `<nlohmann/json.hpp>` or
+`<nlohmann/json_fwd.hpp>`.
+
+## Naming Conventions
+
+### Types
+
+- Template parameters: `PascalCase` — `BasicJsonType`, `ObjectType`,
+ `InputAdapterType`
+- Type aliases: `snake_case` — `value_t`, `object_t`, `string_t`,
+ `number_integer_t`
+- Internal classes: `snake_case` — `iter_impl`, `binary_reader`,
+ `json_sax_dom_parser`
+
+### Functions and Methods
+
+- All functions: `snake_case` — `parse()`, `dump()`, `push_back()`,
+ `is_null()`, `get_to()`, `merge_patch()`
+- Private methods: `snake_case` — `set_parent()`, `assert_invariant()`
+
+### Variables
+
+- Member variables: `m_` prefix — `m_type`, `m_value`, `m_parent`
+- Local variables: `snake_case` — `reference_tokens`, `token_buffer`
+
+### Macros
+
+- All macros: `SCREAMING_SNAKE_CASE` with project prefix
+- Public macros: `NLOHMANN_` prefix or `JSON_` prefix
+ - `NLOHMANN_DEFINE_TYPE_INTRUSIVE`
+ - `NLOHMANN_JSON_SERIALIZE_ENUM`
+ - `JSON_DIAGNOSTICS`
+ - `JSON_USE_IMPLICIT_CONVERSIONS`
+- Internal macros: `NLOHMANN_JSON_` prefix for implementation detail macros
+- All macros are undefined by `macro_unscope.hpp` to avoid pollution
+
+### Namespaces
+
+```cpp
+namespace nlohmann {
+ // Public API: basic_json, json, ordered_json, json_pointer, ...
+ namespace detail {
+ // Internal implementation
+ }
+ namespace literals {
+ namespace json_literals {
+ // _json, _json_pointer UDLs
+ }
+ }
+}
+```
+
+The `NLOHMANN_JSON_NAMESPACE_BEGIN` / `NLOHMANN_JSON_NAMESPACE_END` macros
+handle optional ABI versioning via inline namespaces.
+
+## Template Style
+
+### SFINAE Guards
+
+The library uses SFINAE extensively to constrain overloads:
+
+```cpp
+template<typename BasicJsonType, typename T,
+ enable_if_t<is_compatible_type<BasicJsonType, T>::value, int> = 0>
+void to_json(BasicJsonType& j, T&& val);
+```
+
+The `enable_if_t<..., int> = 0` pattern is used throughout instead of
+`enable_if_t<..., void>` or return-type SFINAE.
+
+### Tag Dispatch
+
+Priority tags resolve overload ambiguity:
+
+```cpp
+template<unsigned N> struct priority_tag : priority_tag<N - 1> {};
+template<> struct priority_tag<0> {};
+```
+
+Higher-numbered tags are tried first (since they inherit from lower ones).
+
+### `static_assert` Guards
+
+Critical type requirements use `static_assert` with readable messages:
+
+```cpp
+static_assert(std::is_default_constructible<T>::value,
+ "T must be default constructible");
+```
+
+## Header Guards
+
+Each header uses `#ifndef` guards following the pattern:
+
+```cpp
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+// ...
+#endif // INCLUDE_NLOHMANN_JSON_HPP_
+```
+
+Detail headers follow `INCLUDE_NLOHMANN_JSON_DETAIL_*` naming.
+
+## Code Documentation
+
+### Doxygen-Style Comments
+
+Public API methods use `///` or `/** */` with standard Doxygen tags:
+
+```cpp
+/// @brief parse a JSON value from a string
+/// @param[in] i the input to parse
+/// @param[in] cb a callback function (default: none)
+/// @param[in] allow_exceptions whether exceptions should be thrown
+/// @return the parsed JSON value
+static basic_json parse(InputType&& i, ...);
+```
+
+### `@sa` Cross References
+
+Related methods are linked with `@sa`:
+
+```cpp
+/// @sa dump() for serialization
+/// @sa operator>> for stream parsing
+```
+
+### `@throw` Documentation
+
+Exception-throwing methods document which exceptions they throw:
+
+```cpp
+/// @throw parse_error.101 if unexpected token
+/// @throw parse_error.102 if invalid unicode escape
+```
+
+## Error Handling Style
+
+- Public API methods that can fail throw typed exceptions from the
+ hierarchy (`parse_error`, `type_error`, `out_of_range`,
+ `invalid_iterator`, `other_error`)
+- Each exception has a unique numeric ID for programmatic handling
+- Error messages follow the format:
+ `[json.exception.<type>.<id>] <description>`
+- Internal assertions use `JSON_ASSERT(condition)` which maps to
+ `assert()` by default
+
+## Compatibility
+
+### C++ Standard
+
+- Minimum: C++11
+- Optional features with C++14: heterogeneous lookup
+ (`std::less<>`)
+- Optional features with C++17: `std::string_view`, `std::optional`,
+ `std::variant`, `std::filesystem::path`, structured bindings,
+ `if constexpr`
+- Optional features with C++20: modules, `operator<=>`
+
+### Compiler Notes
+
+Tested compilers include GCC ≥ 4.8, Clang ≥ 3.4, MSVC ≥ 2015, Intel
+C++, and various others. Compiler-specific workarounds are guarded with
+preprocessor conditionals.
diff --git a/docs/handbook/json4cpp/custom-types.md b/docs/handbook/json4cpp/custom-types.md
new file mode 100644
index 0000000000..086fa0ebcc
--- /dev/null
+++ b/docs/handbook/json4cpp/custom-types.md
@@ -0,0 +1,465 @@
+# json4cpp — Custom Type Serialization
+
+## ADL-Based Serialization
+
+The library uses **Argument-Dependent Lookup** (ADL) to find `to_json()`
+and `from_json()` free functions for user-defined types. This allows
+seamless conversion without modifying the library.
+
+### Basic Pattern
+
+Define `to_json()` and `from_json()` as free functions in the **same
+namespace** as your type:
+
+```cpp
+namespace myapp {
+
+struct Person {
+ std::string name;
+ int age;
+};
+
+void to_json(nlohmann::json& j, const Person& p) {
+ j = nlohmann::json{{"name", p.name}, {"age", p.age}};
+}
+
+void from_json(const nlohmann::json& j, Person& p) {
+ j.at("name").get_to(p.name);
+ j.at("age").get_to(p.age);
+}
+
+} // namespace myapp
+```
+
+Usage:
+
+```cpp
+myapp::Person alice{"alice", 30};
+
+// Serialization
+json j = alice; // calls myapp::to_json via ADL
+// or
+json j2;
+j2 = alice;
+
+// Deserialization
+auto bob = j.get<myapp::Person>(); // calls myapp::from_json via ADL
+// or
+myapp::Person carol;
+j.get_to(carol);
+```
+
+### How ADL Resolution Works
+
+When you write `json j = my_obj;`, the library calls:
+
+```cpp
+nlohmann::adl_serializer<MyType>::to_json(j, my_obj);
+```
+
+The default `adl_serializer` implementation delegates via an unqualified
+call:
+
+```cpp
+template<typename BasicJsonType, typename TargetType>
+static auto to_json(BasicJsonType& j, TargetType&& val)
+ -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
+{
+ ::nlohmann::to_json(j, std::forward<TargetType>(val));
+}
+```
+
+The unqualified call to `::nlohmann::to_json(j, val)` finds:
+1. Built-in overloads in `namespace nlohmann` (via `using` declarations)
+2. User-provided overloads in the type's namespace (via ADL)
+
+## `get_to()` Helper
+
+```cpp
+template<typename ValueType>
+ValueType& get_to(ValueType& v) const;
+```
+
+Converts and writes into an existing variable:
+
+```cpp
+json j = {{"x", 1}, {"y", 2}};
+int x, y;
+j.at("x").get_to(x);
+j.at("y").get_to(y);
+```
+
+## Automatic Macros
+
+The library provides macros to auto-generate `to_json()` and `from_json()`
+without writing them manually. All macros are defined in
+`include/nlohmann/detail/macro_scope.hpp`.
+
+### `NLOHMANN_DEFINE_TYPE_INTRUSIVE`
+
+Defines `to_json()` and `from_json()` as **friend functions** inside the
+class body. Requires all fields to be present during deserialization:
+
+```cpp
+struct Point {
+ double x;
+ double y;
+ double z;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(Point, x, y, z)
+};
+```
+
+This expands to:
+
+```cpp
+friend void to_json(nlohmann::json& nlohmann_json_j, const Point& nlohmann_json_t) {
+ nlohmann_json_j["x"] = nlohmann_json_t.x;
+ nlohmann_json_j["y"] = nlohmann_json_t.y;
+ nlohmann_json_j["z"] = nlohmann_json_t.z;
+}
+
+friend void from_json(const nlohmann::json& nlohmann_json_j, Point& nlohmann_json_t) {
+ nlohmann_json_j.at("x").get_to(nlohmann_json_t.x);
+ nlohmann_json_j.at("y").get_to(nlohmann_json_t.y);
+ nlohmann_json_j.at("z").get_to(nlohmann_json_t.z);
+}
+```
+
+### `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT`
+
+Same as above, but uses `value()` instead of `at()` during deserialization.
+Missing keys get the default-constructed or current value instead of
+throwing:
+
+```cpp
+struct Config {
+ std::string host = "localhost";
+ int port = 8080;
+ bool debug = false;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, host, port, debug)
+};
+```
+
+Now parsing `{}` produces a Config with all default values instead of
+throwing.
+
+### `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE`
+
+Generates only the `to_json()` function (no `from_json()`). Useful for
+types that should be serializable but not deserializable:
+
+```cpp
+struct LogEntry {
+ std::string timestamp;
+ std::string message;
+ int level;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(LogEntry, timestamp, message, level)
+};
+```
+
+### `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`
+
+Defines `to_json()` and `from_json()` as **free functions** outside the
+class. Requires all members to be public:
+
+```cpp
+struct Color {
+ int r, g, b;
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Color, r, g, b)
+```
+
+### `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`
+
+Non-intrusive version with default values for missing keys:
+
+```cpp
+struct Margin {
+ int top = 0;
+ int right = 0;
+ int bottom = 0;
+ int left = 0;
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Margin, top, right, bottom, left)
+```
+
+### `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE`
+
+Non-intrusive, serialize-only:
+
+```cpp
+struct Metric {
+ std::string name;
+ double value;
+};
+
+NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Metric, name, value)
+```
+
+## Derived Type Macros
+
+For inheritance hierarchies, use the `DERIVED_TYPE` variants. These include
+the base class fields:
+
+### `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE`
+
+```cpp
+struct Base {
+ std::string id;
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(Base, id)
+};
+
+struct Derived : Base {
+ int value;
+ NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Derived, Base, value)
+};
+```
+
+This generates serialization that includes both `id` (from Base) and
+`value` (from Derived).
+
+### All Derived Variants
+
+| Macro | Intrusive | Default | Serialize-Only |
+|---|---|---|---|
+| `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE` | Yes | No | No |
+| `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT` | Yes | Yes | No |
+| `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE` | Yes | — | Yes |
+| `NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE` | No | No | No |
+| `NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT` | No | Yes | No |
+| `NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE` | No | — | Yes |
+
+## Low-Level Macros
+
+### `NLOHMANN_JSON_TO` / `NLOHMANN_JSON_FROM`
+
+Building-block macros for custom serialization:
+
+```cpp
+#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
+#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
+#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) \
+ nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1);
+```
+
+These are used internally by the `NLOHMANN_DEFINE_TYPE_*` macros and can
+be used directly for custom patterns.
+
+## Custom `adl_serializer` Specialization
+
+For types where you can't add free functions (e.g., third-party types),
+specialize `adl_serializer`:
+
+```cpp
+namespace nlohmann {
+
+template<>
+struct adl_serializer<third_party::Point3D> {
+ static void to_json(json& j, const third_party::Point3D& p) {
+ j = json{{"x", p.x()}, {"y", p.y()}, {"z", p.z()}};
+ }
+
+ static void from_json(const json& j, third_party::Point3D& p) {
+ p = third_party::Point3D(
+ j.at("x").get<double>(),
+ j.at("y").get<double>(),
+ j.at("z").get<double>()
+ );
+ }
+};
+
+} // namespace nlohmann
+```
+
+### Non-Default-Constructible Types
+
+For types without a default constructor, implement `from_json()` as a
+static method returning the constructed value:
+
+```cpp
+namespace nlohmann {
+
+template<>
+struct adl_serializer<Immutable> {
+ static Immutable from_json(const json& j) {
+ return Immutable(j.at("x").get<int>(), j.at("y").get<int>());
+ }
+
+ static void to_json(json& j, const Immutable& val) {
+ j = json{{"x", val.x()}, {"y", val.y()}};
+ }
+};
+
+} // namespace nlohmann
+```
+
+Usage:
+
+```cpp
+json j = {{"x", 1}, {"y", 2}};
+auto val = j.get<Immutable>(); // calls adl_serializer<Immutable>::from_json(j)
+```
+
+## Enum Serialization
+
+### Default: Integer Mapping
+
+By default, enums are serialized as their underlying integer value:
+
+```cpp
+enum class Status { active, inactive, pending };
+
+json j = Status::active; // 0
+auto s = j.get<Status>(); // Status::active
+```
+
+### Disabling Enum Serialization
+
+```cpp
+#define JSON_DISABLE_ENUM_SERIALIZATION 1
+```
+
+Or via CMake:
+
+```cmake
+set(JSON_DisableEnumSerialization ON)
+```
+
+### Custom Enum Mapping with `NLOHMANN_JSON_SERIALIZE_ENUM`
+
+```cpp
+enum class Color { red, green, blue };
+
+NLOHMANN_JSON_SERIALIZE_ENUM(Color, {
+ {Color::red, "red"},
+ {Color::green, "green"},
+ {Color::blue, "blue"},
+})
+```
+
+This generates both `to_json()` and `from_json()` that map between enum
+values and strings:
+
+```cpp
+json j = Color::red; // "red"
+auto c = j.get<Color>(); // Color::red
+
+json j2 = "unknown";
+auto c2 = j2.get<Color>(); // Color::red (first entry is the default)
+```
+
+The first entry in the mapping serves as the default for unrecognized
+values during deserialization.
+
+## Nested Types
+
+```cpp
+struct Address {
+ std::string city;
+ std::string zip;
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(Address, city, zip)
+};
+
+struct Employee {
+ std::string name;
+ Address address;
+ std::vector<std::string> skills;
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(Employee, name, address, skills)
+};
+```
+
+The library handles nesting automatically — `Address` has its own
+serialization, and `Employee` uses it implicitly:
+
+```cpp
+Employee emp{"alice", {"wonderland", "12345"}, {"c++", "python"}};
+json j = emp;
+// {
+// "name": "alice",
+// "address": {"city": "wonderland", "zip": "12345"},
+// "skills": ["c++", "python"]
+// }
+
+auto emp2 = j.get<Employee>();
+```
+
+## Optional Fields
+
+Use `std::optional` (C++17) for truly optional fields:
+
+```cpp
+struct UserProfile {
+ std::string username;
+ std::optional<std::string> bio;
+ std::optional<int> age;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(UserProfile, username, bio, age)
+};
+```
+
+`std::optional<T>` serializes as:
+- The value `T` if it has a value
+- `null` if it's `std::nullopt`
+
+With `_WITH_DEFAULT`, missing keys leave the optional as `std::nullopt`.
+
+## Collection Types
+
+Standard containers are automatically handled:
+
+```cpp
+struct Team {
+ std::string name;
+ std::vector<Person> members;
+ std::map<std::string, int> scores;
+ std::set<std::string> tags;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE(Team, name, members, scores, tags)
+};
+```
+
+Any type that satisfies the required type traits is automatically
+serializable:
+- Sequence containers (`std::vector`, `std::list`, `std::deque`, etc.)
+- Associative containers (`std::map`, `std::set`, `std::unordered_map`, etc.)
+- `std::pair`, `std::tuple`
+- `std::array`
+
+## Smart Pointers
+
+`std::unique_ptr<T>` and `std::shared_ptr<T>` are supported if `T` is
+serializable:
+
+```cpp
+struct Node {
+ int value;
+ std::shared_ptr<Node> next;
+
+ NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Node, value, next)
+};
+```
+
+- Non-null pointer → serializes the pointed-to value
+- Null pointer → serializes as JSON `null`
+- JSON `null` → deserializes as null pointer
+
+## Type Traits
+
+The library uses SFINAE-based type traits to detect capabilities:
+
+| Trait | Purpose |
+|---|---|
+| `is_compatible_type` | Can be converted to/from JSON |
+| `has_to_json` | Has a `to_json()` function |
+| `has_from_json` | Has a `from_json()` function |
+| `is_compatible_object_type` | Looks like a JSON object |
+| `is_compatible_array_type` | Looks like a JSON array |
+| `is_compatible_string_type` | Looks like a JSON string |
+| `is_compatible_integer_type` | Looks like a JSON integer |
+
+These traits live in `include/nlohmann/detail/meta/type_traits.hpp`.
diff --git a/docs/handbook/json4cpp/element-access.md b/docs/handbook/json4cpp/element-access.md
new file mode 100644
index 0000000000..73d52126bb
--- /dev/null
+++ b/docs/handbook/json4cpp/element-access.md
@@ -0,0 +1,581 @@
+# json4cpp — Element Access
+
+## Overview
+
+The `basic_json` class provides several ways to access elements:
+
+| Method | Applicable To | Behaviour on Missing |
+|---|---|---|
+| `operator[]` | array, object, null | Inserts default (creates if null) |
+| `at()` | array, object | Throws `out_of_range` |
+| `value()` | object | Returns caller-supplied default |
+| `front()` | array, object, scalar | UB if empty |
+| `back()` | array, object, scalar | UB if empty |
+| `find()` | object | Returns `end()` |
+| `contains()` | object | Returns `false` |
+| `count()` | object | Returns `0` |
+
+## `operator[]`
+
+### Array Access
+
+```cpp
+reference operator[](size_type idx);
+const_reference operator[](size_type idx) const;
+```
+
+Accesses the element at index `idx`. If the JSON value is **null**, it is
+automatically converted to an **array** before accessing:
+
+```cpp
+json j; // null
+j[0] = "first"; // j is now ["first"]
+j[1] = "second"; // j is now ["first", "second"]
+```
+
+If `idx` is beyond the current array size, the array is extended with null
+elements:
+
+```cpp
+json j = {1, 2};
+j[5] = 99;
+// j is now [1, 2, null, null, null, 99]
+```
+
+**Warning:** `const` array access does **not** extend the array and has
+undefined behavior for out-of-bounds access.
+
+### Object Access
+
+```cpp
+reference operator[](const typename object_t::key_type& key);
+const_reference operator[](const typename object_t::key_type& key) const;
+
+// C++14 heterogeneous lookup (KeyType template)
+template<typename KeyType>
+reference operator[](KeyType&& key);
+template<typename KeyType>
+const_reference operator[](KeyType&& key) const;
+```
+
+Accesses the element with key `key`. If the key does not exist in a mutable
+context, it is **inserted** with a null value:
+
+```cpp
+json j = {{"name", "alice"}};
+j["age"] = 30; // inserts "age"
+std::string name = j["name"];
+
+// const access does not insert
+const json& cj = j;
+// cj["missing"]; // undefined behavior if key doesn't exist
+```
+
+If the JSON value is **null**, it is automatically converted to an **object**:
+
+```cpp
+json j; // null
+j["key"] = "value"; // j is now {"key": "value"}
+```
+
+### String Literal vs. Integer Ambiguity
+
+Be careful with `0`:
+
+```cpp
+json j = {{"key", "value"}};
+// j[0] — array access (selects first element of object iteration) — NOT recommended
+// j["key"] — object access
+```
+
+### Using `json::object_t::key_type`
+
+The non-const `operator[]` accepts a `key_type` (default: `std::string`).
+The `KeyType` template overloads accept any type that satisfies these
+constraints via `detail::is_usable_as_key_type`:
+
+- Must be comparable with `object_comparator_t`
+- Not convertible to `basic_json`
+- Not a `value_t`
+- Not a `BasicJsonType`
+
+## `at()`
+
+### Array Access
+
+```cpp
+reference at(size_type idx);
+const_reference at(size_type idx) const;
+```
+
+Returns a reference to the element at index `idx`. Throws
+`json::out_of_range` (id 401) if the index is out of bounds:
+
+```cpp
+json j = {1, 2, 3};
+j.at(0); // 1
+j.at(3); // throws out_of_range::401: "array index 3 is out of range"
+```
+
+When `JSON_DIAGNOSTIC_POSITIONS` is enabled, the exception includes
+byte-offset information.
+
+### Object Access
+
+```cpp
+reference at(const typename object_t::key_type& key);
+const_reference at(const typename object_t::key_type& key) const;
+
+template<typename KeyType>
+reference at(KeyType&& key);
+template<typename KeyType>
+const_reference at(KeyType&& key) const;
+```
+
+Returns a reference to the element with key `key`. Throws
+`json::out_of_range` (id 403) if the key is not found:
+
+```cpp
+json j = {{"name", "alice"}, {"age", 30}};
+j.at("name"); // "alice"
+j.at("missing"); // throws out_of_range::403: "key 'missing' not found"
+```
+
+### Type Mismatch
+
+Both `at()` overloads throw `json::type_error` (id 304) if the JSON value
+is not of the expected type:
+
+```cpp
+json j = 42;
+j.at(0); // throws type_error::304: "cannot use at() with number"
+j.at("key"); // throws type_error::304: "cannot use at() with number"
+```
+
+## `value()`
+
+```cpp
+// With default value
+ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const;
+
+// With JSON pointer
+ValueType value(const json_pointer& ptr, const ValueType& default_value) const;
+
+// KeyType template overloads
+template<typename KeyType>
+ValueType value(KeyType&& key, const ValueType& default_value) const;
+```
+
+Returns the value for a given key or JSON pointer, or `default_value` if
+the key/pointer does not resolve. Unlike `operator[]` and `at()`, this
+method **never modifies** the JSON value.
+
+```cpp
+json j = {{"name", "alice"}, {"age", 30}};
+
+std::string name = j.value("name", "unknown"); // "alice"
+std::string addr = j.value("address", "N/A"); // "N/A"
+int height = j.value("height", 170); // 170
+
+// With JSON pointer
+int age = j.value("/age"_json_pointer, 0); // 30
+int foo = j.value("/foo"_json_pointer, -1); // -1
+```
+
+Throws `json::type_error` (id 306) if the JSON value is not an object (for
+the key overloads) or if the found value cannot be converted to `ValueType`.
+
+### `value()` vs `operator[]`
+
+| Feature | `operator[]` | `value()` |
+|---|---|---|
+| Modifies on miss | Yes (inserts null) | No |
+| Returns | Reference | Value copy |
+| Default on miss | null (always) | Caller-specified |
+| Applicable to arrays | Yes | No (objects only) |
+
+## `front()` and `back()`
+
+```cpp
+reference front();
+const_reference front() const;
+
+reference back();
+const_reference back() const;
+```
+
+Return references to the first/last element. For **arrays**, this is the
+first/last element by index. For **objects**, this is the first/last element
+by iteration order (which depends on the comparator — insertion order for
+`ordered_json`). For **non-compound types**, the value itself is returned
+(the JSON value is treated as a single-element container).
+
+```cpp
+json j = {1, 2, 3};
+j.front(); // 1
+j.back(); // 3
+
+json j2 = 42;
+j2.front(); // 42
+j2.back(); // 42
+```
+
+**Warning:** Calling `front()` or `back()` on an empty container is
+**undefined behavior** (same as STL containers).
+
+## `find()`
+
+```cpp
+iterator find(const typename object_t::key_type& key);
+const_iterator find(const typename object_t::key_type& key) const;
+
+template<typename KeyType>
+iterator find(KeyType&& key);
+template<typename KeyType>
+const_iterator find(KeyType&& key) const;
+```
+
+Returns an iterator to the element with the given key, or `end()` if not
+found. Only works on objects:
+
+```cpp
+json j = {{"name", "alice"}, {"age", 30}};
+
+auto it = j.find("name");
+if (it != j.end()) {
+ std::cout << it.key() << " = " << it.value() << "\n";
+}
+
+auto it2 = j.find("missing");
+assert(it2 == j.end());
+```
+
+For non-objects, `find()` always returns `end()`.
+
+## `contains()`
+
+```cpp
+bool contains(const typename object_t::key_type& key) const;
+
+template<typename KeyType>
+bool contains(KeyType&& key) const;
+
+// JSON pointer overload
+bool contains(const json_pointer& ptr) const;
+```
+
+Returns `true` if the key or pointer exists:
+
+```cpp
+json j = {{"name", "alice"}, {"address", {{"city", "wonderland"}}}};
+
+j.contains("name"); // true
+j.contains("phone"); // false
+
+// JSON pointer — checks nested paths
+j.contains("/address/city"_json_pointer); // true
+j.contains("/address/zip"_json_pointer); // false
+```
+
+## `count()`
+
+```cpp
+size_type count(const typename object_t::key_type& key) const;
+
+template<typename KeyType>
+size_type count(KeyType&& key) const;
+```
+
+Returns the number of elements with the given key. Since JSON objects have
+unique keys, the result is always `0` or `1`:
+
+```cpp
+json j = {{"name", "alice"}};
+j.count("name"); // 1
+j.count("missing"); // 0
+```
+
+## `erase()`
+
+### Erase by Iterator
+
+```cpp
+iterator erase(iterator pos);
+iterator erase(const_iterator pos);
+```
+
+Removes the element at the given iterator position. Returns an iterator to
+the element after the erased one:
+
+```cpp
+json j = {1, 2, 3, 4, 5};
+auto it = j.erase(j.begin() + 2); // removes 3
+// j is now [1, 2, 4, 5], it points to 4
+```
+
+### Erase by Iterator Range
+
+```cpp
+iterator erase(iterator first, iterator last);
+iterator erase(const_iterator first, const_iterator last);
+```
+
+Removes all elements in the range `[first, last)`:
+
+```cpp
+json j = {1, 2, 3, 4, 5};
+j.erase(j.begin() + 1, j.begin() + 3);
+// j is now [1, 4, 5]
+```
+
+### Erase by Key
+
+```cpp
+size_type erase(const typename object_t::key_type& key);
+
+template<typename KeyType>
+size_type erase(KeyType&& key);
+```
+
+Removes the element with the given key from an object. Returns the number
+of elements removed (0 or 1):
+
+```cpp
+json j = {{"name", "alice"}, {"age", 30}};
+j.erase("age");
+// j is now {"name": "alice"}
+```
+
+### Erase by Index
+
+```cpp
+void erase(const size_type idx);
+```
+
+Removes the element at the given index from an array. Throws
+`out_of_range::401` if the index is out of range:
+
+```cpp
+json j = {"a", "b", "c"};
+j.erase(1);
+// j is now ["a", "c"]
+```
+
+### Erase on Primitive Types
+
+Erasing by iterator on primitive types (number, string, boolean) is
+supported only if the iterator points to the single element:
+
+```cpp
+json j = 42;
+j.erase(j.begin()); // j is now null
+```
+
+## `size()`, `empty()`, `max_size()`
+
+```cpp
+size_type size() const noexcept;
+bool empty() const noexcept;
+size_type max_size() const noexcept;
+```
+
+| Type | `size()` | `empty()` |
+|---|---|---|
+| null | 0 | `true` |
+| object | number of key-value pairs | `true` if no pairs |
+| array | number of elements | `true` if no elements |
+| scalar (string, number, boolean, binary) | 1 | `false` |
+
+```cpp
+json j_null;
+j_null.size(); // 0
+j_null.empty(); // true
+
+json j_arr = {1, 2, 3};
+j_arr.size(); // 3
+j_arr.empty(); // false
+
+json j_str = "hello";
+j_str.size(); // 1
+j_str.empty(); // false (primitive → always 1)
+```
+
+`max_size()` returns the maximum number of elements the container can hold
+(delegates to the underlying container's `max_size()` for arrays and
+objects; returns 1 for scalars).
+
+## `clear()`
+
+```cpp
+void clear() noexcept;
+```
+
+Resets the value to a default-constructed value of the same type:
+
+| Type | Result after `clear()` |
+|---|---|
+| null | null |
+| object | `{}` |
+| array | `[]` |
+| string | `""` |
+| boolean | `false` |
+| number_integer | `0` |
+| number_unsigned | `0` |
+| number_float | `0.0` |
+| binary | `[]` (empty, no subtype) |
+
+## `push_back()` and `emplace_back()`
+
+### Array Operations
+
+```cpp
+void push_back(basic_json&& val);
+void push_back(const basic_json& val);
+
+template<typename... Args>
+reference emplace_back(Args&&... args);
+```
+
+Appends an element at the end:
+
+```cpp
+json j = {1, 2, 3};
+j.push_back(4);
+j.emplace_back(5);
+// j is now [1, 2, 3, 4, 5]
+```
+
+If the value is `null`, it's first converted to an empty array.
+
+### Object Operations
+
+```cpp
+void push_back(const typename object_t::value_type& val);
+void push_back(initializer_list_t init);
+```
+
+Inserts a key-value pair:
+
+```cpp
+json j = {{"a", 1}};
+j.push_back({"b", 2}); // initializer_list pair
+j.push_back(json::object_t::value_type("c", 3)); // explicit pair
+// j is now {"a": 1, "b": 2, "c": 3}
+```
+
+### `operator+=`
+
+Alias for `push_back()`:
+
+```cpp
+json j = {1, 2};
+j += 3;
+j += {4, 5}; // pushes an array [4, 5] as a single element
+```
+
+## `emplace()`
+
+```cpp
+template<typename... Args>
+std::pair<iterator, bool> emplace(Args&&... args);
+```
+
+For objects, inserts a key-value pair if the key doesn't already exist.
+Returns a pair of iterator and bool (whether insertion took place):
+
+```cpp
+json j = {{"a", 1}};
+auto [it, inserted] = j.emplace("b", 2);
+// inserted == true, it points to {"b": 2}
+auto [it2, inserted2] = j.emplace("a", 99);
+// inserted2 == false, existing value unchanged
+```
+
+## `insert()`
+
+### Array Insert
+
+```cpp
+iterator insert(const_iterator pos, const basic_json& val);
+iterator insert(const_iterator pos, basic_json&& val);
+iterator insert(const_iterator pos, size_type cnt, const basic_json& val);
+iterator insert(const_iterator pos, const_iterator first, const_iterator last);
+iterator insert(const_iterator pos, initializer_list_t ilist);
+```
+
+Inserts elements at the given position:
+
+```cpp
+json j = {1, 2, 5};
+j.insert(j.begin() + 2, 3);
+j.insert(j.begin() + 3, 4);
+// j is now [1, 2, 3, 4, 5]
+
+// Insert count copies
+j.insert(j.end(), 2, 0);
+// j is now [1, 2, 3, 4, 5, 0, 0]
+```
+
+### Object Insert
+
+```cpp
+void insert(const_iterator first, const_iterator last);
+```
+
+Inserts elements from another object:
+
+```cpp
+json j1 = {{"a", 1}};
+json j2 = {{"b", 2}, {"c", 3}};
+j1.insert(j2.begin(), j2.end());
+// j1 is now {"a": 1, "b": 2, "c": 3}
+```
+
+## `update()`
+
+```cpp
+void update(const_reference j, bool merge_objects = false);
+void update(const_iterator first, const_iterator last, bool merge_objects = false);
+```
+
+Updates an object with keys from another object. Existing keys are
+**overwritten**:
+
+```cpp
+json j1 = {{"a", 1}, {"b", 2}};
+json j2 = {{"b", 99}, {"c", 3}};
+j1.update(j2);
+// j1 is now {"a": 1, "b": 99, "c": 3}
+```
+
+When `merge_objects` is `true`, nested objects are merged recursively
+instead of being overwritten:
+
+```cpp
+json j1 = {{"config", {{"debug", true}, {"port", 8080}}}};
+json j2 = {{"config", {{"port", 9090}, {"host", "localhost"}}}};
+j1.update(j2, true);
+// j1["config"] is now {"debug": true, "port": 9090, "host": "localhost"}
+```
+
+## `swap()`
+
+```cpp
+void swap(reference other) noexcept;
+
+void swap(array_t& other);
+void swap(object_t& other);
+void swap(string_t& other);
+void swap(binary_t& other);
+void swap(typename binary_t::container_type& other);
+```
+
+Swaps contents with another value or with a compatible container.
+The typed overloads throw `type_error::310` if the types don't match:
+
+```cpp
+json j = {1, 2, 3};
+std::vector<json> v = {4, 5, 6};
+j.swap(v);
+// j is now [4, 5, 6], v contains the old j's elements
+```
diff --git a/docs/handbook/json4cpp/exception-handling.md b/docs/handbook/json4cpp/exception-handling.md
new file mode 100644
index 0000000000..58e52a4598
--- /dev/null
+++ b/docs/handbook/json4cpp/exception-handling.md
@@ -0,0 +1,368 @@
+# json4cpp — Exception Handling
+
+## Exception Hierarchy
+
+All exceptions derive from `json::exception`, which itself inherits from
+`std::exception`. Defined in `include/nlohmann/detail/exceptions.hpp`:
+
+```
+std::exception
+ └── json::exception
+ ├── json::parse_error
+ ├── json::invalid_iterator
+ ├── json::type_error
+ ├── json::out_of_range
+ └── json::other_error
+```
+
+## Base Class: `json::exception`
+
+```cpp
+class exception : public std::exception
+{
+public:
+ const char* what() const noexcept override;
+ const int id; // numeric error identifier
+
+protected:
+ exception(int id_, const char* what_arg);
+
+ static std::string name(const std::string& ename, int id_);
+ static std::string diagnostics(std::nullptr_t leaf_element);
+ static std::string diagnostics(const BasicJsonType* leaf_element);
+
+private:
+ std::runtime_error m; // stores the what() message
+};
+```
+
+### Error Message Format
+
+```
+[json.exception.<type>.<id>] <description>
+```
+
+Example:
+```
+[json.exception.type_error.302] type must be string, but is number
+```
+
+### Diagnostics Mode
+
+When `JSON_DIAGNOSTICS` is enabled, error messages include the **path**
+from the root to the problematic value:
+
+```cpp
+#define JSON_DIAGNOSTICS 1
+#include <nlohmann/json.hpp>
+```
+
+Error message with diagnostics:
+```
+[json.exception.type_error.302] (/config/server/port) type must be string, but is number
+```
+
+The path is computed by walking up the parent chain (stored in each
+`basic_json` node when diagnostics are enabled).
+
+## `parse_error`
+
+Thrown when parsing fails.
+
+```cpp
+class parse_error : public exception
+{
+public:
+ const std::size_t byte; // byte position of the error
+
+ static parse_error create(int id_, const position_t& pos,
+ const std::string& what_arg,
+ BasicJsonType* context);
+ static parse_error create(int id_, std::size_t byte_,
+ const std::string& what_arg,
+ BasicJsonType* context);
+};
+```
+
+### Error IDs
+
+| ID | Condition | Example |
+|---|---|---|
+| 101 | Unexpected token | `parse("}")` |
+| 102 | Invalid `\u` escape | `parse("\"\\u000g\"")` |
+| 103 | Invalid surrogate pair | `parse("\"\\uDC00\"")` |
+| 104 | Invalid JSON Patch | `patch(json::array({42}))` |
+| 105 | JSON Patch missing field | `patch(json::array({{{"op","add"}}}))` |
+| 106 | Number overflow | Very large number literal |
+| 107 | Invalid JSON Pointer | `json_pointer("no-slash")` |
+| 108 | Invalid Unicode code point | Code point > U+10FFFF |
+| 109 | Invalid UTF-8 in input | Binary data as string |
+| 110 | Binary format marker error | Invalid CBOR/MsgPack byte |
+| 112 | BSON parse error | Malformed BSON input |
+| 113 | UBJSON parse error | Invalid UBJSON type marker |
+| 114 | BJData parse error | Invalid BJData structure |
+| 115 | Incomplete binary input | Truncated binary data |
+
+### Catching Parse Errors
+
+```cpp
+try {
+ json j = json::parse("{invalid}");
+} catch (json::parse_error& e) {
+ std::cerr << "Parse error: " << e.what() << "\n";
+ std::cerr << "Error ID: " << e.id << "\n";
+ std::cerr << "Byte position: " << e.byte << "\n";
+}
+```
+
+### Avoiding Exceptions
+
+```cpp
+json j = json::parse("invalid", nullptr, false);
+if (j.is_discarded()) {
+ // Handle parse failure without exception
+}
+```
+
+## `type_error`
+
+Thrown when a method is called on a JSON value of the wrong type.
+
+```cpp
+class type_error : public exception
+{
+public:
+ static type_error create(int id_, const std::string& what_arg,
+ BasicJsonType* context);
+};
+```
+
+### Error IDs
+
+| ID | Condition | Example |
+|---|---|---|
+| 301 | Cannot create from type | `json j = std::complex<double>()` |
+| 302 | Type mismatch in get | `json("str").get<int>()` |
+| 303 | Type mismatch in get_ref | `json(42).get_ref<std::string&>()` |
+| 304 | Wrong type for at() | `json(42).at("key")` |
+| 305 | Wrong type for operator[] | `json("str")[0]` |
+| 306 | Wrong type for value() | `json(42).value("k", 0)` |
+| 307 | Cannot erase from type | `json(42).erase(0)` |
+| 308 | Wrong type for push_back | `json("str").push_back(1)` |
+| 309 | Wrong type for insert | `json(42).insert(...)` |
+| 310 | Wrong type for swap | `json(42).swap(vec)` |
+| 311 | Wrong type for iterator | `json(42).begin().key()` |
+| 312 | Cannot serialize binary to text | `json::binary(...).dump()` |
+| 313 | Wrong type for push_back pair | `json(42).push_back({"k",1})` |
+| 314 | unflatten: value not primitive | `json({"/a": [1]}).unflatten()` |
+| 315 | unflatten: conflicting paths | `json({"/a": 1, "/a/b": 2}).unflatten()` |
+| 316 | Invalid UTF-8 in dump (strict) | Invalid byte in string |
+| 317 | to_bson: top level not object | `json::to_bson(json::array())` |
+| 318 | to_bson: key too long | Key > max int32 length |
+
+### Common Type Errors
+
+```cpp
+json j = 42;
+
+// 302: type mismatch
+try {
+ std::string s = j.get<std::string>();
+} catch (json::type_error& e) {
+ // type must be string, but is number
+}
+
+// 304: wrong type for at()
+try {
+ j.at("key");
+} catch (json::type_error& e) {
+ // cannot use at() with number
+}
+
+// 316: invalid UTF-8
+try {
+ json j = std::string("\xC0\xAF"); // overlong encoding
+ j.dump();
+} catch (json::type_error& e) {
+ // invalid utf-8 byte
+}
+```
+
+## `out_of_range`
+
+Thrown when accessing elements outside valid bounds.
+
+```cpp
+class out_of_range : public exception
+{
+public:
+ static out_of_range create(int id_, const std::string& what_arg,
+ BasicJsonType* context);
+};
+```
+
+### Error IDs
+
+| ID | Condition | Example |
+|---|---|---|
+| 401 | Array index out of range | `json({1,2}).at(5)` |
+| 402 | Array index `-` in at() | `j.at("/-"_json_pointer)` |
+| 403 | Key not found | `j.at("missing")` |
+| 404 | JSON Pointer reference error | `j["/bad/path"_json_pointer]` |
+| 405 | back()/pop_back() on empty ptr | `json_pointer("").back()` |
+| 406 | Numeric overflow in get | Large float → int |
+| 407 | Number not representable | `json(1e500).get<int>()` |
+| 408 | BSON key conflict | Key "0" in BSON array |
+
+```cpp
+json j = {1, 2, 3};
+
+try {
+ j.at(10);
+} catch (json::out_of_range& e) {
+ // [json.exception.out_of_range.401] array index 10 is out of range
+}
+```
+
+## `invalid_iterator`
+
+Thrown when iterators are used incorrectly.
+
+```cpp
+class invalid_iterator : public exception
+{
+public:
+ static invalid_iterator create(int id_, const std::string& what_arg,
+ BasicJsonType* context);
+};
+```
+
+### Error IDs
+
+| ID | Condition |
+|---|---|
+| 201 | Iterator not dereferenceable |
+| 202 | Iterator += on non-array |
+| 203 | Iterator compare across values |
+| 204 | Iterator - on non-array |
+| 205 | Iterator > on non-array |
+| 206 | Iterator + on non-array |
+| 207 | Cannot use key() on array iterator |
+| 209 | Range [first, last) not from same container |
+| 210 | Range not valid for erase |
+| 211 | Range not valid for insert |
+| 212 | Range from different container in insert |
+| 213 | Insert iterator for non-array |
+| 214 | Insert range for non-object |
+
+```cpp
+json j1 = {1, 2, 3};
+json j2 = {4, 5, 6};
+
+try {
+ j1.erase(j2.begin()); // wrong container
+} catch (json::invalid_iterator& e) {
+ // iterator does not fit current value
+}
+```
+
+## `other_error`
+
+Thrown for miscellaneous errors that don't fit the other categories.
+
+```cpp
+class other_error : public exception
+{
+public:
+ static other_error create(int id_, const std::string& what_arg,
+ BasicJsonType* context);
+};
+```
+
+### Error IDs
+
+| ID | Condition |
+|---|---|
+| 501 | JSON Patch test operation failed |
+
+```cpp
+json doc = {{"name", "alice"}};
+json patch = json::array({
+ {{"op", "test"}, {"path", "/name"}, {"value", "bob"}}
+});
+
+try {
+ doc.patch(patch);
+} catch (json::other_error& e) {
+ // [json.exception.other_error.501] unsuccessful: /name
+}
+```
+
+## Exception-Free API
+
+### `parse()` with `allow_exceptions = false`
+
+```cpp
+json j = json::parse("invalid", nullptr, false);
+if (j.is_discarded()) {
+ // Handle gracefully
+}
+```
+
+### `get()` Alternatives
+
+Use `value()` for safe object access with defaults:
+
+```cpp
+json j = {{"timeout", 30}};
+int t = j.value("timeout", 60); // 30
+int r = j.value("retries", 3); // 3 (missing key)
+```
+
+Use `contains()` before access:
+
+```cpp
+if (j.contains("key")) {
+ auto val = j["key"];
+}
+```
+
+Use `find()` for iterator-based access:
+
+```cpp
+auto it = j.find("key");
+if (it != j.end()) {
+ // use *it
+}
+```
+
+### Type Checking Before Access
+
+```cpp
+json j = /* unknown content */;
+
+if (j.is_string()) {
+ auto s = j.get<std::string>();
+}
+```
+
+## Catching All JSON Exceptions
+
+```cpp
+try {
+ // JSON operations
+} catch (json::exception& e) {
+ std::cerr << "JSON error [" << e.id << "]: " << e.what() << "\n";
+}
+```
+
+Since `json::exception` derives from `std::exception`, it can also be
+caught generically:
+
+```cpp
+try {
+ // ...
+} catch (const std::exception& e) {
+ std::cerr << e.what() << "\n";
+}
+```
diff --git a/docs/handbook/json4cpp/iteration.md b/docs/handbook/json4cpp/iteration.md
new file mode 100644
index 0000000000..be32a21ea8
--- /dev/null
+++ b/docs/handbook/json4cpp/iteration.md
@@ -0,0 +1,339 @@
+# json4cpp — Iteration
+
+## Iterator Types
+
+The `basic_json` class provides a full set of iterators modeled after STL
+container iterators. All are defined in
+`include/nlohmann/detail/iterators/`:
+
+| Type | Class | Header |
+|---|---|---|
+| `iterator` | `iter_impl<basic_json>` | `iter_impl.hpp` |
+| `const_iterator` | `iter_impl<const basic_json>` | `iter_impl.hpp` |
+| `reverse_iterator` | `json_reverse_iterator<iterator>` | `json_reverse_iterator.hpp` |
+| `const_reverse_iterator` | `json_reverse_iterator<const_iterator>` | `json_reverse_iterator.hpp` |
+
+## `iter_impl` Internals
+
+The `iter_impl<BasicJsonType>` template is the core iterator
+implementation. It wraps an `internal_iterator` struct:
+
+```cpp
+struct internal_iterator
+{
+ typename BasicJsonType::object_t::iterator object_iterator;
+ typename BasicJsonType::array_t::iterator array_iterator;
+ primitive_iterator_t primitive_iterator;
+};
+```
+
+Only one of these three fields is active at a time, determined by the
+`m_object` pointer's `type()`:
+
+- **Object**: uses `object_iterator` (delegates to the underlying map/ordered_map iterator)
+- **Array**: uses `array_iterator` (delegates to `std::vector::iterator`)
+- **Primitive** (null, boolean, number, string, binary): uses `primitive_iterator_t`
+
+### `primitive_iterator_t`
+
+Primitive types are treated as single-element containers. The
+`primitive_iterator_t` is a wrapper around `std::ptrdiff_t`:
+
+- Value `0` → points to the element (equivalent to `begin()`)
+- Value `1` → past-the-end (equivalent to `end()`)
+- Value `-1` → before-the-begin (sentinel, `end_value`)
+
+```cpp
+json j = 42;
+for (auto it = j.begin(); it != j.end(); ++it) {
+ // executes exactly once, *it == 42
+}
+```
+
+## Range Functions
+
+### Forward Iteration
+
+```cpp
+iterator begin() noexcept;
+const_iterator begin() const noexcept;
+const_iterator cbegin() const noexcept;
+
+iterator end() noexcept;
+const_iterator end() const noexcept;
+const_iterator cend() const noexcept;
+```
+
+```cpp
+json j = {1, 2, 3};
+for (auto it = j.begin(); it != j.end(); ++it) {
+ std::cout << *it << " ";
+}
+// Output: 1 2 3
+```
+
+### Reverse Iteration
+
+```cpp
+reverse_iterator rbegin() noexcept;
+const_reverse_iterator rbegin() const noexcept;
+const_reverse_iterator crbegin() const noexcept;
+
+reverse_iterator rend() noexcept;
+const_reverse_iterator rend() const noexcept;
+const_reverse_iterator crend() const noexcept;
+```
+
+```cpp
+json j = {1, 2, 3};
+for (auto it = j.rbegin(); it != j.rend(); ++it) {
+ std::cout << *it << " ";
+}
+// Output: 3 2 1
+```
+
+## Range-Based For Loops
+
+### Simple Iteration
+
+```cpp
+json j = {"alpha", "beta", "gamma"};
+for (const auto& element : j) {
+ std::cout << element << "\n";
+}
+```
+
+### Mutable Iteration
+
+```cpp
+json j = {1, 2, 3};
+for (auto& element : j) {
+ element = element.get<int>() * 2;
+}
+// j is now [2, 4, 6]
+```
+
+### Object Iteration
+
+When iterating over objects, each element is a **value** (not a key-value
+pair). Use `it.key()` and `it.value()` on an explicit iterator, or use
+`items()`:
+
+```cpp
+json j = {{"name", "alice"}, {"age", 30}};
+
+// Method 1: explicit iterator
+for (auto it = j.begin(); it != j.end(); ++it) {
+ std::cout << it.key() << ": " << it.value() << "\n";
+}
+
+// Method 2: range-for gives values only
+for (const auto& val : j) {
+ // val is the value, key is not accessible
+}
+```
+
+## `items()` — Key-Value Iteration
+
+```cpp
+iteration_proxy<iterator> items() noexcept;
+iteration_proxy<const_iterator> items() const noexcept;
+```
+
+Returns an `iteration_proxy` that wraps the iterator to provide `key()`
+and `value()` accessors in range-based for loops:
+
+```cpp
+json j = {{"name", "alice"}, {"age", 30}, {"active", true}};
+
+for (auto& [key, value] : j.items()) {
+ std::cout << key << " = " << value << "\n";
+}
+```
+
+### Array Keys
+
+For arrays, `items()` synthesizes string keys from the index:
+
+```cpp
+json j = {"a", "b", "c"};
+
+for (auto& [key, value] : j.items()) {
+ std::cout << key << ": " << value << "\n";
+}
+// Output:
+// 0: "a"
+// 1: "b"
+// 2: "c"
+```
+
+### Primitive Keys
+
+For primitives, the key is always `""` (empty string):
+
+```cpp
+json j = 42;
+for (auto& [key, value] : j.items()) {
+ // key == "", value == 42
+}
+```
+
+## `iteration_proxy` Implementation
+
+Defined in `include/nlohmann/detail/iterators/iteration_proxy.hpp`:
+
+```cpp
+template<typename IteratorType>
+class iteration_proxy
+{
+ class iteration_proxy_value
+ {
+ IteratorType anchor; // underlying iterator
+ std::size_t array_index = 0; // cached index for arrays
+ mutable std::size_t array_index_last = 0;
+ mutable string_type array_index_str = "0";
+
+ const string_type& key() const;
+ typename IteratorType::reference value() const;
+ };
+
+public:
+ iteration_proxy_value begin() const noexcept;
+ iteration_proxy_value end() const noexcept;
+};
+```
+
+The `key()` method dispatches based on the value type:
+- **Array**: converts `array_index` to string (`std::to_string`)
+- **Object**: returns `anchor.key()`
+- **Other**: returns empty string
+
+## Structured Bindings
+
+C++17 structured bindings work with `items()`:
+
+```cpp
+json j = {{"x", 1}, {"y", 2}};
+for (const auto& [key, value] : j.items()) {
+ // key is const std::string&, value is const json&
+}
+```
+
+This is enabled by specializations in `<nlohmann/detail/iterators/iteration_proxy.hpp>`:
+
+```cpp
+namespace std {
+ template<std::size_t N, typename IteratorType>
+ struct tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType>>;
+
+ template<typename IteratorType>
+ struct tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>>;
+}
+```
+
+## Iterator Arithmetic (Arrays Only)
+
+Array iterators support random access:
+
+```cpp
+json j = {10, 20, 30, 40, 50};
+
+auto it = j.begin();
+it += 2; // points to 30
+auto it2 = it + 1; // points to 40
+auto diff = it2 - it; // 1
+
+j[it - j.begin()]; // equivalent of *it
+```
+
+Object iterators support only increment and decrement (bidirectional).
+
+## `json_reverse_iterator`
+
+Extends `std::reverse_iterator` with `key()` and `value()` methods:
+
+```cpp
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+public:
+ // Inherited from std::reverse_iterator:
+ // operator*, operator->, operator++, operator--, operator+, operator- ...
+
+ // Added:
+ const typename Base::key_type& key() const;
+ typename Base::reference value() const;
+};
+```
+
+```cpp
+json j = {{"a", 1}, {"b", 2}, {"c", 3}};
+for (auto it = j.rbegin(); it != j.rend(); ++it) {
+ std::cout << it.key() << ": " << it.value() << "\n";
+}
+// Output (reversed iteration order)
+```
+
+## Iterator Invalidation
+
+Iterator invalidation follows the rules of the underlying containers:
+
+| Operation | Object (`std::map`) | Array (`std::vector`) |
+|---|---|---|
+| `push_back()` | Not invalidated | May invalidate all |
+| `insert()` | Not invalidated | Invalidates at/after pos |
+| `erase()` | Only erased | At/after erased pos |
+| `clear()` | All invalidated | All invalidated |
+| `operator[]` (new key) | Not invalidated | May invalidate all |
+
+For `ordered_json` (backed by `std::vector`), all iterators may be
+invalidated on any insertion/erasure since the ordered_map inherits from
+`std::vector`.
+
+## Iterating Null Values
+
+Null values behave as empty containers:
+
+```cpp
+json j; // null
+for (const auto& el : j) {
+ // never executes
+}
+assert(j.begin() == j.end());
+```
+
+## Complete Example
+
+```cpp
+#include <nlohmann/json.hpp>
+#include <iostream>
+
+using json = nlohmann::json;
+
+int main() {
+ json config = {
+ {"server", {
+ {"host", "localhost"},
+ {"port", 8080},
+ {"features", {"auth", "logging", "metrics"}}
+ }},
+ {"debug", false}
+ };
+
+ // Iterate top-level keys
+ for (auto& [key, value] : config.items()) {
+ std::cout << key << " [" << value.type_name() << "]\n";
+ }
+
+ // Iterate nested array
+ for (const auto& feature : config["server"]["features"]) {
+ std::cout << " feature: " << feature << "\n";
+ }
+
+ // Reverse iterate
+ auto& features = config["server"]["features"];
+ for (auto it = features.rbegin(); it != features.rend(); ++it) {
+ std::cout << " reverse: " << *it << "\n";
+ }
+}
+```
diff --git a/docs/handbook/json4cpp/json-patch.md b/docs/handbook/json4cpp/json-patch.md
new file mode 100644
index 0000000000..4c9de8fad5
--- /dev/null
+++ b/docs/handbook/json4cpp/json-patch.md
@@ -0,0 +1,341 @@
+# json4cpp — JSON Patch & Merge Patch
+
+## JSON Patch (RFC 6902)
+
+JSON Patch defines a JSON document structure for expressing a sequence of
+operations to apply to a JSON document.
+
+### `patch()`
+
+```cpp
+basic_json patch(const basic_json& json_patch) const;
+```
+
+Returns a new JSON value with the patch applied. Does not modify the
+original. Throws `parse_error::104` if the patch document is malformed.
+
+```cpp
+json doc = {
+ {"name", "alice"},
+ {"age", 30},
+ {"scores", {90, 85}}
+};
+
+json patch = json::array({
+ {{"op", "replace"}, {"path", "/name"}, {"value", "bob"}},
+ {{"op", "add"}, {"path", "/scores/-"}, {"value", 95}},
+ {{"op", "remove"}, {"path", "/age"}}
+});
+
+json result = doc.patch(patch);
+// {"name": "bob", "scores": [90, 85, 95]}
+```
+
+### `patch_inplace()`
+
+```cpp
+void patch_inplace(const basic_json& json_patch);
+```
+
+Applies the patch directly to the JSON value (modifying in place):
+
+```cpp
+json doc = {{"key", "old"}};
+doc.patch_inplace(json::array({
+ {{"op", "replace"}, {"path", "/key"}, {"value", "new"}}
+}));
+// doc is now {"key": "new"}
+```
+
+### Patch Operations
+
+Each operation is a JSON object with an `"op"` field and operation-specific
+fields:
+
+#### `add`
+
+Adds a value at the target location. If the target exists and is in an
+object, it is replaced. If the target is in an array, the value is inserted
+before the specified index.
+
+```json
+{"op": "add", "path": "/a/b", "value": 42}
+```
+
+The path's parent must exist. The `-` token appends to arrays:
+
+```cpp
+json doc = {{"arr", {1, 2}}};
+json p = json::array({{{"op", "add"}, {"path", "/arr/-"}, {"value", 3}}});
+doc.patch(p); // {"arr": [1, 2, 3]}
+```
+
+#### `remove`
+
+Removes the value at the target location:
+
+```json
+{"op": "remove", "path": "/a/b"}
+```
+
+Throws `out_of_range` if the path does not exist.
+
+#### `replace`
+
+Replaces the value at the target location (equivalent to `remove` + `add`):
+
+```json
+{"op": "replace", "path": "/name", "value": "bob"}
+```
+
+Throws `out_of_range` if the path does not exist.
+
+#### `move`
+
+Moves a value from one location to another:
+
+```json
+{"op": "move", "from": "/a/b", "path": "/c/d"}
+```
+
+Equivalent to `remove` from source + `add` to target. The `from` path
+must not be a prefix of the `path`.
+
+#### `copy`
+
+Copies a value from one location to another:
+
+```json
+{"op": "copy", "from": "/a/b", "path": "/c/d"}
+```
+
+#### `test`
+
+Tests that the value at the target location equals the specified value:
+
+```json
+{"op": "test", "path": "/name", "value": "alice"}
+```
+
+If the test fails, `patch()` throws `other_error::501`:
+
+```cpp
+json doc = {{"name", "alice"}};
+json p = json::array({
+ {{"op", "test"}, {"path", "/name"}, {"value", "bob"}}
+});
+
+try {
+ doc.patch(p);
+} catch (json::other_error& e) {
+ // [json.exception.other_error.501] unsuccessful: ...
+}
+```
+
+### Patch Validation
+
+The `patch()` method validates each operation:
+- `op` must be one of: `add`, `remove`, `replace`, `move`, `copy`, `test`
+- `path` is required for all operations
+- `value` is required for `add`, `replace`, `test`
+- `from` is required for `move`, `copy`
+
+Missing or invalid fields throw `parse_error::105`.
+
+### Operation Order
+
+Operations are applied sequentially. Each operation acts on the result of
+the previous one:
+
+```cpp
+json doc = {};
+json ops = json::array({
+ {{"op", "add"}, {"path", "/a"}, {"value", 1}},
+ {{"op", "add"}, {"path", "/b"}, {"value", 2}},
+ {{"op", "replace"}, {"path", "/a"}, {"value", 10}},
+ {{"op", "remove"}, {"path", "/b"}}
+});
+
+json result = doc.patch(ops);
+// {"a": 10}
+```
+
+## `diff()` — Computing Patches
+
+```cpp
+static basic_json diff(const basic_json& source,
+ const basic_json& target,
+ const string_t& path = "");
+```
+
+Generates a JSON Patch that transforms `source` into `target`:
+
+```cpp
+json source = {{"name", "alice"}, {"age", 30}};
+json target = {{"name", "alice"}, {"age", 31}, {"city", "wonderland"}};
+
+json patch = json::diff(source, target);
+// [
+// {"op": "replace", "path": "/age", "value": 31},
+// {"op": "add", "path": "/city", "value": "wonderland"}
+// ]
+
+// Verify roundtrip
+assert(source.patch(patch) == target);
+```
+
+### Diff Algorithm
+
+The algorithm works recursively:
+1. If `source == target`, produce no operations
+2. If types differ, produce a `replace` operation
+3. If both are objects:
+ - Keys in `source` but not `target` → `remove`
+ - Keys in `target` but not `source` → `add`
+ - Keys in both with different values → recurse
+4. If both are arrays:
+ - Compare element-by-element
+ - Produce `replace` for changed elements
+ - Produce `add` for extra elements in target
+ - Produce `remove` for extra elements in source
+5. For primitives with different values → `replace`
+
+Note: The generated patch uses only `add`, `remove`, and `replace`
+operations (not `move` or `copy`).
+
+### Custom Base Path
+
+The `path` parameter sets a prefix for all generated paths:
+
+```cpp
+json patch = json::diff(a, b, "/config");
+// All paths will start with "/config/..."
+```
+
+## Merge Patch (RFC 7396)
+
+Merge Patch is a simpler alternative to JSON Patch. Instead of an array of
+operations, a merge patch is a JSON object that describes the desired
+changes directly.
+
+### `merge_patch()`
+
+```cpp
+void merge_patch(const basic_json& apply_patch);
+```
+
+Applies a merge patch to the JSON value in place:
+
+```cpp
+json doc = {
+ {"title", "Hello"},
+ {"author", {{"name", "alice"}}},
+ {"tags", {"example"}}
+};
+
+json patch = {
+ {"title", "Goodbye"},
+ {"author", {{"name", "bob"}}},
+ {"tags", nullptr} // null means "remove"
+};
+
+doc.merge_patch(patch);
+// {
+// "title": "Goodbye",
+// "author": {"name": "bob"},
+// }
+// "tags" was removed because the patch value was null
+```
+
+### Merge Patch Rules
+
+The merge patch algorithm (per RFC 7396):
+
+1. If the patch is not an object, replace the target entirely
+2. If the patch is an object:
+ - For each key in the patch:
+ - If the value is `null`, remove the key from the target
+ - Otherwise, recursively merge_patch the target's key with the value
+
+```cpp
+// Partial update — only specified fields change
+json config = {{"debug", false}, {"port", 8080}, {"host", "0.0.0.0"}};
+
+config.merge_patch({{"port", 9090}});
+// {"debug": false, "port": 9090, "host": "0.0.0.0"}
+
+config.merge_patch({{"debug", nullptr}});
+// {"port": 9090, "host": "0.0.0.0"}
+```
+
+### Limitations of Merge Patch
+
+- Cannot set a value to `null` (null means "delete")
+- Cannot manipulate arrays — arrays are replaced entirely
+- Cannot express "move" or "copy" semantics
+
+```cpp
+json doc = {{"items", {1, 2, 3}}};
+doc.merge_patch({{"items", {4, 5}}});
+// {"items": [4, 5]} — array replaced, not merged
+```
+
+## JSON Patch vs. Merge Patch
+
+| Feature | JSON Patch (RFC 6902) | Merge Patch (RFC 7396) |
+|---|---|---|
+| Format | Array of operations | JSON object |
+| Operations | add, remove, replace, move, copy, test | Implicit merge |
+| Array handling | Per-element operations | Replace entire array |
+| Set value to null | Yes (explicit `add`/`replace`) | No (null = delete) |
+| Test assertions | Yes (`test` op) | No |
+| Reversibility | Can `diff()` to reverse | No |
+| Complexity | More verbose | Simpler |
+
+## Complete Example
+
+```cpp
+#include <nlohmann/json.hpp>
+#include <iostream>
+
+using json = nlohmann::json;
+
+int main() {
+ // Original document
+ json doc = {
+ {"name", "Widget"},
+ {"version", "1.0"},
+ {"settings", {
+ {"color", "blue"},
+ {"size", 10},
+ {"enabled", true}
+ }},
+ {"tags", {"production", "stable"}}
+ };
+
+ // JSON Patch: precise operations
+ json patch = json::array({
+ {{"op", "replace"}, {"path", "/version"}, {"value", "2.0"}},
+ {{"op", "add"}, {"path", "/settings/theme"}, {"value", "dark"}},
+ {{"op", "remove"}, {"path", "/settings/size"}},
+ {{"op", "add"}, {"path", "/tags/-"}, {"value", "updated"}},
+ {{"op", "test"}, {"path", "/name"}, {"value", "Widget"}}
+ });
+
+ json patched = doc.patch(patch);
+
+ // Compute diff to verify
+ json computed_patch = json::diff(doc, patched);
+ assert(doc.patch(computed_patch) == patched);
+
+ // Merge Patch: simple update
+ json merge = {
+ {"version", "2.1"},
+ {"settings", {{"color", "red"}}},
+ {"tags", nullptr} // remove tags
+ };
+
+ patched.merge_patch(merge);
+ std::cout << patched.dump(2) << "\n";
+}
+```
diff --git a/docs/handbook/json4cpp/json-pointer.md b/docs/handbook/json4cpp/json-pointer.md
new file mode 100644
index 0000000000..0fb0283fe9
--- /dev/null
+++ b/docs/handbook/json4cpp/json-pointer.md
@@ -0,0 +1,361 @@
+# json4cpp — JSON Pointer (RFC 6901)
+
+## Overview
+
+JSON Pointer (RFC 6901) provides a string syntax for identifying a specific
+value within a JSON document. The library implements this as the
+`json_pointer` class template, defined in
+`include/nlohmann/detail/json_pointer.hpp`.
+
+```cpp
+template<typename RefStringType>
+class json_pointer
+{
+ friend class basic_json;
+
+ std::vector<string_t> reference_tokens; // parsed path segments
+};
+```
+
+The default alias is:
+
+```cpp
+using json_pointer = json_pointer<std::string>;
+```
+
+## Syntax
+
+A JSON Pointer is a string of zero or more tokens separated by `/`:
+
+```
+"" → whole document
+"/foo" → key "foo" in root object
+"/foo/0" → first element of array at key "foo"
+"/a~1b" → key "a/b" (escaped /)
+"/m~0n" → key "m~n" (escaped ~)
+```
+
+### Escape Sequences
+
+| Sequence | Represents |
+|---|---|
+| `~0` | `~` |
+| `~1` | `/` |
+
+Escaping is applied **before** splitting (per RFC 6901 §3).
+
+## Construction
+
+### From String
+
+```cpp
+json_pointer(const string_t& s = "");
+```
+
+Parses the pointer string and populates `reference_tokens`. Throws
+`parse_error::107` if the string is not a valid JSON Pointer (e.g.,
+a non-empty string that doesn't start with `/`):
+
+```cpp
+json_pointer ptr("/foo/bar/0");
+
+// Invalid:
+// json_pointer ptr("foo"); // parse_error::107 — must start with /
+```
+
+### User-Defined Literal
+
+```cpp
+using namespace nlohmann::literals;
+
+auto ptr = "/server/host"_json_pointer;
+```
+
+## Accessing Values
+
+### `operator[]` with Pointer
+
+```cpp
+json j = {{"server", {{"host", "localhost"}, {"port", 8080}}}};
+
+j["/server/host"_json_pointer]; // "localhost"
+j["/server/port"_json_pointer]; // 8080
+j["/server"_json_pointer]; // {"host":"localhost","port":8080}
+```
+
+### `at()` with Pointer
+
+```cpp
+json j = {{"a", {{"b", 42}}}};
+
+j.at("/a/b"_json_pointer); // 42
+j.at("/a/missing"_json_pointer); // throws out_of_range::403
+```
+
+### `value()` with Pointer
+
+```cpp
+json j = {{"timeout", 30}};
+
+j.value("/timeout"_json_pointer, 60); // 30
+j.value("/retries"_json_pointer, 3); // 3 (key not found, returns default)
+```
+
+### `contains()` with Pointer
+
+```cpp
+json j = {{"a", {{"b", 42}}}};
+
+j.contains("/a/b"_json_pointer); // true
+j.contains("/a/c"_json_pointer); // false
+j.contains("/x"_json_pointer); // false
+```
+
+## Pointer Manipulation
+
+### `to_string()`
+
+```cpp
+string_t to_string() const;
+```
+
+Reconstructs the pointer string with proper escaping:
+
+```cpp
+json_pointer ptr("/a~1b/0");
+ptr.to_string(); // "/a~1b/0"
+```
+
+### `operator string_t()`
+
+Implicit conversion to string (same as `to_string()`).
+
+### `operator/=` — Append Token
+
+```cpp
+json_pointer& operator/=(const string_t& token);
+json_pointer& operator/=(std::size_t array_index);
+```
+
+Appends a reference token:
+
+```cpp
+json_pointer ptr("/a");
+ptr /= "b"; // "/a/b"
+ptr /= 0; // "/a/b/0"
+```
+
+### `operator/` — Concatenate
+
+```cpp
+friend json_pointer operator/(const json_pointer& lhs, const string_t& token);
+friend json_pointer operator/(const json_pointer& lhs, std::size_t array_index);
+friend json_pointer operator/(const json_pointer& lhs, const json_pointer& rhs);
+```
+
+```cpp
+auto ptr = "/a"_json_pointer / "b" / 0; // "/a/b/0"
+auto combined = "/a"_json_pointer / "/b/c"_json_pointer; // "/a/b/c"
+```
+
+### `parent_pointer()`
+
+```cpp
+json_pointer parent_pointer() const;
+```
+
+Returns the parent pointer (all tokens except the last):
+
+```cpp
+auto ptr = "/a/b/c"_json_pointer;
+ptr.parent_pointer().to_string(); // "/a/b"
+
+auto root = ""_json_pointer;
+root.parent_pointer().to_string(); // "" (root's parent is root)
+```
+
+### `back()`
+
+```cpp
+const string_t& back() const;
+```
+
+Returns the last reference token:
+
+```cpp
+auto ptr = "/a/b/c"_json_pointer;
+ptr.back(); // "c"
+```
+
+Throws `out_of_range::405` if the pointer is empty (root).
+
+### `push_back()`
+
+```cpp
+void push_back(const string_t& token);
+void push_back(string_t&& token);
+```
+
+Appends a token:
+
+```cpp
+json_pointer ptr;
+ptr.push_back("a");
+ptr.push_back("b");
+ptr.to_string(); // "/a/b"
+```
+
+### `pop_back()`
+
+```cpp
+void pop_back();
+```
+
+Removes the last token:
+
+```cpp
+auto ptr = "/a/b/c"_json_pointer;
+ptr.pop_back();
+ptr.to_string(); // "/a/b"
+```
+
+Throws `out_of_range::405` if the pointer is empty.
+
+### `empty()`
+
+```cpp
+bool empty() const noexcept;
+```
+
+Returns `true` if the pointer has no reference tokens (i.e., it refers to
+the whole document):
+
+```cpp
+json_pointer("").empty(); // true (root pointer)
+json_pointer("/a").empty(); // false
+```
+
+## Array Indexing
+
+JSON Pointer uses string tokens for array indices. The token `"0"` refers
+to the first element, `"1"` to the second, etc.:
+
+```cpp
+json j = {"a", "b", "c"};
+
+j["/0"_json_pointer]; // "a"
+j["/1"_json_pointer]; // "b"
+j["/2"_json_pointer]; // "c"
+```
+
+### The `-` Token
+
+The special token `-` refers to the "past-the-end" position in an array.
+It can be used with `operator[]` to **append** to an array:
+
+```cpp
+json j = {1, 2, 3};
+j["/-"_json_pointer] = 4;
+// j is now [1, 2, 3, 4]
+```
+
+Using `-` with `at()` throws `out_of_range::402` since there's no element
+at that position.
+
+## `flatten()` and `unflatten()`
+
+### `flatten()`
+
+```cpp
+basic_json flatten() const;
+```
+
+Converts a nested JSON value into a flat object where each key is a JSON
+Pointer and each value is a primitive:
+
+```cpp
+json j = {
+ {"name", "alice"},
+ {"address", {
+ {"city", "wonderland"},
+ {"zip", "12345"}
+ }},
+ {"scores", {90, 85, 92}}
+};
+
+json flat = j.flatten();
+// {
+// "/name": "alice",
+// "/address/city": "wonderland",
+// "/address/zip": "12345",
+// "/scores/0": 90,
+// "/scores/1": 85,
+// "/scores/2": 92
+// }
+```
+
+### `unflatten()`
+
+```cpp
+basic_json unflatten() const;
+```
+
+The inverse of `flatten()`. Reconstructs a nested structure from a flat
+pointer-keyed object:
+
+```cpp
+json flat = {
+ {"/a/b", 1},
+ {"/a/c", 2},
+ {"/d", 3}
+};
+
+json nested = flat.unflatten();
+// {"a": {"b": 1, "c": 2}, "d": 3}
+```
+
+Throws `type_error::314` if a value is not primitive, or
+`type_error::315` if values at a path conflict (e.g., both
+`/a` and `/a/b` have values).
+
+### Roundtrip
+
+```cpp
+json j = /* any JSON value */;
+assert(j == j.flatten().unflatten());
+```
+
+Note: `unflatten()` cannot reconstruct arrays from flattened form since
+numeric keys (`/0`, `/1`) become object keys. The result will have
+object-typed containers where the original had arrays.
+
+## Internal Implementation
+
+### Token Resolution
+
+The `get_checked()` and `get_unchecked()` methods resolve a pointer
+against a JSON value by walking through the reference tokens:
+
+```cpp
+// Simplified logic
+BasicJsonType* ptr = &value;
+for (const auto& token : reference_tokens) {
+ if (ptr->is_object()) {
+ ptr = &ptr->at(token);
+ } else if (ptr->is_array()) {
+ ptr = &ptr->at(std::stoi(token));
+ }
+}
+return *ptr;
+```
+
+### Error IDs
+
+| ID | Condition |
+|---|---|
+| `parse_error::107` | Invalid pointer syntax |
+| `out_of_range::401` | Array index out of range |
+| `out_of_range::402` | Array index `-` used with `at()` |
+| `out_of_range::403` | Key not found in object |
+| `out_of_range::404` | Unresolved reference token |
+| `out_of_range::405` | `back()` / `pop_back()` on empty pointer |
diff --git a/docs/handbook/json4cpp/overview.md b/docs/handbook/json4cpp/overview.md
new file mode 100644
index 0000000000..6737ebcc6a
--- /dev/null
+++ b/docs/handbook/json4cpp/overview.md
@@ -0,0 +1,330 @@
+# json4cpp — Overview
+
+## What is json4cpp?
+
+json4cpp is the Project-Tick vendored copy of **nlohmann/json** (version 3.12.0),
+a header-only C++ library for working with JSON data. Created by Niels Lohmann,
+it provides a first-class JSON type (`nlohmann::json`) that behaves like an STL
+container and integrates seamlessly with modern C++ idioms.
+
+The library is designed around one central class template:
+
+```cpp
+template<
+ template<typename U, typename V, typename... Args> class ObjectType = std::map,
+ template<typename U, typename... Args> class ArrayType = std::vector,
+ class StringType = std::string,
+ class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename U> class AllocatorType = std::allocator,
+ template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer,
+ class BinaryType = std::vector<std::uint8_t>,
+ class CustomBaseClass = void
+>
+class basic_json;
+```
+
+The default specialization is the convenient type alias:
+
+```cpp
+using json = basic_json<>;
+```
+
+An insertion-order-preserving variant is also provided:
+
+```cpp
+using ordered_json = basic_json<nlohmann::ordered_map>;
+```
+
+## Key Features
+
+### Header-Only Design
+
+The entire library ships in a single header (`single_include/nlohmann/json.hpp`)
+or as a multi-header tree rooted at `include/nlohmann/json.hpp`. No compilation
+of library code is needed — just `#include` and use.
+
+The multi-header layout, used when `JSON_MultipleHeaders` is ON in CMake,
+breaks the implementation into focused files under `include/nlohmann/detail/`:
+
+| Directory / File | Purpose |
+|---|---|
+| `detail/value_t.hpp` | `value_t` enumeration of JSON types |
+| `detail/exceptions.hpp` | Exception hierarchy (`parse_error`, `type_error`, etc.) |
+| `detail/json_pointer.hpp` | RFC 6901 JSON Pointer |
+| `detail/input/lexer.hpp` | Tokenizer / lexical analyzer |
+| `detail/input/parser.hpp` | Recursive-descent parser |
+| `detail/input/json_sax.hpp` | SAX interface and DOM builders |
+| `detail/input/binary_reader.hpp` | CBOR / MessagePack / UBJSON / BSON / BJData reader |
+| `detail/input/input_adapters.hpp` | Input source abstraction (file, stream, string, iterators) |
+| `detail/output/serializer.hpp` | JSON text serializer with UTF-8 validation |
+| `detail/output/binary_writer.hpp` | Binary format writers |
+| `detail/output/output_adapters.hpp` | Output sink abstraction |
+| `detail/iterators/iter_impl.hpp` | Iterator implementation |
+| `detail/iterators/iteration_proxy.hpp` | `items()` proxy for key-value iteration |
+| `detail/conversions/from_json.hpp` | Default `from_json()` overloads |
+| `detail/conversions/to_json.hpp` | Default `to_json()` overloads |
+| `detail/macro_scope.hpp` | Configuration macros, `NLOHMANN_DEFINE_TYPE_*` |
+| `detail/meta/type_traits.hpp` | SFINAE helpers and concept checks |
+
+### Intuitive Syntax
+
+```cpp
+#include <nlohmann/json.hpp>
+using json = nlohmann::json;
+
+// Create a JSON object
+json j = {
+ {"name", "Project-Tick"},
+ {"version", 3},
+ {"features", {"parsing", "serialization", "patch"}},
+ {"active", true}
+};
+
+// Access values
+std::string name = j["name"];
+int version = j.at("version");
+
+// Iterate
+for (auto& [key, val] : j.items()) {
+ std::cout << key << ": " << val << "\n";
+}
+
+// Serialize
+std::string pretty = j.dump(4);
+```
+
+### STL Container Compatibility
+
+`basic_json` models an STL container — it defines the standard type aliases
+and fulfills the Container concept requirements:
+
+```cpp
+// Container type aliases defined by basic_json:
+using value_type = basic_json;
+using reference = value_type&;
+using const_reference = const value_type&;
+using difference_type = std::ptrdiff_t;
+using size_type = std::size_t;
+using allocator_type = AllocatorType<basic_json>;
+using pointer = typename std::allocator_traits<allocator_type>::pointer;
+using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
+using iterator = iter_impl<basic_json>;
+using const_iterator = iter_impl<const basic_json>;
+using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
+using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
+```
+
+This means `basic_json` works with STL algorithms:
+
+```cpp
+json arr = {3, 1, 4, 1, 5};
+std::sort(arr.begin(), arr.end());
+auto it = std::find(arr.begin(), arr.end(), 4);
+```
+
+### Implicit Type Conversions
+
+By default (`JSON_USE_IMPLICIT_CONVERSIONS=1`), values can be implicitly
+converted to native C++ types:
+
+```cpp
+json j = 42;
+int x = j; // implicit conversion
+std::string s = j; // throws type_error::302 — type mismatch
+```
+
+This can be disabled at compile time with `-DJSON_ImplicitConversions=OFF`
+(sets `JSON_USE_IMPLICIT_CONVERSIONS` to 0), requiring explicit `.get<T>()`
+calls instead.
+
+### Comprehensive JSON Value Types
+
+Every JSON value type maps to a C++ type through the `value_t` enumeration
+defined in `detail/value_t.hpp`:
+
+| JSON Type | `value_t` Enumerator | C++ Storage Type | Default |
+|---|---|---|---|
+| Object | `value_t::object` | `object_t*` | `std::map<std::string, basic_json>` |
+| Array | `value_t::array` | `array_t*` | `std::vector<basic_json>` |
+| String | `value_t::string` | `string_t*` | `std::string` |
+| Boolean | `value_t::boolean` | `boolean_t` | `bool` |
+| Integer | `value_t::number_integer` | `number_integer_t` | `std::int64_t` |
+| Unsigned | `value_t::number_unsigned` | `number_unsigned_t` | `std::uint64_t` |
+| Float | `value_t::number_float` | `number_float_t` | `double` |
+| Binary | `value_t::binary` | `binary_t*` | `byte_container_with_subtype<vector<uint8_t>>` |
+| Null | `value_t::null` | (none) | — |
+| Discarded | `value_t::discarded` | (none) | — |
+
+Variable-length types (object, array, string, binary) are stored as heap
+pointers to keep the `json_value` union at 8 bytes on 64-bit platforms.
+
+### Binary Format Support
+
+Beyond JSON text, the library supports round-trip conversion to and from
+several binary serialization formats:
+
+- **CBOR** (RFC 7049) — `to_cbor()` / `from_cbor()`
+- **MessagePack** — `to_msgpack()` / `from_msgpack()`
+- **UBJSON** — `to_ubjson()` / `from_ubjson()`
+- **BSON** (MongoDB) — `to_bson()` / `from_bson()`
+- **BJData** — `to_bjdata()` / `from_bjdata()`
+
+### RFC Compliance
+
+| Feature | Specification |
+|---|---|
+| JSON Pointer | RFC 6901 — navigating JSON documents with path syntax |
+| JSON Patch | RFC 6902 — describing mutations as operation arrays |
+| JSON Merge Patch | RFC 7396 — simplified document merging |
+
+### SAX-Style Parsing
+
+For memory-constrained scenarios or streaming, the SAX interface
+(`json_sax<BasicJsonType>`) allows event-driven parsing without building
+a DOM tree in memory.
+
+### Custom Type Serialization
+
+The ADL-based serializer architecture lets users define `to_json()` and
+`from_json()` free functions for any user-defined type. Convenience macros
+automate this:
+
+- `NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, member1, member2, ...)`
+- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, member1, member2, ...)`
+- `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...)`
+- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...)`
+- `NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...)`
+- `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...)`
+- `NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...)`
+
+### User-Defined Literals
+
+When `JSON_USE_GLOBAL_UDLS` is enabled (the default), two string literals
+are available globally:
+
+```cpp
+auto j = R"({"key": "value"})"_json;
+auto ptr = "/key"_json_pointer;
+```
+
+These are always available as `nlohmann::literals::json_literals::operator""_json`
+and `operator""_json_pointer`.
+
+## Version Information
+
+The library provides compile-time version macros:
+
+```cpp
+NLOHMANN_JSON_VERSION_MAJOR // 3
+NLOHMANN_JSON_VERSION_MINOR // 12
+NLOHMANN_JSON_VERSION_PATCH // 0
+```
+
+And a runtime introspection method:
+
+```cpp
+json meta = json::meta();
+// Returns:
+// {
+// "copyright": "(C) 2013-2026 Niels Lohmann",
+// "name": "JSON for Modern C++",
+// "url": "https://github.com/nlohmann/json",
+// "version": {"string": "3.12.0", "major": 3, "minor": 12, "patch": 0},
+// "compiler": {...},
+// "platform": "linux"
+// }
+```
+
+## Compiler Support
+
+The library requires C++11 at minimum. Higher standard modes unlock
+additional features:
+
+| Standard | Features Enabled |
+|---|---|
+| C++11 | Full library functionality |
+| C++14 | `constexpr` support for `get<>()`, transparent comparators (`std::less<>`) |
+| C++17 | `std::string_view` support, `std::any` integration, `if constexpr` |
+| C++20 | Three-way comparison (`<=>` / `std::partial_ordering`), `std::format` |
+
+Automatic detection uses `__cplusplus` (or `_MSVC_LANG` on MSVC) and defines:
+
+- `JSON_HAS_CPP_11` — always 1
+- `JSON_HAS_CPP_14` — C++14 or above
+- `JSON_HAS_CPP_17` — C++17 or above
+- `JSON_HAS_CPP_20` — C++20 or above
+
+## Configuration Macros
+
+The library's behavior is controlled by preprocessor macros, typically set
+via CMake options:
+
+| Macro | CMake Option | Default | Effect |
+|---|---|---|---|
+| `JSON_DIAGNOSTICS` | `JSON_Diagnostics` | `OFF` | Extended diagnostic messages with parent paths |
+| `JSON_DIAGNOSTIC_POSITIONS` | `JSON_Diagnostic_Positions` | `OFF` | Track byte positions in parsed values |
+| `JSON_USE_IMPLICIT_CONVERSIONS` | `JSON_ImplicitConversions` | `ON` | Allow implicit `operator ValueType()` |
+| `JSON_DISABLE_ENUM_SERIALIZATION` | `JSON_DisableEnumSerialization` | `OFF` | Disable automatic enum-to-int conversion |
+| `JSON_USE_GLOBAL_UDLS` | `JSON_GlobalUDLs` | `ON` | Place UDLs in global namespace |
+| `JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON` | `JSON_LegacyDiscardedValueComparison` | `OFF` | Old comparison behavior for discarded values |
+| `JSON_NO_IO` | — | (not set) | Disable `<iosfwd>` / stream operators |
+
+## License
+
+nlohmann/json is released under the **MIT License**.
+
+```
+SPDX-License-Identifier: MIT
+SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
+```
+
+## Directory Structure in Project-Tick
+
+```
+json4cpp/
+├── CMakeLists.txt # Top-level build configuration
+├── include/nlohmann/ # Multi-header source tree
+│ ├── json.hpp # Main header
+│ ├── json_fwd.hpp # Forward declarations
+│ ├── adl_serializer.hpp # ADL serializer
+│ ├── byte_container_with_subtype.hpp
+│ ├── ordered_map.hpp # Insertion-order map
+│ └── detail/ # Implementation details
+├── single_include/nlohmann/
+│ ├── json.hpp # Amalgamated single header
+│ └── json_fwd.hpp # Forward declarations
+├── tests/ # Doctest-based test suite
+├── docs/ # Upstream documentation source
+├── tools/ # Code generation and maintenance scripts
+├── cmake/ # CMake modules and configs
+├── BUILD.bazel # Bazel build file
+├── MODULE.bazel # Bazel module definition
+├── Package.swift # Swift Package Manager support
+├── meson.build # Meson build file
+└── Makefile # Convenience Makefile
+```
+
+## Further Reading
+
+The remaining handbook documents cover:
+
+- **architecture.md** — Internal class hierarchy and template structure
+- **building.md** — Integration methods, CMake support, compilation options
+- **basic-usage.md** — Creating JSON values, accessing data, type system
+- **value-types.md** — All JSON value types and their C++ representation
+- **element-access.md** — `operator[]`, `at()`, `value()`, `find()`, `contains()`
+- **iteration.md** — Iterators, range-for, `items()`, structured bindings
+- **serialization.md** — `dump()`, `parse()`, stream I/O, `to_json`/`from_json`
+- **binary-formats.md** — MessagePack, CBOR, BSON, UBJSON, BJData
+- **json-pointer.md** — RFC 6901 JSON Pointer navigation
+- **json-patch.md** — RFC 6902 JSON Patch and RFC 7396 Merge Patch
+- **custom-types.md** — ADL serialization and `NLOHMANN_DEFINE_TYPE_*` macros
+- **parsing-internals.md** — Lexer, parser, and SAX DOM builder internals
+- **exception-handling.md** — Exception types, error IDs, when they are thrown
+- **sax-interface.md** — SAX-style event-driven parsing
+- **performance.md** — Performance characteristics and tuning
+- **code-style.md** — Source code conventions
+- **testing.md** — Test framework and running tests
diff --git a/docs/handbook/json4cpp/parsing-internals.md b/docs/handbook/json4cpp/parsing-internals.md
new file mode 100644
index 0000000000..ecbc946dee
--- /dev/null
+++ b/docs/handbook/json4cpp/parsing-internals.md
@@ -0,0 +1,493 @@
+# json4cpp — Parsing Internals
+
+## Parser Architecture
+
+The parsing pipeline consists of three stages:
+
+```
+Input → InputAdapter → Lexer → Parser → JSON value
+ ↓
+ SAX Handler
+```
+
+1. **Input adapters** normalize various input sources into a uniform byte stream
+2. **Lexer** tokenizes the byte stream into JSON tokens
+3. **Parser** implements a recursive descent parser driven by SAX events
+
+## Input Adapters
+
+Defined in `include/nlohmann/detail/input/input_adapters.hpp`.
+
+### Adapter Hierarchy
+
+```cpp
+// File input
+class file_input_adapter {
+ std::FILE* m_file;
+ std::char_traits<char>::int_type get_character();
+};
+
+// Stream input
+class input_stream_adapter {
+ std::istream* is;
+ std::streambuf* sb;
+ std::char_traits<char>::int_type get_character();
+};
+
+// Iterator-based input
+template<typename IteratorType>
+class iterator_input_adapter {
+ IteratorType current;
+ IteratorType end;
+ std::char_traits<char>::int_type get_character();
+};
+```
+
+All adapters expose a `get_character()` method that returns the next byte
+or `std::char_traits<char>::eof()` at end of input.
+
+### `input_adapter()` Factory
+
+The free function `input_adapter()` selects the appropriate adapter:
+
+```cpp
+// From string/string_view
+auto adapter = input_adapter(std::string("{}"));
+
+// From iterators
+auto adapter = input_adapter(vec.begin(), vec.end());
+
+// From stream
+auto adapter = input_adapter(std::cin);
+```
+
+### Span Input Adapter
+
+For contiguous memory (C++17):
+
+```cpp
+template<typename CharT>
+class contiguous_bytes_input_adapter {
+ const CharT* current;
+ const CharT* end;
+};
+```
+
+This is the fastest adapter since it reads directly from memory without
+virtual dispatch.
+
+## Lexer
+
+Defined in `include/nlohmann/detail/input/lexer.hpp`. The lexer
+(scanner/tokenizer) converts a byte stream into a sequence of tokens.
+
+### Token Types
+
+```cpp
+enum class token_type
+{
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the 'true' literal
+ literal_false, ///< the 'false' literal
+ literal_null, ///< the 'null' literal
+ value_string, ///< a string (includes the quotes)
+ value_unsigned, ///< an unsigned integer
+ value_integer, ///< a signed integer
+ value_float, ///< a floating-point number
+ begin_array, ///< the character '['
+ begin_object, ///< the character '{'
+ end_array, ///< the character ']'
+ end_object, ///< the character '}'
+ name_separator, ///< the character ':'
+ value_separator, ///< the character ','
+ parse_error, ///< indicating a parse error
+ end_of_input ///< indicating the end of the input buffer
+};
+```
+
+### Lexer Class
+
+```cpp
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+
+ // Main scanning entry point
+ token_type scan();
+
+ // Access scanned values
+ constexpr number_integer_t get_number_integer() const noexcept;
+ constexpr number_unsigned_t get_number_unsigned() const noexcept;
+ constexpr number_float_t get_number_float() const noexcept;
+ string_t& get_string();
+
+ // Error information
+ constexpr position_t get_position() const noexcept;
+ std::string get_token_string() const;
+ const std::string& get_error_message() const noexcept;
+
+private:
+ InputAdapterType ia; // input source
+ char_int_type current; // current character
+ bool next_unget = false; // lookahead flag
+ position_t position {}; // line/column tracking
+ std::vector<char_type> token_string {}; // raw token for error messages
+ string_t token_buffer {}; // decoded string value
+ // Number storage (only one is valid at a time)
+ number_integer_t value_integer = 0;
+ number_unsigned_t value_unsigned = 0;
+ number_float_t value_float = 0;
+};
+```
+
+### Position Tracking
+
+```cpp
+struct position_t
+{
+ std::size_t chars_read_total = 0; // total characters read
+ std::size_t chars_read_current_line = 0; // characters on current line
+ std::size_t lines_read = 0; // lines read (newline count)
+};
+```
+
+### String Scanning
+
+The `scan_string()` method handles:
+- Regular characters
+- Escape sequences: `\"`, `\\`, `\/`, `\b`, `\f`, `\n`, `\r`, `\t`
+- Unicode escapes: `\uXXXX` (including surrogate pairs for `\uD800`–`\uDBFF` + `\uDC00`–`\uDFFF`)
+- UTF-8 validation using a state machine
+
+### Number Scanning
+
+The `scan_number()` method determines the number type:
+
+1. Parse sign (optional `-`)
+2. Parse integer part
+3. If `.` follows → parse fractional part → `value_float`
+4. If `e`/`E` follows → parse exponent → `value_float`
+5. Otherwise, try to fit into `number_integer_t` or `number_unsigned_t`
+
+The method first accumulates the raw characters, then converts:
+- Integers: `std::strtoull` / `std::strtoll`
+- Floats: `std::strtod`
+
+### Comment Scanning
+
+When `ignore_comments` is enabled:
+
+```cpp
+bool scan_comment() {
+ // After seeing '/', check next char:
+ // '/' → scan to end of line (C++ comment)
+ // '*' → scan to '*/' (C comment)
+}
+```
+
+## Parser
+
+Defined in `include/nlohmann/detail/input/parser.hpp`. Implements a
+**recursive descent** parser that generates SAX events.
+
+### Parser Class
+
+```cpp
+template<typename BasicJsonType, typename InputAdapterType>
+class parser
+{
+public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+
+ parser(InputAdapterType&& adapter,
+ const parser_callback_t<BasicJsonType> cb = nullptr,
+ const bool allow_exceptions_ = true,
+ const bool skip_comments = false,
+ const bool ignore_trailing_commas_ = false);
+
+ void parse(const bool strict, BasicJsonType& result);
+ bool accept(const bool strict = true);
+
+ template<typename SAX>
+ bool sax_parse(SAX* sax, const bool strict = true);
+
+private:
+ template<typename SAX>
+ bool sax_parse_internal(SAX* sax);
+
+ lexer_t m_lexer;
+ token_type last_token = token_type::uninitialized;
+ bool allow_exceptions;
+ bool ignore_trailing_commas;
+};
+```
+
+### Recursive Descent Grammar
+
+The parser implements the JSON grammar:
+
+```
+json → value
+value → object | array | string | number | "true" | "false" | "null"
+object → '{' (pair (',' pair)* ','?)? '}'
+pair → string ':' value
+array → '[' (value (',' value)* ','?)? ']'
+```
+
+The trailing comma handling is optional (controlled by
+`ignore_trailing_commas`).
+
+### SAX-Driven Parsing
+
+The parser calls SAX handler methods as it encounters JSON structure:
+
+```cpp
+template<typename SAX>
+bool sax_parse_internal(SAX* sax)
+{
+ switch (last_token) {
+ case token_type::begin_object:
+ // 1. sax->start_object(...)
+ // 2. For each key-value:
+ // a. sax->key(string)
+ // b. recurse into sax_parse_internal for value
+ // 3. sax->end_object()
+ break;
+ case token_type::begin_array:
+ // 1. sax->start_array(...)
+ // 2. For each element: recurse into sax_parse_internal
+ // 3. sax->end_array()
+ break;
+ case token_type::value_string:
+ return sax->string(m_lexer.get_string());
+ case token_type::value_unsigned:
+ return sax->number_unsigned(m_lexer.get_number_unsigned());
+ case token_type::value_integer:
+ return sax->number_integer(m_lexer.get_number_integer());
+ case token_type::value_float:
+ // Check for NaN: store as null
+ return sax->number_float(m_lexer.get_number_float(), ...);
+ case token_type::literal_true:
+ return sax->boolean(true);
+ case token_type::literal_false:
+ return sax->boolean(false);
+ case token_type::literal_null:
+ return sax->null();
+ default:
+ return sax->parse_error(...);
+ }
+}
+```
+
+### DOM Construction
+
+Two SAX handlers build the DOM tree:
+
+#### `json_sax_dom_parser`
+
+Standard DOM builder. Each SAX event creates or appends to the JSON tree:
+
+```cpp
+template<typename BasicJsonType>
+class json_sax_dom_parser
+{
+ BasicJsonType& root;
+ std::vector<BasicJsonType*> ref_stack; // stack of parent nodes
+ BasicJsonType* object_element = nullptr;
+ bool errored = false;
+ bool allow_exceptions;
+
+ bool null();
+ bool boolean(bool val);
+ bool number_integer(number_integer_t val);
+ bool number_unsigned(number_unsigned_t val);
+ bool number_float(number_float_t val, const string_t& s);
+ bool string(string_t& val);
+ bool binary(binary_t& val);
+ bool start_object(std::size_t elements);
+ bool end_object();
+ bool start_array(std::size_t elements);
+ bool end_array();
+ bool key(string_t& val);
+ bool parse_error(std::size_t position, const std::string& last_token,
+ const detail::exception& ex);
+};
+```
+
+The `ref_stack` tracks the current nesting path. On `start_object()` /
+`start_array()`, a new container is pushed. On `end_object()` /
+`end_array()`, the stack is popped.
+
+#### `json_sax_dom_callback_parser`
+
+Extends the DOM builder with callback support. When the callback returns
+`false`, the value is discarded:
+
+```cpp
+template<typename BasicJsonType>
+class json_sax_dom_callback_parser
+{
+ BasicJsonType& root;
+ std::vector<BasicJsonType*> ref_stack;
+ std::vector<bool> keep_stack; // tracks which values to keep
+ std::vector<bool> key_keep_stack;
+ BasicJsonType* object_element = nullptr;
+ BasicJsonType discarded = BasicJsonType::value_t::discarded;
+ parser_callback_t<BasicJsonType> callback;
+ bool errored = false;
+ bool allow_exceptions;
+};
+```
+
+## `accept()` Method
+
+The `accept()` method checks validity without building a DOM:
+
+```cpp
+bool accept(const bool strict = true);
+```
+
+Internally it uses `json_sax_acceptor` — a SAX handler where all methods
+return `true` (accepting everything) and `parse_error()` returns `false`:
+
+```cpp
+template<typename BasicJsonType>
+struct json_sax_acceptor
+{
+ bool null() { return true; }
+ bool boolean(bool) { return true; }
+ bool number_integer(number_integer_t) { return true; }
+ // ... all return true ...
+ bool parse_error(...) { return false; }
+};
+```
+
+## `sax_parse()` — Static SAX Entry Point
+
+```cpp
+template<typename InputType, typename SAX>
+static bool sax_parse(InputType&& i, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false,
+ const bool ignore_trailing_commas = false);
+```
+
+The `input_format_t` enum selects the parser:
+
+```cpp
+enum class input_format_t {
+ json,
+ cbor,
+ msgpack,
+ ubjson,
+ bson,
+ bjdata
+};
+```
+
+For `json`, the text parser is used. For binary formats, the
+`binary_reader` is used (which also generates SAX events).
+
+## Error Reporting
+
+### Parse Error Format
+
+```
+[json.exception.parse_error.101] parse error at line 3, column 5:
+syntax error while parsing object key - unexpected end of input;
+expected string literal
+```
+
+The error message includes:
+- Exception ID (e.g., 101)
+- Position (line and column, or byte offset)
+- Description of what was expected vs. what was found
+- The last token read (for context)
+
+### Error IDs
+
+| ID | Condition |
+|---|---|
+| 101 | Unexpected token |
+| 102 | `\u` escape with invalid hex digits |
+| 103 | Invalid UTF-8 surrogate pair |
+| 104 | JSON Patch: invalid patch document |
+| 105 | JSON Patch: missing required field |
+| 106 | Invalid number format |
+| 107 | Invalid JSON Pointer syntax |
+| 108 | Invalid Unicode code point |
+| 109 | Invalid UTF-8 byte sequence |
+| 110 | Unrecognized CBOR/MessagePack/UBJSON/BSON marker |
+| 112 | Parse error in BSON |
+| 113 | Parse error in UBJSON |
+| 114 | Parse error in BJData |
+| 115 | Parse error due to incomplete binary data |
+
+### Diagnostic Positions
+
+When `JSON_DIAGNOSTIC_POSITIONS` is enabled at compile time, the library
+tracks byte positions for each value. Error messages then include
+`start_position` and `end_position` for the offending value:
+
+```cpp
+#define JSON_DIAGNOSTICS 1
+#define JSON_DIAGNOSTIC_POSITIONS 1
+```
+
+## Parser Callback Events
+
+The parser callback receives events defined by `parse_event_t`:
+
+```cpp
+enum class parse_event_t : std::uint8_t
+{
+ object_start, // '{' read
+ object_end, // '}' read
+ array_start, // '[' read
+ array_end, // ']' read
+ key, // object key read
+ value // value read
+};
+```
+
+Callback invocation points in the parser:
+1. `object_start` — after `{` is consumed, before any key
+2. `key` — after a key string is consumed, `parsed` = the key string
+3. `value` — after any value is fully parsed, `parsed` = the value
+4. `object_end` — after `}` is consumed, `parsed` = the complete object
+5. `array_start` — after `[` is consumed, before any element
+6. `array_end` — after `]` is consumed, `parsed` = the complete array
+
+### Callback Return Value
+
+- `true` → keep the value
+- `false` → discard (replace with `discarded`)
+
+For container events (`object_start`, `array_start`), returning `false`
+skips the **entire** container and all its contents.
+
+## Performance Characteristics
+
+| Stage | Complexity | Dominant Cost |
+|---|---|---|
+| Input adapter | O(n) | Single pass over input |
+| Lexer | O(n) | Character-by-character scan, string copy |
+| Parser | O(n) | Recursive descent, SAX event dispatch |
+| DOM construction | O(n) | Memory allocation for containers |
+
+The overall parsing complexity is O(n) in the input size. Memory usage is
+proportional to the nesting depth (parser stack) plus the size of the
+resulting DOM (heap allocations for strings, arrays, objects).
+
+For large inputs where the full DOM is not needed, using the SAX interface
+directly avoids DOM construction overhead entirely.
diff --git a/docs/handbook/json4cpp/performance.md b/docs/handbook/json4cpp/performance.md
new file mode 100644
index 0000000000..a35d0bc4b8
--- /dev/null
+++ b/docs/handbook/json4cpp/performance.md
@@ -0,0 +1,275 @@
+# json4cpp — Performance
+
+## Memory Layout
+
+### `json_value` Union
+
+The core storage is a union of 8 members:
+
+```cpp
+union json_value
+{
+ object_t* object; // 8 bytes (pointer)
+ array_t* array; // 8 bytes (pointer)
+ string_t* string; // 8 bytes (pointer)
+ binary_t* binary; // 8 bytes (pointer)
+ boolean_t boolean; // 1 byte
+ number_integer_t number_integer; // 8 bytes
+ number_unsigned_t number_unsigned; // 8 bytes
+ number_float_t number_float; // 8 bytes
+};
+```
+
+The union is **8 bytes** on 64-bit platforms. Variable-length types
+(object, array, string, binary) are stored as heap-allocated pointers to
+keep the union small.
+
+### Total `basic_json` Size
+
+Each `basic_json` node contains:
+
+```cpp
+struct data
+{
+ value_t m_type = value_t::null; // 1 byte (uint8_t enum)
+ // + padding
+ json_value m_value = {}; // 8 bytes
+};
+```
+
+With alignment: **16 bytes per node** on most 64-bit platforms (1 byte
+type + 7 bytes padding + 8 bytes value).
+
+When `JSON_DIAGNOSTICS` is enabled, each node additionally stores a parent
+pointer:
+
+```cpp
+struct data
+{
+ value_t m_type;
+ json_value m_value;
+ const basic_json* m_parent = nullptr; // 8 bytes extra
+};
+```
+
+Total with diagnostics: **24 bytes per node**.
+
+## Allocation Strategy
+
+### Object Storage (default `std::map`)
+
+- Red-black tree nodes: ~48–64 bytes each (key + value + pointers + color)
+- O(log n) lookup, insert, erase
+- Good cache locality within individual nodes, poor across the tree
+
+### Array Storage (`std::vector`)
+
+- Contiguous memory: amortized O(1) push_back
+- Reallocations: capacity doubles, causing copies of all elements
+- Each element is 16 bytes (`basic_json`)
+
+### String Storage (`std::string`)
+
+- SSO (Small String Optimization): strings ≤ ~15 chars stored inline
+ (no allocation). Exact threshold is implementation-defined.
+- Longer strings: heap allocation
+
+## `ordered_map` Performance
+
+`ordered_json` uses `ordered_map<std::string, basic_json>` which inherits
+from `std::vector<std::pair<const Key, T>>`:
+
+| Operation | `std::map` (json) | `ordered_map` (ordered_json) |
+|---|---|---|
+| Lookup by key | O(log n) | O(n) linear search |
+| Insert | O(log n) | O(1) amortized (push_back) |
+| Erase by key | O(log n) | O(n) (shift elements) |
+| Iteration | O(n), sorted order | O(n), insertion order |
+| Memory | Tree nodes (fragmented) | Contiguous vector |
+
+Use `ordered_json` only when insertion order matters and the number of
+keys is small (< ~100).
+
+## Destruction
+
+### Iterative Destruction
+
+Deeply nested JSON values would cause stack overflow with recursive
+destructors. The library uses **iterative destruction**:
+
+```cpp
+void data::destroy(value_t t)
+{
+ if (t == value_t::array || t == value_t::object)
+ {
+ // Move children to a flat list
+ std::vector<basic_json> stack;
+ if (t == value_t::array) {
+ stack.reserve(m_value.array->size());
+ std::move(m_value.array->begin(), m_value.array->end(),
+ std::back_inserter(stack));
+ } else {
+ // Extract values from object pairs
+ for (auto& pair : *m_value.object) {
+ stack.push_back(std::move(pair.second));
+ }
+ }
+ // Continue flattening until stack is empty
+ while (!stack.empty()) {
+ // Pop and flatten nested containers
+ }
+ }
+ // Destroy the container itself
+}
+```
+
+This ensures O(1) stack depth regardless of JSON nesting depth.
+
+### Destruction Cost
+
+- Primitives (null, boolean, number): O(1), no heap deallocation
+- String: O(1), single `delete`
+- Array: O(n), iterative flattening + deallocation of each element
+- Object: O(n), iterative flattening + deallocation of each key-value
+- Binary: O(1), single `delete`
+
+## Parsing Performance
+
+### Lexer Optimizations
+
+- Single-character lookahead (no backtracking)
+- Token string is accumulated in a pre-allocated buffer
+- Number parsing avoids `std::string` intermediate: raw chars → integer or
+ float directly via `strtoull`/`strtod`
+- UTF-8 validation uses a compact state machine (400-byte lookup table)
+
+### Parser Complexity
+
+- O(n) in input size
+- O(d) stack depth where d = maximum nesting depth
+- SAX approach avoids intermediate DOM allocations
+
+### Fastest Parsing Path
+
+For maximum speed:
+1. Use contiguous input (`std::string`, `const char*`, `std::vector<uint8_t>`)
+ — avoids virtual dispatch in input adapter
+2. Disable comments (`ignore_comments = false`)
+3. Disable trailing commas (`ignore_trailing_commas = false`)
+4. No callback (`cb = nullptr`)
+5. Allow exceptions (`allow_exceptions = true`) — avoids extra bookkeeping
+
+## Serialization Performance
+
+### Number Formatting
+
+- **Integers**: Custom digit-by-digit algorithm writing to a 64-byte stack
+ buffer. Faster than `std::to_string` (no `std::string` allocation).
+- **Floats**: `std::snprintf` with `max_digits10` precision. The format
+ string is `%.*g`.
+
+### String Escaping
+
+- ASCII-only strings: nearly zero overhead (copy + quote wrapping)
+- Strings with special characters: per-byte check against escape table
+- `ensure_ascii`: full UTF-8 decode + `\uXXXX` encoding (slower)
+
+### Output Adapter
+
+- `output_string_adapter` (default for `dump()`): writes to `std::string`
+ with `push_back()` / `append()`
+- `output_stream_adapter`: writes to `std::ostream` via `put()` / `write()`
+- `output_vector_adapter`: writes to `std::vector<char>` via `push_back()`
+
+## Compilation Time
+
+Being header-only, json.hpp can add significant compilation time. Strategies:
+
+### Single Include vs. Multi-Header
+
+| Approach | Files | Compilation Model |
+|---|---|---|
+| `single_include/nlohmann/json.hpp` | 1 file (~25K lines) | Include everywhere |
+| `include/nlohmann/json.hpp` | Many small headers | Better incremental builds |
+
+### Reducing Compilation Time
+
+1. **Precompiled headers**: Add `nlohmann/json.hpp` to your PCH
+2. **Forward declarations**: Use `nlohmann/json_fwd.hpp` in headers, full
+ include only in `.cpp` files
+3. **Extern template**: Pre-instantiate in one TU:
+
+```cpp
+// json_instantiation.cpp
+#include <nlohmann/json.hpp>
+template class nlohmann::basic_json<>; // explicit instantiation
+```
+
+4. **Minimize includes**: Only include where actually needed
+
+## Binary Format Performance
+
+Size and speed characteristics compared to JSON text:
+
+| Aspect | JSON Text | CBOR | MessagePack | UBJSON |
+|---|---|---|---|---|
+| Encoding speed | Fast | Fast | Fast | Moderate |
+| Decoding speed | Moderate | Fast | Fast | Moderate |
+| Output size | Largest | Compact | Most compact | Moderate |
+| Human readable | Yes | No | No | No |
+
+Binary formats are generally faster to parse because:
+- No string-to-number conversion (numbers stored in binary)
+- Size-prefixed containers (no scanning for delimiters)
+- No whitespace handling
+- No string escape processing
+
+## Best Practices
+
+### Avoid Copies
+
+```cpp
+// Bad: copies the entire array
+json arr = j["data"];
+
+// Good: reference
+const auto& arr = j["data"];
+```
+
+### Use `get_ref()` for String Access
+
+```cpp
+// Bad: copies the string
+std::string s = j.get<std::string>();
+
+// Good: reference (no copy)
+const auto& s = j.get_ref<const std::string&>();
+```
+
+### Reserve Capacity
+
+```cpp
+json j = json::array();
+// If you know the size, reserve first (via underlying container)
+// The API doesn't expose reserve() directly, but you can:
+json j = json::parse(input); // parser pre-allocates when size hints are available
+```
+
+### SAX for Large Documents
+
+```cpp
+// Bad: loads entire 1GB file into DOM
+json j = json::parse(huge_file);
+
+// Good: process streaming with SAX
+struct my_handler : nlohmann::json_sax<json> { /* ... */ };
+my_handler handler;
+json::sax_parse(huge_file, &handler);
+```
+
+### Move Semantics
+
+```cpp
+json source = get_data();
+json dest = std::move(source); // O(1) move, source becomes null
+```
diff --git a/docs/handbook/json4cpp/sax-interface.md b/docs/handbook/json4cpp/sax-interface.md
new file mode 100644
index 0000000000..3164be9694
--- /dev/null
+++ b/docs/handbook/json4cpp/sax-interface.md
@@ -0,0 +1,337 @@
+# json4cpp — SAX Interface
+
+## Overview
+
+The SAX (Simple API for XML/JSON) interface provides an event-driven
+parsing model. Instead of building a complete DOM tree in memory, the
+parser reports structural events to a handler as it reads the input.
+
+This is useful for:
+- Processing very large JSON documents without loading them fully
+- Filtering or transforming data during parsing
+- Building custom data structures directly from JSON input
+- Reducing memory usage
+
+## `json_sax` Abstract Class
+
+Defined in `include/nlohmann/detail/input/json_sax.hpp`:
+
+```cpp
+template<typename BasicJsonType>
+struct json_sax
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+
+ virtual bool null() = 0;
+ virtual bool boolean(bool val) = 0;
+ virtual bool number_integer(number_integer_t val) = 0;
+ virtual bool number_unsigned(number_unsigned_t val) = 0;
+ virtual bool number_float(number_float_t val, const string_t& s) = 0;
+ virtual bool string(string_t& val) = 0;
+ virtual bool binary(binary_t& val) = 0;
+
+ virtual bool start_object(std::size_t elements) = 0;
+ virtual bool key(string_t& val) = 0;
+ virtual bool end_object() = 0;
+
+ virtual bool start_array(std::size_t elements) = 0;
+ virtual bool end_array() = 0;
+
+ virtual bool parse_error(std::size_t position,
+ const std::string& last_token,
+ const detail::exception& ex) = 0;
+
+ json_sax() = default;
+ json_sax(const json_sax&) = default;
+ json_sax(json_sax&&) noexcept = default;
+ json_sax& operator=(const json_sax&) = default;
+ json_sax& operator=(json_sax&&) noexcept = default;
+ virtual ~json_sax() = default;
+};
+```
+
+## Event Methods
+
+### Scalar Events
+
+| Method | Called When | Arguments |
+|---|---|---|
+| `null()` | `null` literal parsed | — |
+| `boolean(val)` | `true` or `false` parsed | `bool val` |
+| `number_integer(val)` | Signed integer parsed | `number_integer_t val` |
+| `number_unsigned(val)` | Unsigned integer parsed | `number_unsigned_t val` |
+| `number_float(val, s)` | Float parsed | `number_float_t val`, `string_t s` (raw text) |
+| `string(val)` | String parsed | `string_t& val` |
+| `binary(val)` | Binary data parsed | `binary_t& val` |
+
+### Container Events
+
+| Method | Called When | Arguments |
+|---|---|---|
+| `start_object(n)` | `{` read | Element count hint (or -1 if unknown) |
+| `key(val)` | Object key read | `string_t& val` |
+| `end_object()` | `}` read | — |
+| `start_array(n)` | `[` read | Element count hint (or -1 if unknown) |
+| `end_array()` | `]` read | — |
+
+### Error Event
+
+| Method | Called When | Arguments |
+|---|---|---|
+| `parse_error(pos, tok, ex)` | Parse error | Byte position, last token, exception |
+
+### Return Values
+
+All methods return `bool`:
+- `true` — continue parsing
+- `false` — abort parsing immediately
+
+For `parse_error()`:
+- `true` — abort with no exception (return discarded value)
+- `false` — abort with no exception (return discarded value)
+
+In practice, the return value of `parse_error()` has the same effect
+regardless — the parser stops. The distinction matters for whether
+exceptions are thrown (controlled by `allow_exceptions`).
+
+## Using `sax_parse()`
+
+```cpp
+template<typename InputType, typename SAX>
+static bool sax_parse(InputType&& i,
+ SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false,
+ const bool ignore_trailing_commas = false);
+```
+
+```cpp
+MySaxHandler handler;
+bool success = json::sax_parse(input_string, &handler);
+```
+
+The `format` parameter supports binary formats too:
+
+```cpp
+json::sax_parse(cbor_data, &handler, json::input_format_t::cbor);
+json::sax_parse(msgpack_data, &handler, json::input_format_t::msgpack);
+```
+
+## Implementing a Custom SAX Handler
+
+### Minimal Handler (Count Elements)
+
+```cpp
+struct counter_handler : nlohmann::json_sax<json>
+{
+ std::size_t values = 0;
+ std::size_t objects = 0;
+ std::size_t arrays = 0;
+
+ bool null() override { ++values; return true; }
+ bool boolean(bool) override { ++values; return true; }
+ bool number_integer(json::number_integer_t) override { ++values; return true; }
+ bool number_unsigned(json::number_unsigned_t) override { ++values; return true; }
+ bool number_float(json::number_float_t, const std::string&) override { ++values; return true; }
+ bool string(std::string&) override { ++values; return true; }
+ bool binary(json::binary_t&) override { ++values; return true; }
+
+ bool start_object(std::size_t) override { ++objects; return true; }
+ bool key(std::string&) override { return true; }
+ bool end_object() override { return true; }
+
+ bool start_array(std::size_t) override { ++arrays; return true; }
+ bool end_array() override { return true; }
+
+ bool parse_error(std::size_t, const std::string&,
+ const nlohmann::detail::exception&) override
+ { return false; }
+};
+
+// Usage
+counter_handler handler;
+json::sax_parse(R"({"a": [1, 2], "b": true})", &handler);
+// handler.values == 3 (1, 2, true)
+// handler.objects == 1
+// handler.arrays == 1
+```
+
+### Key Extractor
+
+Extract all keys from a JSON document without building the DOM:
+
+```cpp
+struct key_extractor : nlohmann::json_sax<json>
+{
+ std::vector<std::string> keys;
+ int depth = 0;
+
+ bool null() override { return true; }
+ bool boolean(bool) override { return true; }
+ bool number_integer(json::number_integer_t) override { return true; }
+ bool number_unsigned(json::number_unsigned_t) override { return true; }
+ bool number_float(json::number_float_t, const std::string&) override { return true; }
+ bool string(std::string&) override { return true; }
+ bool binary(json::binary_t&) override { return true; }
+
+ bool start_object(std::size_t) override { ++depth; return true; }
+ bool key(std::string& val) override {
+ keys.push_back(val);
+ return true;
+ }
+ bool end_object() override { --depth; return true; }
+
+ bool start_array(std::size_t) override { return true; }
+ bool end_array() override { return true; }
+
+ bool parse_error(std::size_t, const std::string&,
+ const nlohmann::detail::exception&) override
+ { return false; }
+};
+```
+
+### Early Termination
+
+Return `false` from any method to stop parsing immediately:
+
+```cpp
+struct find_key_handler : nlohmann::json_sax<json>
+{
+ std::string target_key;
+ json found_value;
+ bool found = false;
+ bool capture_next = false;
+
+ bool key(std::string& val) override {
+ capture_next = (val == target_key);
+ return true;
+ }
+
+ bool string(std::string& val) override {
+ if (capture_next) {
+ found_value = val;
+ found = true;
+ return false; // stop parsing
+ }
+ return true;
+ }
+
+ bool number_integer(json::number_integer_t val) override {
+ if (capture_next) {
+ found_value = val;
+ found = true;
+ return false;
+ }
+ return true;
+ }
+
+ // ... remaining methods return true ...
+ bool null() override { return !capture_next || (found = true, false); }
+ bool boolean(bool v) override {
+ if (capture_next) { found_value = v; found = true; return false; }
+ return true;
+ }
+ bool number_unsigned(json::number_unsigned_t v) override {
+ if (capture_next) { found_value = v; found = true; return false; }
+ return true;
+ }
+ bool number_float(json::number_float_t v, const std::string&) override {
+ if (capture_next) { found_value = v; found = true; return false; }
+ return true;
+ }
+ bool binary(json::binary_t&) override { return true; }
+ bool start_object(std::size_t) override { capture_next = false; return true; }
+ bool end_object() override { return true; }
+ bool start_array(std::size_t) override { capture_next = false; return true; }
+ bool end_array() override { return true; }
+ bool parse_error(std::size_t, const std::string&,
+ const nlohmann::detail::exception&) override { return false; }
+};
+```
+
+## Built-in SAX Handlers
+
+### `json_sax_dom_parser`
+
+The default handler used by `parse()`. Builds a complete DOM tree:
+
+```cpp
+template<typename BasicJsonType>
+class json_sax_dom_parser
+{
+ BasicJsonType& root;
+ std::vector<BasicJsonType*> ref_stack;
+ BasicJsonType* object_element = nullptr;
+};
+```
+
+### `json_sax_dom_callback_parser`
+
+Used when `parse()` is called with a callback. Adds filtering logic:
+
+```cpp
+template<typename BasicJsonType>
+class json_sax_dom_callback_parser
+{
+ BasicJsonType& root;
+ std::vector<BasicJsonType*> ref_stack;
+ std::vector<bool> keep_stack;
+ std::vector<bool> key_keep_stack;
+ parser_callback_t<BasicJsonType> callback;
+};
+```
+
+### `json_sax_acceptor`
+
+Used by `accept()`. All event methods return `true`, `parse_error()`
+returns `false`:
+
+```cpp
+template<typename BasicJsonType>
+struct json_sax_acceptor {
+ bool null() { return true; }
+ bool boolean(bool) { return true; }
+ // ... all true ...
+ bool parse_error(...) { return false; }
+};
+```
+
+## SAX with Binary Formats
+
+The SAX interface works uniformly across all supported formats. The
+`binary_reader` generates the same SAX events from binary input:
+
+```cpp
+struct my_handler : nlohmann::json_sax<json> { /* ... */ };
+
+my_handler handler;
+
+// JSON text
+json::sax_parse(json_text, &handler);
+
+// CBOR
+json::sax_parse(cbor_bytes, &handler, json::input_format_t::cbor);
+
+// MessagePack
+json::sax_parse(msgpack_bytes, &handler, json::input_format_t::msgpack);
+```
+
+The `start_object(n)` and `start_array(n)` methods receive the element
+count as `n` for binary formats (where the count is known from the header).
+For JSON text, `n` is always `static_cast<std::size_t>(-1)` (unknown).
+
+## Performance Considerations
+
+SAX parsing avoids DOM construction overhead:
+- No heap allocations for JSON containers
+- No recursive destruction of the DOM tree
+- Constant memory usage (proportional to nesting depth only)
+- Can process arbitrarily large documents
+
+For streaming scenarios where you need to process multiple JSON values
+from a single input, use `sax_parse()` with `strict = false` in a loop.
diff --git a/docs/handbook/json4cpp/serialization.md b/docs/handbook/json4cpp/serialization.md
new file mode 100644
index 0000000000..56265d62f1
--- /dev/null
+++ b/docs/handbook/json4cpp/serialization.md
@@ -0,0 +1,528 @@
+# json4cpp — Serialization & Deserialization
+
+## Parsing (Deserialization)
+
+### `parse()`
+
+```cpp
+template<typename InputType>
+static basic_json parse(InputType&& i,
+ const parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false,
+ const bool ignore_trailing_commas = false);
+```
+
+Parses JSON text from multiple source types.
+
+### Accepted Input Types
+
+| Input Type | Example |
+|---|---|
+| `std::string` / `string_view` | `json::parse("{}")` |
+| `const char*` | `json::parse(ptr)` |
+| `std::istream&` | `json::parse(file_stream)` |
+| Iterator pair | `json::parse(vec.begin(), vec.end())` |
+| `FILE*` | `json::parse(std::fopen("f.json", "r"))` |
+| Contiguous container | `json::parse(std::vector<uint8_t>{...})` |
+
+### String Parsing
+
+```cpp
+auto j = json::parse(R"({"key": "value", "num": 42})");
+```
+
+### File Parsing
+
+```cpp
+std::ifstream f("config.json");
+json j = json::parse(f);
+```
+
+### Iterator Parsing
+
+```cpp
+std::string s = R"([1, 2, 3])";
+json j = json::parse(s.begin(), s.end());
+
+std::vector<uint8_t> bytes = {'{', '}' };
+json j2 = json::parse(bytes);
+```
+
+### Error Handling
+
+By default, `parse()` throws `json::parse_error` on invalid input:
+
+```cpp
+try {
+ json j = json::parse("not json");
+} catch (json::parse_error& e) {
+ std::cerr << e.what() << "\n";
+ // "[json.exception.parse_error.101] parse error at line 1, column 1:
+ // syntax error while parsing value - invalid literal; ..."
+ std::cerr << "byte: " << e.byte << "\n"; // position of error
+}
+```
+
+Set `allow_exceptions = false` to get a discarded value instead:
+
+```cpp
+json j = json::parse("not json", nullptr, false);
+assert(j.is_discarded());
+```
+
+### Comments
+
+JSON does not support comments by standard, but the parser can skip them:
+
+```cpp
+std::string input = R"({
+ // line comment
+ "key": "value",
+ /* block comment */
+ "num": 42
+})";
+
+json j = json::parse(input, nullptr, true, true); // ignore_comments=true
+```
+
+Both C-style (`/* */`) and C++-style (`//`) comments are supported.
+
+### Trailing Commas
+
+```cpp
+std::string input = R"({
+ "a": 1,
+ "b": 2,
+})";
+
+json j = json::parse(input, nullptr, true, false, true); // ignore_trailing_commas=true
+```
+
+### `operator>>`
+
+Stream extraction operator:
+
+```cpp
+std::istringstream ss(R"({"key": "value"})");
+json j;
+ss >> j;
+```
+
+### `_json` User-Defined Literal
+
+```cpp
+using namespace nlohmann::literals;
+
+auto j = R"({"key": "value"})"_json;
+auto j2 = "[1, 2, 3]"_json;
+```
+
+The UDL is also available via `using namespace nlohmann::json_literals` or
+`using namespace nlohmann::literals`. When `JSON_GlobalUDLs` is enabled
+(the default), the literals are in the global namespace via
+`inline namespace`.
+
+### `_json_pointer` Literal
+
+```cpp
+using namespace nlohmann::literals;
+
+auto ptr = "/foo/bar/0"_json_pointer;
+```
+
+## Parser Callbacks
+
+### `parse_event_t`
+
+```cpp
+enum class parse_event_t : std::uint8_t
+{
+ object_start, ///< the parser read `{` and started to process a JSON object
+ object_end, ///< the parser read `}` and finished processing a JSON object
+ array_start, ///< the parser read `[` and started to process a JSON array
+ array_end, ///< the parser read `]` and finished processing a JSON array
+ key, ///< the parser read a key of a value in an object
+ value ///< the parser finished reading a JSON value
+};
+```
+
+### Callback Signature
+
+```cpp
+using parser_callback_t = std::function<bool(int depth,
+ parse_event_t event,
+ basic_json& parsed)>;
+```
+
+- `depth` — nesting depth (0 = top level)
+- `event` — current parse event
+- `parsed` — the parsed value (for `value`/`key` events) or null (for start/end)
+- Return `true` to keep the value, `false` to discard
+
+### Filtering Example
+
+```cpp
+// Remove all keys named "password"
+json j = json::parse(input, [](int /*depth*/, json::parse_event_t event, json& parsed) {
+ if (event == json::parse_event_t::key && parsed == "password") {
+ return false;
+ }
+ return true;
+});
+```
+
+### Depth-Limited Parsing
+
+```cpp
+// Only keep top-level keys
+json j = json::parse(input, [](int depth, json::parse_event_t event, json&) {
+ if (depth > 1 && event == json::parse_event_t::value) {
+ return false;
+ }
+ return true;
+});
+```
+
+## Serialization
+
+### `dump()`
+
+```cpp
+string_t dump(const int indent = -1,
+ const char indent_char = ' ',
+ const bool ensure_ascii = false,
+ const error_handler_t error_handler = error_handler_t::strict) const;
+```
+
+Converts a JSON value to a string.
+
+### Compact Output
+
+```cpp
+json j = {{"name", "alice"}, {"scores", {90, 85, 92}}};
+std::string s = j.dump();
+// {"name":"alice","scores":[90,85,92]}
+```
+
+### Pretty Printing
+
+```cpp
+std::string s = j.dump(4);
+// {
+// "name": "alice",
+// "scores": [
+// 90,
+// 85,
+// 92
+// ]
+// }
+```
+
+You can change the indent character:
+
+```cpp
+std::string s = j.dump(1, '\t');
+// {
+// "name": "alice",
+// "scores": [
+// 90,
+// 85,
+// 92
+// ]
+// }
+```
+
+### ASCII Escaping
+
+When `ensure_ascii = true`, all non-ASCII characters are escaped:
+
+```cpp
+json j = "München";
+j.dump(); // "München"
+j.dump(-1, ' ', true); // "M\u00FCnchen"
+```
+
+### UTF-8 Error Handling
+
+The `error_handler` controls what happens when the serializer encounters
+invalid UTF-8:
+
+```cpp
+enum class error_handler_t
+{
+ strict, ///< throw type_error::316 on invalid UTF-8
+ replace, ///< replace invalid bytes with U+FFFD
+ ignore ///< skip invalid bytes silently
+};
+```
+
+```cpp
+// String with invalid UTF-8 byte 0xFF
+std::string bad = "hello\xFFworld";
+json j = bad;
+
+j.dump(); // throws type_error::316
+
+j.dump(-1, ' ', false, json::error_handler_t::replace);
+// "hello\uFFFDworld"
+
+j.dump(-1, ' ', false, json::error_handler_t::ignore);
+// "helloworld"
+```
+
+### `operator<<`
+
+Stream insertion operator for convenience:
+
+```cpp
+json j = {{"key", "value"}};
+std::cout << j << "\n"; // compact: {"key":"value"}
+std::cout << std::setw(4) << j; // pretty: 4-space indent
+```
+
+Using `std::setw()` with the stream sets the indentation level.
+
+## Serializer Internals
+
+The actual serialization is performed by `detail::serializer<basic_json>`
+(in `include/nlohmann/detail/output/serializer.hpp`):
+
+```cpp
+template<typename BasicJsonType>
+class serializer
+{
+public:
+ serializer(output_adapter_t<char> s, const char ichar,
+ error_handler_t error_handler_ = error_handler_t::strict);
+
+ void dump(const BasicJsonType& val, const bool pretty_print,
+ const bool ensure_ascii, const unsigned int indent_step,
+ const unsigned int current_indent = 0);
+
+private:
+ void dump_escaped(const string_t& s, const bool ensure_ascii);
+ void dump_integer(number_integer_t x);
+ void dump_integer(number_unsigned_t x);
+ void dump_float(number_float_t x, std::true_type is_ieee);
+ void dump_float(number_float_t x, std::false_type is_ieee);
+};
+```
+
+### Number Serialization
+
+**Integers** are serialized using a custom digit-by-digit algorithm that
+writes into a stack buffer (`number_buffer`), avoiding `std::to_string`:
+
+```cpp
+// Internal buffer for number conversion
+std::array<char, 64> number_buffer{{}};
+```
+
+**Floating-point** values use different strategies:
+- On IEEE 754 platforms (`std::true_type`): uses `std::snprintf` with
+ `%.*g` format, with precision from `std::numeric_limits<number_float_t>::max_digits10`
+- On non-IEEE platforms (`std::false_type`): same `snprintf` approach
+
+Special float values:
+- `NaN` → serialized as `null`
+- `Infinity` → serialized as `null`
+
+### String Escaping
+
+The `dump_escaped()` method handles:
+- Control characters (`\n`, `\t`, `\r`, `\b`, `\f`, `\\`, `\"`)
+- Characters 0x00–0x1F are escaped as `\u00XX`
+- Non-ASCII characters can be escaped as `\uXXXX` (if `ensure_ascii = true`)
+- Surrogate pairs for characters above U+FFFF
+- Invalid UTF-8 handling per `error_handler_t`
+
+The UTF-8 decoder uses a state machine:
+
+```cpp
+static const std::array<std::uint8_t, 400> utf8d;
+std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte);
+```
+
+States: `UTF8_ACCEPT` (0) and `UTF8_REJECT` (1).
+
+## Output Adapters
+
+Defined in `include/nlohmann/detail/output/output_adapters.hpp`:
+
+```cpp
+template<typename CharType>
+class output_adapter
+{
+public:
+ // Adapts std::vector<CharType>
+ output_adapter(std::vector<CharType>& vec);
+
+ // Adapts std::basic_ostream
+ output_adapter(std::basic_ostream<CharType>& s);
+
+ // Adapts std::basic_string
+ output_adapter(StringType& s);
+};
+```
+
+Three concrete adapters:
+- `output_vector_adapter` — writes to `std::vector<char>`
+- `output_stream_adapter` — writes to `std::ostream`
+- `output_string_adapter` — writes to `std::string`
+
+## ADL Serialization Mechanism
+
+### How `to_json()` / `from_json()` Work
+
+The library uses **Argument-Dependent Lookup (ADL)** to find conversion
+functions. When you call `json j = my_obj;`, the library ultimately invokes:
+
+```cpp
+nlohmann::adl_serializer<MyType>::to_json(j, my_obj);
+```
+
+The default `adl_serializer` delegates to a free function found via ADL:
+
+```cpp
+template<typename ValueType, typename>
+struct adl_serializer
+{
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto from_json(BasicJsonType&& j, TargetType& val)
+ -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
+ {
+ ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+ }
+
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto to_json(BasicJsonType& j, TargetType&& val)
+ -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
+ {
+ ::nlohmann::to_json(j, std::forward<TargetType>(val));
+ }
+};
+```
+
+### Built-in Conversions
+
+The library provides `to_json()` and `from_json()` overloads for:
+
+| C++ Type | JSON Type |
+|---|---|
+| `bool` | boolean |
+| `int`, `long`, `int64_t`, etc. | number_integer |
+| `unsigned`, `uint64_t`, etc. | number_unsigned |
+| `float`, `double` | number_float |
+| `std::string`, `const char*` | string |
+| `std::nullptr_t` | null |
+| `std::vector<T>`, `std::list<T>`, ... | array |
+| `std::array<T, N>` | array |
+| `std::map<string, T>` | object |
+| `std::unordered_map<string, T>` | object |
+| `std::pair<T1, T2>` | array of 2 |
+| `std::tuple<Ts...>` | array of N |
+| `std::optional<T>` (C++17) | value or null |
+| `std::variant<Ts...>` (C++17) | depends on held type |
+| Enum types | integer (unless disabled) |
+
+### Priority Tags for Overload Resolution
+
+Built-in `to_json()` overloads use a priority tag system to resolve
+ambiguity:
+
+```cpp
+template<typename BasicJsonType, typename T>
+void to_json(BasicJsonType& j, T val, priority_tag<1>); // higher priority
+template<typename BasicJsonType, typename T>
+void to_json(BasicJsonType& j, T val, priority_tag<0>); // lower priority
+```
+
+The `priority_tag<N>` inherits from `priority_tag<N-1>`, so higher-tagged
+overloads are preferred.
+
+## Roundtrip Guarantees
+
+### Integers
+
+Integer values survive a parse → dump → parse roundtrip exactly, as long
+as they fit within `int64_t` or `uint64_t` range.
+
+### Floating-Point
+
+The library uses `max_digits10` precision (typically 17 for `double`) to
+ensure roundtrip fidelity:
+
+```cpp
+json j = 3.141592653589793;
+std::string s = j.dump(); // "3.141592653589793"
+json j2 = json::parse(s);
+assert(j == j2); // true
+```
+
+### Strings
+
+UTF-8 strings roundtrip exactly. The serializer preserves all valid UTF-8
+sequences. Unicode escapes in input (`\uXXXX`) are converted to UTF-8 on
+parsing and will be re-escaped only if `ensure_ascii = true`.
+
+## `accept()`
+
+```cpp
+template<typename InputType>
+static bool accept(InputType&& i,
+ const bool ignore_comments = false,
+ const bool ignore_trailing_commas = false);
+```
+
+Checks whether the input is valid JSON without constructing a value:
+
+```cpp
+json::accept("{}"); // true
+json::accept("[1,2,3]"); // true
+json::accept("not json"); // false
+json::accept("{\"a\": 1,}"); // false
+json::accept("{\"a\": 1,}", false, true); // true (trailing commas)
+```
+
+## Conversion to/from STL Types
+
+### Explicit Conversion (`get<T>()`)
+
+```cpp
+json j = 42;
+int i = j.get<int>();
+double d = j.get<double>();
+
+json j2 = {1, 2, 3};
+auto v = j2.get<std::vector<int>>();
+auto s = j2.get<std::set<int>>();
+```
+
+### Implicit Conversion
+
+When `JSON_ImplicitConversions` is enabled (default), implicit conversions
+are available:
+
+```cpp
+json j = 42;
+int i = j; // implicit conversion
+std::string s = j; // throws type_error::302 (wrong type)
+
+json j2 = "hello";
+std::string s2 = j2; // "hello"
+```
+
+To disable implicit conversions (recommended for new code):
+
+```cmake
+set(JSON_ImplicitConversions OFF)
+```
+
+Or define the macro:
+
+```cpp
+#define JSON_USE_IMPLICIT_CONVERSIONS 0
+```
+
+When disabled, only explicit `get<T>()` works.
diff --git a/docs/handbook/json4cpp/testing.md b/docs/handbook/json4cpp/testing.md
new file mode 100644
index 0000000000..4439b71a42
--- /dev/null
+++ b/docs/handbook/json4cpp/testing.md
@@ -0,0 +1,190 @@
+# json4cpp — Testing
+
+## Test Framework
+
+The test suite uses **doctest** (a single-header C++ testing framework).
+Tests are located in `json4cpp/tests/src/` with one file per feature area.
+
+## Test File Naming
+
+All test files follow the pattern `unit-<feature>.cpp`:
+
+| File | Covers |
+|---|---|
+| `unit-allocator.cpp` | Custom allocator support |
+| `unit-alt-string.cpp` | Alternative string types |
+| `unit-bson.cpp` | BSON serialization/deserialization |
+| `unit-bjdata.cpp` | BJData format |
+| `unit-capacity.cpp` | `size()`, `empty()`, `max_size()` |
+| `unit-cbor.cpp` | CBOR format |
+| `unit-class_const_iterator.cpp` | `const_iterator` behavior |
+| `unit-class_iterator.cpp` | `iterator` behavior |
+| `unit-class_lexer.cpp` | Lexer token scanning |
+| `unit-class_parser.cpp` | Parser behavior |
+| `unit-comparison.cpp` | Comparison operators |
+| `unit-concepts.cpp` | Concept/type trait checks |
+| `unit-constructor1.cpp` | Constructor overloads (part 1) |
+| `unit-constructor2.cpp` | Constructor overloads (part 2) |
+| `unit-convenience.cpp` | Convenience methods (`type_name`, etc.) |
+| `unit-conversions.cpp` | Type conversions |
+| `unit-deserialization.cpp` | Parsing from various sources |
+| `unit-diagnostics.cpp` | `JSON_DIAGNOSTICS` mode |
+| `unit-element_access1.cpp` | `operator[]`, `at()` (part 1) |
+| `unit-element_access2.cpp` | `value()`, `front()`, `back()` (part 2) |
+| `unit-hash.cpp` | `std::hash` specialization |
+| `unit-inspection.cpp` | `is_*()`, `type()` methods |
+| `unit-items.cpp` | `items()` iteration proxy |
+| `unit-iterators1.cpp` | Forward iterators |
+| `unit-iterators2.cpp` | Reverse iterators |
+| `unit-json_patch.cpp` | JSON Patch (RFC 6902) |
+| `unit-json_pointer.cpp` | JSON Pointer (RFC 6901) |
+| `unit-large_json.cpp` | Large document handling |
+| `unit-merge_patch.cpp` | Merge Patch (RFC 7396) |
+| `unit-meta.cpp` | Library metadata |
+| `unit-modifiers.cpp` | `push_back()`, `insert()`, `erase()`, etc. |
+| `unit-msgpack.cpp` | MessagePack format |
+| `unit-ordered_json.cpp` | `ordered_json` behavior |
+| `unit-ordered_map.cpp` | `ordered_map` internals |
+| `unit-pointer_access.cpp` | `get_ptr()` |
+| `unit-readme.cpp` | Examples from README |
+| `unit-reference_access.cpp` | `get_ref()` |
+| `unit-regression1.cpp` | Regression tests (part 1) |
+| `unit-regression2.cpp` | Regression tests (part 2) |
+| `unit-serialization.cpp` | `dump()`, stream output |
+| `unit-testsuites.cpp` | External test suites (JSONTestSuite, etc.) |
+| `unit-to_chars.cpp` | Float-to-string conversion |
+| `unit-ubjson.cpp` | UBJSON format |
+| `unit-udt.cpp` | User-defined type conversions |
+| `unit-udt_macro.cpp` | `NLOHMANN_DEFINE_TYPE_*` macros |
+| `unit-unicode1.cpp` | Unicode handling (part 1) |
+| `unit-unicode2.cpp` | Unicode handling (part 2) |
+| `unit-unicode3.cpp` | Unicode handling (part 3) |
+| `unit-unicode4.cpp` | Unicode handling (part 4) |
+| `unit-unicode5.cpp` | Unicode handling (part 5) |
+| `unit-wstring.cpp` | Wide string support |
+
+## CMake Configuration
+
+From `tests/CMakeLists.txt`:
+
+```cmake
+# Test data files
+file(GLOB_RECURSE JSON_TEST_DATA_FILES
+ "${CMAKE_CURRENT_SOURCE_DIR}/data/*.json"
+ "${CMAKE_CURRENT_SOURCE_DIR}/data/*.cbor"
+ "${CMAKE_CURRENT_SOURCE_DIR}/data/*.msgpack"
+ "${CMAKE_CURRENT_SOURCE_DIR}/data/*.bson"
+ "${CMAKE_CURRENT_SOURCE_DIR}/data/*.ubjson"
+ "${CMAKE_CURRENT_SOURCE_DIR}/data/*.bjdata")
+
+# Each unit-*.cpp compiles to its own test executable
+```
+
+Key CMake options affecting tests:
+
+| Option | Effect |
+|---|---|
+| `JSON_BuildTests` | Enable/disable test building |
+| `JSON_Diagnostics` | Build tests with `JSON_DIAGNOSTICS=1` |
+| `JSON_Diagnostic_Positions` | Build tests with position tracking |
+| `JSON_MultipleHeaders` | Use multi-header include paths |
+| `JSON_ImplicitConversions` | Test with implicit conversions on/off |
+| `JSON_DisableEnumSerialization` | Test with enum serialization off |
+| `JSON_GlobalUDLs` | Test with global UDLs on/off |
+
+## Building Tests
+
+```bash
+cd json4cpp
+mkdir build && cd build
+
+# Configure with tests enabled
+cmake .. -DJSON_BuildTests=ON
+
+# Build
+cmake --build .
+
+# Run all tests
+ctest --output-on-failure
+
+# Run a specific test
+./tests/test-unit-json_pointer
+```
+
+## Test Structure
+
+Tests use doctest's `TEST_CASE` and `SECTION` macros:
+
+```cpp
+#include <doctest/doctest.h>
+#include <nlohmann/json.hpp>
+
+using json = nlohmann::json;
+
+TEST_CASE("element access") {
+ SECTION("array") {
+ json j = {1, 2, 3};
+
+ SECTION("operator[]") {
+ CHECK(j[0] == 1);
+ CHECK(j[1] == 2);
+ CHECK(j[2] == 3);
+ }
+
+ SECTION("at()") {
+ CHECK(j.at(0) == 1);
+ CHECK_THROWS_AS(j.at(5), json::out_of_range);
+ }
+ }
+
+ SECTION("object") {
+ json j = {{"key", "value"}};
+
+ CHECK(j["key"] == "value");
+ CHECK(j.at("key") == "value");
+ CHECK_THROWS_AS(j.at("missing"), json::out_of_range);
+ }
+}
+```
+
+### Common Assertions
+
+| Macro | Purpose |
+|---|---|
+| `CHECK(expr)` | Value assertion (non-fatal) |
+| `REQUIRE(expr)` | Value assertion (fatal) |
+| `CHECK_THROWS_AS(expr, type)` | Exception type assertion |
+| `CHECK_THROWS_WITH_AS(expr, msg, type)` | Exception message + type |
+| `CHECK_NOTHROW(expr)` | No exception assertion |
+
+## Test Data
+
+The `tests/data/` directory contains JSON files for conformance testing:
+- Input from JSONTestSuite (parsing edge cases)
+- Binary format test vectors (CBOR, MessagePack, UBJSON, BSON, BJData)
+- Unicode test cases
+- Large nested structures
+
+## Running Specific Tests
+
+doctest supports command-line filtering:
+
+```bash
+# Run tests matching a substring
+./test-unit-json_pointer -tc="JSON pointer"
+
+# List all test cases
+./test-unit-json_pointer -ltc
+
+# Run with verbose output
+./test-unit-json_pointer -s
+```
+
+## Continuous Integration
+
+Tests are run across multiple compilers and platforms via CI (see `ci/`
+directory). The `ci/supportedBranches.js` file lists which branches are
+tested. The test matrix covers:
+- GCC, Clang, MSVC
+- C++11 through C++20
+- Various option combinations (diagnostics, implicit conversions, etc.)
diff --git a/docs/handbook/json4cpp/value-types.md b/docs/handbook/json4cpp/value-types.md
new file mode 100644
index 0000000000..c9f9bf0e6f
--- /dev/null
+++ b/docs/handbook/json4cpp/value-types.md
@@ -0,0 +1,474 @@
+# json4cpp — Value Types
+
+## The `value_t` Enumeration
+
+Defined in `include/nlohmann/detail/value_t.hpp`, the `value_t` enumeration
+identifies the type of a `basic_json` value:
+
+```cpp
+enum class value_t : std::uint8_t
+{
+ null, ///< null value
+ object, ///< unordered set of name/value pairs
+ array, ///< ordered collection of values
+ string, ///< string value
+ boolean, ///< boolean value
+ number_integer, ///< signed integer number
+ number_unsigned, ///< unsigned integer number
+ number_float, ///< floating-point number
+ binary, ///< binary array (ordered collection of bytes)
+ discarded ///< discarded by the parser callback function
+};
+```
+
+The underlying type is `std::uint8_t` (1 byte), stored in `m_data.m_type`.
+
+## Type-to-Storage Mapping
+
+| `value_t` | C++ Type Alias | Default C++ Type | Storage in `json_value` |
+|---|---|---|---|
+| `null` | — | — | No active member (pointer set to `nullptr`) |
+| `object` | `object_t` | `std::map<std::string, basic_json>` | `object_t* object` |
+| `array` | `array_t` | `std::vector<basic_json>` | `array_t* array` |
+| `string` | `string_t` | `std::string` | `string_t* string` |
+| `boolean` | `boolean_t` | `bool` | `boolean_t boolean` |
+| `number_integer` | `number_integer_t` | `std::int64_t` | `number_integer_t number_integer` |
+| `number_unsigned` | `number_unsigned_t` | `std::uint64_t` | `number_unsigned_t number_unsigned` |
+| `number_float` | `number_float_t` | `double` | `number_float_t number_float` |
+| `binary` | `binary_t` | `byte_container_with_subtype<vector<uint8_t>>` | `binary_t* binary` |
+| `discarded` | — | — | No storage (used only during parse callback filtering) |
+
+Variable-length types (object, array, string, binary) are stored as **heap-
+allocated pointers** to keep the `json_value` union at 8 bytes on 64-bit.
+
+## Type Inspection Methods
+
+### `type()`
+
+Returns the `value_t` of the stored value:
+
+```cpp
+constexpr value_t type() const noexcept;
+```
+
+```cpp
+json j = 42;
+assert(j.type() == json::value_t::number_integer);
+
+json j2 = "hello";
+assert(j2.type() == json::value_t::string);
+```
+
+### `type_name()`
+
+Returns a human-readable string for the current type:
+
+```cpp
+const char* type_name() const noexcept;
+```
+
+| `value_t` | Returned String |
+|---|---|
+| `null` | `"null"` |
+| `object` | `"object"` |
+| `array` | `"array"` |
+| `string` | `"string"` |
+| `boolean` | `"boolean"` |
+| `binary` | `"binary"` |
+| `number_integer` | `"number"` |
+| `number_unsigned` | `"number"` |
+| `number_float` | `"number"` |
+| `discarded` | `"discarded"` |
+
+Note that all three numeric types return `"number"`.
+
+```cpp
+json j = {1, 2, 3};
+std::cout << j.type_name(); // "array"
+```
+
+### `is_*()` Methods
+
+All return `constexpr bool` and are `noexcept`:
+
+```cpp
+constexpr bool is_null() const noexcept;
+constexpr bool is_boolean() const noexcept;
+constexpr bool is_number() const noexcept;
+constexpr bool is_number_integer() const noexcept;
+constexpr bool is_number_unsigned() const noexcept;
+constexpr bool is_number_float() const noexcept;
+constexpr bool is_object() const noexcept;
+constexpr bool is_array() const noexcept;
+constexpr bool is_string() const noexcept;
+constexpr bool is_binary() const noexcept;
+constexpr bool is_discarded() const noexcept;
+constexpr bool is_primitive() const noexcept;
+constexpr bool is_structured() const noexcept;
+```
+
+### Category Methods
+
+```cpp
+// is_primitive() == is_null() || is_string() || is_boolean() || is_number() || is_binary()
+// is_structured() == is_array() || is_object()
+// is_number() == is_number_integer() || is_number_float()
+// is_number_integer() == (type == number_integer || type == number_unsigned)
+```
+
+Important: `is_number_integer()` returns `true` for **both** signed and unsigned
+integers. Use `is_number_unsigned()` to distinguish.
+
+```cpp
+json j = 42u;
+j.is_number() // true
+j.is_number_integer() // true
+j.is_number_unsigned() // true
+
+json j2 = -5;
+j2.is_number_integer() // true
+j2.is_number_unsigned() // false
+```
+
+### `operator value_t()`
+
+Implicit conversion to `value_t`:
+
+```cpp
+constexpr operator value_t() const noexcept;
+```
+
+```cpp
+json j = "hello";
+json::value_t t = j; // value_t::string
+```
+
+## Null Type
+
+Null is the default value:
+
+```cpp
+json j; // null
+json j = nullptr; // null
+json j(json::value_t::null); // null
+
+j.is_null() // true
+j.type_name() // "null"
+```
+
+Null values have special behavior:
+- `size()` returns 0
+- `empty()` returns `true`
+- `operator[]` with a string key converts null to an object
+- `operator[]` with a numeric index converts null to an array
+- `push_back()` converts null to an array
+
+## Object Type
+
+### Internal Representation
+
+```cpp
+using object_t = ObjectType<StringType, basic_json,
+ default_object_comparator_t,
+ AllocatorType<std::pair<const StringType, basic_json>>>;
+```
+
+Default: `std::map<std::string, basic_json, std::less<>, std::allocator<...>>`
+
+With C++14 transparent comparators, heterogeneous lookup is supported.
+
+### `ordered_json` Objects
+
+When using `ordered_json = basic_json<nlohmann::ordered_map>`, objects
+preserve insertion order:
+
+```cpp
+nlohmann::ordered_json j;
+j["z"] = 1;
+j["a"] = 2;
+j["m"] = 3;
+// iteration order: z, a, m
+```
+
+The `ordered_map` uses linear search (O(n) lookup) instead of tree-based
+(O(log n)).
+
+## Array Type
+
+### Internal Representation
+
+```cpp
+using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+```
+
+Default: `std::vector<basic_json, std::allocator<basic_json>>`
+
+Arrays can contain mixed types (heterogeneous):
+
+```cpp
+json j = {1, "two", 3.0, true, nullptr, {{"nested", "object"}}};
+```
+
+## String Type
+
+### Internal Representation
+
+```cpp
+using string_t = StringType; // default: std::string
+```
+
+Strings in JSON are Unicode (UTF-8). The library validates UTF-8 during
+serialization but stores raw bytes. The `dump()` method's `error_handler`
+parameter controls what happens with invalid UTF-8:
+
+- `error_handler_t::strict` — throw `type_error::316`
+- `error_handler_t::replace` — replace with U+FFFD
+- `error_handler_t::ignore` — skip invalid bytes
+
+## Boolean Type
+
+```cpp
+using boolean_t = BooleanType; // default: bool
+```
+
+Stored directly in the union (no heap allocation):
+
+```cpp
+json j = true;
+bool b = j.get<bool>();
+```
+
+## Number Types
+
+### Three Distinct Types
+
+The library distinguishes three numeric types:
+
+```cpp
+using number_integer_t = NumberIntegerType; // default: std::int64_t
+using number_unsigned_t = NumberUnsignedType; // default: std::uint64_t
+using number_float_t = NumberFloatType; // default: double
+```
+
+During parsing, the lexer determines the best-fit type:
+1. If the number has a decimal point or exponent → `number_float`
+2. If it fits in `int64_t` → `number_integer`
+3. If it fits in `uint64_t` → `number_unsigned`
+4. Otherwise → `number_float` (as approximation)
+
+### Cross-Type Comparisons
+
+Numbers of different types are compared correctly:
+
+```cpp
+json(1) == json(1.0) // true
+json(1) == json(1u) // true
+json(-1) < json(0u) // true (signed < unsigned via cast)
+```
+
+The comparison logic in `JSON_IMPLEMENT_OPERATOR` handles all 6 cross-type
+combinations (int×float, float×int, unsigned×float, float×unsigned,
+unsigned×int, int×unsigned).
+
+### NaN Handling
+
+`NaN` values result in unordered comparisons:
+
+```cpp
+json nan_val = std::numeric_limits<double>::quiet_NaN();
+nan_val == nan_val; // false (IEEE 754 semantics)
+```
+
+## Binary Type
+
+### Internal Representation
+
+```cpp
+using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
+// BinaryType default: std::vector<std::uint8_t>
+```
+
+`byte_container_with_subtype<BinaryType>` extends `BinaryType` with an
+optional subtype tag:
+
+```cpp
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+public:
+ using container_type = BinaryType;
+ using subtype_type = std::uint64_t;
+
+ void set_subtype(subtype_type subtype_) noexcept;
+ constexpr subtype_type subtype() const noexcept;
+ constexpr bool has_subtype() const noexcept;
+ void clear_subtype() noexcept;
+
+private:
+ subtype_type m_subtype = 0;
+ bool m_has_subtype = false;
+};
+```
+
+### Creating Binary Values
+
+```cpp
+// Without subtype
+json j = json::binary({0x01, 0x02, 0x03, 0x04});
+
+// With subtype
+json j = json::binary({0x01, 0x02}, 128);
+
+// Access the binary container
+json::binary_t& bin = j.get_binary();
+bin.push_back(0x05);
+assert(bin.has_subtype() == false); // depends on how it was created
+```
+
+### Subtype Significance
+
+The subtype is meaningful for binary formats:
+- **MessagePack**: maps to ext type
+- **CBOR**: maps to tag
+- **BSON**: maps to binary subtype
+
+JSON text format does not support binary data natively.
+
+## Discarded Type
+
+The `discarded` type is special — it's used only during parsing with
+callbacks to indicate that a value should be excluded from the result:
+
+```cpp
+json j = json::parse(input, [](int depth, json::parse_event_t event, json& parsed) {
+ if (event == json::parse_event_t::key && parsed == "secret") {
+ return false; // discard this key-value pair
+ }
+ return true;
+});
+```
+
+When `json::parse()` is called with `allow_exceptions=false` and parsing
+fails, the result is a discarded value:
+
+```cpp
+json j = json::parse("invalid", nullptr, false);
+assert(j.is_discarded());
+```
+
+## Type Ordering
+
+The `value_t` enumeration has a defined ordering used for cross-type
+comparisons when values can't be compared directly:
+
+```
+null < boolean < number < object < array < string < binary
+```
+
+This means:
+
+```cpp
+json(nullptr) < json(false); // true (null < boolean)
+json(42) < json::object(); // true (number < object)
+json("abc") > json::array(); // true (string > array)
+```
+
+The ordering is implemented via a lookup array in `value_t.hpp`:
+
+```cpp
+static constexpr std::array<std::uint8_t, 9> order = {{
+ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+ 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
+ 6 /* binary */
+}};
+```
+
+All three numeric types share order index 2.
+
+## Type Conversions
+
+### Widening Conversions
+
+```cpp
+json j = 42; // number_integer
+double d = j.get<double>(); // OK: int → double
+
+json j2 = 3.14; // number_float
+int i = j2.get<int>(); // OK: truncates to 3
+```
+
+### Container Conversions
+
+```cpp
+json arr = {1, 2, 3};
+auto v = arr.get<std::vector<int>>();
+auto l = arr.get<std::list<int>>();
+auto s = arr.get<std::set<int>>();
+
+json obj = {{"a", 1}, {"b", 2}};
+auto m = obj.get<std::map<std::string, int>>();
+auto um = obj.get<std::unordered_map<std::string, int>>();
+```
+
+### String Conversions
+
+```cpp
+json j = 42;
+std::string s = j.dump(); // "42" (serialization, not type conversion)
+// j.get<std::string>() // throws type_error::302
+
+json j = "hello";
+std::string s = j.get<std::string>(); // "hello"
+```
+
+## Constructing from `value_t`
+
+You can construct an empty value of a specific type:
+
+```cpp
+json j_null(json::value_t::null); // null
+json j_obj(json::value_t::object); // {}
+json j_arr(json::value_t::array); // []
+json j_str(json::value_t::string); // ""
+json j_bool(json::value_t::boolean); // false
+json j_int(json::value_t::number_integer); // 0
+json j_uint(json::value_t::number_unsigned); // 0
+json j_float(json::value_t::number_float); // 0.0
+json j_bin(json::value_t::binary); // binary([], no subtype)
+```
+
+## Pointer Access
+
+Low-level pointer access to the underlying value:
+
+```cpp
+json j = "hello";
+
+// Returns nullptr if type doesn't match
+const std::string* sp = j.get_ptr<const std::string*>();
+assert(sp != nullptr);
+
+const int* ip = j.get_ptr<const int*>();
+assert(ip == nullptr); // wrong type
+
+// Mutable pointer access
+std::string* sp = j.get_ptr<std::string*>();
+*sp = "world";
+assert(j == "world");
+```
+
+## Reference Access
+
+```cpp
+json j = "hello";
+
+const std::string& ref = j.get_ref<const std::string&>();
+assert(ref == "hello");
+
+// Throws type_error::303 if type doesn't match
+try {
+ const int& ref = j.get_ref<const int&>();
+} catch (json::type_error& e) {
+ // "incompatible ReferenceType for get_ref, actual type is string"
+}
+```