Compare commits

..

18 Commits

Author SHA1 Message Date
Niels Lohmann fa9741f0ce 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>
2026-07-11 00:10:23 +02:00
Niels Lohmann da95d1184e 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>
2026-07-10 23:25:21 +02:00
Niels Lohmann 146e5e0bc7 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>
2026-07-10 23:15:17 +02:00
Niels Lohmann 2269656bc6 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>
2026-07-10 19:50:04 +02:00
Niels Lohmann 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 Lohmann 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 Lohmann 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 Lohmann 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 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 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 Lohmann 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
Niels Lohmann 855f511db4 Fix stale Clang -Weverything suppression comments; drop -Wno-missing-noreturn (#5250)
* Fix stale Clang -Weverything suppression comments; eliminate -Wno-missing-noreturn

cmake/clang_flags.cmake claimed -Wno-unsafe-buffer-usage was needed only
for Doctest and that -Wno-missing-noreturn had "no way to silence...
otherwise" (PR #4871, which never actually attempted a source fix).
Neither held up under investigation (todo 130):

- -Wno-unsafe-buffer-usage is pervasive (208 distinct sites across 19
  files measured with clang trunk in silkeh/clang:dev), spanning the
  library's own low-level numeric/buffer code (to_chars, serializer,
  lexer, binary reader/writer, input adapters, json_pointer) as well as
  vendored Doctest itself (96 of the 208 sites). A source-level fix is
  not feasible at this scale; the comment now says so instead of
  blaming Doctest alone.

- -Wno-missing-noreturn had exactly two real trigger sites, both
  genuinely and unconditionally non-returning: a test-only throwing
  allocator (tests/src/unit-allocator.cpp) and, previously undiscovered,
  wide_string_input_adapter::get_elements<T>() in
  include/nlohmann/detail/input/input_adapters.hpp. Verified this isn't
  a wider pattern by checking all 160 JSON_THROW call sites in the
  library for functions whose entire body is an unconditional throw.
  Annotated both ([[noreturn]] in the test file, since JSON_HEDLEY_NO_RETURN
  is #undef'd by the time test code runs; JSON_HEDLEY_NO_RETURN in the
  library file, its first real use anywhere in the codebase) and
  dropped the suppression entirely.

single_include/nlohmann/json.hpp regenerated via `make amalgamate`;
`make check-amalgamation` passes.

Verified in Docker (silkeh/clang:dev, matching the ci_static_analysis_clang
CI job): baseline builds clean, and the full 194-target test suite builds
with zero warnings under the corrected CLANG_CXXFLAGS (-Wno-missing-noreturn
no longer in the list). Also sanity-compiled and ran unit-allocator.cpp and
unit-wstring.cpp on host Apple Clang to confirm behavior is unchanged.

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

* Fix MSVC C4702 warning caused by JSON_HEDLEY_NO_RETURN on get_elements()

PR #5250 annotated wide_string_input_adapter::get_elements<T>() with
JSON_HEDLEY_NO_RETURN (it unconditionally throws). On MSVC this expands to
__declspec(noreturn), and MSVC correctly determined that the code following
its call in binary_reader.hpp is unreachable for that instantiation, firing
C4702 under /W4 /WX in the msvc, msvc-vs2026, and msvc-arm64 Debug jobs.

Clang doesn't flag this case, so the Docker verification for #5250 (which
only checked Clang -Weverything) didn't catch it.

