Commit Graph
231 Commits
Author SHA1 Message Date
Niels Lohmann 43afb5bebc 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>
2026-08-28 18:17:15 +00:00
Niels Lohmann 0e4ad2e8da 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>
2026-08-28 17:38:58 +00:00
Niels Lohmann 26b10a7b18 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>
2026-08-28 17:38:58 +00:00
Niels Lohmann 690c3be01d 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>
2026-08-28 17:38:57 +00:00
Niels Lohmann ca47dd539d 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>
2026-08-28 17:38:57 +00:00
Niels Lohmann 681fb07eb2 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>
2026-08-28 17:38:57 +00:00
Niels Lohmann a02741fd28 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>
2026-08-28 17:38:57 +00:00
Niels Lohmann b8482ed7f4 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>
2026-08-28 17:38:56 +00:00
Niels Lohmann d386e0aa52 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>
2026-08-28 17:38:56 +00:00
Niels LohmannandClaude Opus 5 5d93f35463 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>
2026-08-28 17:38:56 +00:00
Niels LohmannandClaude Opus 5 96806af2dc 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>
2026-08-28 17:38:56 +00:00
Niels LohmannandClaude Opus 5 7a37a27a67 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>
2026-08-28 17:38:55 +00:00
Niels LohmannandClaude Opus 5 06feaa8d04 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>
2026-08-28 17:38:55 +00:00
Niels LohmannandClaude Opus 5 599bb1b68c 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>
2026-08-28 17:38:55 +00:00
whnandGitHub 1ac268d409 docs: correct to_bson complexity (#5334)
Signed-off-by: whn <142425816+Whning0513@users.noreply.github.com>
2026-08-28 13:27:18 +01:00
3fa93dac65 docs: document std::pair/std::tuple serializing as an object for string-keyed pairs (#5442)
A std::pair or std::tuple whose every element is itself a two-element array
with a string first element (e.g. std::pair<std::string, int>) serializes to
a JSON object instead of a JSON array, because to_json builds the value with a
brace initializer and the initializer-list object-detection rule fires. The
resulting object cannot be read back into the original type and collapses
duplicate keys. Document this quirk in the conversions guide, together with the
unaffected cases and the idiom to force an array.


Claude-Session: https://claude.ai/code/session_016cwQq8WQRFzQcGQtbJTtJg

Signed-off-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-28 13:26:01 +01:00
whnandGitHub 01853ed6bc docs: document lenient BSON input handling (#5333)
Signed-off-by: whn <142425816+Whning0513@users.noreply.github.com>
2026-08-25 07:18:50 +01:00
Krishnanand GandGitHub 2f025f401e Throw other_error.502 when UBJSON use_type is set without use_size (#5380)
* Throw other_error.502 when UBJSON use_type is set without use_size

Fixes #5321

Signed-off-by: Krishnanand G <118352827+Krishnanand-G@users.noreply.github.com>

* Scope UBJSON use_type check to container branches and expand tests

Signed-off-by: Krishnanand G <118352827+Krishnanand-G@users.noreply.github.com>

* Re-amalgamate single_include/json.hpp

The previous commit updated the split headers but the amalgamated
file didn't go back through astyle before I committed it, so CI's
amalgamation check caught formatting drift in json_fwd.hpp and a
few noexcept clauses in basic_json, plus one doc example. None of
it touches the UBJSON logic. Applied the patch CI generated to
bring single_include back in sync.

Signed-off-by: Krishnanand G <118352827+Krishnanand-G@users.noreply.github.com>

---------

Signed-off-by: Krishnanand G <118352827+Krishnanand-G@users.noreply.github.com>
2026-08-25 07:16:10 +01:00
Niels LohmannandGitHub 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
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
36187cacfb ⬆️ Bump wheel from 0.47.0 to 0.48.0 in /docs/mkdocs (#5385)
Bumps [wheel](https://github.com/pypa/wheel) from 0.47.0 to 0.48.0.
- [Release notes](https://github.com/pypa/wheel/releases)
- [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst)
- [Commits](https://github.com/pypa/wheel/compare/0.47.0...0.48.0)

---
updated-dependencies:
- dependency-name: wheel
  dependency-version: 0.48.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-20 12:32:34 +02:00
Sahil_KamateandGitHub b5378e8deb Fix CBOR tag handlers not recognizing tags 0-5 and 21-23 (#5331)
* Fix CBOR tag handlers not recognizing tags 0-5 and 21-23

The tagged-item switch in binary_reader::parse_cbor_internal() only handled
head bytes 0xC6-0xD4 and 0xD8-0xDB. Bytes 0xC0-0xC5 (tags 0-5: date/time,
epoch, bignum, decimal, bigfloat) and 0xD5-0xD7 (tags 21-23: base64url,
base64, base16 conversion hints) fell through to the default case and were
reported as invalid bytes, even under cbor_tag_handler_t::ignore and ::store,
despite being valid CBOR major-type-6 tags per RFC 8949.

Add the missing case labels so the full 0xC0-0xDB range is handled
uniformly. Extend the "Tagged values" test in unit-cbor.cpp to cover
0xC0-0xD7, and update the CBOR docs to state the corrected tag range.

Fixes #5315

Signed-off-by: sahilkamate03 <45514385+sahilkamate03@users.noreply.github.com>

* Fix stale CBOR tag docs and add store-mode binary-payload test

The "Incomplete mapping" warning still listed tags 0-5 (date/time,
bignum, decimal fraction, bigfloat) and 21-23 (expected conversions)
as unsupported, even though they now parse correctly under
cbor_tag_handler_t::ignore/store, same as 0xC6..0xD4/0xD8..0xDB.
Remove those five bullets and cross-reference the "Tagged items"
warning below, matching the equivalent docs fix landed independently
in PR #5367.

Also add a cbor_tag_handler_t::store test that wraps a binary
payload (not just a string) for every byte in 0xC0..0xD7, confirming
these tags are unwrapped the same way as 0xC6..0xD4 rather than
mistaken for the 0xD8..0xDB binary-subtype marker syntax, per review
feedback on #5331.

Signed-off-by: sahilkamate03 <45514385+sahilkamate03@users.noreply.github.com>

---------

Signed-off-by: sahilkamate03 <45514385+sahilkamate03@users.noreply.github.com>
2026-08-19 20:19:47 +02:00
Niels LohmannandGitHub 23518f54fe Add an Ecosystem page for third-party projects built on nlohmann::json (#5369) 2026-08-07 19:29:58 +02:00
Niels LohmannandGitHub 9a091d2b82 Do not write BJData ndarrays whose size overflows std::size_t (#5362)
* Do not write BJData ndarrays whose size overflows std::size_t

write_bjdata_ndarray() multiplied the _ArraySize_ dimensions into a
std::size_t without checking for overflow. A product that wraps around
to a value that happens to match the size of _ArrayData_ passed the
length check, and the writer emitted an ndarray header announcing an
element count that cannot be represented:

    {"_ArrayType_":"uint8","_ArraySize_":[9223372036854775808,2],"_ArrayData_":[]}

was encoded as 5b 24 55 23 5b 4d 00 00 00 00 00 00 00 80 69 02 5d, an
ndarray of 2^64 elements followed by no data. Reading that back throws
out_of_range.408 ("excessive ndarray size caused overflow"), so to_bjdata
produced output that from_bjdata rejects. This is reachable by parsing
untrusted JSON and re-encoding it as BJData.

Mirror the overflow check the binary reader already performs, and also
reject a single dimension that does not fit into std::size_t, which the
previous cast silently truncated where std::size_t is narrower than 64
bits. Such objects now fall back to a plain object encoding, which is
what the surrounding type and length validation already does for
annotations it cannot represent, and they round-trip unchanged.

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

* Document when to_bjdata converts a JData annotation to an ND-array

The BJData page described the 1-D vector case as the only situation in
which an object carrying _ArrayType_/_ArraySize_/_ArrayData_ is not
written as a compact ND-array. The writer has always had several other
fallbacks -- an unknown _ArrayType_, a dimension that is not a
non-negative integer, an _ArrayData_ whose length does not match the
product of the dimensions, and elements that are not numbers of the
annotated kind -- all of which cause the value to be serialized as a
regular JSON object instead.

Spell out the conditions, including the size-overflow check added in the
preceding commit, so the documented behavior matches the implementation.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-05 13:44:15 +02:00
Petr BělohlávekandGitHub acd87e2336 CI: Add clang 21&22 to Ubuntu CLang build matrix (#5347)
* Add clang 21 to ubuntu build matrix (CI)

Signed-off-by: Petr Belohlavek <me@petrbel.cz>

* Add clang 22 to ubuntu build matrix (CI)

Signed-off-by: Petr Belohlavek <me@petrbel.cz>

* Register Clang 22.1.8 to quality_assurance.md

Signed-off-by: Petr Belohlavek <me@petrbel.cz>

---------

Signed-off-by: Petr Belohlavek <me@petrbel.cz>
2026-08-04 16:14:35 +02:00
Niels LohmannandGitHub ad94fb01cc docs: document size-mismatch behavior of fixed-size conversions (#5352)
Conversions whose element count is fixed by the destination C++ type --
`std::pair`, `std::tuple`, `std::array<T, N>`, C arrays, and
`std::map`/`std::unordered_map` with a non-string key -- read exactly the
elements they need via `at` and never compare the JSON array's size to
that number. Excess elements are silently discarded, while a shortfall
throws `out_of_range.401` rather than a `type_error`. Neither direction
was documented in `conversions.md`, `get.md`, or `from_json.md`.

The existing warning covered only `std::array` and stated that a too-short
JSON array leaves the remaining elements default-constructed with no
exception thrown; that is not what happens. Generalize it to all
fixed-size destinations and correct the shortfall direction.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-04 08:45:50 +02:00
Niels LohmannandGitHub c2e1cc50e0 docs: document the complexity of ordered_map operations (#5353)
* docs: document the complexity of ordered_map operations

ordered_map stores its elements in a std::vector in insertion order and
has no lookup index, so emplace, operator[], at, find, count, erase, and
insert are all linear scans. The documentation stated no complexity for
any operation, neither in ordered_map.md nor in ordered_json.md.

Add a per-operation complexity table and note the consequence: building
or parsing an ordered_json object of n keys is O(n^2). Measured with
-O2 -DNDEBUG for parsing a flat object of n keys, ordered_json is 5x
slower than json at n=2000 and 54x slower at n=16000, with the timings
quadrupling per doubling of n. Cross-reference the table from
ordered_json.md and from the object order page, which recommends
ordered_json without mentioning the cost.

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

* docs: move the Complexity section after Member functions

scripts/check_structure.py enforces a fixed section order for pages under
docs/mkdocs/docs/api, in which Complexity comes after Member functions.
The section had been placed right after Iterator invalidation, which made
ci_test_build_documentation fail with structure/section_order.

No content change beyond the move; the table columns are realigned to the
narrower content.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-04 08:45:35 +02:00
Niels LohmannandGitHub 173f2a7407 Documentation review: exceptions from binary-format hardening, and tuple reference types (#5359)
* 📝 Document exceptions newly thrown by the binary-format hardening

A round of binary-format input validation (#5274, #5284, #5287, #5332)
added new failure modes without updating exceptions.md, and left two
descriptions factually narrower than the code:

- parse_error.110 said "CBOR or MessagePack"; BSON and UBJSON also
  throw it. Generalized, and added the BSON EOF example (#5332).
- parse_error.112: added the BSON document-size mismatch example
  (#5287).
- parse_error.113 said "while parsing a map key", but its own existing
  UBJSON char example already contradicted that. Broadened to cover
  invalid length specifications, and added the negative-string-length
  example (#5284).
- out_of_range.408 said "of an UBJSON array or object"; CBOR now throws
  it too (#5274). Generalized and added both CBOR examples.

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

* 📝 Correct which types may be referenced in a tuple extraction

The note added in #5271 said a referenced type must be one the library
stores "or an arithmetic type it can convert to/from". The parenthetical
is wrong: is_compatible_reference_type requires an exact match against
the stored types, so std::tuple<int&> is rejected by static_assert even
though int converts fine as a value. Only the value case is permissive.

Spell out the eight admissible types, give the int& counter-example, and
separate the reference restriction from by-value conversion.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-04 08:45:18 +02:00
Niels LohmannandGitHub 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
78821cd9c2 docs: document standards compliance and parse() vs operator>> strictness (#5326)
* docs: document RFC 8259 / JSONTestSuite compliance and parse() vs operator>> strictness

The compliance story lived only in tests/src/unit-testsuites.cpp, so
drive-by comparisons kept claiming the library "does not fully pass
JSONTestSuite". Make it discoverable:

- README: add a "Standards compliance" note stating that both nst
  JSONTestSuite revisions run in CI, that all mandatory y_/n_ cases pass
  through the strict parse() entry point, and listing the deliberate
  implementation-defined i_ choices (unbounded nesting, silent BOM
  stripping, noncharacters forwarded, strict rejection of invalid UTF-8
  and lone surrogates, out_of_range.406 on numeric overflow).
- features/parsing: add a "Strictness and trailing data" section
  documenting that parse() is strict and rejects trailing data while
  operator>> follows relaxed iostream semantics (parses one value and
  leaves the stream positioned after it) -- the single place a naive
  test yields a "non-compliant" result.

Documentation only; no parser behavior change. Closes #5290.

Signed-off-by: manon <youdie006@users.noreply.github.com>

* docs: correct test-data vendoring and parse()/operator>> claims per review

- README: the JSONTestSuite data is downloaded from nlohmann/json_test_data at
  configure time, not vendored/committed; say so.
- README: only the updated suite runs y_ and n_ cases through strict parse();
  the original suite's y_ cases go through operator>>. Narrow the claim.
- parsing/index.md and operator_gtgt.md: note that operator>> consumes a number's
  terminating byte, so concatenated numbers must be whitespace-separated (1 2
  works, 1true does not); structural and literal values are unaffected.

Signed-off-by: manon <youdie006@users.noreply.github.com>

---------

Signed-off-by: manon <youdie006@users.noreply.github.com>
Co-authored-by: manon <youdie006@users.noreply.github.com>
2026-08-03 19:15:53 +02:00
Niels LohmannandGitHub 31ba5208c8 docs: qualify the operator>> stream positioning guarantee (#5343)
operator>>'s notes state that it leaves the stream positioned right
after the parsed value, so that concatenated JSON values can be read
back to back. That does not hold when the value is a number: a number
is only terminated by the character that follows it, and the lexer's
unget() is simulated (it rewinds only the lexer's own bookkeeping),
so that character stays consumed from the stream.

Document the actual behaviour: the guarantee holds for all value types
except numbers, which must be followed by whitespace. Also qualify the
cross-reference on the JSON Lines page, which repeated the unqualified
claim.

Documentation only; the behaviour itself is tracked in #5340.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-03 08:18:03 +02:00
YingqiDuanandGitHub de8a099ba5 Document BSON interoperability and subtype-less binary round trips (#5330)
- warn about BSON marker 0x11 interoperability in both directions
- explain subtype-less binary normalization to subtype 0x00
- add a round-trip test for binary values without a subtype

Signed-off-by: YingqiDuan <141370165+YingqiDuan@users.noreply.github.com>
2026-07-31 08:33:07 +02:00
Patrick10199andGitHub 868506dcc0 Fix CBOR half-float assertion bounds (#5335)
Signed-off-by: Patrick Armstrong <patrick@erpassistant.ai>
2026-07-30 23:38:13 +02:00
Luke BanicevicandGitHub 2e23687092 to_bson() silently emits corrupt documents when a length exceeds INT32_MAX (#5314) 2026-07-28 09:08:57 +00:00
dependabot[bot]andGitHub 227c5cdfb1 ⬆️ Bump mkdocs-material from 9.7.6 to 9.7.7 in /docs/mkdocs (#5324) 2026-07-27 17:36:07 +00:00
Niels LohmannandGitHub 06ac77f4fd Remove Lion Yang from sponsors list (sponsorship cancelled) (#5306) 2026-07-26 19:42:28 +00:00
Luke BanicevicandGitHub dfa51af692 Enhance documentation on serializing untrusted input in dump() (#5304) 2026-07-26 08:29:31 +00:00
Luke BanicevicandGitHub 3296a3ad8c Docs: clarify there is no official npm package (impersonation/typosquat awareness) (#5299) 2026-07-23 16:02:29 +00:00
Niels LohmannandGitHub c197feff81 Extend memcpy fast path to sized sentinels (e.g. std::counted_iterator) (#5268) 2026-07-12 09:16:06 +02:00
Niels LohmannandGitHub b2b47c69b1 📝 Document std::pair/std::tuple conversion and C++20 range-view construction (#5271)
Both had zero documentation anywhere in docs/mkdocs/. The tuple/pair
gap was first spotted in the very first git-log audit pass but never
turned into an actionable todo, so it persisted uncaught across four
subsequent passes.

- Document basic positional std::pair/std::tuple <-> json array
  conversion, plus #5016's reference-extraction capability
  (get<std::tuple<T&, T&>>() returning references into the stored
  array elements).
- Document #5205's new json-from-C++20-range-view constructor
  (e.g. nums | std::views::filter(...)).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 23:58:49 +02:00
6a406ee141 Document that JSON_Diagnostics CMake option doesn't apply to pre-installed packages (#5270)
Closes #3106. set(JSON_Diagnostics ON) before find_package() has no
effect on a package built and installed elsewhere (Homebrew, vcpkg, a
system package, etc.) -- the compile definition is baked into the
exported nlohmann_jsonTargets.cmake at install time and the generated
config script never re-reads that variable. Verified empirically
against the real Homebrew-installed 3.12.0 package: the exported
target carries a fixed $<$<BOOL:OFF>:JSON_DIAGNOSTICS=1>, and the
suggested set(JSON_Diagnostics ON) snippet produces no change in
exception output.

Documents the actual working fix (overriding the imported target's
INTERFACE_COMPILE_DEFINITIONS property after find_package()) and the
multi-target "JSON_DIAGNOSTICS redefined" pitfall reported earlier in
the issue thread.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 23:58:18 +02:00
Niels LohmannandGitHub ca76c37650 Add iterator+sentinel tests and docs for binary deserializers (#5265)
* Add iterator+sentinel tests and docs for binary deserializers

This commit extends the C++20 ranges support (iterator+sentinel pairs) to the
binary format deserializers from_cbor, from_msgpack, from_ubjson, from_bjdata,
and from_bson, matching what was already done for parse(), accept(), and
sax_parse().

Changes:
- Add istreambuf_sentinel helper to test_utils.hpp for EOF detection in tests
- Add 5 new test cases that read binary files directly via
  std::istreambuf_iterator<char> + sentinel, without pre-buffering
- Update documentation for all 5 from_* functions to document overload (3)
  with SentinelType parameter
- All tests pass; verified against existing test suite data
- Fix potential buffer over-read warning in heterogeneous iterator test

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

* Merge iterator+sentinel overloads and fix ambiguity/CI issues

Address PR review feedback and CI failures:

- Merge the separate same-type and sentinel-type iterator overloads of
  parse(), accept(), sax_parse(), and the five from_* binary deserializers
  into a single overload with SentinelType defaulted to IteratorType,
  as suggested in review. Applied the same simplification to the
  detail::input_adapter() free functions.
- Fix a latent ambiguity: some compilers (e.g. GCC 4.8) unreliably SFINAE
  the operator!= detection for std::nullptr_t against container/string
  types, making calls like parse(s, nullptr, ...) ambiguous with the
  compatible-input overload. can_compare_ne now explicitly excludes
  std::nullptr_t as a SentinelType.
- Use a named enable_if_t template parameter instead of an unnamed
  function parameter for the SFINAE guard, fixing a clang-tidy
  hicpp-named-parameter/readability-named-parameter failure.
- Update parse.md, accept.md, sax_parse.md, and the five from_*.md pages
  to document the merged overload instead of separate (2)/(3) overloads,
  also fixing an over-160-char line that broke the documentation
  style_check CI job.
- Rework the BSON iterator+sentinel test to parse a BSON file already
  present in the test suite instead of writing/deleting a temp file.

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

* Fix -Wunneeded-internal-declaration for CustomSentinel in test

CustomSentinel lives in an anonymous namespace (internal linkage), and
the library's parse loop only ever evaluates the iterator-first
direction (it != last), so the reversed-order friend operator!= was
never referenced. Clang's -Weverything flags such unused internal
declarations as an error. Drop the unused overload; the used direction
is enough to satisfy can_compare_ne's either-order detection.

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

* Fix clang-tidy hicpp-named-parameter and misc-const-correctness

- Drop the unused reversed-order operator!= overload from
  utils::istreambuf_sentinel (only iterator != sentinel is ever
  evaluated) and name the remaining friend's sentinel parameter, fixing
  hicpp-named-parameter/readability-named-parameter.
- Mark the istreambuf_iterator first/last helper variable const in the
  five binary-format sentinel tests, fixing misc-const-correctness.

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

* Fix clang-tidy misc-const-correctness in heterogeneous sentinel test

json_str is only read via .data()/.size() and never reassigned, so
clang-tidy correctly flags it as const-able. Verified against the exact
CI job (silkeh/clang:dev, ci_clang_tidy target) by running clang-tidy
directly on this file plus the five binary-format sentinel tests
touched by prior commits; all are now clean.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 15:09:28 +02:00
Niels LohmannandGitHub fe2bcc080f Custom BinaryType direct assignment (#5266)
* Fix discussion #4209: custom BinaryType direct assignment and extraction

When a custom BinaryType is configured (other than the default std::vector<uint8_t>),
users can now:
1. Assign values of that type directly to create binary values (not arrays)
2. Extract binary values back to that type with get<>()
3. Extract arrays to that type (for backward compatibility)

Implementation:
- Add is_compatible_binary_type trait to centralize SFINAE condition
- Update to_json to accept custom BinaryType values directly
- Update from_json to handle both binary and array inputs for custom BinaryType
- Add #include <vector> with IWYU comment to from_json.hpp
- Add comprehensive tests for assignment and array extraction
- Update binary_t documentation with example

This is purely additive and invisible to the default nlohmann::json alias, which
continues to treat std::vector<uint8_t> as arrays.

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

* Fix CI: missing include and const-correctness

- Add #include <vector> to type_traits.hpp for the new
  is_compatible_binary_type trait's std::vector<std::uint8_t> reference
  (caught by cpplint's include-what-you-use check)
- Mark test-local json variables const where never reassigned
  (caught by clang-tidy's misc-const-correctness check)

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-11 15:09:05 +02:00
Niels LohmannandGitHub 6ba332c7df Migrate remaining CI jobs off custom json-ci image to official images (#5263)
* Migrate ci_icpc/ci_test_compilers_gcc_old/ci_infer off custom json-ci image

Replaces the last three consumers of ghcr.io/nlohmann/json-ci with official
images: ci_icpc now uses Intel's own intel/oneapi-hpckit:2023.2.1-devel-ubuntu22.04
(the last release with classic icc/icpc before Intel dropped it in oneAPI
2024.0), ci_test_compilers_gcc_old installs old GCCs on official ubuntu:20.04
via the same PPA/archive setup the custom image used (working around
actions/checkout's incompatibility with official gcc:4/5/6 images), and
ci_infer runs directly on ubuntu-latest, fetching Facebook's official Infer
release tarball inline instead of a maintained image. No job in
ubuntu.yml references the custom image anymore.

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

* Update quality-assurance compiler table for CI image migration

Reflects the ci_icpc/ci_test_compilers_gcc_old container migration: the
old-GCC jobs (4.8/4.9/5/6) now compile inside official ubuntu:20.04 rather
than the custom Focal-based json-ci image (same OS, just now attributed to
the official image), and ci_icpc now uses Intel's official
intel/oneapi-hpckit:2023.2.1-devel-ubuntu22.04, bumping the reported ICC
version from 2021.5.0 to 2021.10.0 and the OS from Ubuntu 20.04.3 to 22.04.

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

* Fix CI failures from the json-ci image migration

- ci_icpc: the official intel/oneapi-hpckit image has no CMake preinstalled
  (the custom image bundled one); add the missing lukka/get-cmake step.
- ci_test_compilers_gcc_old: official ubuntu:20.04 has no build tool, so
  CMake's default Unix Makefiles generator failed with "CMAKE_MAKE_PROGRAM
  is not set"; install make alongside the PPA-provided g++.
- ci_infer: Infer v1.1.0's bundled Clang frontend can't parse GCC 14's
  headers (ubuntu-latest's default toolchain), failing with parse errors in
  <bits/unicode.h>; bump to the latest release, v1.3.0, whose newer bundled
  frontend understands them (release asset also renamed upstream from
  infer-linux64-v1.1.0.tar.xz to infer-linux-x86_64-v1.3.0.tar.xz).

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

* Fix ci_test_compilers_gcc_old: g++-6 missing from xenial-only archives

My inline PPA/archive replication only added the xenial main/universe
suites, but g++-6 isn't available there ("has no installation candidate").
The original custom Dockerfile also pulled from bionic main/universe and
xenial-updates main/universe; add those back to match.

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

* Fix ci_test_compilers_gcc_old: install git for CMake's FetchContent tests

Official ubuntu:20.04 ships no git at all (actions/checkout only succeeded
via its API-download fallback). The cmake_fetch_content(2) tests invoke
CMake's own ExternalProject_Add, which needs a real git binary and failed
with "could not find git for clone of json-populate". Install git alongside
the other build prerequisites.

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

* Fix ci_icpc: drop redundant setvars.sh sourcing

Unlike the old custom image, the official intel/oneapi-hpckit image already
has the oneAPI environment (icc/icpc on PATH) baked in at the container
level. Explicitly re-sourcing setvars.sh in the Build step failed with
"setvars.sh has already been run. Skipping re-execution." (exit code 3,
aborting the step under `sh -e`). Drop the now-unnecessary sourcing.

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

* Fix ci_icpc: exclude classic ICC from the std::span regression test

Bumping to Intel's official intel/oneapi-hpckit:2023.2.1 image (see previous
commit) also bumped classic icc/icpc from 2021.5.0 to 2021.10.0. The newer
version's __has_include(<span>) now returns true, but it still can't
actually compile std::span/std::as_bytes usage:

  error: namespace "std" has no member "as_bytes"
  error: namespace "std" has no member "span"

Exclude __ICC/__INTEL_COMPILER the same way _LIBCPP_VERSION is already
excluded for issue #4490.

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

* Fix amalgamation/style check: indent comment per astyle

Verified with the pinned astyle 3.4.13 (make install_astyle) locally;
no further diff.

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

* Fix ci_icpc: skip UTF-8 u8-literal comparison test on classic ICC

test-deserialization_cpp20 failed:

  ERROR: CHECK( j2["emoji"] == "😀" ) is NOT correct!

check_utf8() only guards against MSVC's ANSI-codepage quirk (its docstring
example), but classic ICC has an analogous problem: it doesn't encode a
narrow string literal containing non-ASCII source characters as UTF-8,
so comparing a decoded u8R"(...)" literal against a narrow literal with
the same characters fails. Extend the existing guard.

Verified with the pinned astyle 3.4.13; no diff.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 16:11:41 +02:00
Niels LohmannandGitHub e9c3985f0a Fix documentation gaps found in a full GitHub Discussions review (#5264)
* 📝 Fix documentation gaps found in a full GitHub Discussions review

Reviewed all 1008 GitHub Discussions (2020-2026) for recurring questions
that better or more visible documentation would have avoided. Adds/expands
documentation for ~26 distinct gaps, including:

- New "Debugging" page collecting natvis, GDB pretty printer, LLDB status,
  and JSON_DIAGNOSTICS pointers (previously scattered/undiscoverable)
- Thread-safety and schema-validation FAQ entries
- StringType's char-based requirement (no wstring/u16string/u32string)
- Brace-initialization-yields-arrays warning directly on the constructor
  reference page (previously only in the FAQ, missed by users reading
  the constructor docs)
- std::any exclusion from get<T>(), with a manual-dispatch example
- Non-string-keyed std::map serializing as an array of pairs
- ordered_json compatibility with NLOHMANN_DEFINE_TYPE_* macros
  (already worked, was undocumented)
- std::array truncation on size-mismatched conversion (no exception)
- static_cast vs. get<std::optional<T>>() divergence
- Recipe for omitting a std::optional field instead of emitting null
- No built-in nesting-depth limit during parsing + a callback-based
  workaround recipe
- Recipe for streaming a large homogeneous array via parser callbacks
- operator>> stream-position semantics for concatenated JSON values
- JSON Pointer array-vs-object creation rule for non-existing paths
- CMake target name (nlohmann_json_modules) needed to link C++20 modules
- ESP-IDF/PlatformIO: no official package, link to a community fork
- get(key, default) as the Python dict.get() equivalent
- reserve() recipe for pre-allocating array capacity
- JSONC as an alias for the existing ignore_comments/ignore_trailing_commas
  combination (distinct from the unsupported JSON5)
- items() dereferenced-element type: decltype() idiom + detail-namespace
  stability caveat
- Various macro/type-conversion limitations (MSGPACK_DEFINE_ARRAY
  equivalent, char-array round-tripping, ADL serializer macro gap)

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

* 🎨 fix format

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 16:01:08 +02:00
Niels LohmannandGitHub b630f5e9c7 Fix container input_adapter SFINAE for lvalue-only ADL begin/end (#5260)
* Fix container input_adapter SFINAE for lvalue-only ADL begin/end (#111)

The container overload of json::parse(c) / accept(c) / sax_parse(c, ...)
silently dropped from overload resolution for user types whose ADL
begin(T&) / end(T&) accepted only non-const lvalue references
(a legitimate pattern matching std::begin semantics). This was because
the detection code used std::declval<ContainerType>() which synthesized
an rvalue, and the rvalue failed to bind to lvalue-only ADL functions.

Fix by making both the outer input_adapter(ContainerType&&) and the
factory's create(ContainerType&&) forwarding references, preserving the
caller's value category and constness via reference collapsing. This
ensures detection (std::declval) and actual use (std::forward) always
match without needing decay/remove_reference.

- Rewrite input_adapters.hpp container overload with forwarding refs
- Add regression tests for lvalue-only non-const ADL begin/end
- Add regression test for rvalue containers (no breakage)
- Update API docs (parse, accept, sax_parse, from_*) to clarify
  that begin/end must match std::begin/std::end semantics
- Add version history notes for 3.13.0
- Regenerate amalgamation

Second-order effect: binary_reader.hpp's internal call to
input_adapter(number_vector) now deduces iterator vs const_iterator
based on the lvalue; functionally harmless (iterator_input_adapter is
iterator-type-agnostic), verified via unit-ubjson/unit-bjdata tests.

Closes remaining limitation from #4354 / PR #5218 (todo 106).

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

* Avoid strlen() in test container to fix Codacy CWE-126 flag

Suppressing the strlen()-based CWE-126 warning with NOLINT/nosec
comments only silenced clang-tidy and the standalone Flawfinder
Action; Codacy's own analysis (which also flags this pattern and
doesn't honor those suppression comments) still reported it as a new
issue, plus flagged the near-duplicate begin/end pair as cloned code.

Store the buffer's size explicitly in MyContainerNonConstADL instead
of computing it via strlen() in end(), which removes the flagged
pattern outright and also de-duplicates the struct from the existing
MyContainer's char*-based begin/end pair.

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

* Avoid trailing return type to satisfy clang-tidy fuchsia-trailing-return

The forwarding-reference input_adapter(ContainerType&&) entry point was
written with an auto/trailing-decltype return type, but this project's
ci_clang_tidy job enables the fuchsia-trailing-return check as an
error, which rejects it. The return type only depends on the template
parameter ContainerType, not on the runtime parameter, so it can be
written as an ordinary leading return type instead - no functional
change.

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

* Avoid C-style array in test to satisfy clang-tidy avoid-c-arrays

clang-tidy's cppcoreguidelines/hicpp/modernize-avoid-c-arrays checks
flagged the char raw_data[] declaration used to reproduce the
lvalue-only non-const ADL begin/end scenario. Use std::string instead
and take a mutable pointer via &raw_data[0], which is the standard
way to get a non-const char* into a string's buffer under C++11
(std::string::data() only returns non-const in C++17 and later).

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-10 13:56:06 +02:00
Niels Lohmann 4d8e7a7210 💚 fix build
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 23:05:56 +02:00
Niels LohmannandGitHub 75e8fbac32 Documentation review: fix stale version-history placeholder in operator_ne.md (#5261)
* 📝 Fix stale 3.12.x placeholder in operator_ne.md version history

PR #5253 (removing the hand-written operator!= to fix #3868/P2468R2)
merged after the earlier 3.12.x -> 3.13.0 global sweep, so its new
version-history entries were written with the stale placeholder.

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

* 🐛 Fix stale twitter.com link in docset.json

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 20:57:41 +02:00
Niels LohmannandGitHub 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
d0a43141ea Fix #3868: Remove operator!= to enable P2468R2 rewritten candidate synthesis (#5253)
* Fix #3868: Remove operator!= to enable P2468R2 rewritten candidate synthesis

Under C++20 P2468R2, a hand-written operator!= suppresses the compiler's
rewritten-candidate synthesis for operator==, preventing heterogeneous
comparisons like `std::string s; json j; s == j;` from compiling.

Fix by removing the hand-written operator!=, allowing the compiler to
synthesize != as !(a==b) in all language modes (C++20 member functions
and pre-C++20 friend functions).

Behavior change: operator!= now returns !(a==b) unconditionally, including
for special values like NaN and discarded. This means:
- NaN != NaN now returns true (matches IEEE-754 semantics)
- discarded != x now returns true for any x (matches !(discarded == x))

This also fixes underlying defects in previously-working code:
- Restores direct == comparison for views vs json (reverts std::ranges::equal
  workaround added in PR #3950 to dodge this bug)
- Re-enables std::string == json comparisons (uncomments check in
  unit-constructor1.cpp)

Fixes: #3868, #3979

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* 🚨 fix warning

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 20:38:39 +02:00
Niels LohmannandGitHub ecff144b3a 📝 Document nvcc CUDA 12.0/12.1 JSON_HAS_RANGES exclusion (#5258)
PR #5248 added a 5th JSON_HAS_RANGES exclusion branch to
macro_scope.hpp (nvcc CUDA 12.0.x/12.1.x, fixed in 12.2, issue #3907)
shortly after #5252 added the "Known compiler/stdlib exclusions"
list to json_has_ranges.md, so the new branch was missing from the
just-added doc section. Bring the list back to parity with the code
(5 exclusion branches, 5 documented).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 20:19:22 +02:00