Commit Graph
38 Commits
Author SHA1 Message Date
Niels Lohmann 330fbead59 Document clang-tidy exclusions, drop unneeded ones, and clean up doc examples (#5909)
* Document clang-tidy exclusions and drop unneeded ones

Re-enable every excluded check in a run of clang-tidy 22 over all tests
and remove the seven that never fire (cert-err58-cpp,
clang-analyzer-webkit.NoUncountedMemberChecker,
cppcoreguidelines-virtual-class-destructor, google-readability-function-size,
hicpp-function-size, hicpp-no-assembler, readability-function-size).

Document the reason for every remaining exclusion at the top of the file.

Exclude in advance the checks that only exist in clang-tidy 23 and are
noise here (bugprone-signed-bitwise, *-explicit-constructor,
readability-trailing-comma, modernize-use-string-view), plus
modernize-use-integer-sign-comparison, which needs C++20 std::cmp_less
and flags deliberate casts.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix clang-tidy findings in documentation examples and replace redundant aliases

Apply clang-tidy fixes to the documentation examples:
- performance-avoid-endl: std::endl -> '\n'
- readability-qualified-auto: auto -> auto* / const auto*
- performance-for-range-copy: copy -> const reference
- google-readability-casting: C-style cast -> static_cast

Replace redundant aliases (readability-redundant-qualified-alias) such as
"using json = nlohmann::json;" with "using nlohmann::json;" in the
documentation, README, examples, tests, and benchmarks. Aliases at class
scope and aliases of other types (with_base_class_t etc.) are kept.

No changes to the library or its public API. All changed examples were
compiled and run; their output matches the stored .output files (except
max_size and meta, which depend on the platform and compiler).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Format doc examples with the pinned astyle

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 12:07:51 +01:00
Niels Lohmann fe231dc692 docs: write std::chrono durations as unit/value objects; reword a title with escaped quotes (#5911)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 12:07:32 +01:00
Niels Lohmann ffd4242dc2 Docs: fill documentation gaps found in user questions (#5907)
* Docs: fill documentation gaps found in user questions

Add pages on class-hierarchy/smart-pointer/variant/chrono recipes, working
without exceptions, and large, streamed and partial input. Extend the FAQ
(23 entries, Known bugs first), parse error documentation (empty-input
checklist, line/column, diagnostic positions), element access, conversions,
creating values, ADL troubleshooting, memory, and build/distribution notes.
Add 14 examples with generated output. Correct the statement that parse errors
cannot be recovered from with -fno-exceptions.

The topics come from recurring questions on Stack Overflow, Reddit, Hacker
News, YouTube, and the project's GitHub discussions and issues.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: declare range-for lists in variables to keep astyle quiet

astyle reformats a braced init list placed directly in a range-for
header; declare the list first so check_amalgamation passes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 12:07:13 +01:00
Niels Lohmann cc917d7123 docs: cross-reference json_view and json_document for 3.13.0 (#5892)
* docs: cross-reference json_view and json_document for 3.13.0

Document the new zero-copy document/view API across the docs: add a
"Documents and views" section to the basic_json API hub linking to all
specializations, point parsing/conversion/modifying pages to the view-based
alternatives, add document/view sections to feature pages, update the
landing page to mention json_view.hpp/json_literals.hpp, and cross-link
debugging/exceptions.

Also document wide-string inputs (and ill-formed UTF-16/32 rejection),
add parse/accept examples for wide strings, fix related doc gaps, and
align features/parsing and FAQ with the added support.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: fix article before std::istream in FAQ

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: clarify document inputs, decoding, get<T>() and editing

Address review comments: say which inputs are borrowed and that a wide
string pointer is an extra input, which values are decoded during parsing,
that get<T>() reads only built-in types directly, all forms of set() and
that the document's insert() is array-only, and how to store wide strings.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: integers are converted while parsing a document

Also say "They accept every input" in basic_json_document::parse.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 06:18:07 +01:00
Niels Lohmann 4971200b54 Add images of json_documents: save() and load() (#5632)
* Add images of json_documents: save() and load()

An image is a document stored so that loading it needs no
parsing: save() writes the node index, the text and the decoded
strings; the static load() reads an image written by save().

load() takes a pointer and size, a borrowed vector, or an owned
rvalue vector; the nodes are copied so they are aligned and can
be edited, while the text and decoded strings stay in the image.

image_check controls how much load() trusts the input: full
checks structure, bounds, strings and numbers, the parser's own
guarantees; bounds checks structure and bounds only; none skips
all checks, for images from a trusted source.

Layout is little-endian only ("NJVI" header, nodes, text, decoded
strings), following the idea of zero-copy formats such as
FlatBuffers and YaFF; the check follows FlatBuffers' Verifier.

New errors: parse_error.116 for a malformed image or a failed
check, type_error.320 for a discarded document or a big-endian
target.

A dedicated fuzzer and 6,000 seeded corruptions, checked under
ASan/UBSan, found and fixed two gaps: unchecked reserved header
fields, and unbounded null/boolean offsets that could make
dump() throw std::length_error.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix MSVC errors in image test and add image_check doc anchor

Move the raw string literal out of the CHECK macro (MSVC preprocessor),
cast 64-bit header fields to std::size_t (C4244 on 32-bit), and give the
image_check section in load.md a real anchor for the mkdocs strict build.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Check each distinct string and float range once in the full image check

The full check scanned the contents of every string node and every float token over its own range, so many nodes pointing to one large range made load() quadratic. The structural walk now only collects the ranges; after it, each kind is sorted and checked once per distinct range. Ranges of one kind that overlap without being identical are rejected, as save() never writes them (nodes that share a value share the whole range). The cost is linear in the size of the image plus sorting the ranges.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Adapt the image tests to named documents and last-wins lookups

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Test that the targets of links never reach an image

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Test lookups in loaded images with duplicate and colliding keys

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Release the list of large objects once load() has built the indexes

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Test that invalid UTF-8 of a loaded image is not copied into an editable document

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Document that editable documents refuse invalid UTF-8 copied from a loaded image

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Expect first-wins lookups in the image tests

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Avoid useless casts of header fields in the image tests

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix clang-tidy 22 findings in unit-json_view_image.cpp and image.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix MSVC C4244 and C2017 in the image test

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the indentation of a loop in the image test

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make json_view.hpp pass include-what-you-use

ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), and <version> for
std::nullptr_t, and removing <cstddef>.

- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
  json_view.hpp only includes json.hpp, so including them here would duplicate
  their definitions
- drop edit_storage.hpp, which edit.hpp includes

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Silence a clang-tidy C++17 finding in the image test

bugprone-suspicious-stringview-data-usage flags data() of the source view
under C++17, although the length is passed and checked by the REQUIRE
before it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Build the image link test without exceptions

load_result() exists only with exceptions; with JSON_NOEXCEPTION the test
loads the image with the check directly (a failed check aborts).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Follow the renames from the review of the view builder

The review of #5620 renamed document_data's tape/tape_size/tape_cap to
nodes/node_count/node_capacity and made the decode arena a
std::vector<char>; adapt the code of this change to it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Rename the emit lambda in the image writer for Qt compatibility

Qt defines emit as an empty macro, so a Qt header included before
json_view.hpp broke the lambda's declaration (see #5865). The slots
variables in the image tests are renamed as well.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review of json_document images

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 10:21:29 +02:00
Niels Lohmann 823eee3b59 Add editable json_documents: set, push_back, insert, and erase (#5630)
* Add editable json_documents: set, push_back, insert, and erase

basic_json_document gets a second template parameter, Editable
(false by default), plus the aliases json_editable_document,
json_editable_view, ordered_json_editable_document and
ordered_json_editable_view.

Editable documents can change values and structure without
rewriting the source text: set()/push_back() on values, keys,
array indices and JSON pointers; insert() before an array
element; erase() of an object key, array index or JSON pointer.

New values and element sequences go into edit storage that the
document owns and never moves, so views keep referring to their
value across edits and a parsed node never moves. Read-only
documents walk the plain node array and are unaffected.

Strings are checked for UTF-8 on entry, so dump() of an editable
document never throws type_error.316. Binary values cannot be
stored (type_error.319).

A seeded differential test applies random edits to an editable
document and to the equivalent ordered_json and compares both
after every step.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix old clang and MSVC C4127 in editable json documents

Value-initialize the const std::less in find_parent (clang 3.4/3.6 do not
implement DR 253), and test the Editable template argument through a
function to avoid MSVC C4127.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Copy nested values into a document without recursion

Counting and copying the nodes of a view or a basic_json value into an
editable document recursed once per nesting level, so that a deeply
nested value overflowed the stack. The four functions now walk the value
with an explicit stack, as materialize() does.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not leave a half-assigned container when set_moved() throws

assign() rewrote the kind, length and extent of a slot before set_moved()
registered its new element sequence. set_moved() can throw
(std::bad_alloc from reserving the bookkeeping vectors) for a slot that
is not moved yet, which left a container without the moved flag that
showed its old children. The bookkeeping is now reserved first
(reserve_moved()), so that nothing after the first write to the slot can
throw. block_of() reserves before it marks anything for the same reason.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Check strings copied from another document for valid UTF-8

An editable document only holds valid UTF-8, but copy_scalar() copied the
strings and keys of a view of another document unchecked. A document of
a weaker check (or a borrowed text that changed after parsing) could
therefore bring ill-formed UTF-8 into it. The copy is checked now, with
the error that dump() reports for the string; copies within the same
document stay unchecked.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Allow the edit arena to grow up to 4 GiB - 1 bytes

append_text() doubled the capacity of the arena and rejected the result
if it exceeded 4 GiB, so that an arena of more than 2 GiB could not grow
even though the 32-bit offsets of nodes address 4 GiB - 1 bytes. The
capacity is now clamped to that limit (text_capacity()), and an append
is rejected only if the bytes themselves do not fit.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Set a null value below a pointer as basic_json does

set(json_pointer, value) turned a null parent into an object for every
last reference token, so "/a/0" and "/a/-" below {"a":null} made
{"a":{"0":1}}, where basic_json's operator[](json_pointer) makes an
array. A null parent now becomes an array for "-" and for digit tokens
(filled with nulls up to the index) and an object otherwise. An invalid
index is reported before the parent changes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Replace a container by a scalar in constant time once it is linked

Replacing an array or object that spans several nodes by a scalar looks
up its parent from the root (find_parent), which is linear in the size
of the document: setting every element of a 20,000 element array took
more than half a second. The parent only has to switch to links once;
afterward the extent of the replaced value no longer matters. Nodes that
an entry of a moved sequence links to are now marked (node_flags::linked),
and assign() skips the lookup for them.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Update the json_view banner and type_error.319 for editable documents

The banner still described a read-only view, and the documentation of
type_error.319 listed set and push_back only, although insert rejects
binary values as well.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Assign the member that lookups find when set() meets duplicate keys

An editable document reads the last member of a repeated key (as the
read-only document does), but set(object, key, value) assigned the first
one: a view taken from object[key] before the call did not show the new
value. It now assigns the last member's value, keeps the key at the
position of its first occurrence (where materialize() puts it), and drops
the other members.

The seeded differential test now also edits documents whose objects
repeat keys and compares every lookup (operator[], at, find, value,
contains, count, and JSON pointers) with the parsed basic_json value.
A targeted test covers duplicates before and after edits that move the
object, in large objects with an index, in moved arrays, and in values
copied from other documents.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Assign the first member when set() meets duplicate keys again

Lookups find the first member of a repeated key again, so set(object, key,
value) assigns that member (keeping the key where it is) and drops the
others, as documented before, and a view taken from object[key] shows the
new value. This undoes the code and documentation changes of 5cbb8e6d6.
Lookups in edited objects (navigation of editable documents) stop at the
first match, like those of parsed objects.

The tests that commit added expect the first member from lookups now. In the
seeded differential test, the documents with repeated keys repeat them after
the real members; the reference holds the first members, which the edits
address and the lookups are compared with, while materialize() is compared
with what parse() makes of the document's dump.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix MSVC C4127 in the lookups of editable json documents

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix clang-tidy 22 findings in the json_view headers

Silence bugprone-casting-through-void for the SSE loads (a reinterpret_cast
would trip -Wcast-align=strict), use auto for a cast initialiser, add
parentheses to a mixed expression, and name bugprone-std-namespace-modification
in the NOLINTs of the tuple_size/tuple_element specialisations.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
(cherry picked from commit 0effbaa7a2)

* Fix clang-tidy 22 findings in unit-json_view_edit.cpp and edit.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Keep a raw string with a backslash out of a CHECK (MSVC C2017)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make json_view.hpp pass include-what-you-use

ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), and the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), plus <version> for
std::nullptr_t, and removing <cstddef> and edit_storage.hpp.

- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
  json_view.hpp only includes json.hpp, so including them here would duplicate
  their definitions
- drop edit_storage.hpp, which edit.hpp includes

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Keep the view's edit headers clean for clang-tidy in C++17 mode

clang-tidy 22 checks the headers with -std=gnu++17 as well, where it asks
for a transparent comparator and std::make_unique. The regions map uses its
default comparator, and the allocation of the edit state keeps new (the
library is C++11) with a NOLINT.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Follow the renames from the review of the view builder

The review of #5620 renamed document_data's tape/tape_size/tape_cap to
nodes/node_count/node_capacity and made the decode arena a
std::vector<char>; adapt the code of this change to it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Expect count() to count every member in the edit tests

Since #5622, count() counts every member with the key, so documents
with duplicate keys no longer give 1.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Count matching keys as unsigned in the json_view edit tests

Like in the access test, adding the int result of the conditional to a
std::size_t fails -Werror=sign-conversion with GCC.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review of erase() in editable json documents

From gregmarr's review of #5630:

- invalid_iterator.202 is thrown by overloads 1 and 2 only; the page said
  "every overload" and then excluded 3 in parentheses.
- The note on links says what moves (the links after the removed element,
  as insert() moves them the other way) and that every iterator over the
  container is invalidated, not only those after the removed element.
- edit.hpp compares keys with document_data::key_equals().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review of editable json documents

From gregmarr's review of #5630:

- set() with a JSON pointer below a null value checks for "-" before
  scanning the token for digits.
- node_flags::storage is defined as escaped | edited, and the comment on
  document_data::base says that the fourth slot is unused.
- source_extent() names its root check.
- The integer-index overloads of set(), insert(), and erase() use a named
  trait instead of a negated condition, which astyle formats like the
  library's other enable_ifs.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the rest of the review of editable json documents

From gregmarr's review of #5630:

- alloc_nodes() gives a request of half of the next chunk or more a chunk
  of its own, so that the rest of the current chunk stays available for
  smaller requests instead of being abandoned.
- The comments on edit_state, append_text(), and reserve_moved() say that
  views stay valid across every edit (not just the first), that n is 0 for
  empty strings, and why both capacities are checked.
- The architecture page names flag bits 3, 4, and 5 (linked) separately
  and says that a discarded view has no node; erase.md rewords overload 3
  and says why it never throws invalid_iterator.202.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Call alloc_nodes() before reading chunks.back() in the json_view chunk test

The operands of == are unsequenced, and MSVC evaluated
d->edits->chunks.back() before alloc_nodes() added the new chunk, so the
check compared the result with the previous chunk and failed on
msvc-arm64 (Debug and Release).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 10:21:29 +02:00
Niels Lohmann 4c3a3eb5b5 Add dump() and comparisons to json_view (#5624)
* Add dump() and comparisons to json_view

Add basic_json_view::dump() and the comparison operators, and read
floats from the parser's digit layout instead of rescanning the
token.

dump(indent, indent_char, ensure_ascii, number_format) writes a
value the way ordered_json::parse(text).dump() writes it for the
same arguments: members in document order, all of them should a
key occur more than once; strings escaped by the same rules, using
the library's scanning kernels; floats written with the library's
to_chars conversion, so the output equals basic_json's byte for
byte; integers copied from the source, where they are already
canonical, except -0, which parse() reads as 0. There is no
error_handler argument, because the view only holds valid UTF-8.
number_format::source copies numbers exactly as they appear in the
source (e.g. "1.50", "1E2", "-0"), which basic_json cannot provide.
operator<< takes the indentation from the stream width, as for
basic_json. The writer walks iteratively, so nesting depth is
limited by memory only.

operator== and operator!= compare two views, or a view and a
basic_json value in either order, by the rules basic_json's
operator== uses: numbers compare by value across their types,
objects compare by their members with duplicate keys resolved as
parse() resolves them, member order matters only where the object
type keeps one, and discarded views compare as discarded basic_json
values do, including under JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON.
Nothing is materialized except single scalars.

While parsing, the view now records where the integer digits, the
fraction digits, and the exponent of a float token are, so floats
and doubles with at most 19 digits are read from that layout with
the library's decimal_to_float() instead of rescanning the token.
Both round correctly, so the values are those of parse(). get<double>(),
materialize(), dump(), and the comparisons all use it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Avoid raw string with escapes inside CHECK macro (MSVC C2017)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Size the dump buffer of a small value by its own extent

The source extent of a value was read from the next node, falling back to the rest of the document when that node held a decoded string. dump() of a small value could thus allocate a buffer as large as the document. Skip a few such nodes, cap the fallback estimate, and shrink a buffer that is much larger than its output.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Hold the documents of the dump and comparison tests in names

root() of a temporary document is deleted: its views would dangle.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not shrink the output of dump()

finish() copied the whole output when the buffer was more than twice as
large as the result (citm dump +11%). The tighter source_extent()
estimate already keeps the buffer of a small value small.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Document and test first-wins lookups next to dump() and ==

dump() writes every member and == resolves duplicate keys as parse()
does, whereas lookups find the first member of a duplicate key.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the clang-tidy 22 findings in json_view and its tests

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Split the dump and comparison tests off unit-json_view.cpp

The MinGW linker of the Windows clang jobs cannot link object files with more
than 32767 sections ("relocation truncated to fit: IMAGE_REL_AMD64_REL32
against `.rdata'"). unit-json_view.cpp reaches that limit as the stack
grows, so its "json_view dump" and "json_view comparison" test cases move
into unit-json_view_dump.cpp. The test generator and has_duplicate_keys()
that both files use move into json_view_test_helpers.hpp.

The new file mentions JSON_HAS_CPP_17, so it is built for C++17 like the file
it was split from.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make json_view.hpp pass include-what-you-use

ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), and <version> for
std::nullptr_t, and removing <cstddef>.

- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
  json_view.hpp only includes json.hpp, so including them here would duplicate
  their definitions

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Follow the renames from the review of the view builder

The review of #5620 renamed document_data's tape/tape_size/tape_cap to
nodes/node_count/node_capacity and made the decode arena a
std::vector<char>; adapt the code of this change to it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Keep the stream includes of json_view.hpp for IWYU

json_view.hpp uses std::istream and std::ostream itself, but IWYU sees
them through the exported json.hpp and asked to remove both includes,
which fails ci_single_binaries (single_json_view).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review of json_view dump() and comparisons

From gregmarr's review of #5624:

- The output buffer takes string literals without a length.
- write_float() names the IEEE single/double condition once, as the
  library's serializer does.
- equal() checks the types first and compares with !=; the reference to
  the top frame is noted as dangling after pop_back().
- dump.md: one paragraph per case in the notes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Exclude all json_view test files from Infer's reports

The same std::unique_ptr false positive (USE_AFTER_DELETE) also shows for
two json_documents parsed in one lambda in unit-json_view_dump.cpp
(ci_infer). It affects any test that holds several documents, so the
exclusion now covers tests/src/unit-json_view*.cpp; ASan and Valgrind
cover these tests.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Allow the string literal parameter of the view serializer's put() for clang-tidy

The overload takes the literal by array reference so that its length is a
template parameter, like scan_literal() and the C array overloads of
to_json() and from_json().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Bind the serializer's frame only after the check for its end

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 10:21:28 +02:00
Niels Lohmann 43e97b629b Add element access, iteration, values, and JSON pointers to json_view (#5622)
* Add element access, iteration, values, and JSON pointers to json_view

Give basic_json_view the read-only access functions of basic_json:
operator[] and at() with keys and indices, front()/back(), find(),
contains(), count(), begin()/end() and cbegin()/cend(), items()
with structured bindings from C++17 on, and type_name().

Exceptions have the ids and messages of the const functions of
basic_json. Where basic_json has undefined behavior the view
answers safely: operator[] with a missing key or an out-of-range
index returns a discarded view, and front()/back() of an empty
container throw invalid_iterator.214. Objects are iterated in
document order, and all members are visited; duplicate-key lookups
find the first member (as yyjson and simdjson do), while parse(),
materialize(), and the map conversions keep the last value, as
parse() does. Keys of up to 16 bytes are compared with two
overlapping loads.

Add value conversions: get<T>()/get_to() for arithmetic types,
bool, nullptr_t, strings (std::basic_string copied,
string_view_t without a copy), BasicJsonType, views, std::vector,
and maps with string keys; get_string() for the string without a
copy; number_token() for the number exactly as written in the
source; value() with keys and JSON pointers; and operator[]/at()/
contains() with JSON pointers. Everything else, including types
with from_json(), goes through materialize() of that subtree.
get<T>() of arithmetic types is inlined down to the conversion, so
reading an integer needs no call.

detail::json_pointer_access exposes a pointer's reference tokens
to code outside basic_json.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make view lookups last-wins and chained access safe

* Lookups (operator[], at, find, contains, count, value, JSON pointers)
  return the last member of a duplicate key, as materialize() and
  parse() keep it.
* operator[] on a discarded view returns a discarded view instead of
  throwing, so v["a"]["b"] is safe for a missing "a".
* operator[] and at() take any integer type (not only int and size_t),
  fixing ambiguous calls with unsigned, long, std::int64_t, ...
* Fix the operator[] documentation, which claimed a discarded view for
  a type mismatch where type_error.305 is thrown.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Replace the removed operator bool of basic_json_view in the header and its tests

A view is tested with is_discarded(); the lookups in at(), value() and
contains(json_pointer) and the unit tests no longer rely on the explicit
conversion to bool.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Use named documents in the json_view tests that called root() on a temporary

root() of an rvalue document is deleted, because the views would dangle.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Test discarded views with is_discarded() in the operator[] docs and examples

The explicit conversion to bool of basic_json_view is gone.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Return the first member of duplicate keys from lookups again

Looking up the last member of a duplicate key cannot stop at a match, so
every lookup scanned the whole object (1.6 to 3.4 times slower for small
objects). operator[](key), at, find, value, contains, count, and JSON
pointer resolution return the first member again, as yyjson and simdjson
do; materialize() and get<map>() keep the last value, as parse().

The documentation says so in the feature page and on each lookup page,
and explains how to get the value parse() would give. The integer index
templates and the discarded chaining of operator[] stay.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not force-inline the view's functions on MSVC

__forceinline makes MSVC report C4714 for function templates it cannot
inline (get<std::string>), an error under /WX. The forced inlining is
only a performance hint, so use plain inline there.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make json_view.hpp pass include-what-you-use

Mark the includes that are needed by design (json.hpp, the macro
unscope, <tuple> for the tuple_size specialization), take size_t from
<cstring>, and drop the forward declaration of basic_json_document that
the friend declaration makes redundant.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Avoid GCC useless-cast warnings in the integer index tests

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make json_view.hpp pass include-what-you-use

ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), and <version> for
std::nullptr_t, and removing <cstddef>.

- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
  json_view.hpp only includes json.hpp, so including them here would duplicate
  their definitions

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the clang-tidy 22 findings in json_view and its tests

The same changes as on the dump branch, where they were first made, so
that this branch passes clang-tidy on its own.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Report one-character non-numeric array indices like basic_json in the view

Develop's #5800 made basic_json report a JSON pointer array index such as
"/a" as parse_error.109 instead of out_of_range.404, as longer ones were;
the view's array_index() follows, with "-" and the empty token still
unresolved.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review of json_view element access

From gregmarr's review of #5622:

- count() counts every member with the key, so count(key) > 1 detects
  duplicate keys; it was contains() as 0/1 before.
- get_to() passes types that get<T>() does not convert directly to
  from_json with the existing value, as basic_json::get_to() does, so that
  a from_json which sets only the fields present keeps the others.
- array_index() checks the empty token and "-" first; the results are
  unchanged.
- Docs: no "if an exception is thrown" where a view never changes
  anything; the borrowed source must also stay unmodified; items() on a
  temporary view is safe; back()/front() notes shortened; the basic_json
  see-also entries say how the view's results can differ.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Count matching keys as unsigned in the json_view access test

The conditional added 1 or 0 as int to a std::size_t, which GCC rejects
with -Werror=sign-conversion (ci_test_standards_gcc).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the second review round of json_view element access

From gregmarr's review of #5622:

- begin()/end(): "a null value or a discarded view iterates as an empty
  range", singular like the primitive case before it.
- The class page's list of function groups has no trailing commas.
- document_data::key_equals() compares a key node with a string, for the
  key comparisons that were spelled out in each lookup.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Write the negated enable_if conditions of json_view with negation<>

With a ! or && in the condition, astyle spaces the whole template
header (template < ... >); detail::negation and a named trait keep it
compact, as the library writes such conditions elsewhere. From gregmarr's
review of #5630.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Keep detail::negation out of json_view.hpp's enable_ifs

include-what-you-use asks json_view.hpp to include
detail/meta/type_traits.hpp for it, which the single header must not do
(the amalgamation would duplicate json's internals). The two conditions of
value() go through a trait in value.hpp, like the others.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 10:21:27 +02:00
Niels Lohmann 122d718330 Add json_document and json_view: node index, parser, and document (#5620)
* Add json_document and json_view: node index, parser, and document

Add json_document and json_view, a read-only, zero-copy index of a
JSON text, as the first public slice of the zero-copy view (#5295).

A parse produces a flat array of 16-byte nodes in document order,
one per value and one per object key. Strings stay in the source
text; escaped strings are decoded into an arena. Integers are
converted while their digits are in the cache; floats keep only
their digit layout and are converted on read. Containers store the
size of their subtree, so a reader can step over one in constant
time. A document makes a handful of allocations, however many
values it has.

The parser accepts exactly what json::parse accepts, with every
combination of ignore_comments and ignore_trailing_commas, with and
without a trailing NUL, and under JSON_STRICT_NUL_HANDLING. It is
portable C++11 and does not depend on byte order.

basic_json_document adds parse, parse_copy, accept, read (reuses a
document's memory), root, is_discarded, source, owns_source,
node_count, memory_usage, and shrink_to_fit. basic_json_view adds
type, the is_* queries, operator bool, size, empty, materialize,
and source_offset. A parse error throws the same exception
basic_json::parse would throw for the same input, message and
position included.

detail::abi_config keeps JSON_STRICT_NUL_HANDLING and
JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON readable after json.hpp
undefines them, in the ABI namespace so they always match the
basic_json in use.

A NUL byte that ends a // comment is the end of the input, as in
parse() since #5696.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix document_data for old Clang (3.4-3.6)

Drop the empty braced NSDMIs of the std::string members: old Clang
rejects the defaulted constructor when it is used by a member
initializer before the end of the class definition.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Exclude libstdc++'s string_view comparison from the sign-change check

basic_string_view::_S_compare stores the difference of two lengths in a
signed difference_type on purpose, which -fsanitize=integer reports for
every comparison with a shorter view (test-json_view_cpp17).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Reject integer lengths in json_document::parse

parse(ptr, len) compiled: len converted to allow_exceptions, and ptr was
read as a C string, past the end of a buffer without a terminating NUL.
Delete the overloads of parse, parse_copy, accept, and read that take an
integer other than bool where the flags are expected.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Copy const rvalue strings and document the accepted inputs

json_document::parse(std::move(const_string)) failed to compile with
"no matching read_kind": only a non-const rvalue std::string can be moved
from. Treat a const rvalue as a copied byte container.

parse() does not accept everything BasicJsonType::parse() does: a FILE*
and pointers to or arrays of wide characters are rejected at compile time.
List the supported inputs instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Remove the conversion to bool from json_view

explicit operator bool meant "refers to a value", which silently differs
from what a basic_json converts to. Use !v.is_discarded() instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Delete json_document::root() on temporary documents

auto v = json_document::parse(text).root() compiled and left the view
dangling. Delete the overload for rvalue documents, take a named document
in the tests, and document the lifetime rule.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Store node integers by halves on big-endian targets

The non-little-endian path of the node writer wrote the integer's native
word over len and next, so len got the high half there. Compose and split
the value explicitly (len is the low half, next the high half); the
little-endian path stays a plain memcpy.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Check the node array size for overflow

reserve(n) and the growth of the index computed n * sizeof(node) without a
check, which wraps around on 32-bit targets for inputs of about 1 GiB and
allocates a too small array. Throw std::bad_alloc for a count beyond the
address space and clamp the growth step to it.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* State the exact input size limit of json_document

The check rejects inputs of 0xFFFFFFF0 bytes or more, but the exception
message and the documentation said 4 GiB. Name the limit once
(max_input_size), and state 4 GiB minus 16 bytes in the message and the
documentation. Test the limit with a container that only claims the size.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Remove the unused legacy_discarded_value_comparison from abi_config

Nothing reads it: the view does not compare values.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Align the banner comment of json_view.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fuzz json_document with exact-size buffers and parse options

The fuzzer only parsed a std::string with default options. Also parse an
exact-size byte vector, which has no NUL after its last byte and takes the
bounds-checked path, and derive ignore_comments and ignore_trailing_commas
from the first input byte, comparing against json::parse and json::accept
with the same options.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Give document_data a user-provided constructor

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix clang-tidy findings in unit-json_view_builder.cpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Annotate intentional patterns in unit-json_view.cpp for clang-tidy

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Suppress Flawfinder's read() finding on the deleted overload

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Check the steps of the deep-nesting test separately

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make max_input_size a function (GCC -Wunused-const-variable)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Make json_view.hpp pass include-what-you-use

ci_single_binaries runs IWYU with --error on every header. For json_view.hpp
it suggested adding <array> (std::array is used), the headers that json.hpp
already provides (abi_config, abi_macros, input_adapters, json_pointer,
cpp_future, string_concat, value_t, json_fwd), and <version> for
std::nullptr_t, and removing <cstddef>.

- include <array>
- keep <cstddef> (nullptr_t, size_t; IWYU attributes them to <version> and <cstring>)
- tell IWYU not to suggest the headers that json.hpp provides: the amalgamated
  json_view.hpp only includes json.hpp, so including them here would duplicate
  their definitions
- export json.hpp, keep macro_unscope.hpp, and drop the unused forward declaration of the document class

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Read the node array base after emit() in the view builder's open()

emit() moves the node array when it grows, and the subtraction read base
in the same expression, so the order was unspecified. MSVC Release builds
without forced inlining read the old base; the container index then
pointed outside the array and close() wrote out of bounds.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Silence a clang-tidy use-after-move finding in the json_view test

const_text is const, so std::move does not move from it; the second call
is part of the const rvalue test.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Regenerate the amalgamated headers

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review of the view builder and document

Changes promised in the replies to the review of #5620:

- read() reuses the owned copy: a copied input (an rvalue byte container,
  a stream, FILE*, wide characters, an iterator range) is appended to the
  cleared owned buffer instead of a fresh std::string being move-assigned
  over it. A moved-in std::string still replaces it.
- The error replay runs the library parser with a SAX handler that stores
  nothing and throws the reported exception, instead of
  BasicJsonType::parse(), which built a DOM up to the error.
- Pointers to and arrays of wide characters, and FILE*, take the input
  adapter path (converted to UTF-8 into an owned buffer) instead of
  failing a static_assert. Wide pointers are NUL-terminated.
- Streams are read in blocks through rdbuf() into the buffer, instead of
  character by character through the input adapter; eofbit is set at the
  end, as by the library's stream adapter.
- tape/tape_size/tape_cap are now nodes/node_count/node_capacity.
- The builder's members get descriptive, m_-prefixed names.
- The shallow frame stack is a std::array (in a wrapper with a
  user-provided constructor: -Weffc++ wants the member initialized, and
  value-initializing the 64 frames costs up to 30 % on small documents).
- The value dispatch macro has no NEXT parameter; the switch is left with
  break, and the macro is undefined after its last use.
- literal() and digits_exceed() deduce the length from the string literal;
  each literal has one helper; the two `if (!is_float)` blocks are merged.
- arena_ensure() sits right before the writes; the decode arena is a
  std::vector<char>.
- The builder records the deepest nesting, and materialize() sizes its
  stack from it once (growing it only if a subtree is deeper).
- Comment fixes (hex4, slow_string, the NUL rule, put_utf8, 0 - m, the
  arena resize, grow()) and formatting (macro backslashes, digit cases,
  one power of ten per line, value_t assertion next to is_container()).
- build_with's template parameters follow the builder's order.

Greg's idea of keeping the source offset in `off` and the arena offset in
`next` for escaped strings was measured and not adopted: string access got
7-18 % slower with a select, and 3.5-18 % slower with a branch-free field
pick (Apple M-series, clang -O3), parsing was unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Apply the review's wording fixes to the json_view documentation

- Copying is disabled because of its cost, not because of two documents
  borrowing the same buffer; the unique_ptr follows from move-only.
- Inputs: streams, FILE*, and wide characters (strings, arrays, pointers)
  are all supported and owned; point to parse_copy() for owned copies.
- The error replay only runs for failing input, builds no values, and is
  skipped with allow_exceptions = false.
- read() writes copied inputs into the previous owned copy.
- Views can dangle like std::string_view, and why there is no
  shared/weak pointer design.
- The 4 GiB limit (16-byte nodes, 16-byte scanner steps), streams being
  read to the end first, which macros apply, why materialize() has no
  source positions, and wording nits (accept() vs basic_json::accept(),
  is_binary, is_discarded, see-also lists, size table, "back into").

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Say that json_view converts integers while parsing

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address the review: null inline nodes, trait spelling, and max() in json_view

- inline_nodes is null when the document has no inline nodes, instead of
  pointing past the end of the allocation; shrink_to_fit() no longer
  passes it to memcpy() for an empty node array
- is_byte_char and is_wide_char use conjunction/disjunction, which
  astyle leaves unspaced
- append_stream() uses (std::max) for the count read

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Include materialize.hpp in the view builder test

The test calls detail::view::materialize(), which only the single header
pulled in; the multiple-header build (JSON_TEST_USING_MULTIPLE_HEADERS)
failed with "'materialize' is not a member of detail::view".

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Rename emit() in the view builder for Qt compatibility

Qt defines emit as an empty macro, so a Qt header included before
json_view.hpp broke the declaration of the builder's emit(), see
issue #5865. It is now called push_node().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not copy an empty run into an unallocated arena

memcpy() must not be passed a null pointer even for zero bytes, which
arena_run() did for an empty run while the arena was still empty
(ci_test_clang_sanitizer: "null pointer passed as argument 1" in
builder.hpp). Only the slow path can see such a run.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Exclude the view builder test from Infer's reports

Infer's std::unique_ptr model confuses the values that the test's build()
helper returns by value and reports USE_AFTER_DELETE for one of them after
the destructor of an unrelated temporary (ci_infer, 12 reports, all in
tests/src/unit-json_view_builder.cpp). AddressSanitizer is clean on the
same tests.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Skip the deleted-overload checks of json_view on MSVC 2015

MSVC 2015 does not treat selecting a deleted function in decltype as a
substitution failure, so is_detected reports the deleted
parse/parse_copy/accept/read(ptr, len) overloads as callable and the
static_asserts in "integer arguments do not compile" fail (AppVeyor
VS2015 jobs). Calling them still fails to compile there. Guard the
negative checks as unit-delete_deprecated_functions.cpp does.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 10:21:27 +02:00
satelliteprogrammer 40a62947d3 support JSON Merge Patch (RFC 7396) diff creation (#4965)
* support JSON Merge Patch (RFC 7396) diff creation

The JSON merge patch document format describes the set of modifications
to a resource's content, that more closely mimics the syntax of the
resource being modified.

However, and in contrast to JSON Patch (RFC 6902), a JSON Merge Patch
cannot express certain modifications, e.g., changing an array element
at a specific index, or setting a specific object value to null.
The null value in a JSON Merge Patch is used to remove the key from the
object.

The diff algorithm is not part of the RFC 7396, but it was tested
against all examples provided, plus additional cases on how null values
are handled.

Signed-off-by: Luís Murta <luis@murta.dev>

* improve algorithm efficiency

Signed-off-by: Luís Murta <luis@murta.dev>

* fix inconsistent nulls and empty arrays

Also add CHECKs for the patch, documentation and run `make amalgamate`.

Signed-off-by: Luís Murta <luis@murta.dev>

* documentation cleanups

Signed-off-by: Luís Murta <luis@murta.dev>

* add an iterative fallback to merge_diff

Bound the recursion depth like diff() and merge_patch(), and test values
nested past the bound.

Signed-off-by: Luís Murta <luis@murta.dev>

* address the remaining review comments

Signed-off-by: Luís Murta <luis@murta.dev>

* move multi-line raw strings out of CHECK macros

GCC 4.8/5, ICPC and NVHPC mis-tokenize multi-line raw string literals
inside macro arguments, breaking the build or the parsed JSON.

Signed-off-by: Luís Murta <luis@murta.dev>

---------

Signed-off-by: Luís Murta <luis@murta.dev>
2026-10-12 09:33:43 +02:00
Niels Lohmann d540750f21 Use the with_*_t aliases in the remaining tests and docs (#5790)
#5787 missed the instantiations spelled as `basic_json <` (astyle's
formatting) or ending in the CustomBaseClass argument. Convert them,
including the binary_t.md example pointed out in review.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-09 16:56:14 +02:00
Niels Lohmann d8dfc0d0f9 Fix unused-result warnings in the contains and parse_error examples (#5788)
contains(json_pointer) is marked JSON_HEDLEY_WARN_UNUSED_RESULT since #5477,
and parse() has been for longer. The contains example ignored the result in
two try blocks waiting for a parse_error that contains() never throws, so
they printed nothing; print the result for those pointers instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-08 16:21:59 +02:00
Niels Lohmann 3d7f554927 Use the with_*_t aliases in tests, examples, and docs (#5787)
* Use the with_*_t aliases in tests, examples, and docs

Replace spelled-out basic_json<...> instantiations that only change one
or two template parameters with nlohmann::json::with_*_t (or
ordered_json::with_*_t when the object type is ordered_map). Types that
change all three number types chain with_integers_t and with_float_t.

The raw basic_json<...> spelling stays where the template parameter
list itself is the subject: the alias tests in unit-udt.cpp, explicit
instantiations, and the ordered_json/compile-time docs.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix unit-large_json for clang and JSON_DIAGNOSTICS

Two test problems from #5781 broke CI on develop: CAPTURE(depth); trips
clang's -Wextra-semi-stmt, and the type_error.321 messages did not
account for the diagnostics path prefix.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Use static_cast in unit-hash for clang-tidy

#5772 added functional casts that clang-tidy reports as C-style casts
(google-readability-casting). Also append a char instead of a
one-character string in unit-large_json.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Declare the expected message prefix const in unit-large_json

Without JSON_DIAGNOSTICS the prefix was never modified, which
clang-tidy reports (misc-const-correctness).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-08 16:11:16 +02:00
Niels LohmannandAfonso Januário a5f5d3059b Make std::hash<basic_json> consistent with operator== for numbers (#5772)
* Make std::hash<basic_json> consistent with operator== for numbers

operator== converts between number_integer, number_unsigned, and
number_float before comparing, so json(0), json(0U), and json(0.0)
all compare equal. hash() folded the specific value_t into the
result for each of the three numeric cases, giving each a distinct
hash and breaking the standard Hash requirement that a == b implies
hash(a) == hash(b). A std::unordered_set could therefore hold all
three as separate elements even though they compare equal.

hash() now treats all three numeric variants the same way: it
converts the value to number_float_t and combines it with a single
shared type tag, so any two numbers operator== considers equal hash
identically regardless of which internal type actually holds them.

Updated the accompanying test to check this consistency directly
(including via an actual unordered_set) instead of asserting that 0,
0U, and 0.0 hash differently, since that assumption was the bug.
Also corrected the function's own doc comment and the std::hash API
docs, which described the old behavior as intended.

Fixes #5400

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Remove now-unused number_integer_t/number_unsigned_t typedefs in hash()

Merging the three numeric branches into one that only reads
number_float_t left these two aliases unused, which several CI
configurations treat as a build error under -Wunused-local-typedefs.

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Mark the unordered_set in the hash regression test const

clang-tidy's misc-const-correctness check flagged it: the set is
never mutated after construction, only read via size().

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Normalize -0.0 in number hashes and test range ends

operator== compares numbers exactly since #5459, so equal numbers
share one value and convert to the same number_float_t. Update the
comment accordingly, map -0.0 to 0.0 before hashing (std::hash need
not do that), and test -0.0 and the ends of the integer ranges. Show
hash(0.0) in the docs example and note the change in the version
history.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Clarify hash documentation after review

- Say "may hash differently" for null, false, and numbers, since a
  collision across types is possible.
- Name the storage types (signed integer, unsigned integer,
  floating-point number) instead of example literals.
- Explain that the hash survives converting an integer to
  number_float_t but not the lossy conversion back, and that unequal
  numbers may share a hash.
- State that the example hash values are illustrative only and vary by
  platform, compiler, compiler version, and library version.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Afonso Januário <afonso-januario@hotmail.com>
2026-10-07 19:18:51 +02:00
162e13b86f Add with_*_t alias templates to create basic_json types with changed template parameters (#5758)
* Add helper types to make it easier to create a basic_json type with modified template parameters

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Rename with_changed_*_t aliases to with_*_t and merge integer/unsigned aliases

Per review discussion on #3898 between gregmarr and nlohmann:
- rename with_changed_X_t to with_X_t for brevity
- replace the separate with_changed_integer_t/with_changed_unsigned_t
  aliases with a single with_integers_t<NumberIntegerType2, NumberUnsignedType2>
- add @sa doc comment links for the upcoming documentation page

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Add documentation for the with_*_t member alias templates

Add docs/mkdocs/docs/api/basic_json/with_t.md documenting with_object_t,
with_array_t, with_string_t, with_boolean_t, with_integers_t, with_float_t,
with_allocator_t, with_json_serializer_t, with_binary_t and with_base_class_t,
with an accompanying example, and link the page from the basic_json member
types list and the mkdocs navigation.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Add tests for the with_*_t member alias templates

Check with std::is_same that each with_*_t alias produces the expected
basic_json type, and that with_string_t keeps nlohmann::ordered_map as
the object type when used on ordered_json.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix with_t nav entry and document chaining of the with_*_t aliases

Indent the with_t entry in mkdocs.yml so it is listed under basic_json,
explain that the aliases can be chained and work on ordered_json, and
test both, including json::with_object_t<ordered_map> == ordered_json.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Add docset entry for basic_json::with_t

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: barcode <barcode@example.com>
Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
2026-10-06 22:33:29 +02:00
Niels Lohmann 73e9eae3c1 Add an error_handler parameter for UTF-8 to the binary readers and writers (#5746)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-04 12:13:49 +02:00
Niels LohmannandRaphael Grimm 40021f38fb Add basic_json::as_base_class and document name conflicts with custom base classes (#5589)
* Add basic_json::as_base_class and document name conflicts with custom base classes

Members of basic_json hide members of a custom base class with the same
name, and future releases may add members that hide ones accessible
today. Document this in json_base_class_t and add as_base_class() to
reach hidden members without spelling out the cast.

Also make json_base_class_t a public member type. It was documented
since 3.12.0, but declared private, so users could not name it.

Supersedes #3899.

Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Add as_base_class to the docset search index

New public members get an entry in docs/docset/docSet.sql (as done for
to_bon8/from_bon8 in #2998). Without it, the Dash/Zeal docset built from
the documentation cannot find basic_json::as_base_class.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Silence clang-tidy for the hidden type_name() in the base class test

ci_clang_tidy failed with readability-convert-member-functions-to-static
on base_class_with_hidden_members::type_name(). It must stay a
non-static member: the test shows that it is hidden by the non-static
basic_json::type_name() and reachable through as_base_class().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Raphael Grimm <1005058+barcode@users.noreply.github.com>
2026-10-04 12:00:31 +02:00
Niels Lohmann 63c10a51fc Review and extend the documentation, and check it in CI (#5638)
* Review and extend the documentation, and check it in CI

A review of all documentation pages found factual errors, dead links,
missing cross-references, and gaps in examples. This fixes them and adds
checks so the same problems are caught automatically.

Fixes:
- wrong signatures and version histories (operator!= C++20 member,
  binary() subtype type, get<PointerType>(), JSON_NO_THREAD_LOCAL, ...)
- stale descriptions (number parsing since #5283, UBJSON table, SAX
  example that no longer compiled, tsl::ordered_map advice)
- dead internal and external links; repology.org badges (the domain is
  suspended) replaced by badges that query the registries directly
- deprecation notes link the migration guide; the guide itself fixed

Additions:
- "See also" sections, cross-references, 25 runnable examples, 12
  Mermaid diagrams, new API pages for json_pointer::operator<=> and
  byte_container_with_subtype::operator==/!=
- landing page, guides for untrusted input and performance
- "unreleased" badge after versions newer than the latest release

Checks:
- strict documentation build (broken links/anchors fail it); CI and
  the publish workflow fetch the full history the build needs
- weekly external link check, Mermaid syntax check in CI
- check_structure.py: example titles, heading levels, alt texts,
  header links, docset index coverage; its unused-example check works
  again
- all examples produce the same output on every platform

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Keep the customer links that could not be fixed

A dead link on the customers page is still the evidence of where the
use of the library was documented. Keep the original URLs of the entries
without a working replacement (Marne, Cisco Webex Desk Camera, Philips
Hue, CyberArk) and exclude exactly these URLs from the link check.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Correct the duplicate-key recipe's claim about SAX positions

The SAX interface's key() receives no position either; only parse_error()
does. Also note that the recipe does not report the path to the repeated
key (see discussion #5085).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Say the library is available as a single header and mention json_fwd.hpp

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Correct documentation errors found while hunting for bugs

- patch/patch_inplace: list the JSON pointer errors parse_error.106-109
  and out_of_range.402/404, and quote the actual parse_error.105 message.
- unflatten: list parse_error.106/107/108 and out_of_range.404.
- to_bson: list out_of_range.415 (binary subtype above 255) and note
  that 412 and 415 are new in 3.13.0.
- to_string: state that string_t must be convertible to std::string, also
  in the StringType requirements table.
- JSON Lines: a `while (input >> j)` loop also throws after the last value
  for concatenated JSON values; show a loop that works for both.
- BON8: a string gets 0xFF only if nothing follows it in the message; a
  string at the end of an array or object is ended by 0xFE.
- custom_string_type.hpp: add operator+=(char), which the "Always
  required" list asks for (json_pointer::to_string, flatten, unflatten,
  and diff did not compile), and an ADL int_to_string for diff and items.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Cache the release headers with functools.lru_cache

Codacy (Pylint) flagged the mutable default argument that header() used
as its cache. functools.lru_cache keeps the same memoization without it.
The script's output is unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-02 11:32:15 +02:00
Niels Lohmann 791cd88dfc Fix raw-TeX formulas and other documentation infrastructure debt (#5736)
* Fix math formulas rendering as raw TeX in the published docs

The privacy plugin self-hosts MathJax 2.7.0 but drops its
?config=TeX-MML-AM_CHTML query string, so the rehosted script loads no
input jax and the 10 formulas across 6 pages render as raw TeX to
readers. Remove pymdownx.arithmatex and the MathJax extra_javascript
entry, and rewrite the formulas in plain HTML (<sup>, <i>) instead.
This also drops a nine-year-old third-party script from every page.

Part of #5718

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix publish_documentation triggers and persist unneeded git credentials

publish_documentation.yml only triggered on docs/mkdocs/** pushes, but
the site also embeds .github/CODE_OF_CONDUCT.md, CONTRIBUTING.md,
SECURITY.md, cmake/{clang,gcc}_flags.cmake, .clang-tidy,
tools/astyle/.astylerc and tests/fmt_formatter/project/main.cpp via
pymdownx.snippets, so changes to those files never republished the
site. Extend the path filter to cover them, and switch runs-on from
the long-pinned ubuntu-22.04 to ubuntu-latest to match
ci_test_documentation.

Also add persist-credentials: false to the checkouts in ci_icpx and
ci_nvhpc (ubuntu.yml) and msvc-vs2026/msvc-arm64 (windows.yml), none
of which pushes with git, matching every other checkout in these
workflows.

Part of #5718

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix example build: broken debug echo, deprecations hidden for everything

docs/Makefile's debug echo used a space instead of a comma in
$(call cxx_standard ...), so it always printed an empty standard.
Every example was also compiled with -Wno-deprecated-declarations,
which would silently hide an accidental deprecated-API call in any of
them. Factor the duplicated compile flags into EXAMPLE_CPPFLAGS/
EXAMPLE_WARNFLAGS, build with -Werror=deprecated-declarations by
default, and only allow the three examples that intentionally
document deprecated API (the DEPRECATED_EXAMPLES list) to suppress it.

Part of #5718

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix check_structure.py NOLINT parsing and an off-by-one report line

`line.strip("<!-- NOLINT")` followed by `.strip(" -->")` strips any of
the characters in those sets from both ends, not a literal prefix; it
only happened to work for "Examples". A NOLINT'd section name
starting with N, O, L, I or T (e.g. "Notes", "Template parameters",
"Iterator invalidation", "Literals") was silently mangled, so the
suppression did not apply and the checker could report a spurious
missing/misordered section. Parse the comment with a regex instead.
The same fragile strip() pattern was used for heading text; replace it
with a plain prefix slice. Also fix the admonition_title report, which
used the 0-based line index while every other report in the file uses
lineno+1.

Part of #5718

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix docset README's non-existent make target and stale fallback URL

The README told readers to run `make nlohmann_json.docset`, but the
Makefile's targets are `all`, `JSON_for_Modern_C++.docset` and
`install_docset_zeal`; the documented command has failed with "No
rule to make target" since #2967 (2021). Point the README at the real
target and folder name. Info.plist's DashDocSetFallbackURL also still
pointed at the old nlohmann.github.io/json/ URL instead of the
canonical https://json.nlohmann.me/ from mkdocs.yml's site_url.

Leave list_missing_pages/list_removed_paths alone: they may become
redundant once #5638's check_docset() lands, which is a follow-up.

Part of #5718

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Remove two leftover Doxygen-era .link files from the examples directory

parse__iterator_pair.link and parse__pointers.link each held only a
Wandbox "online" permalink from the old Doxygen docs. #3071 deleted
every other .link file in 2021; these two came in through a parallel
PR (#3100) and were never referenced by any page, script or config.

Part of #5718

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix MacPorts CMake example to include its own snippet files

The MacPorts "Example: CMake" block included
integration/homebrew/example.cpp and
integration/homebrew/CMakeLists.txt instead of the MacPorts files
right next to it, a copy-paste slip from the Homebrew section. Nothing
referenced integration/macports/CMakeLists.txt as a result. The page
rendered correctly only because the homebrew, macports and
vcpkg/CMakeLists.txt snippets are byte-identical, so a future edit to
the MacPorts files would not have shown up on the page.

Part of #5718 item 6

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not persist git credentials in publish_documentation's checkout

The checkout step in publish_documentation.yml left the default
persist-credentials: true, so GITHUB_TOKEN stayed writable in
.git/config for the rest of the job (zizmor's artipacked finding). The
Deploy documentation step authenticates through its own github_token
input to peaceiris/actions-gh-pages and does not push with the
checked-out credentials, so persist-credentials: false is safe here,
matching every other checkout in the workflow set.

Overlaps #5638, which edits this same checkout step (adds
fetch-depth: 0); expect a rebase conflict there.

Part of #5718 item 2

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Replace list_missing_pages/list_removed_paths with a comm(1)-based diff

The docset Makefile's list_missing_pages ran one sqlite3 query per
mkdocs page, and list_removed_paths nested a loop over all mkdocs pages
inside a loop over all docset index paths (O(n*m) shell iteration).
Issue #5718 item 5 suggested removing or reducing these targets once
#5638's check_docset() lands, but that PR is still open and covers only
API pages and macros, not the full page set these targets check.
Replace the loops with two sorted path lists (DOCSET_PAGE_PATHS from
mkdocs' markdown sources, DOCSET_INDEX_PATHS from the built docset
index) compared with a single comm(1) call each, verified to produce
output identical to the old loops against the current docSet.dsidx.

The sed expression used '#' as its delimiter, which GNU Make reads as
a comment character even inside a variable assignment, truncating the
line and orphaning the closing paren of $(shell ...) ("unterminated
call to function 'shell': missing ')'"). Use '@' as the delimiter
instead.

Part of #5718 item 5

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 22:15:20 +02:00
Niels Lohmann 1e101ecac1 Add BON8 support (#2998)
* Add BON8 support

Add to_bon8/from_bon8 and input_format_t::bon8 for BON8, a binary format
that uses the byte values that cannot begin a UTF-8 character as type
markers, so strings need no length prefix. It is the most compact of the
supported binary formats on the benchmark files.

The reader is non-recursive like the other binary readers. A string ends
at the first byte that cannot continue it, so the reader hands the one or
two bytes it reads past a string back to the value that follows. The
writer produces the canonical representation of the specification, except
for NFC normalization; its output is identical to that of the reference
implementation (HikoGUI) on all files of the test data.

The round-trip tests need the .bon8 files of json_test_data 3.2.0.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address review comments

- Reuse detail::validate_one_utf8 to check strings in to_bon8; the error
  now names the first byte of the invalid sequence.
- Document that to_bon8 leaves bytes in the output adapter on an
  exception, and that string_open is only an output of write_bon8_marker.
- Explain why the pushback buffer of the BON8 reader cannot overflow.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Select the BON8 float prefix by type

get_bon8_float_prefix only depends on the type of its argument, so make
the type a template parameter instead of passing an unused value.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Rename a test variable that Flawfinder mistakes for read()

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the BON8 CI failures

- compare the float in write_bon8_float with number_float_t constants,
  so GCC does not warn about a float-to-double conversion
- mark check_bon8_utf8's context as used when exceptions are disabled
- choose the compact float prefix in a helper rather than with nested
  conditional operators (clang-tidy)
- use auto for the cast in the BON8 integer reader (clang-tidy)
- write the int32 minimum test values as long long literals (MSVC C4146)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Amalgamate

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Read BON8 strings in bulk from contiguous input

- copy the valid UTF-8 of a string in one step when the input is
  contiguous (twitter.json is read in 1.68 instead of 2.52 ms,
  jeopardy.json in 196 instead of 297 ms, close to CBOR and MessagePack)
- share the new valid_utf8_prefix() with the writer's UTF-8 check, which
  now skips ASCII 8 bytes at a time
- let the fuzzer check that contiguous and stream input give the same
  value or error, and test both paths in the unit tests
- clarify that a second 0xFF after a string is an empty string

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Link the BON8 functions from the other binary format pages

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Name the bulk scan flag after the input, not BON8

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Read BSON keys in bulk from contiguous input

BSON keys (and array indices) are C-style strings, which were read byte
by byte. For contiguous input they are now read up to their \x00-byte in
one step, using the same bulk_scan flag as BON8 strings: twitter.json is
read in 1.46 instead of 2.01 ms, citm_catalog.json in 2.93 instead of
3.33 ms, jeopardy.json in 182 instead of 207 ms. canada.json, whose keys
are almost all one-digit array indices, takes 2 % longer.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix the BON8 CI failures of the bulk-read tests

- skip the contiguous-versus-stream tests of BON8 strings and BSON keys
  when exceptions are disabled: they catch the parse errors of invalid
  input, and without exceptions the library aborts instead
- use static_cast for the int64 test value (google-readability-casting)

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Move the explicit basic_json instantiation into its own test file

Linking test-regression3_cpp20 with clang and MinGW failed with
"relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'",
as test-regression2 did before #5511. The explicit instantiation of
basic_json<> for #4825 compiles every member function, including the
BON8 reader and writer, into that object, and it was already close to
the limit (2,226,104 bytes on develop, 2,234,960 with BON8; clang -O1,
C++20).

Give the instantiation a file of its own: unit-regression3 is now
1,594,736 bytes and unit-explicit_instantiation 1,095,064. The new file
mentions JSON_HAS_CPP_17 and JSON_HAS_CPP_20 so it keeps being built
for the C++17 standard the regression was about.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Convert the bytes of the BON8 test strings explicitly

The str() helper constructed a std::string from a byte range, which
converts each unsigned char implicitly; -fsanitize=integer reports that
for bytes of 0x80 and above (ci_test_clang_sanitizer).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 16:56:21 +02:00
Niels LohmannandClaude Opus 5 c72f37a40d Support custom object/array types and improve template parameter handling (#5443)
* docs: document the implicit requirements on basic_json's template parameters

The requirements that basic_json places on its eleven template parameters
were only implied by how the library uses the resulting object_t, array_t,
string_t, etc. Consumers had to discover them by trial and error.

Add "Template Parameter Requirements" collecting them, split into what is
always required and what is only required when a particular part of the API
is instantiated. Notable findings that were previously undocumented:

- ObjectType must provide a key_compare member type (actual_object_comparator
  names object_t::key_compare in both arms of a std::conditional), and its
  third template parameter is used as a comparator, so std::unordered_map
  cannot be used without a wrapper.
- ArrayType must provide capacity() -- push_back(), emplace_back(),
  operator+=(), and operator[](size_type) call it unconditionally -- and
  needs random-access iterators, so std::deque and std::list do not work.
- StringType needs contiguous, null-terminated data(), a one-byte value_type,
  and either assignability from std::to_string or an ADL int_to_string().
- NumberFloatType must be float, double, or long double for parsing and
  serialization; the integer types must satisfy std::is_integral.
- AllocatorType must be stateless, support incomplete types, and use plain
  pointers.
- BooleanType and the number types are union members and must be trivial.

Link the new page from the basic_json overview, the types feature page, and
the individual type alias pages, and correct the container examples given for
ObjectType (std::unordered_map) and ArrayType (std::list), which do not work.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix object_comparator_t for object types without key_compare

detail::actual_object_comparator selected between object_t::key_compare and
default_object_comparator_t with std::conditional. Both type arguments of
std::conditional are named eagerly, so object_t::key_compare had to exist
regardless of the condition, and the has_key_compare guard added in 3.11.0
never took effect: any ObjectType without a key_compare member type failed to
compile while instantiating basic_json itself.

Use detected_or_t instead, which resolves through a SFINAE partial
specialization and only names object_t::key_compare when it exists. The
selected type is unchanged for every object type that compiled before, so
object_comparator_t -- a public member type -- keeps its meaning and ABI.

has_key_compare had no other users and is removed.

Add a regression test using an adapter around std::unordered_map, which has no
key_compare; it fails to compile without this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: list the types that are known to work for each template parameter

Follow up on the template parameter requirements page: state, for every
template parameter, which concrete types work and where they stop working.
Each entry was verified by compiling and running a common workload (DOM
access, dump, parse, CBOR/MessagePack round-trip, flatten, hash) against that
instantiation.

Findings worth calling out:

- ObjectType no longer needs a key_compare member type, so the std::unordered_map
  adapter only has to restore the template argument order. A hash-ordered
  ObjectType works everywhere except unflatten(), which reconstructs an array
  only when it meets the reference token 0 before the other indices.
- ArrayType: std::deque works when wrapped to add capacity(); std::list does not.
- StringType: std::pmr::string and std::basic_string with a custom allocator
  compile for the DOM, dump, and parse, but not for the binary readers, flatten,
  or diff, because the library assigns std::string values to string_t and
  int_to_string cannot be overloaded for a type in namespace std.
- NumberFloatType: long double works for dump and parse but not for the binary
  formats, which have no encoding for it.
- BinaryType: std::vector<std::byte> supports assignment, get, and the binary
  formats, but neither dump nor std::hash<basic_json>.

Also record the object_comparator_t fix in its version history.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix unflatten and binary dumping for non-default configurations

unflatten() decided between array and object by looking at the first reference
token it happened to see for a node: it started an array only when that token
was 0. With a sorted object type the token 0 always arrives first, so the
result was correct by accident; with an object type whose iteration order is
unspecified, {"/c/2":3,"/c/1":2,"/c/0":1} unflattened to an object with the
keys "0", "1", and "2" instead of an array.

Collect the pointer prefixes that have a reference token 0 among their children
before building the result, and let get_and_create() consult that set. The
outcome is now independent of the iteration order and matches, for every input,
what a sorted object type produced before: a value is restored as an array if
and only if one of its keys is 0. Iterating the flattened object in a different
order would have been simpler, but it would have changed the key order of the
result for insertion-ordered object types.

The serializer, std::hash, and the UBJSON writer converted the elements of a
binary value to an integer implicitly, which does not compile for a BinaryType
whose value type is std::byte, and which made dump() write the bytes of a
signed value type as negative numbers. Convert to std::uint8_t explicitly in
all three places, so every byte type dumps as 0..255. The default
std::vector<std::uint8_t> configuration is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: note which Abseil containers can be used as template arguments

Checked against Abseil release 20250127.0 with the same workload as the other
entries on the page (DOM access, dump, parse, CBOR/MessagePack/UBJSON
round-trip, flatten, hash), with and without JSON_DIAGNOSTICS.

absl::flat_hash_map and absl::node_hash_map work as ObjectType through an
adapter that restores the template argument order and makes erase(iterator)
return the following iterator, which Abseil's returns as void. The page now
carries that adapter, and notes that absl::flat_hash_map does not keep
references to the mapped values valid across insertions while
absl::node_hash_map does. Both have a capacity() member, so JSON_DIAGNOSTICS
already refreshes the parent pointers conservatively for them.

absl::btree_map and absl::InlinedVector cannot be used at all: object_t and
array_t are formed while basic_json is still incomplete, and both inspect
their value type at class scope. std::map and std::vector are required by the
standard to tolerate this, third-party containers generally are not, so the
page states the constraint on its own rather than only per container.

absl::InlinedVector does work as BinaryType, where it is instantiated with a
complete type. absl::FixedArray and absl::Cord are not usable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Relax the ArrayType and ObjectType requirements

Two requirements forced users of otherwise suitable containers to write a
wrapper, and neither was load-bearing.

array_t::capacity() was read in push_back(), emplace_back(), operator+=(), and
operator[](size_type), but set_parent() only looks at the value under
JSON_DIAGNOSTICS; without diagnostics it was computed and discarded. Read it
through array_capacity(), which reports unknown_size() when diagnostics are off
or when the array type has no capacity() at all, and treat an unknown capacity
as "the elements may have moved" so the parent pointers are refreshed
conservatively. std::deque now works as ArrayType, in both builds, and
capacity() is no longer named at all in a default build. Since the capacity is
now only meaningful for array insertions, it moves out of set_parent() into
set_parent_after_array_insert().

basic_json::erase(iterator) assigned the object's erase() return value, which
requires the container to return the following iterator. Abseil's hash maps
return void to avoid computing a successor the caller may not need. Detect that
and compute the successor before erasing; containers that return an iterator,
including the vector-backed ordered_map where a precomputed successor would be
wrong, keep the existing path.

Together these leave an Abseil hash map needing only an alias that restores the
template argument order, and no adapter at all for std::deque.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not require string_t to be convertible from std::string

Three places built a std::string and handed it to something expecting a
string_t: the UBJSON high-precision number reader, which every binary reader
instantiates, and the BSON writer's array element size calculation and write.
That silently required string_t to be implicitly convertible from std::string,
which std::string itself and types with a string_view conversion satisfy, but
many string types do not.

Construct the string_t explicitly from the data and size, which the
requirements already cover. This makes boost::container::string, eastl::string,
std::pmr::string, and std::basic_string with a custom allocator work as
StringType, none of which could previously be used with any binary format.

Add binary format coverage to the alt_string test, which had none, including a
UBJSON high-precision number -- the case that goes through the reader path.
BSON stays uncovered there: it additionally needs string_t::find(value_type),
which alt_string does not provide.

Also record which containers from Boost, Abseil, and EASTL work for each
template parameter, and correct two claims: std::pmr::string is usable after
this change, and tsl::ordered_map is not usable at all, because its iterators
expose the mapped value as const while basic_json modifies it in place.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: record compatibility for the common header-only hash maps

ankerl::unordered_dense (map and segmented_map), phmap (flat_hash_map and
node_hash_map), and robin_hood::unordered_flat_map all work as ObjectType
through the same adapter as Abseil's and Boost's hash maps, which only has to
restore the template argument order.

phmap::btree_map and robin_hood::unordered_node_map do not: like the other
btree containers they require a complete value type.

Note that none of these hash maps defines key_compare, so every one of them
depends on object_comparator_t falling back to default_object_comparator_t.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: record Folly and the remaining vector replacements

Folly works, with the caveat that its headers need C++20: folly::fbstring as
StringType, folly::fbvector and folly::small_vector as ArrayType,
folly::fbvector<std::uint8_t> as BinaryType, and folly::F14NodeMap as
ObjectType through the usual argument-order adapter. folly::F14FastMap is the
exception and requires a complete value type.

For ArrayType, boost::container::devector, boost::container::static_vector
(within its fixed capacity), and std::pmr::vector work as well.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: cover fifo_map, gtl, folly::sorted_vector_map, and Qt

nlohmann::fifo_map works through the adapter that has always been documented
for it, and preserves the insertion order. Restore its mention in the object
order page, which was dropped together with the tsl::ordered_map one: unlike
ordered_map it keeps a lookup index, so it is the insertion-ordered option
without the quadratic cost.

gtl::flat_hash_map and folly::sorted_vector_map work as well, the latter
through an alias that drops the allocator, whose value type it disagrees on.
gtl::btree_map does not, for the same reason as the other btree containers.

None of the Qt containers can be used, each for its own reason: QMap has no
value_type, QHash iterators yield the mapped value rather than a pair, QList
has no max_size(), QByteArray spells empty() as isEmpty(), and QString is
UTF-16.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: qualify the std::pmr::string support claim

Listing std::pmr::string as fully supported was an overclaim: it was only ever
checked with the default memory resource, which is not what PMR is for.

basic_json cannot be given an allocator or a memory resource, so a pmr string
inside a value always allocates from std::pmr::get_default_resource(), and
assigning an arena-backed string into a value silently drops its resource,
because polymorphic_allocator does not propagate on copy construction. Passing
polymorphic_allocator as AllocatorType does not compile either. Only the
process-global set_default_resource() redirects these allocations.

Say so, and separate the row from std::basic_string with a custom stateless
allocator, which is unaffected.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: remove a duplicated StringType compatibility section

The StringType section carried two 'Compatible types' tables and two copies of
the reference-implementation tip. The second table was a stale copy from before
the binary format string fixes and still listed std::pmr::string and
std::basic_string with a custom allocator as unusable, contradicting the
corrected table a few lines above it, and it dragged along the old explanation
that blamed int_to_string.

Drop the stale copy and put the surviving table before the notes, so the
'see below' in the std::pmr::string row points forwards.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: correct the template parameter requirements after independent verification

Every claim on the page was re-checked by compiling and running it, including
the rows that say a type cannot be used, which were checked to fail for the
documented reason and not merely to fail. Twenty-four claims were wrong.

The most consequential: the incomplete-type constraint applies to ObjectType
only. object_t is instantiated inside the class definition, because it is
probed for key_compare; array_t is only named there and is not instantiated
until basic_json is complete. So eastl::vector, QList and QVector are not
excluded by incomplete types at all -- they simply have no max_size() -- and
absl::InlinedVector is excluded for a subtler reason of its own.

Further corrections: ObjectType does not need erase(key), which has a fallback,
but does need at(key) for UBJSON output; only == and < are used, or == and <=>
under C++20, not all six; the documented adapter does not fit ankerl or
robin_hood. ArrayType needs no initializer-list insert, and value_type, the
(count, value) constructor and swappability are per-function, not always.
BinaryType needs a range insert for CBOR indefinite-length byte strings and
does not need push_back. StringType needs append(const StringType&)
unconditionally, and does not need operator!= or operator== against const
char*; empty(), resize(n) and reserve(n) are per-subsystem; int_to_string is
needed by diff, items and std::hash rather than by JSON Pointer or flatten.
BooleanType must be implicitly convertible from bool, and JSONSerializer's
second parameter need not carry a default.

std::pmr::string was wrong in the other direction this time: a moved-in string
does keep its memory resource, and later growth allocates from it. Only copies
land on the default resource.

Five requirement violations are not caught at compile time rather than the two
the page claimed; they are now listed together up front. Split every
compatibility table into what works and what does not, as the reasons in the
second half are the useful part.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Reduce the string_t and array_t members the library requires

Several members were required only because of how the library happened to
be written, not because the functionality needs them. Dropping them widens
the set of usable string and array types, and one of them was also a
performance problem.

string_t:

- c_str() is gone. Every call site already knew the length and passed it
  along, so data() is enough. The one place that did not, the diagnostics
  path in exceptions.hpp, now builds the token from data() and size(),
  which also stops it from truncating keys that contain a null byte.
- back() is gone; the serializer indexes the last character instead.
- find(str, pos), replace(), and substr() are gone. escape() and
  unescape() rebuilt the string with one replace() per escaped character,
  which moves the tail every time: escaping a string of n characters that
  all need escaping cost O(n^2). Both now scan with find_first_of() -- a
  member the pointer parser already required -- and append whole runs, so
  the common case is one search and one copy. Escaping 64000 tildes drops
  from 717 ms to 20 ms; a string with nothing to escape gets faster too
  (8.4 ms to 5.8 ms), because the scan is still a single memchr per pass.
  json_pointer::split() takes its reference tokens with the
  (const char*, size_type) constructor rather than substr().
- json_pointer::to_string() accumulates with concat<string_t> instead of
  letting concat default to std::string and converting afterwards, so
  streaming a json_pointer no longer requires string_t to be assignable
  from a std::string.

array_t:

- at(size_type) is gone. basic_json::at(size_type) checked the index by
  calling array_t::at() and translating std::out_of_range, which also
  required the array type to throw that exact exception. It now compares
  against size() and uses operator[]. The thrown exception, its message,
  and the behaviour under JSON_NOEXCEPTION are unchanged.

The BSON writer wrote the terminating null byte out of the string's own
buffer (size() + 1). It now writes the byte itself, so string_t::data()
need not be null-terminated for to_bson().

The tests pin the reduced API: alt_string loses the five dropped members
and gains coverage of the escaping paths, and a std::vector whose at() is
hidden is used as an ArrayType.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: record the reduced string_t and array_t requirements

Drop c_str(), back(), find(str, pos), replace(), and substr() from the
StringType requirements and at(size_type) from the ArrayType ones, and
note the string assignment the JSON pointer code performs. Streaming a
json_pointer no longer needs assignability from a std::string.

Add the non-null-terminated data() to the list of violations that are not
diagnosed at compile time -- it was described in the StringType section
but missing from the summary at the top -- and correct the QString row,
which no longer fails for the c_str() it lacks.

JSON_CATCH_USER no longer wraps a catch of std::out_of_range: the last one
went away with array_t::at(). Describe what the library actually catches.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Use character literals for the signed BinaryType test

MSVC rejects char(0xFF) with C4310 (cast truncates constant value),
which the Windows workflow treats as an error. The character literals
carry the same byte values without a narrowing cast.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not instantiate a hash map with an incomplete basic_json in the tests

object_t is probed for key_compare inside the definition of basic_json, so
it is instantiated while basic_json is still incomplete. Whether a hash map
survives that depends on the standard library: libstdc++ 9 needs the size of
the mapped type to instantiate std::unordered_map's node type and rejects
the adapter, which broke the GCC 9 builds.

The test now derives its no-key_compare object type from std::map -- which
does cope -- and shadows the inherited key_compare member type with an
entity that is not a type, so the library's probe finds none, exactly as for
a hash map. The unflatten() order-independence checks in unit-json_pointer
already cover the behaviour that the unordered object type was there for.
The limitation is documented for std::unordered_map.

Also address two Clang-Tidy findings the earlier commits introduced:
erase_from_object() declares its iterator with auto, and at(size_type) checks
the type first and then falls through to the return instead of throwing from
an else branch.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Keep diagnostic key paths null-terminated

Building the token from data() and size() kept an embedded null byte in the
key, and since what() hands out a C string, that truncated the whole message
rather than just the key: to_bson() on a key containing U+0000 reported
"[json.exception.out_of_range.409] (/en" instead of the full explanation.
This broke test-bson under JSON_DIAGNOSTICS.

Constructing from data() alone stops at the first null byte, which is what
c_str() did before, so the message is unchanged -- without requiring
string_t to provide c_str().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not parse the value in the array-at() test

JSON_DIAGNOSTIC_POSITIONS adds the byte range of the value to the exception
message, which a parsed value has and an in-memory one does not, so the two
message checks failed in that configuration. Build the array in memory
instead of parsing it; the test is about at(size_type) not needing
array_t::at(), and the byte range is beside the point.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Move the custom BinaryType tests into their own translation unit

The two sections added to unit-regression2.cpp brought a third full
basic_json instantiation into a translation unit that was already large.
With Clang on MinGW that pushed the object over the reach of a 32-bit
relocation and test-regression2_cpp20.exe failed to link:

    relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'

unit-regression2.cpp is restored to exactly what it was before, and the
coverage moves to unit-custom-binary-type.cpp, next to the object and array
type tests it belongs with. The signed value type is now also covered in
C++11, where std::byte is not available.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Do not require the container iterators to be nothrow move constructible

iter_impl declared its defaulted move operations noexcept. The exception
specification a defaulted function gets implicitly follows from its members,
here internal_iterator, which holds the object and array iterators. libstdc++
gives std::deque's iterator a user-provided copy constructor without noexcept
before version 11, so the implicit specification is noexcept(false) and does
not match the declared one. That deletes the function -- and with g++ 4.8,
which predates CWG 1778, it is an error outright:

    error: function 'iter_impl<basic_json<std::map, std::deque> >::iter_impl(
    iter_impl&&)' defaulted on its first declaration with an
    exception-specification that differs from the implicit declaration

So std::deque, which this branch documents as a usable array type, could not
be used with an older standard library. Leaving the specification to be
computed cannot mismatch; iteration_proxy_value already spells out the same
condition next door.

The default configuration is unaffected: json::iterator, json::const_iterator
and ordered_json::iterator stay nothrow move constructible and move
assignable, which the test now checks so it cannot regress unnoticed.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Address two Clang-Tidy findings the custom container tests exposed

Both come from instantiating basic_json with containers other than the
default ones, and neither shows up with the Clang-Tidy version available
outside CI:

- insert(const_iterator, basic_json&&) forwards its by-value iterator to
  the const-reference overload. performance-unnecessary-value-param asks
  for the copy to be a move; it only fires for an iterator that is not
  trivially copyable, as std::deque's is not. The NOLINT on the function
  does not cover it, because the finding is reported where the parameter
  is used rather than where it is declared. Move it, which is what the
  check asks for and is a (very small) improvement in its own right.

- cppcoreguidelines-use-enum-class rejects the unnamed enum that shadowed
  the inherited key_compare member type. An enum class would not do, since
  it declares a type of that name and the probe would find it again; a
  member function declaration hides the name just as well.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Assert the iterators' exception specification relative to the container

The test pinned that nlohmann::json's iterators stay nothrow movable after
iter_impl's defaulted move operations lost their declared noexcept. That is
not a property of the library, though: the exception specification is now
computed from the container iterators, so it holds only for standard library
implementations whose iterators are themselves nothrow movable.

MSVC's checked iterators before VS2017 are not -- _Iterator_base12 registers
the iterator with the container's debug proxy in a copy constructor that
carries no noexcept -- so the assertions fail on a Visual Studio 2015 debug
build, which is the one debug configuration in the AppVeyor matrix and has no
counterpart in the GitHub Actions matrix.

Assert what the change actually guarantees instead: the iterators are nothrow
movable exactly when the object and array iterators they are built from are.
That still pins the default configuration against a silent regression, and it
is true whatever the standard library provides.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Detect a void-returning erase() through a named trait

erase_from_object() distinguished its two overloads with a decltype of a
member call written inline in a default template argument. Every other
detection in the library goes through the detector machinery in detected.hpp
instead -- has_erase_with_key_type is the same question about the same member
function -- and the inline form is the one shape older compilers are least
reliable about.

Express it the same way: detect_erase_with_iterator plus is_detected_exact,
both of which the library already relies on elsewhere. No behaviour changes.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Give the custom container types only the constructors the library uses

The three container types in the new tests inherited every constructor of
their base with using Base::Base. That asks for more than the test needs: the
library builds an object or an array by default construction, by copy or move,
and -- when converting between two basic_json types or from an initializer
list -- from an iterator range. Declaring those directly makes the requirement
visible in the test, and keeps object types out of a corner where a compiler
has to declare std::map's whole constructor set for a derived class while
basic_json is still incomplete.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Temporarily disable the new custom container tests

AppVeyor is the only CI that builds MSVC 2015 and 2017, and it has now
rejected three heads of this branch. Its build log is not reachable from
where this is being worked on, so the verdict is a single bit and the cause
has to be narrowed down by bisection.

Everything else stays: the library changes, the reduced alt_string, and the
unflatten() tests. If AppVeyor passes with these three translation units
disabled, the cause is one of the six basic_json instantiations they add; if
it fails, it is in the library. Either way this commit is reverted.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Guard the disabled tests with a macro rather than #if 0

Clang-Tidy's readability-avoid-unconditional-preprocessor-if rejects a literal
#if 0. Use a macro that is never defined instead, which the check does not
look at. Still temporary, and reverted together with the previous commit.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Re-enable the array and binary container tests

AppVeyor passed with all three new translation units disabled, so the library
changes, the reduced alt_string, and the unflatten() tests are fine on MSVC
2015 and 2017; the cause is one of the six basic_json instantiations the new
tests add.

Bring back two of the three. If AppVeyor passes again, the cause is in
unit-custom-object-type.cpp, which is the one still disabled; if it fails, it
is in one of these two and needs one more split. Still temporary.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Diagnose two silently violated template parameter requirements

Both were on the list of requirements that are not caught at compile time and
corrupt values rather than failing, and both are a plain size comparison:

- A BinaryType whose value_type is wider than one byte, which the readers and
  writers reinterpret as raw bytes anyway.
- A NumberUnsignedType too narrow to hold the absolute value of every
  NumberIntegerType value, which makes basic_json(INT64_MIN).dump() yield -0
  for std::int64_t with std::uint32_t.

Neither static_assert rejects a configuration that worked before: both only
fire where the result was already wrong. Also add the two comments the review
asked for, in write_bson_string() and calc_bson_array_size(), matching the
ones their counterparts already carry.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Align the template parameter tables and record what is now diagnosed

Every table in the page is reformatted so each column is exactly as wide as
its widest cell, which is what the review asked for in a dozen places: the
separator rows that ran two dashes long, the stray spaces, and the columns
padded well past their content.

The row listing six containers that require a complete mapped type is split
in two so that one cell no longer sets the width of the whole table.

Content changes: NumberUnsignedType is described as any unsigned integer type
at least as wide as NumberIntegerType rather than any unsigned integer type;
the two requirements that are now static_asserts move out of the list of
violations that are not caught at compile time; and the two places that
require a non-const operator[] say why data() will not do (std::string has no
non-const data() before C++17).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Bisect the other way: only the object container tests

The previous head touched only docs/, which AppVeyor's only_commits filter
skips, so it produced no build and no status at all -- the pull request looked
green without ever having been built on MSVC 2015 or 2017.

Swap the guards instead of repeating that step: unit-custom-object-type.cpp is
enabled and the array and binary translation units are disabled. AppVeyor
already passed with all three disabled, so a failure here pins the cause on
no_key_compare_json or void_erase_json, and a pass pins it on the array or
binary file. Still temporary.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Split the two object types apart

AppVeyor failed with only unit-custom-object-type.cpp enabled and passed with
all three new translation units disabled, so the cause is one of the two
object types in this file and not the array or binary ones.

Guard out void_erase_map and leave no_key_compare_map, which separates the two
constructs under suspicion: shadowing the inherited key_compare member type
with an entity that is not a type, and hiding the inherited erase with a
void-returning overload. A failure here points at the first, a pass at the
second. Still temporary.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Build the no-key_compare object type by composition, not inheritance

The "object type without key_compare" test failed on AppVeyor's MSVC
2017 jobs (/std:c++17): its no_key_compare_map derived publicly from
std::map and shadowed the inherited key_compare type with a same-named
member function, relying on ordinary member hiding to make key_compare
unreachable as a type for the library's detection trait. MSVC 2017
does not honor that hiding for a typename-qualified lookup performed
from outside the class and still resolves key_compare to the base's
comparator type, so object_comparator_t incorrectly picked it up
instead of falling back to default_object_comparator_t.

Wrapping a std::map by composition instead removes the base class
entirely, so there is no key_compare to find under any lookup rule,
on any compiler. Also drops the now-unneeded JSON_BISECT_CUSTOM_CONTAINER_TESTS
guard left over from narrowing this down: the void_erase_map test in
the same file was never the cause and is re-enabled unconditionally.

Verified locally with clang++ and g++ under C++17 and C++20.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Re-enable the array and binary custom-container tests

unit-custom-array-type.cpp and unit-custom-binary-type.cpp were still
guarded behind JSON_BISECT_CUSTOM_CONTAINER_TESTS from bisecting the
AppVeyor failure fixed in 7c39f3227, which was unrelated to either
file. The macro was never defined, so none of these tests actually ran
in CI. Verified locally with clang++ and g++ under C++17 and C++20
before removing the guards.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix indentation of custom_object_type per astyle

The one-line function bodies in the composition-based no_key_compare_map
(7c39f3227) do not match the project's Allman brace style, which the
ci_test_amalgamation job enforces with astyle. Reformatted with the
pinned astyle 3.4.13; no functional change.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* docs: add compiled reference implementations for the container template parameters

Each of ObjectType, ArrayType, StringType, and BinaryType now links to a
minimal, self-contained header (docs/mkdocs/docs/examples/custom_*_type.hpp)
that wraps the corresponding standard container by composition and satisfies
every "Always required" member listed on that page. Unlike the prose
requirement lists, these are real code: each header has a companion .cpp that
instantiates a basic_json specialization with it and is compiled and run by
the existing ci_test_examples check (docs/Makefile's check_output_portable),
so the reference implementations cannot silently drift from what the library
actually requires. The .output files were generated with that same target.

StringType's existing pointer to tests/src/unit-alt-string.cpp's alt_string
is kept alongside the new header as a more thorough, battle-tested example.

Verified locally: astyle (pinned 3.4.13, project .astylerc) on the new files;
clang++/g++ under C++11/17/20 for each example against the amalgamated
header; `make check_output_portable` in docs/; `mkdocs build --strict` and
scripts/check_structure.py for the page itself.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Declare no_key_compare_map's accessors noexcept

The GCC C++20 job builds with -Wnoexcept and -Werror, and the standard
library takes noexcept(c.begin()) and noexcept(c.end()) in ranges_base.h and
range_access.h. Forwarding to std::map without repeating its noexcept made
those expressions false, which the warning reports as an error:

  error: noexcept-expression evaluates to 'false' because of a call to
         no_key_compare_map<...>::begin()          [-Werror=noexcept]
  note:  but ... does not throw; perhaps it should be declared 'noexcept'

Give the accessors the exception specification of what they forward to.
std::map declares begin, end, cbegin, cend, empty, size, max_size, and clear
noexcept, so the wrapper does too. swap is left alone: std::map's is only
conditionally noexcept, and nothing asks for it.

void_erase_map is unaffected because it still derives from std::map and
inherits accessors that already carry the specification.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Write out what the defaulted constructor of no_key_compare_map implied

ci_test_gcc builds with -Weffc++, which asks for data to be initialized in a
member initialization list; a defaulted default constructor does not do that:

  error: 'no_key_compare_map<...>::data' should be initialized in the member
         initialization list                              [-Werror=effc++]

Writing the constructor out satisfies that but drops the exception
specification the defaulted one carried, which -Wnoexcept then objects to
where the standard library takes noexcept(construct(...)). Declare it the way
the defaulted constructor was: noexcept when the wrapped map's default
constructor is.

This is the cost of composition -- inheritance carried std::map's exception
specifications and initialization for free, and forwarding by hand has to
restate them.

Checked with the repository's own GCC warning set from cmake/gcc_flags.cmake,
all 346 flags, at C++11, C++17 and C++20: no diagnostics for this file, nor
for the two custom container translation units that were disabled while the
MSVC failure was narrowed down and are built again now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Mark no_key_compare_map::swap noexcept

Clang-Tidy rejects a swap that is not:

  error: swap functions should be marked noexcept
         [cppcoreguidelines-noexcept-swap,performance-noexcept-swap]

It was left unmarked on the grounds that std::map::swap is only
conditionally noexcept, so an unconditional promise would be wrong for a
comparator or allocator that can throw while swapping. Both concerns are met
by taking the specification from the wrapped map rather than asserting one:
noexcept(noexcept(data.swap(other.data))). Clang-Tidy accepts that, and no
NOLINT is needed.

Last in the series of specifications that inheritance used to supply and
composition has to write out by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018hxZxz8svM54c6ATEvXp5E
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 20:46:37 +02:00
Niels Lohmann b17c272f46 Reserve capacity in from_json() object conversion when the target container supports it (#5472)
* Reserve capacity in from_json() object conversion when supported

The object-to-container from_json() overload filled the target
container one element at a time without reserving capacity, even
when the target type supports reserve() (e.g. std::unordered_map)
and the number of elements is already known. This caused unnecessary
rehashing while parsing large objects into such containers.

Add a reserve-detecting overload (from_json_object_impl), mirroring
the priority_tag-based SFINAE technique already used by the array
conversion path (from_json_array_impl), so that reserve(size()) is
called up front when available and the loop falls back unchanged
otherwise (e.g. for std::map).

Fixes #5406

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Update doc example outputs for new object-conversion iteration order

Reserving capacity in from_json()'s object-conversion path before
inserting elements changes libstdc++'s std::unordered_map bucket
layout, which changes the iteration order used by
get__ValueType_const.cpp, get_to.cpp and operator__ValueType.cpp to
print the elements of a converted std::unordered_map<std::string,
json>. Verified against a clean develop checkout (built with the
same GCC/libstdc++ used in CI) that the old order was produced
without this PR's change and the new order is produced with it, and
that the three affected examples now match their updated expected
output byte-for-byte.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Factor out a reserve-dispatch helper instead of duplicating the object from_json loop

Addresses review feedback from @gregmarr on PR #5472: the emplace loop no
longer needs to exist twice for the reserve/no-reserve cases. A small
from_json_object_reserve() overload pair (SFINAE-dispatched on whether
reserve() exists, mirroring the priority_tag technique used elsewhere)
either calls reserve() or is a no-op; from_json_object_impl() calls it once.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Inline from_json_object_impl into from_json now that it is called only once

Addresses review feedback from @gregmarr on PR #5472: with the reserve
loop de-duplicated, from_json_object_impl no longer needs to be a
separate function that from_json immediately delegates to.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-09 09:54:12 +02:00
Niels Lohmann 734fd305a1 Format-check the documentation examples in CI (#5386)
* Reformat parser_callback_t example with astyle

The file uses "json & /*parsed*/" in three lambda parameter lists, which
astyle rewrites to "json& /*parsed*/" per --align-reference=type. The
drift went unnoticed because CI never format-checked the documentation
examples; "make pretty" does cover them.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Format-check the documentation examples in CI

The examples live in docs/mkdocs/docs/examples, but both format checks
still referenced the long-gone docs/examples path:

- check_amalgamation.yml passed it to find, which printed an error for
  the missing path and carried on, so astyle only ever saw include and
  tests. The step still exited 0.
- ci.cmake globbed it into INDENT_FILES, and a GLOB_RECURSE over a
  missing directory silently yields nothing, so the ci_test_amalgamation
  target skipped the examples too.

Either way the 231 example files have never been format-checked. Point
both at the real path, and guard the workflow with an explicit directory
check so a future rename fails the job instead of quietly shrinking the
file list again.

Also drop the dead docs/examples/** path filter from
publish_documentation.yml; docs/mkdocs/** already covers the examples.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-20 12:32:50 +02:00
Niels Lohmann 1c63a120b6 docs: document how discarded values are removed by the parser callback (#5354)
Follow-up to #5342, which fixed the parser callback leaving a discarded
member behind when an array or a value under an object key was rejected.
The documentation of parser_callback_t only stated that discarded values
in structured types are skipped, without saying that this covers object
parents and that the key is removed along with the value, so there was no
way to tell the fixed behavior from the buggy one.

Spell out the discarding rules, add an example that exercises the cases
the fix repaired, and correct the return value description: a discarded
top-level value is replaced by null, not by "an empty discarded object".

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-04 08:45:02 +02:00
Niels Lohmann 631e667fe5 Document a duplicate-object-key rejection recipe (#5259)
* 📝 Document a duplicate-object-key rejection recipe

RFC 8259 leaves handling of duplicate object keys to the implementation;
this library silently keeps only the last value for a repeated key.
Discussion #5085 asked for an opt-in rejection mode. Decision: don't
change library behavior, but document the existing parser-callback
workaround instead.

Adds a "Recipe: rejecting duplicate object keys" section to
parser_callbacks.md, adapted from a community-contributed workaround.
Fixed an off-by-one bug in the original snippet: object_start reports
the depth of the object's parent, while key events inside that object
report depth+1, so indexing the per-depth key set with the same depth
in both places caused an out-of-bounds access on nested objects.
Verified the published snippet compiles and behaves correctly for flat
duplicates, nested duplicates, sibling objects sharing key names, and
arrays of objects.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Cross-link the duplicate-key recipe with the existing object_t behavior docs

object_t.md and features/types/index.md already document that duplicate
object keys resolve to an unspecified value (RFC 8259 leaves this to the
implementation). The new recipe's intro overstated this as a guaranteed
"last value wins" rule, which isn't true in general -- parsing text keeps
the last value, but constructing from an initializer list keeps the first.
Reworded the recipe to point at object_t's "unspecified" behavior instead
of asserting a specific rule, and added cross-links from both existing
pages to the new recipe.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Turn the duplicate-key recipe into a standalone, compiled example

Replace the inline code fence in the "rejecting duplicate object keys"
recipe with a proper docs/mkdocs/docs/examples/*.cpp + .output pair,
included via --8<-- like every other example on the site. The .output
file was generated by running it through the project's actual example
build (docs/Makefile: single_include, -std=c++11, -DJSON_USE_GLOBAL_UDLS=0)
and cross-checked with `make check_output`, and the source passes the
pinned astyle 3.4.13 formatting unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 20:39:59 +02:00
Niels Lohmann 8d7e0046f4 Add std::format and fmt support (#5224)
* ✨ add std::format and fmt support

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* ♻️ reorganize PR

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 💚 fix build

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 💚 fix build

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 💚 fix build

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-02 15:59:36 +02:00
Caillin NugentandNiels Lohmann 58cfecf7f7 Serialize enum (#5151)
* Added NLOHNMANN_JSON_SERIALIZE_ENUM_STRICT
- duplicate of NLOHMANN_JSON_SERIALIZE_ENUM

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* Added failing tests for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* modified NLOHMANN_JSON_SERIALIZE_STRICT to throw

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* added documentation and changed readme to include NLOHMANN_JSON_SERIALIZE_ENUM_STRICT

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* ran amalgamate

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* docs(macros): add page for JSON_SERIALIZE_ENUM_STRICT
- added page to nav
- added links to new page where appropriate

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* refactor(macros): make JSON_SERIALIZE_ENUM_STRICT use JSON_THROW
- added templated wrapper function to fix scope error in calling JSON_THROW

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* refactor(macros): make NLOHMANN_SERIALIZE_ENUM_STRICT use error code 410
- added error code 410 to docs

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* tests(macros): add test for to_json with enum value not mentioned
	       in mapping for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* Apply suggestions from code review

Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
Signed-off-by: Caillin Nugent <nugentcaillin@gmail.com>

* fix(macro): prevent compilation error with -Werror and -Wunused-parameter
            with NLOHMANN_JSON_SERIALIZE_ENUM_STRICT
- casted exception to void to avoid warning

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* fix(docs): add link to NLOHMANN_SERIALIZE_ENUM_STRICT docs to exception page

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* docs(macros): add example of exception throwing for NLOHMANN_JSON_SERIALIZE_ENUM_STRICT

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

* refactor(macros): add more in-depth error message to NLOHMANN_JSON_SERIALIZE_ENUM_STRICT
- changed error message to follow style of nlohmann/json#4989
- made description of throw wrapper more general
- updated tests and example of exceptions

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>

---------

Signed-off-by: Caillin Nugent <caillinn@student.unimelb.edu.au>
Signed-off-by: Caillin Nugent <nugentcaillin@gmail.com>
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
2026-05-18 20:37:07 +02:00
George SedovandNiels Lohmann cba5dc0ed8 New macros for the named JSON convertor generation (#4563)
* Add new macros for named conversions

* Unit tests for the named conversion macros

* Update the docs to include the new macros

* Fix the documentation for the macros

the correct maximum number of member variables is 63

* Fix CI tests

* update the named macros

* move the example files

* update the explicit macros expansion

* update documentation

* fix documentation hiccups

* astyle changes

* add static analysis exceptions

* change md header to explicit html to fit the length

* Small corrections to docs

Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
Signed-off-by: George Sedov <radist.morse@gmail.com>

---------

Signed-off-by: George Sedov <radist.morse@gmail.com>
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
2026-05-16 10:04:22 +02:00
trdesilva a0a4e7cc0b Add front methods to json_pointers (implements #4889) (#5152)
* Added front, pop_front, and push_front methods to json_pointers in order to facilitate root-to-leaf traversals of JSON object trees. (#4889)

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>

* undid VS autoformatting in irrelevant code

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>

* Ran make amalgamate, added navigation to json_pointer's new front methods in mkdocs, and fixed errors in documented complexity for those methods.

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>

* Fixed GCC 4.8 compile error caused by const iterators

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>

* Fixed another gcc-4.8 compile error

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>

* amalgamated

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>

---------

Signed-off-by: trdesilva <5818730+trdesilva@users.noreply.github.com>
2026-05-13 07:59:16 +02:00
Niels Lohmann 515d994acb 📄 adjust year (#5044)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-01-01 20:00:39 +01:00
Andy Choi cda9c1e3bd Use get instead of template get in REAMD.md and docs in non-template context (#4846)
Signed-off-by: Andy Choi <ccpong516@gmail.com>
2025-10-30 10:23:04 +01:00
co63oc 44bee1b138 chore: fix typos (#4921)
Signed-off-by: co63oc <co63oc@users.noreply.github.com>
2025-09-16 08:07:00 +02:00
chirszandNiels Lohmann 4b17f90f65 Add ignore_trailing_commas option (#4609)
Added examples and modified the corresponding documents and unit tests.

Signed-off-by: chirsz-ever <chirsz-ever@outlook.com>
Co-authored-by: Niels Lohmann <niels.lohmann@gmail.com>
2025-05-22 08:01:46 +02:00
Niels Lohmann 1705bfe914 🔖 set version to 3.12.0 (#4727)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2025-04-11 10:41:14 +02:00
Niels Lohmann e90c860d53 Add note on derived return type for value function (#4628)
* 📝 add note on derived return type

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 📝 add note on derived return type

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2025-01-26 18:40:55 +01:00
Harinath Nampally d23291ba26 use diagnostic positions in exceptions (#4585)
* add a ci step for Json_Diagnostic_Positions

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* Update ci.cmake to address review comments

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* address review comment

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix typo in the comment

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix typos in ci.cmake

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* invoke the new ci step from ubuntu.yml

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* issue4561 - use diagnostic positions for exceptions

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix ci_test_documentation check

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* address review comments

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix ci check failures for unit-diagnostic-postions.cpp

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* improvements based on review comments

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix const correctness string

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* further refinements based on reviews

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* add one more test case for full coverage

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* ci check fix - add const

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* add unit tests for json_diagnostic_postions only

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix ci_test_diagnostics

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

* fix ci_test_build_documentation check

Signed-off-by: Harinath Nampally <harinath922@gmail.com>

---------

Signed-off-by: Harinath Nampally <harinath922@gmail.com>
2025-01-19 10:42:21 +01:00
kimci86 8a882f32ed Generate template functions with NLOHMANN_DEFINE_TYPE macros (#4597)
* Support any basic_json type in NLOHMANN_DEFINE_TYPE_* macros

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Test NLOHMANN_DEFINE_TYPE_* macros also support unordered_json

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Simplify test about NLOHMANN_DEFINE_TYPE_ with many arguments

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Remove extra scope in macros tests

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Remove unused test class in macros tests

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Update documentation about NLOHMANN_DEFINE_TYPE_* macros

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Fix NLOHMANN_JSON_SERIALIZE_ENUM documentation

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Mark some variables const in macros tests, fixes clang-tidy

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Workaround clang 3.5 issue with const object initialization

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Update highlighted lines in NLOHMANN_DEFINE_TYPE_* macros examples

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Fix swapped macros in documentation

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Remove extra backslashes at the end of macros

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Require basic_json type in NLOHMANN_DEFINE_TYPE_* generated functions

Signed-off-by: kimci86 <kimci86@hotmail.fr>

* Fix typos in macros documentation

Signed-off-by: kimci86 <kimci86@hotmail.fr>

---------

Signed-off-by: kimci86 <kimci86@hotmail.fr>
2025-01-18 22:59:09 +01:00
Niels Lohmann 26cfec34be Clean up and document project files (#4560) 2025-01-17 06:53:35 +01:00