This is the same warning class already tolerated for Release builds since
PR #5216, where MSVC's optimizer independently found the same dead code
after /Od was removed. Extend that existing /wd4702 suppression to Debug
builds too, instead of reverting the noreturn annotation.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 20:18:28 +02:00
Niels Lohmann d0de6a9111 Document std::optional<T> direct construction limitation (#5247)
* Document std::optional<T> direct-init/copy-init limitation with null

Add regression test pinning current behavior (CHECK_THROWS_AS) in the null
section of unit-conversions.cpp with detailed comment explaining the C++
language-level cause (std::optional's own converting constructor wins
overload resolution over basic_json::operator T()).

Add a warning callout in conversions.md documenting that direct construction/
assignment of std::optional<T> from JSON null throws type_error 302, with a
clear workaround (use get<std::optional<T>>() or get_to() instead, which
correctly produce std::nullopt).

This is a limitation at the language level: there is no SFINAE path to
distinguish "called from inside std::optional's own constructor" from "direct
call", so fixing it would require breaking changes to operator ValueType().
A permanent fix belongs in the 4.0 type-strictness redesign (#3453).

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

* Fix issue reference in std::optional test comment

Update the comment in the null section test to reference #5246 instead of
placeholder #XXXX, clarifying where the direct-init/copy-init limitation is tracked.

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

* Use CHECK_THROWS_AS_WITH for std::optional test assertions

Update the regression tests to use CHECK_THROWS_AS_WITH instead of
CHECK_THROWS_AS to verify both the exception type and the error message.

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

* Fix CI: use CHECK_THROWS_WITH_AS, the macro that actually exists

CHECK_THROWS_AS_WITH is not a doctest macro; the correct one used throughout
this test suite is CHECK_THROWS_WITH_AS(expr, message, exception_type&), with
the message before the type and the type as a reference. The previous commit
didn't catch this because it only compiled the file standalone with default
settings; this TEST_CASE only compiles under
`#if !JSON_USE_IMPLICIT_CONVERSIONS`, which is why ci_test_noimplicitconversions
was the job that failed. Verified by building and running the test in that
exact configuration (JSON_USE_IMPLICIT_CONVERSIONS=0): 14/14 assertions pass.

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

* Run std::optional test under default implicit-conversions build too

TEST_CASE("std::optional") was guarded by #if !JSON_USE_IMPLICIT_CONVERSIONS,
so it only ever compiled in the non-default build with implicit conversions
disabled. This traces back to commit 1d7688aef (fixes #3859), which changed a
previously dead #ifndef JSON_USE_IMPLICIT_CONVERSIONS guard (the macro is
always defined by that point, so it never held) to #if !JSON_USE_IMPLICIT_CONVERSIONS
-- making the test compile for the first time, but only in the disabled-conversions
build. As a result, std::optional support had zero test coverage in the default
configuration almost every user builds with.

Verified the entire test case (all sections: null, string, bool, number, array,
object) compiles and passes identically with JSON_USE_IMPLICIT_CONVERSIONS both
on (default) and off -- nothing in it actually depends on the setting. Removing
the guard closes the coverage gap with no behavior change: 285 assertions pass
with implicit conversions on, 232 with them off (the difference comes from
other, unrelated conditionally-compiled tests in this file).

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 Code <noreply@anthropic.com>
2026-07-09 19:03:49 +02:00
Niels Lohmann f8e99e856c Fix nvcc CUDA 12.0/12.1 C++20 ranges parse error (#3907) (#5248)
* Test ci_cuda_example against a CUDA version matrix at C++20 (#3907)

The ci_cuda_example job compiled against the json-ci image's CUDA
11.0 toolkit at cuda_std_11, which cannot exercise #3907 (a c++20
parse error in iteration_proxy.hpp's enable_borrowed_range reported
under nvcc). Switch the job to pull official nvidia/cuda devel images
directly and matrix across CUDA 11.8-12.6 at cuda_std_20 so CI can
empirically confirm which versions are actually affected before any
source-level fix is attempted.

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

* Fix nvcc CUDA 12.0/12.1 C++20 ranges parse error (#3907)

The diagnostic matrix in this PR confirmed the affected range exactly:
nvcc 12.0.1 and 12.1.1 both fail with "expected initializer before
'<' token" on iteration_proxy.hpp's enable_borrowed_range variable
template specialization at -std=c++20; 12.2.2 and newer already build
cleanly. Guard JSON_HAS_RANGES off for that narrow nvcc version range,
matching the existing GCC-11/libstdc++ carve-outs in the same ifdef
chain, and regenerate single_include accordingly.

Broaden the CUDA smoke test to also exercise comparisons
(operator==/operator<=>, gated independently by
JSON_HAS_THREE_WAY_COMPARISON) and range-based iteration, not just
dump()/erase(), so the fix's actual scope is evidenced by CI rather
than assumed from the single reported symptom.

Have tests/cuda_example/CMakeLists.txt pick the newest C++ standard
the detected nvcc version actually supports (20/17/11) instead of
hard-requiring C++20, so older toolkits build at a lower standard
instead of failing CMake configure outright. This is test-project-local
only; the JSON_HAS_RANGES guard is what protects real client code,
since a header can't control what -std= flag it's compiled with.

Right-size the CI matrix from the 8-version diagnostic sweep down to
11.8.0 (C++17 fallback path) / 12.1.1 (permanent #3907 regression
guard) / 12.6.3 (recent coverage), and update the compiler-version
table in the quality assurance docs to match.

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

* Fix ci_cuda_example CUDA 11.8 build after C++17 fallback (#3907)

The 11.8.0 leg's graceful C++17 fallback (added in the previous commit)
worked correctly, but the broadened smoke test used the <=> operator
unconditionally, which isn't valid syntax pre-C++20 — nvcc rejected it
with "expected an expression" once the CMake logic picked cuda_std_17
for the older toolkit. Gate those two lines behind
JSON_HAS_THREE_WAY_COMPARISON like the library itself does internally.

Sanity-compiled the file as plain C++ at both -std=c++17 (skips the
guarded block) and -std=c++20 (includes it) locally; the actual nvcc
build is verified via CI on PR #5248.

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 19:02:36 +02:00
Niels Lohmann 521a084827 Documentation review (#5257)
* 📡 Fix documentation gaps for 3.13.0 release (todos 138-142)

- Todo 138: Add "Known issues" section to modules.md with compiler-specific troubleshooting (GCC redefinition, MSVC symbol export). Add pointer note to quality_assurance.md.
- Todo 139: Document CBOR/MessagePack half-precision float encoding for NaN/Infinity (0xF9/0xCA with exact byte sequences). Explain pre-3.13.0 double-precision bug mechanism without issue citations.
- Todo 140: Document CBOR negative-integer-overflow rejection (parse_error.112) for magnitudes exceeding int64_t range (already implemented in rev 1).
- Todo 141: Update version history in value.md and operator[].md with behavior-change details, removing issue citations per citation policy (prose is self-contained).
- Todo 142: Global sed replace of 3.12.x → 3.13.0 placeholder across all 20 documentation files.

Revision 2 incorporates feedback to reduce changelog-like issue citations. Only citations that add unique troubleshooting value are retained (#5103 for GCC workaround, #3970 for MSVC symbol export). "Known issues" section follows PR #5252's visual pattern (info admonition with bold-bullet format).

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

* 📡 Document integer type selection, type_name() invalid value, and std::optional get() fix

- number_handling.md: clarify that positive/negative integers select
  unsigned/signed storage based on the leading minus sign (todo 143).
- type_name.md: document the new "invalid" return value for corrupted
  JSON values (todo 145).
- get.md: note that get<std::optional<T>>() was unreachable in every
  configuration prior to 3.13.0 due to an internal macro-guard bug,
  unrelated to JSON_USE_IMPLICIT_CONVERSIONS's actual effect (todo 144).

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-07-09 17:24:19 +02:00
Niels Lohmann ca91678af1 Document compiler/stdlib exclusions in macro_scope.hpp (#5252)
* 📡 Document compiler/stdlib exclusions in macro_scope.hpp

Add "Known compiler/stdlib exclusions" subsections to the public documentation for
JSON_HAS_FILESYSTEM and JSON_HAS_RANGES, listing the exact compiler/stdlib versions
that are silently excluded even when feature-test macros indicate support. Each
exclusion references the originating issue. Also add a pointer note in the compiler
compatibility section linking to these details.

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

* 🧛 fix build

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

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-07-09 17:15:57 +02:00
Niels Lohmann ff34a3fd2f Fix flaky ci_nvhpc job: pin nvc++ target to generic baseline (-tp=px) (#5254) 2026-07-09 15:16:28 +02:00
89 changed files with 1251 additions and 470 deletions
+52 -7
View File
@@ -33,11 +33,21 @@ jobs:
ci_infer:
runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v2.4.0
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Install Infer
run: |
wget -q -O - "https://github.com/facebook/infer/releases/download/v1.3.0/infer-linux-x86_64-v1.3.0.tar.xz" | sudo tar -C /opt -xJ
sudo ln -s /opt/infer-linux-x86_64-v1.3.0/bin/infer /usr/local/bin/infer
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Get latest CMake and ninja
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
- name: Run CMake
run: cmake -S . -B build -DJSON_CI=On
- name: Build
@@ -143,11 +153,30 @@ jobs:
strategy:
matrix:
compiler: ['4.8', '4.9', '5', '6']
container: ghcr.io/nlohmann/json-ci:v2.4.0
# official gcc:4.8/4.9/5/6 images fail to check out code (too old for
# actions/checkout); install the old compilers on top of official ubuntu:20.04
# instead, mirroring what the (now retired) custom json-ci image did.
container: ubuntu:20.04
steps:
- name: Install g++-${{ matrix.compiler }}
run: |
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends software-properties-common ca-certificates gnupg make git
add-apt-repository -y ppa:ubuntu-toolchain-r/test
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main"
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic universe"
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main"
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial universe"
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates main"
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates universe"
apt-get update
apt-get install -y --no-install-recommends g++-${{ matrix.compiler }}
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Get latest CMake and ninja
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
- name: Run CMake
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
- name: Build
@@ -234,11 +263,22 @@ jobs:
ci_cuda_example:
runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v2.4.0
strategy:
fail-fast: false
matrix:
# 11.8.0: newest pre-C++20 CUDA release, exercises the C++17 fallback
# path (tests/cuda_example/CMakeLists.txt picks the standard per nvcc
# version); 12.1.1: permanent regression guard for #3907 (nvcc 12.0/12.1
# choke on enable_borrowed_range at C++20, fixed in 12.2); 12.6.3: recent
# CUDA/C++20 coverage.
cuda: ['11.8.0', '12.1.1', '12.6.3']
container: nvidia/cuda:${{ matrix.cuda }}-devel-ubuntu22.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Get latest CMake and ninja
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
- name: Run CMake
run: cmake -S . -B build -DJSON_CI=On
- name: Build
@@ -276,17 +316,22 @@ jobs:
ci_icpc:
runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v2.2.0
# Intel discontinued the classic icc/icpc compiler in oneAPI 2024.0; this is
# Intel's own last officially published image that still includes it.
container: intel/oneapi-hpckit:2023.2.1-devel-ubuntu22.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Get latest CMake and ninja
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
- name: Run CMake
run: cmake -S . -B build -DJSON_CI=On
- name: Build
run: |
. /opt/intel/oneapi/setvars.sh
cmake --build build --target ci_icpc
# No need to source setvars.sh here: unlike the old custom image, this
# official image already has the oneAPI environment (icc/icpc on PATH)
# baked in, and re-sourcing it fails with "already been run" (exit 3).
run: cmake --build build --target ci_icpc
ci_icpx:
runs-on: ubuntu-latest
+6 -2
View File
@@ -669,7 +669,6 @@ add_custom_target(ci_test_compiler_default
add_custom_target(ci_cuda_example
COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja
-DCMAKE_CUDA_HOST_COMPILER=g++-8
-S${PROJECT_SOURCE_DIR}/tests/cuda_example -B${PROJECT_BINARY_DIR}/build_cuda_example
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_cuda_example
)
@@ -720,6 +719,11 @@ add_custom_target(ci_icpx
# to zero and does not honor NaN ordering; -Kieee restores strict IEEE 754 behavior
# (needed for the dtoa/grisu and NaN-comparison code paths).
#
# -tp=px pins the target processor to the generic x86-64 baseline (SSE2-only) to avoid
# a nvc++ 25.5 / LLVM issue: when nvc++ auto-detects -tp from the runner's CPU (e.g. -tp znver4),
# certain attribute combinations trigger an llc instruction-selection crash on std::ldexp<unsigned>.
# Pinning to px removes this variability and is robust to future llc/nvc++ updates.
#
# The following tests are excluded as they trigger known nvc++ 25.5 defects (not
# library bugs); see https://github.com/nlohmann/json for tracking. Only the
# affected language-standard variants are excluded so coverage is otherwise kept:
@@ -733,7 +737,7 @@ add_custom_target(ci_nvhpc
COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja
-DCMAKE_C_COMPILER=nvc -DCMAKE_CXX_COMPILER=nvc++
-DCMAKE_CXX_FLAGS=-Kieee
-DCMAKE_CXX_FLAGS="-Kieee;-tp=px"
-DJSON_BuildTests=ON -DJSON_FastTests=ON
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_nvhpc
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_nvhpc
+5 -3
View File
@@ -5,8 +5,11 @@
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
# -Wno-padded We do not care about padding warnings.
# -Wno-covered-switch-default All switches list all cases and a default case.
# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile.
# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871
# -Wno-unsafe-buffer-usage Pervasive: the library's own low-level numeric/buffer code
# (to_chars, serializer, lexer, binary reader/writer, input
# adapters, json_pointer) plus vendored Doctest itself (~208
# distinct sites measured 2026-07-08 on clang trunk) all use
# raw pointer arithmetic / libc string calls by necessity.
set(CLANG_CXXFLAGS
-Werror
@@ -18,5 +21,4 @@ set(CLANG_CXXFLAGS
-Wno-padded
-Wno-covered-switch-default
-Wno-unsafe-buffer-usage
-Wno-missing-noreturn
)
+1 -1
View File
@@ -4,7 +4,7 @@
"archive": "JSON_for_Modern_C++.tgz",
"author": {
"name": "Niels Lohmann",
"link": "https://twitter.com/nlohmann"
"link": "https://nlohmann.me"
},
"aliases": ["nlohmann/json"]
}
+17 -7
View File
@@ -8,8 +8,8 @@ static bool accept(InputType&& i,
const bool ignore_trailing_commas = false);
// (2)
template<typename IteratorType>
static bool accept(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static bool accept(IteratorType first, SentinelType last,
const bool ignore_comments = false,
const bool ignore_trailing_commas = false);
```
@@ -17,10 +17,11 @@ static bool accept(IteratorType first, IteratorType last,
Checks whether the input is valid JSON.
1. Reads from a compatible input.
2. Reads from a pair of character iterators
2. Reads from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
respectively as UTF-8, UTF-16, and UTF-32.
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
the iterator type with `operator!=`.
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
(i.e., a parse error) nor creates diagnostic information.
@@ -35,7 +36,8 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters (throws if null)
- a `std::string`
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type, for instance.
@@ -43,6 +45,12 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
- a pair of pointers such as `ptr` and `ptr + len`
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
- a custom sentinel type for C++20 ranges
- `std::counted_iterator` with a different sentinel type
## Parameters
`i` (in)
@@ -60,7 +68,7 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
: iterator to the start of the character range
`last` (in)
: iterator to the end of the character range
: iterator to the end of the character range, or a sentinel value that compares equal to the end iterator with `operator!=`
## Return value
@@ -109,7 +117,9 @@ A UTF-8 byte order mark is silently ignored.
- Added in version 3.0.0.
- Ignoring comments via `ignore_comments` added in version 3.9.0.
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
- Added `ignore_trailing_commas` in version 3.12.x.
- Added `ignore_trailing_commas` in version 3.13.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
+16 -1
View File
@@ -115,7 +115,22 @@ basic_json(basic_json&& other) noexcept;
Function [`array()`](array.md) and [`object()`](object.md) force array and object creation from initializer lists,
respectively.
!!! warning "Brace initialization yields arrays"
Because this constructor takes an `initializer_list_t`, brace-initializing a `json`/`ordered_json` from
another `json` value wraps it in a single-element array rather than copying it:
```cpp
json j1 = "hello";
json j2{j1}; // [!] j2 is ["hello"], NOT a copy of j1
json j3(j1); // j3 is "hello" -- parentheses copy as expected
```
See the FAQ entry on [brace initialization](../../home/faq.md#brace-initialization-yields-arrays) for the
full explanation, an opt-in macro to change this behavior, and how to explicitly create a single-element
array (`json::array({value})`) if that is what you want.
6. Constructs a JSON array value by creating `cnt` copies of a passed value. In case `cnt` is `0`, an empty array is
created.
@@ -37,6 +37,14 @@ represent a byte array in modern C++.
`BinaryType`
: container type to store arrays
Although not formally expressed as a C++ concept, `BinaryType` must be default-constructible,
copy/move-constructible, and support `push_back()`, `.data()`, and `.size()`, because
[`byte_container_with_subtype`](../byte_container_with_subtype/index.md) derives directly from it. Its
`value_type` must additionally be exactly one byte wide (e.g., `std::uint8_t`/`char`/`std::byte`): the binary
serializers (CBOR, MessagePack, BSON, UBJSON) read and write the container's raw bytes via
`reinterpret_cast`, which is only correct for byte-sized elements -- a container like
`#!cpp std::vector<std::intptr_t>` will not work as `BinaryType`.
## Notes
#### Default type
+1 -1
View File
@@ -92,4 +92,4 @@ std::string format_as(const BasicJsonType& j)
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
+12 -5
View File
@@ -7,8 +7,8 @@ static basic_json from_bjdata(InputType&& i,
const bool strict = true,
const bool allow_exceptions = true);
// (2)
template<typename IteratorType>
static basic_json from_bjdata(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static basic_json from_bjdata(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true);
```
@@ -16,7 +16,7 @@ static basic_json from_bjdata(IteratorType first, IteratorType last,
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
1. Reads from a compatible input.
2. Reads from an iterator range.
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md).
@@ -29,11 +29,16 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
custom sentinel type for C++20 ranges
## Parameters
`i` (in)
@@ -43,7 +48,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
: iterator to the start of the input
`last` (in)
: iterator to the end of the input
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
`strict` (in)
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
@@ -101,3 +106,5 @@ Linear in the size of the input.
## Version history
- Added in version 3.11.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
+12 -5
View File
@@ -7,8 +7,8 @@ static basic_json from_bson(InputType&& i,
const bool strict = true,
const bool allow_exceptions = true);
// (2)
template<typename IteratorType>
static basic_json from_bson(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static basic_json from_bson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true);
```
@@ -16,7 +16,7 @@ static basic_json from_bson(IteratorType first, IteratorType last,
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
1. Reads from a compatible input.
2. Reads from an iterator range.
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md).
@@ -29,11 +29,16 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
custom sentinel type for C++20 ranges
## Parameters
`i` (in)
@@ -43,7 +48,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
: iterator to the start of the input
`last` (in)
: iterator to the end of the input
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
`strict` (in)
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
@@ -101,6 +106,8 @@ Linear in the size of the input.
## Version history
- Added in version 3.4.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
+12 -5
View File
@@ -9,8 +9,8 @@ static basic_json from_cbor(InputType&& i,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
// (2)
template<typename IteratorType>
static basic_json from_cbor(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static basic_json from_cbor(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
@@ -19,7 +19,7 @@ static basic_json from_cbor(IteratorType first, IteratorType last,
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
1. Reads from a compatible input.
2. Reads from an iterator range.
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md).
@@ -32,11 +32,16 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
custom sentinel type for C++20 ranges
## Parameters
`i` (in)
@@ -46,7 +51,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
: iterator to the start of the input
`last` (in)
: iterator to the end of the input
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
`strict` (in)
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
@@ -111,6 +116,8 @@ Linear in the size of the input.
- Changed to consume input adapters, removed `start_index` parameter, and added `strict` parameter in version 3.0.0.
- Added `allow_exceptions` parameter in version 3.2.0.
- Added `tag_handler` parameter in version 3.9.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
@@ -7,8 +7,8 @@ static basic_json from_msgpack(InputType&& i,
const bool strict = true,
const bool allow_exceptions = true);
// (2)
template<typename IteratorType>
static basic_json from_msgpack(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static basic_json from_msgpack(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true);
```
@@ -16,7 +16,7 @@ static basic_json from_msgpack(IteratorType first, IteratorType last,
Deserializes a given input to a JSON value using the MessagePack serialization format.
1. Reads from a compatible input.
2. Reads from an iterator range.
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md).
@@ -29,11 +29,16 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
custom sentinel type for C++20 ranges
## Parameters
`i` (in)
@@ -43,7 +48,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
: iterator to the start of the input
`last` (in)
: iterator to the end of the input
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
`strict` (in)
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
@@ -103,6 +108,8 @@ Linear in the size of the input.
- Parameter `start_index` since version 2.1.1.
- Changed to consume input adapters, removed `start_index` parameter, and added `strict` parameter in version 3.0.0.
- Added `allow_exceptions` parameter in version 3.2.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
+12 -5
View File
@@ -7,8 +7,8 @@ static basic_json from_ubjson(InputType&& i,
const bool strict = true,
const bool allow_exceptions = true);
// (2)
template<typename IteratorType>
static basic_json from_ubjson(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static basic_json from_ubjson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true);
```
@@ -16,7 +16,7 @@ static basic_json from_ubjson(IteratorType first, IteratorType last,
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
1. Reads from a compatible input.
2. Reads from an iterator range.
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md).
@@ -29,11 +29,16 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance a
custom sentinel type for C++20 ranges
## Parameters
`i` (in)
@@ -43,7 +48,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
: iterator to the start of the input
`last` (in)
: iterator to the end of the input
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
`strict` (in)
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
@@ -102,6 +107,8 @@ Linear in the size of the input.
- Added in version 3.1.0.
- Added `allow_exceptions` parameter in version 3.2.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
+7
View File
@@ -114,6 +114,13 @@ overload (3).
See [Number conversion](../../features/types/number_handling.md#number-conversion)
for more information.
!!! note "`std::optional` conversions"
Prior to version 3.13.0, `#!cpp get<std::optional<T>>()` (and other conversions to `std::optional<T>`) failed to
compile in every configuration, due to an internal implementation bug that made the `from_json` overload for
`std::optional` unreachable regardless of the [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md)
setting. This has been fixed.
## Examples
??? example
+11
View File
@@ -46,6 +46,17 @@ for (auto& [key, val] : j_object.items())
}
```
If you need to name the type of the dereferenced element explicitly (e.g., to write a standalone function that
takes it as a parameter, or to use `items()` with `std::for_each`), use `decltype`:
```cpp
using element_type = decltype(*j_object.items().begin());
```
The per-element type (`iteration_proxy_value`) lives in the library's internal `detail` namespace and is
intentionally unspecified as a stable, named type -- `decltype` is the supported way to obtain it, but its exact
name/definition may change between versions.
## Return value
iteration proxy object wrapping the current value with an interface to use in range-based for loops
+2 -1
View File
@@ -63,7 +63,8 @@ behavior:
object will agree on the name-value mappings.
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be
chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or
`#!json {"key": 2}`.
`#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see
[this parsing recipe](../../features/parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see
[`dump`](dump.md)) in this order. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored
and serialized as `#!json {"a": 2, "b": 1}`.
+11 -1
View File
@@ -124,6 +124,15 @@ Strong exception safety: if an exception occurs, the original value stays intact
filled with `#!json null`.
- The special value `-` is treated as a synonym for the index past the end.
!!! note "Creating intermediate levels that don't exist yet"
When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing
leaf), each missing level is created as an array or an object depending on whether the corresponding
pointer token parses as a non-negative integer: a numeric token creates an array, a non-numeric token
creates an object. For example, on an initially `#!json null` value, `/foo/0/0/0` creates nested arrays,
while `/foo/one/one/one` creates nested objects. This is not specified by the JSON Pointer RFC; it is
this library's own, intentional disambiguation rule. See also [JSON Pointer](../../features/json_pointer.md).
## Examples
??? example "Example: (1) access specified array element"
@@ -251,5 +260,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. Added in version 1.0.0.
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
in version 3.11.0.
3. Added in version 3.11.0.
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0.
+11 -12
View File
@@ -19,10 +19,8 @@ class basic_json {
};
```
1. Compares two JSON values for inequality according to the following rules:
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either operand is `NaN` and
the other operand is either `NaN` or any other number.
- Otherwise, returns the result of `#!cpp !(lhs == rhs)` (until C++20) or `#!cpp !(*this == rhs)` (since C++20).
1. Compares two JSON values for inequality. Returns `#!cpp !(lhs == rhs)` (until C++20) or `#!cpp !(*this == rhs)` (since C++20).
- This means the comparison is simply the logical negation of `operator==`, including for special values like `NaN` and `discarded`.
2. Compares a JSON value and a scalar or a scalar and a JSON value for inequality by converting the scalar to a JSON
value and comparing both JSON values according to 1.
@@ -54,13 +52,12 @@ Linear.
## Notes
!!! note "Comparing `NaN`"
!!! note "Comparing `NaN` and `discarded`"
`NaN` values are unordered within the domain of numbers.
The following comparisons all yield `#!cpp false`:
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
Since `operator!=` is defined as `!(a == b)`, the behavior for special values follows that of `operator==`:
- For `NaN` values: `NaN == NaN` yields `#!cpp false`, so `NaN != NaN` yields `#!cpp true`.
- For `discarded` values: `discarded == x` yields `#!cpp false` for any `x`, so `discarded != x` yields `#!cpp true`.
## Examples
@@ -94,5 +91,7 @@ Linear.
## Version history
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`.
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`.
+17 -7
View File
@@ -10,8 +10,8 @@ static basic_json parse(InputType&& i,
const bool ignore_trailing_commas = false);
// (2)
template<typename IteratorType>
static basic_json parse(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType>
static basic_json parse(IteratorType first, SentinelType last,
const parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false,
@@ -19,10 +19,11 @@ static basic_json parse(IteratorType first, IteratorType last,
```
1. Deserialize from a compatible input.
2. Deserialize from a pair of character iterators
2. Deserialize from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
The `value_type` of the iterator must be an integral type with size of 1, 2, or 4 bytes, which will be interpreted
respectively as UTF-8, UTF-16, and UTF-32.
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
the iterator type with `operator!=`.
## Template parameters
@@ -34,7 +35,8 @@ static basic_json parse(IteratorType first, IteratorType last,
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters (throws if null)
- a `std::string`
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type, for instance.
@@ -42,6 +44,12 @@ static basic_json parse(IteratorType first, IteratorType last,
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
- a pair of pointers such as `ptr` and `ptr + len`
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
- a custom sentinel type for C++20 ranges
- `std::counted_iterator` with a different sentinel type
## Parameters
`i` (in)
@@ -66,7 +74,7 @@ static basic_json parse(IteratorType first, IteratorType last,
: iterator to the start of a character range
`last` (in)
: iterator to the end of a character range
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
## Return value
@@ -235,7 +243,9 @@ Invalid Unicode escapes and unpaired surrogates in the input are reported as
- Overload for contiguous containers (1) added in version 2.0.3.
- Ignoring comments via `ignore_comments` added in version 3.9.0.
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
- Added `ignore_trailing_commas` in version 3.12.x.
- Added `ignore_trailing_commas` in version 3.13.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
+1 -1
View File
@@ -74,4 +74,4 @@ is thrown. In any case, the original value is not changed: the patch is applied
- Added in version 2.0.0.
- Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's
target location has a non-object/non-array parent in version 3.12.x.
target location has a non-object/non-array parent in version 3.13.0.
@@ -71,4 +71,4 @@ function throws an exception.
- Added in version 3.11.0.
- Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's
target location has a non-object/non-array parent in version 3.12.x.
target location has a non-object/non-array parent in version 3.13.0.
+14 -8
View File
@@ -11,8 +11,8 @@ static bool sax_parse(InputType&& i,
const bool ignore_trailing_commas = false);
// (2)
template<class IteratorType, class SAX>
static bool sax_parse(IteratorType first, IteratorType last,
template<class IteratorType, class SAX, class SentinelType = IteratorType>
static bool sax_parse(IteratorType first, SentinelType last,
SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
@@ -23,10 +23,11 @@ static bool sax_parse(IteratorType first, IteratorType last,
Read from input and generate SAX events
1. Read from a compatible input.
2. Read from a pair of character iterators
2. Read from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
respectively as UTF-8, UTF-16, and UTF-32.
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
the iterator type with `operator!=`.
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
@@ -39,13 +40,16 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
- a `FILE` pointer
- a C-style array of characters
- a pointer to a null-terminated string of single byte characters
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of
iterators.
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
`IteratorType`
: a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
`SentinelType`
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for overload (2)
`SAX`
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
@@ -76,7 +80,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
: iterator to the start of a character range
`last` (in)
: iterator to the end of a character range
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
## Return value
@@ -126,7 +130,9 @@ A UTF-8 byte order mark is silently ignored.
- Added in version 3.2.0.
- Ignoring comments via `ignore_comments` added in version 3.9.0.
- Added `ignore_trailing_commas` in version 3.12.x.
- Added `ignore_trailing_commas` in version 3.13.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
!!! warning "Deprecation"
@@ -54,4 +54,4 @@ provides `<format>`, controlled by the [`JSON_HAS_STD_FORMAT`](../macros/json_ha
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
+3 -12
View File
@@ -6,18 +6,9 @@ namespace std {
}
```
Return a hash value for a JSON object. The hash function tries to rely on `std::hash` where possible. To satisfy the
`std::hash` contract, numeric JSON values that compare equal must hash to the same value. This means:
- `json(42)`, `json(42u)`, and `json(42.0)` all hash to the same value
- `json(0)`, `json(0u)`, and `json(0.0)` all hash to the same value
Different types hash differently for non-numeric types (e.g., `#!json null`, `#!cpp false`, and strings all have distinct hashes).
**Edge case:** For very large integers outside the exact representable range of the floating-point type (beyond ~2^53 for
typical `double`), the hash values for integer and floating-point values may differ, even if the floating-point value
was obtained by casting the integer (due to precision loss). This is a documented limitation arising from how the
comparison operator normalizes numeric types.
Return a hash value for a JSON object. The hash function tries to rely on `std::hash` where possible. Furthermore, the
type of the JSON value is taken into account to have different hash values for `#!json null`, `#!cpp 0`, `#!cpp 0U`, and
`#!cpp false`, etc.
## Examples
@@ -18,6 +18,11 @@ JSON class into byte-sized characters during deserialization.
: the container to store strings (e.g., `std::string`). Note this container is used for keys/names in objects, see
[object_t](object_t.md).
`StringType` must have a `char`-compatible `value_type`: the library relies on UTF-8/`char`-based storage and
processing internally, so `std::wstring`, `std::u16string`, and `std::u32string` are **not** valid choices for
`StringType`. To work with wide-character data, convert it to/from UTF-8 at the boundary instead -- see the
FAQ's [wide string handling](../../home/faq.md#wide-string-handling) section for a conversion recipe.
## Notes
#### Default type
@@ -21,6 +21,12 @@ a string representation of the type ([`value_t`](value_t.md)):
| array | `"array"` |
| binary | `"binary"` |
| discarded | `"discarded"` |
| invalid (corrupted value) | `"invalid"` |
!!! note "The \"invalid\" type"
The `"invalid"` return value indicates a corrupted JSON value — this can occur if an enum value falls outside the
range of valid `value_t` values. This is useful for diagnosing data corruption or internal errors.
## Exception safety
@@ -52,3 +58,4 @@ Constant.
- Part of the public API version since 2.1.0.
- Changed return value to `const char*` and added `noexcept` in version 3.0.0.
- Added support for binary type in version 3.8.0.
- Added `"invalid"` return value for corrupted JSON values in version 3.13.0.
+5 -1
View File
@@ -17,6 +17,8 @@ ValueType value(const json_pointer& ptr,
const ValueType& default_value) const;
```
This is equivalent to Python's `dict.get(key, default)`.
1. Returns either a copy of an object's element at the specified key `key` or a given default value if no element with
key `key` exists.
@@ -184,4 +186,6 @@ changes to any JSON value.
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0.
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
3. Added in version 2.0.2. Extended to work with arrays in version 3.12.x.
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
`default_value`, as documented).
+1 -1
View File
@@ -36,4 +36,4 @@ Constant.
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
@@ -32,4 +32,4 @@ Linear in the number of reference tokens in the `json_pointer`.
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
@@ -35,4 +35,4 @@ Linear in the number of reference tokens in the `json_pointer`.
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
@@ -92,4 +92,4 @@ The default value is `0` (disabled — existing behavior is preserved).
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
@@ -44,4 +44,4 @@ The default value is detected based on preprocessor macros such as `#!cpp __cplu
- Added in version 3.10.5.
- Added `JSON_HAS_CPP_23` in version 3.12.0.
- Added `JSON_HAS_CPP_26` in version 3.12.x.
- Added `JSON_HAS_CPP_26` in version 3.13.0.
@@ -19,6 +19,20 @@ The default value is detected based on the preprocessor macros `#!cpp __cpp_lib_
`#!cpp __cpp_lib_experimental_filesystem`, `#!cpp __has_include(<filesystem>)`, or
`#!cpp __has_include(<experimental/filesystem>)`.
!!! info "Known compiler/stdlib exclusions"
Even when the feature-test macro indicates filesystem support is available, the library disables it on the following broken toolchains:
- **MinGW + GCC 8** — disabled entirely (broken `std::filesystem` implementation; [MinGW-w64 bug 737](https://sourceforge.net/p/mingw-w64/bugs/737/))
- **GCC (non-Clang) < 8** — disabled (no filesystem support)
- **Clang < 7** — disabled (no filesystem support)
- **MSVC < 19.14** — disabled (no filesystem support)
- **iOS < 13** — disabled (no filesystem support)
- **macOS < Catalina (10.15)** — disabled (no filesystem support)
If `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERIMENTAL_FILESYSTEM` is `0` despite `__cpp_lib_filesystem` being defined, one
of the exclusions above likely applies to your toolchain.
## Notes
- Note that older compilers or older versions of libstdc++ also require the library `stdc++fs` to be linked to for
@@ -13,6 +13,20 @@ The default value is detected based on the preprocessor macro `#!cpp __cpp_lib_r
When the macro is not defined, the library will define it to its default value.
!!! info "Known compiler/stdlib exclusions"
Even when the feature-test macro `__cpp_lib_ranges` indicates ranges support is available, the library disables it on
the following incomplete or broken toolchains:
- **GCC 11.1.0** — disabled (the shipped `<ranges>` header has a syntax error; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error
under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907))
If `JSON_HAS_RANGES` is `0` despite `__cpp_lib_ranges` being defined, one of the exclusions above likely applies to your toolchain.
## Examples
??? example
@@ -38,4 +38,4 @@ When the macro is not defined, the library will define it to its default value.
## Version history
- Added in version 3.12.x.
- Added in version 3.13.0.
@@ -62,6 +62,9 @@ See the examples below for the concrete generated code.
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
with more than 63 member variables, you need to define the `to_json`/`from_json` functions manually.
- These macros always produce object-style (named-key) JSON, one key per member. There is no macro variant
that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by
hand, building/reading a `json::array()` of the members in order.
## Examples
@@ -63,6 +63,9 @@ See the examples below for the concrete generated code.
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
with more than 63 member variables, you need to define the `to_json`/`from_json` functions manually.
- These macros always produce object-style (named-key) JSON, one key per member. There is no macro variant
that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by
hand, building/reading a `json::array()` of the members in order.
## Examples
@@ -75,4 +75,4 @@ For further information please refer to the corresponding macros without `WITH_N
## Version history
1. Added in version 3.12.x.
1. Added in version 3.13.0.
@@ -102,4 +102,4 @@ inline void from_json(const BasicJsonType& j, type& e);
## Version history
Added in version 3.12.x.
Added in version 3.13.0.
+12
View File
@@ -33,6 +33,18 @@ A UTF-8 byte order mark is silently ignored.
Invalid Unicode escapes and unpaired surrogates in the input are reported as
[`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) with a detailed message.
`operator>>` parses exactly one JSON value and leaves the stream positioned right after it, so it can be called
repeatedly to read a sequence of concatenated JSON values from the same stream:
```cpp
json j1, j2;
input >> j1; // parses the first value, stream now positioned right after it
input >> j2; // parses the next value
```
Note this does **not** work for [JSON Lines](../features/parsing/json_lines.md) (newline-delimited JSON) input --
see that page for why and for the recommended alternative.
!!! warning "Deprecation"
This function replaces function `#!cpp std::istream& operator<<(basic_json& j, std::istream& i)` which has
@@ -64,4 +64,4 @@ Linear.
- Added in version 1.0.0.
- Moved to namespace `nlohmann::literals::json_literals` in 3.11.0.
- Added `char8_t*` overload in 3.12.x.
- Added `char8_t*` overload in 3.13.0.
@@ -63,4 +63,4 @@ Linear.
- Added in version 2.0.0.
- Moved to namespace `nlohmann::literals::json_literals` in 3.11.0.
- Added `char8_t*` overload in 3.12.x.
- Added `char8_t*` overload in 3.13.0.
@@ -10,6 +10,10 @@ violations will result in a failed build.
Any compiler with complete C++11 support can compile the library without warnings.
Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds.
Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions.
- [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms,
including the oldest versions known to compile the library.
@@ -62,12 +66,14 @@ violations will result in a failed build.
| Clang 20.1.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| Clang 20.1.8 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub |
| Clang 21.1.8 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| CUDA 11.0.221 (nvcc) | x86_64 | Ubuntu 20.04 LTS | GitHub |
| CUDA 11.8.0 (nvcc) | x86_64 | Ubuntu 22.04 LTS | GitHub |
| CUDA 12.1.1 (nvcc) | x86_64 | Ubuntu 22.04 LTS | GitHub |
| CUDA 12.6.3 (nvcc) | x86_64 | Ubuntu 22.04 LTS | GitHub |
| Emscripten 4.0.6 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 4.8.5 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 4.9.3 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 5.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 6.4.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 4.8.5 | x86_64 | Ubuntu 20.04 LTS | GitHub |
| GNU 4.9.3 | x86_64 | Ubuntu 20.04 LTS | GitHub |
| GNU 5.5.0 | x86_64 | Ubuntu 20.04 LTS | GitHub |
| GNU 6.4.0 | x86_64 | Ubuntu 20.04 LTS | GitHub |
| GNU 7.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 8.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 9.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
@@ -84,7 +90,7 @@ violations will result in a failed build.
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
| GNU 16.1.0 | arm64 | Linux 6.1.100 | Cirrus CI |
| icpc (ICC) 2021.5.0 20211109 | x86_64 | Ubuntu 20.04.3 LTS | GitHub |
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
| MSVC 19.0.24241.7 | x86 | Windows 8.1 | AppVeyor |
@@ -0,0 +1,61 @@
#include <iostream>
#include <nlohmann/json.hpp>
#include <stdexcept>
#include <string>
#include <unordered_set>
#include <vector>
using json = nlohmann::json;
json parse_strict(const std::string& input)
{
// one key set per nesting depth, reused across sibling objects
std::vector<std::unordered_set<std::string>> keys;
auto reject_duplicate_keys = [&](int depth, json::parse_event_t event, json & parsed)
{
if (event == json::parse_event_t::object_start)
{
// keys of this object are reported at depth+1 (see the event table above)
const auto child_depth = static_cast<std::size_t>(depth) + 1;
if (keys.size() <= child_depth)
{
keys.resize(child_depth + 1);
}
keys[child_depth].clear();
return true;
}
if (event == json::parse_event_t::key)
{
auto& seen = keys[static_cast<std::size_t>(depth)];
const auto& key = parsed.get_ref<const std::string&>();
if (!seen.insert(key).second)
{
throw std::runtime_error("duplicate JSON object key: " + key);
}
return true;
}
return true;
};
return json::parse(input, reject_duplicate_keys);
}
int main()
{
// parsing succeeds when all keys are unique
json j = parse_strict(R"({"one": 1, "two": 2})");
std::cout << j << '\n';
// parsing throws when a key is repeated
try
{
parse_strict(R"({"one": 1, "one": 2})");
}
catch (const std::exception& e)
{
std::cout << e.what() << '\n';
}
}
@@ -0,0 +1,2 @@
{"one":1,"two":2}
duplicate JSON object key: one
-1
View File
@@ -11,7 +11,6 @@ int main()
<< "hash(false) = " << std::hash<json> {}(json(false)) << '\n'
<< "hash(0) = " << std::hash<json> {}(json(0)) << '\n'
<< "hash(0U) = " << std::hash<json> {}(json(0U)) << '\n'
<< "hash(0.0) = " << std::hash<json> {}(json(0.0)) << '\n'
<< "hash(\"\") = " << std::hash<json> {}(json("")) << '\n'
<< "hash({}) = " << std::hash<json> {}(json::object()) << '\n'
<< "hash([]) = " << std::hash<json> {}(json::array()) << '\n'
+4 -5
View File
@@ -1,9 +1,8 @@
hash(null) = 2654435769
hash(false) = 2654436030
hash(0) = 2654436221
hash(0U) = 2654436221
hash(0.0) = 2654436221
hash("") = 11160318156688833227
hash(0) = 2654436095
hash(0U) = 2654436156
hash("") = 6142509191626859748
hash({}) = 2654435832
hash([]) = 2654435899
hash({"hello": "world"}) = 3701319991624763853
hash({"hello": "world"}) = 4469488738203676328
@@ -180,6 +180,49 @@ For _derived_ classes and structs, use the following macros
}
```
!!! warning "Overriding conversions for natively-supported types"
The library already provides built-in `to_json`/`from_json` conversions for STL containers such as
`std::vector`, `std::array`, and `std::map`. Defining your own free-function `to_json`/`from_json` overload
for one of these container types directly (instead of for your own type) can conflict with the built-in
overload during overload resolution, producing compiler errors ("no matching overloaded function",
"call is ambiguous") that vary by compiler and library version. If you need different conversion behavior
for a container type the library already handles, wrap it in your own type (or use `adl_serializer`
specialization, as shown [above](#how-do-i-convert-third-party-types) for `boost::optional`) instead of
trying to re-specialize `to_json`/`from_json` for the container type itself.
!!! warning "Raw C-style arrays"
Members declared as raw C-style arrays (e.g., `char buf[1024]`) do not round-trip safely through
`NLOHMANN_DEFINE_TYPE_*` macros or the default (de)serializers: `to_json` serializes any `char` array as a
JSON *string* (matching the `std::string`-constructible overload), but the `from_json` overload for
fixed-size arrays expects a JSON *array* and iterates it element-wise, which fails with a `type_error` when
given a string. Use `std::string`, `std::array<char, N>`, or a manually written `to_json`/`from_json` pair
for such members instead.
!!! note "Macros and `nlohmann::ordered_json`"
The `NLOHMANN_DEFINE_TYPE_*`/`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros are generic over any `basic_json`
specialization, including `nlohmann::ordered_json`. Simply use `ordered_json` as the target type and members
are serialized in declaration order -- no separate macro or extra code is needed.
```cpp
namespace ns {
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
}
ns::person p{"Ned Flanders", "744 Evergreen Terrace", 60};
nlohmann::ordered_json j = p; // keys appear in declaration order: name, address, age
```
!!! note "No macro for non-default-constructible types"
There is currently no `NLOHMANN_DEFINE_TYPE_*`-style macro for types that are not
[DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). This is not an
intentional omission of documentation -- no such macro exists yet; see
[How can I use `get()` for non-default constructible/non-copyable types?](#how-can-i-use-get-for-non-default-constructiblenon-copyable-types)
for the manual pattern to use instead.
## How do I convert third-party types?
This requires a bit more advanced technique. But first, let us see how this conversion mechanism works:
@@ -270,6 +313,49 @@ namespace nlohmann {
}
```
## Why can't I convert to/from `std::any`?
`std::any` is intentionally excluded from `get<T>()`/generic conversion support, so `get<std::any>()` and
containers like `std::map<std::string, std::any>` fail to compile by design -- there is no way to know, from a
`json` value alone, which concrete type to store inside the `std::any`. To work with heterogeneous JSON values,
dispatch on the value's type manually and construct the `std::any` (or extract from it) yourself:
```cpp
std::any value_to_any(const json& j) {
if (j.is_boolean()) { return j.get<bool>(); }
if (j.is_number_integer()) { return j.get<int>(); }
if (j.is_number_float()) { return j.get<double>(); }
if (j.is_string()) { return j.get<std::string>(); }
// ... handle other types (arrays, objects) as needed for your use case
return {};
}
json any_to_json(const std::any& a) {
if (a.type() == typeid(bool)) { return std::any_cast<bool>(a); }
if (a.type() == typeid(int)) { return std::any_cast<int>(a); }
if (a.type() == typeid(double)) { return std::any_cast<double>(a); }
if (a.type() == typeid(std::string)) { return std::any_cast<std::string>(a); }
return nullptr;
}
```
## Why does serializing a `std::map`/`std::unordered_map` with non-string keys produce an array?
A `std::map`/`std::unordered_map` whose key type is not string-like (e.g., `std::map<int, std::string>`) is
serialized as a JSON *array* of 2-element `[key, value]` arrays, not as a JSON object -- JSON object keys must be
strings, so the library cannot represent an integer-keyed map as an object.
```cpp
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
json j = m;
// j is [[1,"one"],[2,"two"]], not {"1":"one","2":"two"}
```
## Why does `std::wstring` convert or dump incorrectly?
The library assumes UTF-8 encoding internally, so `std::wstring` is not supported out of the box -- see the FAQ
entry on [wide string handling](../home/faq.md#wide-string-handling) for why, and for a UTF-8 conversion recipe.
## Can I write my own serializer? (Advanced use)
Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-udt.cpp) in the test suite, to see a few examples.
@@ -66,7 +66,15 @@ see "binary" cells in the table above.
!!! info "NaN/infinity handling"
If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the normal JSON serialization which serializes NaN or Infinity to `null`.
`NaN`, `Infinity`, and `-Infinity` are serialized as a CBOR half-precision float (type 0xF9, 3 bytes total):
`NaN` as `0xF9 0x7E 0x00`, `Infinity` as `0xF9 0x7C 0x00`, and `-Infinity` as `0xF9 0xFC 0x00`. This behavior
differs from the normal JSON serialization which serializes NaN or Infinity to `null`.
!!! note
Prior to version 3.13.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
9 bytes total), because the check used to select a smaller encoding compared magnitudes with NaN, which is
always `false` and caused the intended half-precision path to be skipped.
!!! info "Unused CBOR types"
@@ -160,6 +168,13 @@ The library maps CBOR types to JSON value types as follows:
- simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7)
!!! warning "Negative integer overflow"
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
silently.
!!! warning "Object keys"
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.
@@ -67,8 +67,15 @@ specification:
!!! info "NaN/infinity handling"
If NaN or Infinity are stored inside a JSON number, they are serialized properly in contrast to the
[dump](../../api/basic_json/dump.md) function which serializes NaN or Infinity to `null`.
`NaN`, `Infinity`, and `-Infinity` are serialized as a MessagePack float 32 (type 0xCA, 5 bytes total),
regardless of magnitude, in contrast to the [dump](../../api/basic_json/dump.md) function which serializes NaN
or Infinity to `null`.
!!! note
Prior to version 3.13.0, NaN and Infinity were instead serialized as a MessagePack float 64 (type 0xCB, 9 bytes
total), because the check used to select the smaller float 32 encoding compared magnitudes with NaN, which is
always `false` and caused the float 32 path to be skipped.
??? example
+1 -1
View File
@@ -11,7 +11,7 @@ This library does not support comments *by default*. It does so for three reason
3. It is dangerous for interoperability if some libraries add comment support while others do not. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this.
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace.
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. Combined with `ignore_trailing_commas` (also a `parse` parameter), this covers what is commonly referred to as **JSONC** (JSON with Comments, as used e.g. by Visual Studio Code's `.jsonc` files) -- comments and trailing commas, nothing more. This is a different, smaller extension than [JSON5](https://json5.org), which additionally allows unquoted keys, single-quoted strings, and other syntax changes that this library does not support.
For more information, see [JSON With Commas and Comments (JWCC)](https://nigeltao.github.io/blog/2021/json-with-commas-comments.html).
+61
View File
@@ -66,6 +66,67 @@ which forces the explicit `get` form and can catch unintended conversions at com
floating-point value as an integer truncates it, and narrowing conversions may overflow. See
[number conversion](types/number_handling.md#number-conversion) for details and how to guard against it.
!!! warning "std::optional direct construction from JSON null throws"
Constructing or assigning `std::optional<T>` directly from a JSON value does not correctly produce
`std::nullopt` for a JSON `null`:
```cpp
json j_null;
std::optional<std::string> opt = j_null; // ❌ throws type_error 302
```
This is due to C++ language rules: `std::optional<T>` has its own converting constructor that is chosen over
`basic_json::operator T()` when both are viable. Use `get<std::optional<T>>()` or `get_to()` instead:
```cpp
auto opt = j_null.get<std::optional<std::string>>(); // ✅ std::nullopt
j_null.get_to(opt); // ✅ std::nullopt
```
!!! warning "`static_cast` and `get<std::optional<T>>()` are not guaranteed equivalent"
`operator ValueType()` (used by `static_cast` and implicit conversions) intentionally excludes
`std::optional<T>` from delegating to `get<T>()`, to avoid a constructor ambiguity with
`std::optional<T>`'s own converting constructor from `basic_json`. As a result,
`static_cast<std::optional<T>>(json_value)` goes through `std::optional<T>`'s own converting
constructor rather than through `get<std::optional<T>>()`, which can behave differently -- for example,
with a custom `adl_serializer<std::optional<T>>` specialization. Prefer `get<std::optional<T>>()`/`get_to()`
over `static_cast` for optional types.
!!! warning "Converting to a fixed-size `std::array` does not check length"
Converting a JSON array to `#!cpp std::array<T, N>` does not check that the JSON array's size matches `N`:
if the JSON array is longer, the extra elements are silently dropped; if it is shorter, the remaining
`std::array` elements are left default-constructed. No exception is thrown in either case.
```cpp
json j = {1, 2, 3, 4, 5};
auto a = j.get<std::array<int, 3>>(); // {1, 2, 3} -- elements 4 and 5 silently dropped
```
## Omitting a field when serializing `std::optional`
By default, `to_json` for `std::optional<T>` writes either the value or `#!json null` -- there is no built-in way
to make a field disappear from the serialized object entirely when the `std::optional` is `std::nullopt`. Because
a specialization of `adl_serializer<std::optional<T>>` only controls how the *value* is converted (it cannot
prevent the containing object's `to_json` from inserting the key in the first place), omission has to be
implemented in the *containing* type's `to_json`:
```cpp
struct person {
std::string name;
std::optional<int> age;
};
void to_json(json& j, const person& p) {
j = json{{"name", p.name}};
if (p.age) {
j["age"] = *p.age; // key is only inserted when the optional has a value
}
}
```
## Putting values in
The reverse direction works the same way: assigning or constructing a `json` from a C++ value converts it to JSON.
@@ -4,7 +4,8 @@
In many situations, such as configuration files, missing values are not exceptional, but may be treated as if a default
value was present. For this case, use [`value(key, default_value)`](../../api/basic_json/value.md) which takes the key
you want to access and a default value in case there is no value stored with that key.
you want to access and a default value in case there is no value stored with that key. This is equivalent to Python's
`dict.get(key, default)`.
## Example
@@ -102,6 +102,20 @@ that the passed index is the new maximal index. Intermediate values are filled w
`operator[]` can only be used with objects (with a string argument) or with arrays (with a numeric argument). For
other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error305) is thrown.
## Performance: reserving array capacity
There is no public `reserve(count)` member on `basic_json` for pre-allocating array capacity. If you are building
a large array incrementally (e.g., via repeated `push_back()`) and know its final size ahead of time, you can
reserve capacity via `get_ref()` to access the underlying `array_t` directly:
```cpp
json j = json::array();
j.get_ref<json::array_t&>().reserve(1000);
for (int i = 0; i < 1000; ++i) {
j.push_back(i);
}
```
## Summary
| scenario | non-const value | const value |
@@ -77,6 +77,11 @@ auto val2 = j.at(json::json_pointer("/nested/three/1")); // false
auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0
```
!!! note "Creating intermediate levels that don't exist"
See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is
decided when a pointer creates intermediate levels that don't exist yet.
## Flatten / unflatten
The library implements a function [`flatten`](../api/basic_json/flatten.md) to convert any JSON document into a JSON
+19
View File
@@ -27,6 +27,7 @@ json data = json::parse(f);
It should be noted that as modules do not export macros, the `nlohmann.json` module will not export any macros.
## Exported symbols
Only the following symbols are exported from `nlohmann.json`:
- `nlohmann::adl_serializer`
@@ -38,3 +39,21 @@ Only the following symbols are exported from `nlohmann.json`:
- `nlohmann::to_string`
- `nlohmann::literals::json_literals::operator""_json`
- `nlohmann::literals::json_literals::operator""_json_pointer`
Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
and should not be used directly:
- `nlohmann::detail::json_sax_dom_callback_parser`
- `nlohmann::detail::unknown_size`
## Known issues
C++20 modules support is exercised in CI against current GCC and Clang on Ubuntu, and the default MSVC toolset on Windows Server 2022 — there is no documented minimum compiler version, unlike feature-test-macro-gated features such as [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md).
!!! info "Known compiler issues"
- **GCC** may emit "redefinition" errors when `#include <nlohmann/json.hpp>` appears in a module preamble together with other imports. This is an upstream GCC bug, not yet resolved as of GCC 16. Workarounds: include `nlohmann/json.hpp` before other `#include`s, use `import nlohmann.json;` instead, or upgrade GCC. ([issue #5103](https://github.com/nlohmann/json/issues/5103))
- **MSVC** could fail with `C2039: 'json_sax_dom_callback_parser' is not a member of ... detail`; fixed by exporting the required internal symbols from `json.cppm` (see [Exported symbols](#exported-symbols) above). ([issue #3970](https://github.com/nlohmann/json/issues/3970))
If you hit a different module-related build failure, search [existing issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+modules) before filing a new one.
@@ -47,3 +47,6 @@ JSON Lines input with more than one value is treated as invalid JSON by the [`pa
```
with a JSON Lines input does not work, because the parser will try to parse one value after the last one.
This is different from parsing a stream of *concatenated* (non-newline-delimited) JSON values, for which
`operator>>` does work -- see its [notes](../../api/operator_gtgt.md#notes) for details.
@@ -58,6 +58,14 @@ table describes the values of the parameters `depth`, `event`, and `parsed`.
| `array_end` | 1 | `#!json [52.519444,13.406667]` |
| `object_end` | 0 | `#!json {"location":[52.519444,13.406667],"name":"Berlin"}` |
!!! note "No built-in nesting depth limit"
The library has no built-in limit on recursion/nesting depth while parsing. A parser callback can only
*discard* content it has already parsed (by returning `#!c false`); it cannot make parsing fail once a
nesting limit is exceeded partway through reading a deeply nested value. If you need to reject over-deep
untrusted input outright, track `depth` in a callback and `throw` from it once your limit is exceeded (a
thrown exception propagates out of `parse()` as usual).
## Return value
Discarding a value (i.e., returning `#!c false`) has different effects depending on the context in which the function
@@ -81,3 +89,82 @@ was called:
```json
--8<-- "examples/parse__string__parser_callback_t.output"
```
## Recipe: rejecting duplicate object keys
The JSON specification leaves the handling of objects with repeated keys up to the implementation. As described in
[`object_t`](../../api/basic_json/object_t.md#behavior), it is unspecified which value for a repeated key ends up in
the resulting `#!c json` value -- once parsing has produced that value, the duplicate is already gone, because object
storage maps each key to a single value. If duplicate keys should instead be treated as an error, a parser callback
can detect them while the object is still being read, before that ambiguity ever applies.
??? example
```cpp
--8<-- "examples/reject_duplicate_keys.cpp"
```
Output:
```json
--8<-- "examples/reject_duplicate_keys.output"
```
This approach has two limitations:
- The depth-indexed bookkeeping must account for the fact that `object_start` reports the depth of the *parent* of
the object, while the `key` events inside that object are reported one depth deeper (see the event table above);
it is easy to get this off by one for nested objects.
- The thrown exception cannot carry a `parse_error`-style byte offset, because position tracking only exists inside
the parser and lexer, not at the callback layer.
For strict validation with precise error positions, implementing a [SAX interface](sax_interface.md) instead gives
access to the parser's position information directly.
## Recipe: streaming a large homogeneous array
A common use case is a huge top-level array of many similarly-shaped objects, too large to hold entirely in
memory as a `#!c json` value. A parser callback can hand off each completed element to a user function and then
discard it, so memory usage stays bounded by a single element (plus the not-yet-parsed tail of the input) rather
than the whole document. Since the top-level array's `array_start`/`array_end` are reported at `depth == 0` (its
parent is the document root), the object elements it contains are reported at `depth == 1`:
??? example
```cpp
std::ifstream input("large_array.json");
auto callback = [](int depth, json::parse_event_t event, json& parsed) -> bool {
if (depth == 1 && event == json::parse_event_t::object_end) {
handle_element(parsed); // process the element, e.g. write it elsewhere
return false; // discard it -- frees its memory before the next one is parsed
}
return true; // keep everything else, including the (by then empty) top-level array
};
json::parse(input, callback);
```
If the array's elements are scalars or nested arrays instead of objects, check for `parse_event_t::value` or
`parse_event_t::array_end` at `depth == 1` instead. The same approach works for a top-level *object* of many
homogeneous values by checking `object_end`/`value` events at `depth == 1` there too.
## Recipe: max nesting depth via a callback
Since there is no built-in nesting-depth limit (see the note above), a callback can enforce one manually by
tracking the maximum `depth` seen and throwing once it is exceeded:
??? example
```cpp
constexpr int max_depth = 32;
auto callback = [](int depth, json::parse_event_t /*event*/, json& /*parsed*/) -> bool {
if (depth > max_depth) {
throw std::runtime_error("maximum nesting depth exceeded");
}
return true;
};
json::parse(input, callback);
```
+13 -1
View File
@@ -131,7 +131,7 @@ std::map<
The choice of `object_t` influences the behavior of the JSON class. With the default type, objects have the following behavior:
- When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings.
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`.
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see [this parsing recipe](../parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see `dump`) in this order. For instance, both `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored and serialized as `#!json {"a": 2, "b": 1}`.
- When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be treated as equal.
@@ -151,6 +151,18 @@ In this class, the object's limit of nesting is not explicitly constrained. Howe
Objects are stored as pointers in a `basic_json` type. That is, for any access to object values, a pointer of type `object_t*` must be dereferenced.
### Converting maps with non-string keys
A `std::map`/`std::unordered_map` whose key type is not string-like (e.g., `std::map<int, std::string>`) is
converted to a JSON *array* of 2-element `[key, value]` arrays rather than a JSON object, because JSON object
keys must be strings:
```cpp
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
json j = m;
// j is [[1,"one"],[2,"two"]], not {"1":"one","2":"two"}
```
## Arrays
@@ -63,6 +63,10 @@ In the default [`json`](../../api/json.md) type, numbers are stored as `#!c std:
number without loss of precision. If this is impossible (e.g., if the number is too large), the number is stored as
`#!c double`.
Positive integers are stored as `#!c std::uint64_t`, while negative integers are stored as `#!c std::int64_t`. This
distinction is determined at parse time: if the JSON number has a leading minus sign, it uses signed integer storage;
otherwise, it uses unsigned integer storage.
!!! info "Notes"
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
+31
View File
@@ -0,0 +1,31 @@
# Debugging
This page collects the library's built-in debugger integrations and other debugging-related features. They are
not linked from a single place elsewhere in the docs, so are collected here.
## Visual Studio (natvis)
The repository ships [`nlohmann_json.natvis`](https://github.com/nlohmann/json/blob/develop/nlohmann_json.natvis)
at its root, a [Natvis](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects)
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.
Debug engines that wrap LLDB instead of the MSVC debug engine (for example, `codelldb` in VS Code) only have
partial/experimental Natvis support, and commonly fall back to showing raw internal fields even with the
`.natvis` file present. Switching to `cppvsdbg` where available, or checking your debug extension's own Natvis
support/version, are the next things to try if this happens. There is currently no bundled LLDB-native
pretty-printer script in this repository.
## GDB
The repository ships a [GDB Python pretty printer](https://github.com/nlohmann/json/tree/develop/tools/gdb_pretty_printer)
under `tools/gdb_pretty_printer`, with its own usage instructions in that directory's `README.md`.
## Extended exception diagnostics
Defining [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) before including the library augments
`type_error`/`out_of_range`-style exceptions with a JSON Pointer to the offending value, which can help pinpoint
where in a large document a runtime error occurred. This only applies to exceptions thrown *after* a value
exists (e.g. during element access); parse errors, which happen before any value exists to point at, are not
covered by this mechanism -- see [Parsing and exceptions](../features/parsing/parse_exceptions.md) for how parse
errors report their own location instead.
+4 -1
View File
@@ -326,6 +326,9 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
```
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
```
```
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
```
### json.exception.parse_error.113
@@ -893,7 +896,7 @@ A JSON Patch `add` operation cannot be applied because the target location's par
!!! note
This exception was added in version 3.12.x. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
This exception was added in version 3.13.0. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
## Further exceptions
+24 -1
View File
@@ -129,6 +129,29 @@ As described [above](#parse-errors-reading-non-ascii-characters), the library as
}
```
## Usage
### Thread safety
!!! question
Is `basic_json` thread-safe?
No. `basic_json` provides no built-in synchronization, the same as `std::map` or `std::vector`. Concurrent reads of
the same value from multiple threads are safe, as are concurrent (non-overlapping) accesses to independent `json`
objects. However, any concurrent write to a `json` object -- or a concurrent read while another thread writes to the
same object -- is a data race and requires external synchronization (e.g., a `std::mutex`) by the caller.
### Schema validation
!!! question
Does this library support JSON Schema validation?
Not directly, but the companion project [json-schema-validator](https://github.com/pboettch/json-schema-validator)
builds JSON Schema (draft 4, 6, 7, and 2019-09) validation on top of this library and is a common recommendation
for this use case.
## Exceptions
### Parsing without exceptions
@@ -178,7 +201,7 @@ See [this section](../features/types/number_handling.md#number-serialization) on
- Can I use `std::format("{}", j)` on a JSON value?
- Can I use `fmt::format("{}", j)` or `fmt::print("{}", j)` (the [{fmt}](https://github.com/fmtlib/fmt) library) on a JSON value?
`std::format` works out of the box since version 3.12.x, as long as the standard library provides
`std::format` works out of the box since version 3.13.0, as long as the standard library provides
`<format>` (see [`JSON_HAS_STD_FORMAT`](../api/macros/json_has_std_format.md)); see
[`std::formatter<basic_json>`](../api/basic_json/std_formatter.md) for details, including the `#!cpp "{:#}"`
pretty-print spec, indent widths (`#!cpp "{:2}"`), and custom indent characters (`#!cpp "{:.>#}"`).
+12
View File
@@ -181,3 +181,15 @@ Execute the test suite with [Valgrind](https://valgrind.org). This option is `OF
Build the experimental [C++ module](../features/modules.md) `nlohmann.json` (requires CMake 3.28 or later and C++20).
This option is `OFF` by default.
A consuming project must link the dedicated `nlohmann_json_modules` CMake target (not just
`nlohmann_json::nlohmann_json`) for `import nlohmann.json;` to resolve:
```cmake
set(NLOHMANN_JSON_BUILD_MODULES ON)
add_subdirectory(path/to/json)
add_executable(myproject main.cpp)
target_link_libraries(myproject PRIVATE nlohmann_json_modules)
target_compile_definitions(myproject PRIVATE NLOHMANN_JSON_BUILD_MODULES)
```
@@ -930,6 +930,15 @@ If you are using [CocoaPods](https://cocoapods.org), you can use the library by
to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues
[here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open).
## ESP-IDF and PlatformIO
There is no official package published to the [ESP-IDF Component Registry](https://components.espressif.com) or the
[PlatformIO Registry](https://registry.platformio.org). A community-maintained fork,
[Johboh/nlohmann-json](https://github.com/Johboh/nlohmann-json), publishes this library to both registries on each
new release and can be used as an unofficial component/package for ESP-IDF and PlatformIO projects. As the library
is header-only, it can otherwise be used directly by adding its `include/` directory to your component's/project's
include paths, like any other integration method described on this page.
![](https://img.shields.io/cocoapods/v/nlohmann_json)
!!! warning
+1
View File
@@ -56,6 +56,7 @@ nav:
- home/architecture.md
- home/customers.md
- home/sponsors.md
- home/debugging.md
- Features:
- features/index.md
- features/arbitrary_types.md
+5 -90
View File
@@ -11,8 +11,6 @@
#include <cstdint> // uint8_t
#include <cstddef> // size_t
#include <functional> // hash
#include <limits> // numeric_limits
#include <cmath> // isfinite
#include <nlohmann/detail/abi_macros.hpp>
#include <nlohmann/detail/value_t.hpp>
@@ -28,73 +26,12 @@ inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
return seed;
}
// Check if a number_integer_t value is exactly representable as number_float_t
// Returns true if static_cast<number_integer_t>(static_cast<number_float_t>(val)) == val
template<typename BasicJsonType>
inline bool is_exactly_representable_as_float(typename BasicJsonType::number_integer_t val) noexcept
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_float_t = typename BasicJsonType::number_float_t;
// If the float type's mantissa covers the integer type's entire range, all values round-trip
constexpr int float_digits = std::numeric_limits<number_float_t>::digits;
constexpr int int_digits = std::numeric_limits<number_integer_t>::digits;
#ifdef JSON_HEDLEY_MSVC_VERSION
#pragma warning(push )
#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
#endif
if (float_digits >= int_digits)
{
return true;
}
#ifdef JSON_HEDLEY_MSVC_VERSION
#pragma warning( pop )
#endif
// For values outside float's exact range, they don't round-trip
// The safe way to check: compute the max magnitude that round-trips
// Using unsigned arithmetic to avoid UB with negating INT_MIN
// Max magnitude representable exactly: 2^(digits-1) - 1 for signed, 2^digits - 1 for unsigned range
// But we're checking a signed value, so use 2^digits as the threshold
constexpr auto max_exact = static_cast<number_integer_t>(1) << (float_digits - 1);
// Check absolute value against this threshold
if (val >= 0)
{
if (val >= max_exact)
{
return false;
}
}
else
{
// For negative values, check via unsigned wrapping arithmetic
// -val in unsigned domain; if it wraps, the value is too negative
auto unsigned_abs = static_cast<typename BasicJsonType::number_unsigned_t>(-val);
if (unsigned_abs >= static_cast<typename BasicJsonType::number_unsigned_t>(max_exact))
{
return false;
}
}
// For values within the exact range, verify the round-trip
const auto f = static_cast<number_float_t>(val);
return std::isfinite(f) && static_cast<number_integer_t>(f) == val;
}
/*!
@brief hash a JSON value
The hash function tries to rely on std::hash where possible. Furthermore, the
type of the JSON value is taken into account to have different hash values for
most types. However, numeric types (number_integer, number_unsigned, number_float)
are hashed to satisfy the std::hash contract: if two json values compare equal,
they must have equal hash values. This means json(42), json(42u), and json(42.0)
all hash to the same value (since they compare equal). For large integer values
outside the exact representable range of the float type, integer values are hashed
in their own domain to avoid precision loss.
null, 0, 0U, and false, etc.
@tparam BasicJsonType basic_json specialization
@param j JSON value to hash
@@ -153,36 +90,14 @@ std::size_t hash(const BasicJsonType& j)
case BasicJsonType::value_t::number_integer:
{
const auto v = j.template get<number_integer_t>();
// Use a shared numeric type tag so all numeric types that are equal hash the same
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
if (is_exactly_representable_as_float<BasicJsonType>(v))
{
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v));
return combine(numeric_type, h);
}
const auto h = std::hash<number_integer_t> {}(v);
return combine(numeric_type, h);
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
return combine(type, h);
}
case BasicJsonType::value_t::number_unsigned:
{
const auto v = j.template get<number_unsigned_t>();
// Normalize to signed (matching operator== behavior for U-vs-I comparison)
const auto v_as_signed = static_cast<number_integer_t>(v);
// Use a shared numeric type tag so all numeric types that are equal hash the same
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
if (is_exactly_representable_as_float<BasicJsonType>(v_as_signed))
{
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v_as_signed));
return combine(numeric_type, h);
}
const auto h = std::hash<number_integer_t> {}(v_as_signed);
return combine(numeric_type, h);
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
return combine(type, h);
}
case BasicJsonType::value_t::number_float:
@@ -155,7 +155,9 @@ class input_stream_adapter
// General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile.
template<typename IteratorType>
// SentinelType defaults to IteratorType for backward compatibility, but may
// be a different type (e.g., a C++20 sentinel or counted_iterator).
template<typename IteratorType, typename SentinelType = IteratorType>
class iterator_input_adapter
{
public:
@@ -169,9 +171,10 @@ class iterator_input_adapter
// in wide_string_input_adapter, which does not expose this).
static constexpr bool supports_seek =
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
&& std::is_same<IteratorType, SentinelType>::value
&& sizeof(char_type) == 1;
iterator_input_adapter(IteratorType first, IteratorType last)
iterator_input_adapter(IteratorType first, SentinelType last)
: begin(first), current(std::move(first)), end(std::move(last))
{}
@@ -216,12 +219,14 @@ class iterator_input_adapter
private:
// whether IteratorType refers to a contiguous range and therefore supports
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
// library iterators such as those of std::vector and std::string)
// library iterators such as those of std::vector and std::string).
// The fast path also requires SentinelType == IteratorType so std::distance works.
static constexpr bool iterator_is_contiguous =
std::is_same<IteratorType, SentinelType>::value && (
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
std::contiguous_iterator<IteratorType> ||
std::contiguous_iterator<IteratorType> ||
#endif
std::is_pointer<IteratorType>::value;
std::is_pointer<IteratorType>::value);
// contiguous fast path: bulk copy the remaining range with std::memcpy
template<class T>
@@ -267,7 +272,7 @@ class iterator_input_adapter
IteratorType begin;
IteratorType current;
IteratorType end;
SentinelType end;
template<typename BaseInputAdapter, size_t T>
friend struct wide_string_input_helper;
@@ -430,7 +435,7 @@ class wide_string_input_adapter
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
template<class T>
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
{
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
}
@@ -453,19 +458,54 @@ class wide_string_input_adapter
std::size_t utf8_bytes_filled = 0;
};
template<typename IteratorType, typename Enable = void>
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
struct iterator_input_adapter_factory
{
using iterator_type = IteratorType;
using sentinel_type = SentinelType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using adapter_type = iterator_input_adapter<iterator_type>;
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
static adapter_type create(IteratorType first, IteratorType last)
static adapter_type create(IteratorType first, SentinelType last)
{
return adapter_type(std::move(first), std::move(last));
}
};
// Detection: whether IteratorType and SentinelType can be compared with !=
template<typename IteratorType, typename SentinelType, typename = void>
struct can_compare_ne_impl : std::false_type {};
template<typename IteratorType, typename SentinelType>
struct can_compare_ne_impl < IteratorType, SentinelType,
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
: std::true_type {};
// Workaround for reversed operator order
template<typename IteratorType, typename SentinelType, typename = void>
struct can_compare_ne_reversed : std::false_type {};
template<typename IteratorType, typename SentinelType>
struct can_compare_ne_reversed < IteratorType, SentinelType,
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
: std::true_type {};
template<typename IteratorType, typename SentinelType>
struct can_compare_ne_either_order : std::integral_constant < bool,
can_compare_ne_impl<IteratorType, SentinelType>::value ||
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
// SFINAE the `operator!=` detection above for std::nullptr_t against
// container/string types, which would otherwise make such calls ambiguous
// with the compatible-input overload.
template<typename IteratorType, typename SentinelType>
struct can_compare_ne : std::integral_constant < bool,
!std::is_same<SentinelType, std::nullptr_t>::value &&
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
template<typename T>
struct is_iterator_of_multibyte
{
@@ -476,25 +516,31 @@ struct is_iterator_of_multibyte
};
};
template<typename IteratorType>
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
template<typename IteratorType, typename SentinelType>
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
{
using iterator_type = IteratorType;
using sentinel_type = SentinelType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using base_adapter_type = iterator_input_adapter<iterator_type>;
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
static adapter_type create(IteratorType first, IteratorType last)
static adapter_type create(IteratorType first, SentinelType last)
{
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
}
};
// General purpose iterator-based input
template<typename IteratorType>
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
// defaults to IteratorType for the common same-type case, but may differ for
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
// be compared with !=.
template < typename IteratorType, typename SentinelType = IteratorType,
typename = typename std::enable_if <
can_compare_ne<IteratorType, SentinelType>::value >::type >
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
{
using factory_type = iterator_input_adapter_factory<IteratorType>;
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
return factory_type::create(first, last);
}
@@ -517,18 +563,18 @@ struct container_input_adapter_factory< ContainerType,
{
using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
static adapter_type create(const ContainerType& container)
static adapter_type create(ContainerType&& container)
{
return input_adapter(begin(container), end(container));
return input_adapter(begin(std::forward<ContainerType>(container)), end(std::forward<ContainerType>(container)));
}
};
} // namespace container_input_adapter_factory_impl
template<typename ContainerType>
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(ContainerType&& container)
{
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(std::forward<ContainerType>(container));
}
// specialization for std::string
+5
View File
@@ -146,6 +146,11 @@
#define JSON_HAS_RANGES 0
#elif defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
#define JSON_HAS_RANGES 0
// nvcc CUDA 12.0/12.1 chokes on the enable_borrowed_range variable-template
// syntax when compiling as CUDA source; fixed in CUDA 12.2 (issue #3907)
#elif defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) && __CUDACC_VER_MAJOR__ == 12 \
&& defined(__CUDACC_VER_MINOR__) && (__CUDACC_VER_MINOR__ == 0 || __CUDACC_VER_MINOR__ == 1)
#define JSON_HAS_RANGES 0
#elif defined(__cpp_lib_ranges)
#define JSON_HAS_RANGES 1
#else
+32 -39
View File
@@ -3776,17 +3776,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *this == basic_json(rhs);
}
/// @brief comparison: not equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
bool operator!=(const_reference rhs) const noexcept
{
if (compares_unordered(rhs, true))
{
return false;
}
return !operator==(rhs);
}
/// @brief comparison: 3-way
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
@@ -3892,10 +3881,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs == rhs);
}
@@ -4098,12 +4083,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result;
}
/// @brief deserialize from a pair of character iterators
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/parse/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(IteratorType first,
IteratorType last,
SentinelType last,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false,
@@ -4137,10 +4123,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief check if the input is valid JSON
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename IteratorType>
static bool accept(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
static bool accept(IteratorType first, SentinelType last,
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
@@ -4172,11 +4159,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
/// @brief generate SAX events
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
template<class IteratorType, class SAX>
template<class IteratorType, class SAX, class SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_NON_NULL(3)
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false,
@@ -4476,11 +4464,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in CBOR format
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_cbor(IteratorType first, IteratorType last,
static basic_json from_cbor(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
@@ -4533,11 +4522,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in MessagePack format
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_msgpack(IteratorType first, IteratorType last,
static basic_json from_msgpack(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
@@ -4587,11 +4577,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in UBJSON format
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_ubjson(IteratorType first, IteratorType last,
static basic_json from_ubjson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
@@ -4641,11 +4632,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BJData format
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bjdata(IteratorType first, IteratorType last,
static basic_json from_bjdata(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
@@ -4671,11 +4663,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BSON format
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bson(IteratorType first, IteratorType last,
static basic_json from_bson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
+110 -151
View File
@@ -2520,6 +2520,11 @@ JSON_HEDLEY_DIAGNOSTIC_POP
#define JSON_HAS_RANGES 0
#elif defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
#define JSON_HAS_RANGES 0
// nvcc CUDA 12.0/12.1 chokes on the enable_borrowed_range variable-template
// syntax when compiling as CUDA source; fixed in CUDA 12.2 (issue #3907)
#elif defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) && __CUDACC_VER_MAJOR__ == 12 \
&& defined(__CUDACC_VER_MINOR__) && (__CUDACC_VER_MINOR__ == 0 || __CUDACC_VER_MINOR__ == 1)
#define JSON_HAS_RANGES 0
#elif defined(__cpp_lib_ranges)
#define JSON_HAS_RANGES 1
#else
@@ -6677,8 +6682,6 @@ NLOHMANN_JSON_NAMESPACE_END
#include <cstdint> // uint8_t
#include <cstddef> // size_t
#include <functional> // hash
#include <limits> // numeric_limits
#include <cmath> // isfinite
// #include <nlohmann/detail/abi_macros.hpp>
@@ -6696,73 +6699,12 @@ inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
return seed;
}
// Check if a number_integer_t value is exactly representable as number_float_t
// Returns true if static_cast<number_integer_t>(static_cast<number_float_t>(val)) == val
template<typename BasicJsonType>
inline bool is_exactly_representable_as_float(typename BasicJsonType::number_integer_t val) noexcept
{
using number_integer_t = typename BasicJsonType::number_integer_t;
using number_float_t = typename BasicJsonType::number_float_t;
// If the float type's mantissa covers the integer type's entire range, all values round-trip
constexpr int float_digits = std::numeric_limits<number_float_t>::digits;
constexpr int int_digits = std::numeric_limits<number_integer_t>::digits;
#ifdef JSON_HEDLEY_MSVC_VERSION
#pragma warning(push )
#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
#endif
if (float_digits >= int_digits)
{
return true;
}
#ifdef JSON_HEDLEY_MSVC_VERSION
#pragma warning( pop )
#endif
// For values outside float's exact range, they don't round-trip
// The safe way to check: compute the max magnitude that round-trips
// Using unsigned arithmetic to avoid UB with negating INT_MIN
// Max magnitude representable exactly: 2^(digits-1) - 1 for signed, 2^digits - 1 for unsigned range
// But we're checking a signed value, so use 2^digits as the threshold
constexpr auto max_exact = static_cast<number_integer_t>(1) << (float_digits - 1);
// Check absolute value against this threshold
if (val >= 0)
{
if (val >= max_exact)
{
return false;
}
}
else
{
// For negative values, check via unsigned wrapping arithmetic
// -val in unsigned domain; if it wraps, the value is too negative
auto unsigned_abs = static_cast<typename BasicJsonType::number_unsigned_t>(-val);
if (unsigned_abs >= static_cast<typename BasicJsonType::number_unsigned_t>(max_exact))
{
return false;
}
}
// For values within the exact range, verify the round-trip
const auto f = static_cast<number_float_t>(val);
return std::isfinite(f) && static_cast<number_integer_t>(f) == val;
}
/*!
@brief hash a JSON value
The hash function tries to rely on std::hash where possible. Furthermore, the
type of the JSON value is taken into account to have different hash values for
most types. However, numeric types (number_integer, number_unsigned, number_float)
are hashed to satisfy the std::hash contract: if two json values compare equal,
they must have equal hash values. This means json(42), json(42u), and json(42.0)
all hash to the same value (since they compare equal). For large integer values
outside the exact representable range of the float type, integer values are hashed
in their own domain to avoid precision loss.
null, 0, 0U, and false, etc.
@tparam BasicJsonType basic_json specialization
@param j JSON value to hash
@@ -6821,36 +6763,14 @@ std::size_t hash(const BasicJsonType& j)
case BasicJsonType::value_t::number_integer:
{
const auto v = j.template get<number_integer_t>();
// Use a shared numeric type tag so all numeric types that are equal hash the same
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
if (is_exactly_representable_as_float<BasicJsonType>(v))
{
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v));
return combine(numeric_type, h);
}
const auto h = std::hash<number_integer_t> {}(v);
return combine(numeric_type, h);
const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
return combine(type, h);
}
case BasicJsonType::value_t::number_unsigned:
{
const auto v = j.template get<number_unsigned_t>();
// Normalize to signed (matching operator== behavior for U-vs-I comparison)
const auto v_as_signed = static_cast<number_integer_t>(v);
// Use a shared numeric type tag so all numeric types that are equal hash the same
const auto numeric_type = static_cast<std::size_t>(BasicJsonType::value_t::number_float);
if (is_exactly_representable_as_float<BasicJsonType>(v_as_signed))
{
const auto h = std::hash<number_float_t> {}(static_cast<number_float_t>(v_as_signed));
return combine(numeric_type, h);
}
const auto h = std::hash<number_integer_t> {}(v_as_signed);
return combine(numeric_type, h);
const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
return combine(type, h);
}
case BasicJsonType::value_t::number_float:
@@ -7072,7 +6992,9 @@ class input_stream_adapter
// General-purpose iterator-based adapter. It might not be as fast as
// theoretically possible for some containers, but it is extremely versatile.
template<typename IteratorType>
// SentinelType defaults to IteratorType for backward compatibility, but may
// be a different type (e.g., a C++20 sentinel or counted_iterator).
template<typename IteratorType, typename SentinelType = IteratorType>
class iterator_input_adapter
{
public:
@@ -7086,9 +7008,10 @@ class iterator_input_adapter
// in wide_string_input_adapter, which does not expose this).
static constexpr bool supports_seek =
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
&& std::is_same<IteratorType, SentinelType>::value
&& sizeof(char_type) == 1;
iterator_input_adapter(IteratorType first, IteratorType last)
iterator_input_adapter(IteratorType first, SentinelType last)
: begin(first), current(std::move(first)), end(std::move(last))
{}
@@ -7133,12 +7056,14 @@ class iterator_input_adapter
private:
// whether IteratorType refers to a contiguous range and therefore supports
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
// library iterators such as those of std::vector and std::string)
// library iterators such as those of std::vector and std::string).
// The fast path also requires SentinelType == IteratorType so std::distance works.
static constexpr bool iterator_is_contiguous =
std::is_same<IteratorType, SentinelType>::value && (
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
std::contiguous_iterator<IteratorType> ||
std::contiguous_iterator<IteratorType> ||
#endif
std::is_pointer<IteratorType>::value;
std::is_pointer<IteratorType>::value);
// contiguous fast path: bulk copy the remaining range with std::memcpy
template<class T>
@@ -7184,7 +7109,7 @@ class iterator_input_adapter
IteratorType begin;
IteratorType current;
IteratorType end;
SentinelType end;
template<typename BaseInputAdapter, size_t T>
friend struct wide_string_input_helper;
@@ -7347,7 +7272,7 @@ class wide_string_input_adapter
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
template<class T>
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
{
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
}
@@ -7370,19 +7295,54 @@ class wide_string_input_adapter
std::size_t utf8_bytes_filled = 0;
};
template<typename IteratorType, typename Enable = void>
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
struct iterator_input_adapter_factory
{
using iterator_type = IteratorType;
using sentinel_type = SentinelType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using adapter_type = iterator_input_adapter<iterator_type>;
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
static adapter_type create(IteratorType first, IteratorType last)
static adapter_type create(IteratorType first, SentinelType last)
{
return adapter_type(std::move(first), std::move(last));
}
};
// Detection: whether IteratorType and SentinelType can be compared with !=
template<typename IteratorType, typename SentinelType, typename = void>
struct can_compare_ne_impl : std::false_type {};
template<typename IteratorType, typename SentinelType>
struct can_compare_ne_impl < IteratorType, SentinelType,
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
: std::true_type {};
// Workaround for reversed operator order
template<typename IteratorType, typename SentinelType, typename = void>
struct can_compare_ne_reversed : std::false_type {};
template<typename IteratorType, typename SentinelType>
struct can_compare_ne_reversed < IteratorType, SentinelType,
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
: std::true_type {};
template<typename IteratorType, typename SentinelType>
struct can_compare_ne_either_order : std::integral_constant < bool,
can_compare_ne_impl<IteratorType, SentinelType>::value ||
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
// SFINAE the `operator!=` detection above for std::nullptr_t against
// container/string types, which would otherwise make such calls ambiguous
// with the compatible-input overload.
template<typename IteratorType, typename SentinelType>
struct can_compare_ne : std::integral_constant < bool,
!std::is_same<SentinelType, std::nullptr_t>::value &&
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
template<typename T>
struct is_iterator_of_multibyte
{
@@ -7393,25 +7353,31 @@ struct is_iterator_of_multibyte
};
};
template<typename IteratorType>
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
template<typename IteratorType, typename SentinelType>
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
{
using iterator_type = IteratorType;
using sentinel_type = SentinelType;
using char_type = typename std::iterator_traits<iterator_type>::value_type;
using base_adapter_type = iterator_input_adapter<iterator_type>;
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
static adapter_type create(IteratorType first, IteratorType last)
static adapter_type create(IteratorType first, SentinelType last)
{
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
}
};
// General purpose iterator-based input
template<typename IteratorType>
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
// defaults to IteratorType for the common same-type case, but may differ for
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
// be compared with !=.
template < typename IteratorType, typename SentinelType = IteratorType,
typename = typename std::enable_if <
can_compare_ne<IteratorType, SentinelType>::value >::type >
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
{
using factory_type = iterator_input_adapter_factory<IteratorType>;
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
return factory_type::create(first, last);
}
@@ -7434,18 +7400,18 @@ struct container_input_adapter_factory< ContainerType,
{
using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
static adapter_type create(const ContainerType& container)
static adapter_type create(ContainerType&& container)
{
return input_adapter(begin(container), end(container));
return input_adapter(begin(std::forward<ContainerType>(container)), end(std::forward<ContainerType>(container)));
}
};
} // namespace container_input_adapter_factory_impl
template<typename ContainerType>
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(ContainerType&& container)
{
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(std::forward<ContainerType>(container));
}
// specialization for std::string
@@ -24707,17 +24673,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return *this == basic_json(rhs);
}
/// @brief comparison: not equal
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
bool operator!=(const_reference rhs) const noexcept
{
if (compares_unordered(rhs, true))
{
return false;
}
return !operator==(rhs);
}
/// @brief comparison: 3-way
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
@@ -24823,10 +24778,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
{
if (compares_unordered(lhs, rhs, true))
{
return false;
}
return !(lhs == rhs);
}
@@ -25029,12 +24980,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return result;
}
/// @brief deserialize from a pair of character iterators
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/parse/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(IteratorType first,
IteratorType last,
SentinelType last,
parser_callback_t cb = nullptr,
const bool allow_exceptions = true,
const bool ignore_comments = false,
@@ -25068,10 +25020,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
}
/// @brief check if the input is valid JSON
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/accept/
template<typename IteratorType>
static bool accept(IteratorType first, IteratorType last,
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
static bool accept(IteratorType first, SentinelType last,
const bool ignore_comments = false,
const bool ignore_trailing_commas = false)
{
@@ -25103,11 +25056,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
}
/// @brief generate SAX events
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
template<class IteratorType, class SAX>
template<class IteratorType, class SAX, class SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_NON_NULL(3)
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
input_format_t format = input_format_t::json,
const bool strict = true,
const bool ignore_comments = false,
@@ -25407,11 +25361,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in CBOR format
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_cbor(IteratorType first, IteratorType last,
static basic_json from_cbor(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true,
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
@@ -25464,11 +25419,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in MessagePack format
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_msgpack(IteratorType first, IteratorType last,
static basic_json from_msgpack(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
@@ -25518,11 +25474,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in UBJSON format
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_ubjson(IteratorType first, IteratorType last,
static basic_json from_ubjson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
@@ -25572,11 +25529,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BJData format
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bjdata(IteratorType first, IteratorType last,
static basic_json from_bjdata(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
@@ -25602,11 +25560,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
return res ? result : basic_json(value_t::discarded);
}
/// @brief create a JSON value from an input in BSON format
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
template<typename IteratorType>
template<typename IteratorType, typename SentinelType = IteratorType,
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json from_bson(IteratorType first, IteratorType last,
static basic_json from_bson(IteratorType first, SentinelType last,
const bool strict = true,
const bool allow_exceptions = true)
{
+5 -1
View File
@@ -68,7 +68,11 @@ target_compile_options(test_main PUBLIC
# Disable warning C4566: character represented by universal-character-name '\uFF01'
# cannot be represented in the current code page (1252)
# Disable warning C4996: 'nlohmann::basic_json<...>::operator <<': was declared deprecated
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;$<$<CONFIG:Release>:/wd4702>>
# Disable warning C4702: unreachable code; wide_string_input_adapter::get_elements()
# is annotated JSON_HEDLEY_NO_RETURN (it always throws), which
# makes MSVC flag the code following its call in binary_reader.hpp
# as unreachable for that instantiation, in both Debug and Release
$<$<CXX_COMPILER_ID:MSVC>:/W4;/wd4566;/wd4996;/wd4702>
# https://github.com/nlohmann/json/issues/1114
$<$<CXX_COMPILER_ID:MSVC>:/bigobj> $<$<BOOL:${MINGW}>:-Wa,-mbig-obj>
+12 -1
View File
@@ -3,7 +3,18 @@ project(json_cuda LANGUAGES CUDA)
add_executable(json_cuda json_cuda.cu)
target_include_directories(json_cuda PRIVATE ../../include)
target_compile_features(json_cuda PUBLIC cuda_std_11)
# nvcc added C++20 support in CUDA 12.0 and C++17 in CUDA 11.0; pick the
# newest standard the detected compiler actually supports (see #3907)
# instead of hard-requiring one standard for every CUDA version.
if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)
set(json_cuda_std 20)
elseif(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0)
set(json_cuda_std 17)
else()
set(json_cuda_std 11)
endif()
target_compile_features(json_cuda PUBLIC cuda_std_${json_cuda_std})
set_target_properties(json_cuda PROPERTIES
CUDA_EXTENSIONS OFF
CUDA_STANDARD_REQUIRED ON
+16
View File
@@ -16,4 +16,20 @@ int main()
// regression for #3013 (ordered_json::reset() compile error with nvcc)
nlohmann::ordered_json metadata;
metadata.erase("key");
// exercise comparisons (operator==/operator<=>, gated by
// JSON_HAS_THREE_WAY_COMPARISON, independent of JSON_HAS_RANGES) and
// range-based iteration (exercises iteration_proxy/ranges machinery
// beyond just the enable_borrowed_range specialization) — see #3907
nlohmann::json a = {1, 2, 3};
nlohmann::json b = {1, 2, 3};
static_cast<void>(a == b);
#if JSON_HAS_THREE_WAY_COMPARISON
static_cast<void>(a <=> b); // *NOPAD*
static_cast<void>(a <=> 1); // *NOPAD*
#endif
for (const auto& element : a)
{
static_cast<void>(element);
}
}
+13
View File
@@ -30,4 +30,17 @@ inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
return byte_vector;
}
// sentinel for istreambuf_iterator; compares != true until EOF is reached
// lets tests read a file directly via the new iterator+sentinel overloads
// instead of buffering the whole file into a vector first.
// Only the iterator-first direction (it != sentinel) is ever evaluated by
// the library's parse loop, so no reversed-order overload is needed.
struct istreambuf_sentinel
{
friend bool operator!=(const std::istreambuf_iterator<char>& it, const istreambuf_sentinel& /*unused*/) noexcept
{
return it != std::istreambuf_iterator<char>();
}
};
} // namespace utils
+1 -1
View File
@@ -24,7 +24,7 @@ struct bad_allocator : std::allocator<T>
template<class U> bad_allocator(const bad_allocator<U>& /*unused*/) { }
template<class... Args>
void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
[[noreturn]] void construct(T* /*unused*/, Args&& ... /*unused*/) // NOLINT(cppcoreguidelines-missing-std-forward)
{
throw std::bad_alloc();
}
+9
View File
@@ -3699,6 +3699,15 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
}
}
TEST_CASE("Parse BJData directly from a file using iterator and sentinel")
{
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.bjdata";
std::ifstream file(filename, std::ios::binary);
const std::istreambuf_iterator<char> first(file);
const json parsed = json::from_bjdata(first, utils::istreambuf_sentinel{});
CHECK((parsed.is_object() || parsed.is_array()));
}
#if !defined(JSON_NOEXCEPTION)
TEST_CASE("all BJData first bytes")
{
+13
View File
@@ -1208,6 +1208,19 @@ TEST_CASE("BSON numerical data")
}
}
TEST_CASE("Parse BSON directly from a file using iterator and sentinel")
{
std::string const filename = TEST_DATA_DIRECTORY "/json.org/1.json";
std::ifstream f_json(filename);
const json expected = json::parse(f_json);
std::ifstream file(filename + ".bson", std::ios::binary);
const std::istreambuf_iterator<char> first(file);
const json parsed = json::from_bson(first, utils::istreambuf_sentinel{});
CHECK(parsed == expected);
}
TEST_CASE("BSON roundtrips" * doctest::skip())
{
SECTION("reference files")
+9
View File
@@ -1921,6 +1921,15 @@ TEST_CASE("single CBOR roundtrip")
}
}
TEST_CASE("Parse CBOR directly from a file using iterator and sentinel")
{
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.cbor";
std::ifstream file(filename, std::ios::binary);
const std::istreambuf_iterator<char> first(file);
const json parsed = json::from_cbor(first, utils::istreambuf_sentinel{});
CHECK((parsed.is_object() || parsed.is_array()));
}
#if !defined(JSON_NOEXCEPTION)
TEST_CASE("CBOR regressions")
{
+33 -14
View File
@@ -369,6 +369,7 @@ TEST_CASE("lexicographical comparison operators")
SECTION("comparison: not equal")
{
// check that two values compare unequal as expected
// operator!= now means exactly !(a==b) without special cases for NaN/discarded
for (size_t i = 0; i < j_values.size(); ++i)
{
for (size_t j = 0; j < j_values.size(); ++j)
@@ -376,25 +377,12 @@ TEST_CASE("lexicographical comparison operators")
CAPTURE(i)
CAPTURE(j)
if (json::compares_unordered(j_values[i], j_values[j], true))
{
// if two values compare unordered,
// check that the boolean comparison result is always false
CHECK_FALSE(j_values[i] != j_values[j]);
}
else
{
// otherwise, check that they compare according to their definition
// as the inverse of equal
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
}
CHECK((j_values[i] != j_values[j]) == !(j_values[i] == j_values[j]));
}
}
// compare with null pointer
const json j_null;
CHECK((j_null != nullptr) == false);
CHECK((nullptr != j_null) == false);
CHECK((j_null != nullptr) == !(j_null == nullptr));
CHECK((nullptr != j_null) == !(nullptr == j_null));
}
@@ -594,3 +582,34 @@ TEST_CASE("lexicographical comparison operators")
}
#endif
}
#if JSON_HAS_THREE_WAY_COMPARISON
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
TEST_CASE("regression #3868 - heterogeneous comparisons compile under C++20 (P2468R2)")
{
// Issue #3868: operator!= was preventing compiler from synthesizing reversed
// operator== candidates under C++20's P2468R2 rewritten candidate rules.
// Verify that heterogeneous comparisons now work.
SECTION("string vs json")
{
std::string s = "string";
json j = "string";
CHECK(s == j);
CHECK(j == s);
CHECK_FALSE(s != j);
CHECK_FALSE(j != s);
}
SECTION("other heterogeneous types")
{
int i = 42;
json j = 42;
CHECK(i == j);
CHECK(j == i);
CHECK_FALSE(i != j);
CHECK_FALSE(j != i);
}
}
#endif
+1 -1
View File
@@ -278,7 +278,7 @@ TEST_CASE("constructors")
const auto t = j.get<std::tuple<int, float, std::string>>();
CHECK(std::get<0>(t) == j[0]);
CHECK(std::get<1>(t) == j[1]);
// CHECK(std::get<2>(t) == j[2]); // commented out due to CI issue, see https://github.com/nlohmann/json/pull/3985 and https://github.com/nlohmann/json/issues/4025
CHECK(std::get<2>(t) == j[2]);
}
SECTION("std::tuple tie")
+14 -4
View File
@@ -1761,16 +1761,27 @@ TEST_CASE("std::filesystem::path")
}
#endif
#if !JSON_USE_IMPLICIT_CONVERSIONS
TEST_CASE("std::optional")
{
SECTION("null")
{
json j_null;
std::optional<std::string> opt_null;
const json j_null;
const std::optional<std::string> opt_null;
CHECK(json(opt_null) == j_null);
CHECK(j_null.get<std::optional<std::string>>() == std::nullopt);
// Constructing std::optional<T> directly from JSON null throws because
// std::optional's own converting constructor is chosen over basic_json's
// operator T(). This is a language-level limitation (std::optional<T> is
// constructible from T, and T is constructible from basic_json via the
// operator); there is no SFINAE path that distinguishes "call from inside
// std::optional's constructor" from "direct call". Use get<std::optional<T>>()
// or get_to() instead for correct null handling. See #4864 and #5246.
CHECK_THROWS_WITH_AS(std::optional<std::string>(j_null),
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
}
SECTION("string")
@@ -1819,7 +1830,6 @@ TEST_CASE("std::optional")
}
}
#endif
#endif
#ifdef JSON_HAS_CPP_17
#undef JSON_HAS_CPP_17
+5
View File
@@ -227,6 +227,11 @@ bool check_utf8()
// Runtime check of the active ANSI code page
// 65001 == UTF-8
return GetACP() == 65001;
#elif defined(__ICC) || defined(__INTEL_COMPILER)
// classic Intel ICC does not encode narrow string literals containing
// non-ASCII source characters as UTF-8, so comparing a decoded u8 literal
// against a narrow string literal containing the same characters fails
return false;
#else
return true;
#endif
+6 -22
View File
@@ -35,10 +35,10 @@ TEST_CASE("hash<nlohmann::json>")
// number
hashes.insert(std::hash<json> {}(json(0)));
hashes.insert(std::hash<json> {}(json(static_cast<unsigned>(0)))); // now same hash as json(0)
hashes.insert(std::hash<json> {}(json(0.0))); // now same hash as json(0)
hashes.insert(std::hash<json> {}(json(static_cast<unsigned>(0))));
hashes.insert(std::hash<json> {}(json(-1)));
hashes.insert(std::hash<json> {}(json(0.0)));
hashes.insert(std::hash<json> {}(json(42.23)));
// array
@@ -60,16 +60,7 @@ TEST_CASE("hash<nlohmann::json>")
// discarded
hashes.insert(std::hash<json> {}(json(json::value_t::discarded)));
// Note: json(0), json(0U), and json(0.0) now hash to the same value
// (to satisfy the std::hash contract: equal values must hash equally)
// So we expect 19 distinct hashes instead of 21
CHECK(hashes.size() == 19);
// Verify the std::hash contract: equal values must hash equally
CHECK(std::hash<json> {}(json(0)) == std::hash<json> {}(json(static_cast<unsigned>(0))));
CHECK(std::hash<json> {}(json(0)) == std::hash<json> {}(json(0.0)));
CHECK(std::hash<json> {}(json(42)) == std::hash<json> {}(json(42u)));
CHECK(std::hash<json> {}(json(42)) == std::hash<json> {}(json(42.0)));
CHECK(hashes.size() == 21);
}
TEST_CASE("hash<nlohmann::ordered_json>")
@@ -93,10 +84,10 @@ TEST_CASE("hash<nlohmann::ordered_json>")
// number
hashes.insert(std::hash<ordered_json> {}(ordered_json(0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0)))); // now same hash as ordered_json(0)
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0))); // now same hash as ordered_json(0)
hashes.insert(std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0))));
hashes.insert(std::hash<ordered_json> {}(ordered_json(-1)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(42.23)));
// array
@@ -118,12 +109,5 @@ TEST_CASE("hash<nlohmann::ordered_json>")
// discarded
hashes.insert(std::hash<ordered_json> {}(ordered_json(ordered_json::value_t::discarded)));
// Note: ordered_json(0), ordered_json(0U), and ordered_json(0.0) now hash to the same value
CHECK(hashes.size() == 19);
// Verify the std::hash contract for ordered_json as well
CHECK(std::hash<ordered_json> {}(ordered_json(0)) == std::hash<ordered_json> {}(ordered_json(static_cast<unsigned>(0))));
CHECK(std::hash<ordered_json> {}(ordered_json(0)) == std::hash<ordered_json> {}(ordered_json(0.0)));
CHECK(std::hash<ordered_json> {}(ordered_json(42)) == std::hash<ordered_json> {}(ordered_json(42u)));
CHECK(std::hash<ordered_json> {}(ordered_json(42)) == std::hash<ordered_json> {}(ordered_json(42.0)));
CHECK(hashes.size() == 21);
}
+1 -1
View File
@@ -942,7 +942,7 @@ TEST_CASE("iterators 2")
json j_expected{5, 4, 3, 2, 1};
auto reversed = j | std::views::reverse;
CHECK(std::ranges::equal(reversed, j_expected));
CHECK(reversed == j_expected);
}
SECTION("transform")
+9
View File
@@ -1641,6 +1641,15 @@ TEST_CASE("single MessagePack roundtrip")
}
}
TEST_CASE("Parse MessagePack directly from a file using iterator and sentinel")
{
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.msgpack";
std::ifstream file(filename, std::ios::binary);
const std::istreambuf_iterator<char> first(file);
const json parsed = json::from_msgpack(first, utils::istreambuf_sentinel{});
CHECK((parsed.is_object() || parsed.is_array()));
}
TEST_CASE("MessagePack roundtrips" * doctest::skip())
{
SECTION("input from msgpack-python")
+3 -1
View File
@@ -798,7 +798,9 @@ TEST_CASE("regression tests 2")
#ifdef JSON_HAS_CPP_20
#ifndef _LIBCPP_VERSION // see https://github.com/nlohmann/json/issues/4490
#if __has_include(<span>)
// classic Intel ICC reports <span> as includable but cannot actually compile
// std::span/std::as_bytes usage below
#if __has_include(<span>) && !defined(__ICC) && !defined(__INTEL_COMPILER)
SECTION("issue #2546 - parsing containers of std::byte")
{
const char DATA[] = R"("Hello, world!")"; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+9
View File
@@ -2393,6 +2393,15 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
}
}
TEST_CASE("Parse UBJSON directly from a file using iterator and sentinel")
{
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.ubjson";
std::ifstream file(filename, std::ios::binary);
const std::istreambuf_iterator<char> first(file);
const json parsed = json::from_ubjson(first, utils::istreambuf_sentinel{});
CHECK((parsed.is_object() || parsed.is_array()));
}
#if !defined(JSON_NOEXCEPTION)
TEST_CASE("all UBJSON first bytes")
{
+74
View File
@@ -54,6 +54,47 @@ TEST_CASE("Custom container non-member begin/end")
}
struct MyContainerNonConstADL
{
char* data;
std::size_t size;
};
char* begin(MyContainerNonConstADL& c)
{
return c.data;
}
char* end(MyContainerNonConstADL& c)
{
return c.data + c.size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
TEST_CASE("Custom container non-member non-const begin/end")
{
// Container with lvalue-only non-const ADL begin/end (bug reproduction)
std::string raw_data = "[1,2,3,4]";
MyContainerNonConstADL data{&raw_data[0], raw_data.size()}; // NOLINT(readability-container-data-pointer)
const json as_json = json::parse(data);
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
// Same container with accept()
CHECK(json::accept(data));
}
TEST_CASE("Custom container non-member begin/end, rvalue")
{
// Regression check: rvalue container parsing should still work
const json as_json = json::parse(MyContainer{"[1,2,3,4]"});
CHECK(as_json.at(0) == 1);
CHECK(as_json.at(1) == 2);
CHECK(as_json.at(2) == 3);
CHECK(as_json.at(3) == 4);
}
TEST_CASE("Custom container member begin/end")
{
struct MyContainer2
@@ -127,4 +168,37 @@ TEST_CASE("Custom iterator")
CHECK(as_json.at(3) == 4);
}
// Custom sentinel type for testing heterogeneous iterator+sentinel support
struct CustomSentinel
{
const char* end_ptr;
// only the iterator-first direction (it != sentinel) is ever evaluated by
// the library's parse loop; a reversed-order overload would go unused and
// trip -Wunneeded-internal-declaration under -Weverything
friend bool operator!=(const char* it, const CustomSentinel& sentinel)
{
return it != sentinel.end_ptr;
}
};
TEST_CASE("Parse with heterogeneous iterator and sentinel types")
{
std::string json_str = R"({"key":"value"})";
const char* end_ptr = json_str.data() + json_str.size();
// Parse using pointer and sentinel (different types)
json j = json::parse(json_str.data(), CustomSentinel{end_ptr});
CHECK(j["key"] == "value");
// Accept using pointer and sentinel
CHECK(json::accept(json_str.data(), CustomSentinel{end_ptr}));
// Test that the same-type case still works
std::string raw_data = R"([1,2,3])";
std::list<char> data(raw_data.begin(), raw_data.end());
json j2 = json::parse(data.begin(), data.end());
CHECK(j2.at(0) == 1);
}
} // namespace