Compare commits

..
Author SHA1 Message Date
Niels Lohmann 2e55a6a08a Fix the msgpack 2^32-1 element recovery test on 32-bit targets
On a 32-bit target (clang-cl Win32) 0xFFFFFFFF elements exceed the
array's max_size(), so the array is rejected before it is started and
the recovered value is null rather than an empty array. Expect that.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-09 14:50:46 +01:00
Claude b6070980a1 Merge develop into claude/fix-issue-3989-db7e45
Resolve the conflict in binary_reader.hpp: develop dropped the
document_size < 0 check in check_bson_document_size(), because sizes below 5
are rejected before they are stored. Here such a size is only stored after it
was repaired, and the !size_repaired guard skips the check, so the check is
dropped here as well. Regenerate the single headers.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N5NasEVkSyiaFoX2FtPpsU
2026-11-09 06:45:01 +00:00
Niels Lohmann 405b8dcb13 BSON reader: recover from the document size checks made on reading the size
Since #5930, a size below 5, a size that exceeds the enclosing document, and
a size too small for the document's content are reported before the document
is read. When recovering, such a document is now read up to its terminator,
within the enclosing document, and its size is not reported a second time.
BJData now checks an ndarray's type before the object that would hold it is
begun, so that test expects null.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 13:47:31 +01:00
Niels Lohmann 02d2fa8605 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-11-02 13:47:30 +01:00
Niels Lohmann 52e9f098eb Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 18:01:19 +01:00
Niels Lohmann a6c5d970f3 Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-25 06:24:39 +01:00
Niels Lohmann d9c0fda829 State the frame stack invariants of json_sax_dom_parser as assertions
clang-tidy's static analyzer analyzes the forwarding SAX handlers of the
error recovery tests (RecoveringDomParser, BasicRecoveringParser) as entry
points, with the state of their json_sax_dom_parser member unknown. It then
assumes a frame stack of capacity 0 that grow() cannot enlarge
(clang-analyzer-security.ArrayBound) and a collected container without an
enclosing frame (clang-analyzer-core.NonNullParamChecker). Neither can
happen: the stack starts with local_capacity frames, and only a container
inside another one is collected. Assert both, which also lets the analyzer
discard these paths.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-24 14:39:04 +02:00
Niels Lohmann a763c9eb83 Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-24 07:47:46 +02:00
Niels Lohmann 5c2aba70f9 Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-23 06:35:52 +02:00
Niels Lohmann 510ee0ab4f Let the json_view error replay handler return from parse_error()
sax_parse() recovers from an error if parse_error() returns true. For a
handler whose parse_error() is [[noreturn]], MSVC reports that code as
unreachable (C4702), which fails the /WX module build. throwing_sax now
throws under a flag, as json_sax_dom_parser does.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 18:45:37 +02:00
Niels Lohmann 5da0e9181f Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 12:49:18 +02:00
Niels Lohmann 0aff24ca37 Restart the token string of token-tracking adapters after an error
Since #5828, a stream adapter keeps the current token itself
(mark_token_start() / copy_token()), so lazy_token_string no longer
implies a seekable adapter. restart_token_string_impl(std::true_type)
still called get_consumed_count(), which only seekable adapters have, and
failed to compile for std::istream input. Dispatch on adapter_token as
note_token_start() does.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 11:59:41 +02:00
Niels Lohmann c25aad3ca5 Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 11:59:40 +02:00
Niels Lohmann 6a0cf2ba2f Recover by error kind instead of by error message
The lexer records an error_kind with each error_message, and
recover_token() dispatches on it instead of comparing message prefixes.
add_codepoint() now uses encode_utf8(), and the surrogate pair
formula is shared by scan_string() and recover_low_surrogate().
The stubs for AllowRecovery == false select via negation, which astyle
leaves alone.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-21 09:06:28 +02:00
Niels Lohmann 24de5b2f5c Count lines incrementally when a recovering parse reports many errors
With seekable input, the lexer derives line and column on demand (#5809)
by counting the newlines in all input consumed so far. A SAX parser that
recovers from errors asks for the position once per error, so input with
an error at every character took quadratic time: "{" followed by 100000
commas took 85 s at -O0, and the parser tests timed out under Valgrind
(1500 s, was 69 s on develop).

get_position() now remembers what it counted and only scans the input
consumed since the previous call; count_consumed_lines() takes the start
of the range. The 100000 commas now take 0.17 s, unit-class_parser 0.9 s
instead of 86 s. Over 69284 error messages from random multi-line inputs,
the messages are identical to those before the change.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-17 20:10:10 +02:00
Niels Lohmann fb729028ba Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-17 08:35:02 +02:00
Niels Lohmann c9fdfc39db Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-15 22:46:35 +02:00
Niels Lohmann 870bdec01f Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-15 20:56:37 +02:00
Niels Lohmann effa51d528 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 10:47:22 +02:00
Niels Lohmann 545477ffa8 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-11 08:33:14 +02:00
Niels Lohmann aeba637593 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-10 00:08:31 +02:00
Niels Lohmann 466e56d92d Move the MessagePack SAX tests into unit-sax_parse.cpp
With develop's latest changes, the MinGW Debug object of unit-msgpack.cpp
has 33134 COFF sections on its own and 33411 with this PR, more than the
32767 that the MinGW linker of the "clang (20.1.8)" job can relocate. Its
sax_parse calls, all with utils::SaxCountdown, instantiate a parser and
binary reader of their own; moving those tests into unit-sax_parse.cpp
brings the object down to 32271 sections (unit-sax_parse: 20678).

The moved tests are unchanged: the "SAX aborts" sections, the user-defined
SAX consumer of #5405, the deep value read through the SAX interface (#5104),
and "MessagePack SAX parsing stops at every event".

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-09 23:35:38 +02:00
Niels Lohmann b7b75aac8a Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-09 23:30:03 +02:00
Niels Lohmann 516b9b516a Fix the Visual Studio 2015 build of the recovery code and its tests
- parser::report_error() calls only sax->parse_error(). For a SAX parser
  whose parse_error() is static, as in unit-disabled_exceptions.cpp,
  Visual Studio 2015 then reports the parameter as unreferenced (C4100),
  which /WX turns into an error.
- The variadic concatenated() helper in unit-sax_parse.cpp made Visual
  Studio 2015 fail with an internal compiler error (C1001). It now takes
  an initializer list of byte vectors.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-09 23:14:53 +02:00
Niels Lohmann 1d2dea7769 Do not force inlining of the recovery stubs with MSVC
f8ff0942b marked the stubs that stand in for error recovery with
JSON_HEDLEY_ALWAYS_INLINE, which is __forceinline with MSVC. Visual
Studio 2015 and 2017 do not inline them and warn (C4714), which /WX
turns into an error on AppVeyor. The new internal macro
JSON_INTERNAL_ALWAYS_INLINE forces inlining everywhere else (including
clang-cl), and leaves the decision to MSVC. The MinGW objects keep their
smaller section counts, as GCC and clang still inline the stubs.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-09 00:52:35 +02:00
Niels Lohmann f8ff0942b2 Keep the MinGW test objects below the linker's section limit
The MinGW clang job links with a GNU ld that cannot relocate an object
with more than 32767 sections. unit-regression2, unit-regression3 and
unit-msgpack went over it:

- At -O0, the parser and binary reader for parse(), accept() and
  from_*() still emitted the stubs that stand in for recovering, and
  the parser referenced its recovery helpers from code that never runs.
  The stubs are now always inlined, and the parser reaches its helpers
  through the same allow_recovery tag as the stubs, so that this code
  is not even instantiated there. This removes about 300 sections from
  unit-msgpack.
- Every sax_parse call instantiates the parser and binary reader that
  recover from errors. The tests that call it move from
  unit-regression2.cpp (#3989) and unit-regression3.cpp (#5676) into
  unit-sax_parse.cpp, like unit-explicit_instantiation.cpp got a file
  of its own (#5511), and CONTRIBUTING.md points there.

Section counts of the MinGW Debug objects (clang 19, -g0), develop / PR:
unit-msgpack 32281 / 32558, unit-regression2 31272 / 31278,
unit-regression3 32392 / 27462, unit-sax_parse - / 19836.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-08 10:36:16 +02:00
Niels Lohmann d1e6439187 Use the file-scope float_json alias in the #3989 test
The local alias that the out-of-range test declared shadows the identical
file-scope one, which GCC's -Wshadow=global turns into an error.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 22:56:50 +02:00
Niels Lohmann dcf7e2222c Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 19:24:12 +02:00
Niels Lohmann d99932fb3f Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 09:04:47 +02:00
Niels Lohmann 1d2cb49ff3 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 08:54:17 +02:00
Niels Lohmann a2766df787 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 07:42:58 +02:00
Niels Lohmann 2ea5bed0e3 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 08:39:31 +02:00
Niels Lohmann 632f5369fe Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-06 07:43:24 +02:00
Niels Lohmann c2619bf460 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-04 18:03:20 +02:00
Niels Lohmann 351aeb7440 Merge remote-tracking branch 'origin/develop' into HEAD
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-04 17:19:32 +02:00
Niels Lohmann 7b0af0072e Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	include/nlohmann/detail/input/binary_reader.hpp
#	include/nlohmann/detail/string_utils.hpp
#	single_include/nlohmann/json.hpp
2026-10-04 12:22:05 +02:00
Niels Lohmann 650886d126 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	tests/src/unit-alt-string.cpp
2026-10-04 11:50:33 +02:00
Niels Lohmann a759afc99f Fix CI: no semicolon after CAPTURE() in the #3989 tests
#5737 enabled -Wextra-semi-stmt for the tests and removed the
semicolons after CAPTURE(), which expands to a complete statement; the
#3989 tests added twelve more.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-03 09:11:00 +02:00
Niels Lohmann 1143da4faa Fix CI: keep the binary readers' recovery code out of from_*()
The MinGW job ("clang (20.1.8)", Debug) failed to link test-msgpack_cpp17
with "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against
`.rdata'". Its GNU ld (MinGW 12.2.0) cannot link an object file with
more than 32767 sections. Reproduced with clang 20.1.8 and binutils
2.40: unit-msgpack.cpp's object has 31958 sections on develop and had
33210 with #3989; binutils 2.44 links both.

The readers of from_*() never recover, but unoptimized builds still
emitted the recovery code their dead branches referenced: about 20
helpers per reader instantiation, plus the lexer's recovery functions
through the UBJSON high-precision number repair. Now:

- Errors are reported with report_error() only. The helpers that act on
  the SAX parser's answer (repair_requested(), resync(), value_failed(),
  close_open_containers(), and the repairs of high-precision numbers and
  unsupported BSON elements) have one trivial variant for readers that do
  not recover, so the recovery code is not referenced there.
- Object keys that are not strings and BSON elements whose end is lost
  are marked at the error site and skipped by resync() in the reading
  loops; report_repairable_error(), report_error_repairable_if(),
  report_bson_element_error(), and bon8_error_repairable_if() are gone.
- is_cbor_item_head() is a free function, and the BSON element type is
  formatted as on develop again.

The object now has 32638 sections and links. Behavior is unchanged
(regression tests, binary format suites, and fuzzing), and an optimized
program that only calls from_*() is now 256 bytes smaller than on
develop.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-02 19:48:27 +02:00
Niels Lohmann 8de151f928 Fix CI: no backslash sequence in the lexer's doc comments
clang's -Wdocumentation-unknown-command reads "\u" in a doc comment as
an unknown command, even in backticks. Two comments of the recovery code
now say "Unicode escape" instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-02 19:08:41 +02:00
Niels Lohmann ab52c98f71 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	docs/mkdocs/docs/api/json_sax/parse_error.md
#	docs/mkdocs/docs/features/parsing/index.md
2026-10-02 11:38:09 +02:00
Niels Lohmann 5e93415d91 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	include/nlohmann/detail/input/lexer.hpp
#	include/nlohmann/detail/input/parser.hpp
#	single_include/nlohmann/json.hpp
#	tests/src/fuzzer-parse_json.cpp
2026-10-01 07:36:30 +02:00
Niels Lohmann 7e8e8e219b Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	include/nlohmann/detail/input/binary_reader.hpp
#	include/nlohmann/json.hpp
#	single_include/nlohmann/json.hpp
2026-09-30 22:53:03 +02:00
Niels Lohmann 5f1727cef2 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	tests/src/unit-alt-string.cpp
2026-09-30 20:11:28 +02:00
Niels Lohmann c16dd7e4f5 Fix CI: do not hide base class methods in the #3989 test SAX parsers
clang-tidy's bugprone-derived-method-shadowing-base-method rejected the
test SAX parsers that derive from json_sax_dom_parser (or the test's
SaxEventLogger) and redefine their non-virtual event functions. The
recovering DOM parsers of unit-class_parser.cpp and unit-regression2.cpp
now hold a json_sax_dom_parser and forward to it, SaxEventLogger gets a
flag to recover from errors instead of a derived class, and the one
function unit-alt-string.cpp redefines is marked as intended.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 04:50:11 +02:00
Niels Lohmann 792853d725 Fix CI: clang-tidy and JSON_NOEXCEPTION in the #3989 changes
ci_clang_tidy flagged nested conditional operators in the BON8 skip
code and in remove_incomplete_utf8_sequence()
(readability-avoid-nested-conditional-operator), and two branches with
the same body in recover_string() (bugprone-branch-clone). Use if
chains instead of the nested conditionals and merge the two branches
into one condition; the short-circuit order is unchanged.

ci_test_noexceptions aborted in the #3989 regression test: it compares
the first recovered error with the message of the exception that
from_cbor() and friends throw, and under JSON_NOEXCEPTION that call
aborts instead of throwing. Guard binary_error_message() and its uses
with #if !defined(JSON_NOEXCEPTION), as other tests in the suite do.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 16:42:57 +02:00
Niels Lohmann 4bdf1b7e74 Fix CI: no global std::string in the #3989 regression test
The U+FFFD constant was a global std::string, which the clang CI build
rejects with -Wglobal-constructors. It is now a function.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 09:27:46 +02:00
Niels Lohmann b1e9d98e41 Merge remote-tracking branch 'origin/claude/fix-issue-3989-db7e45' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 03:31:07 +02:00
Niels Lohmann f855d257df Fix CI: keep raw strings with backslashes out of test macros for MSVC
MSVC stringizes the arguments of doctest's CHECK() so that a raw string
literal becomes an ordinary one, and then reported the "\q" in the new
alt_string recovery test as warning C4129, an error with /WX. The input
is now a variable. The same pattern with "\u0000" in the parser's
recovery test is replaced by a JSON value built from a std::string.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-29 03:30:39 +02:00
Niels Lohmann 3e683e9c04 Merge branch 'develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 22:22:25 +02:00
Niels Lohmann d1d84ed9af Fix Flawfinder: format the BSON element type without snprintf
Moving the report of an unsupported BSON element type into
skip_unsupported_bson_element() moved its snprintf() call, which
Flawfinder then reported as a new CWE-134 finding. The two hexadecimal
digits are now computed directly; the message is unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 20:36:28 +02:00
Niels Lohmann de8529f99b Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

# Conflicts:
#	include/nlohmann/detail/input/binary_reader.hpp
#	single_include/nlohmann/json.hpp
2026-09-28 17:53:51 +02:00
Niels Lohmann 677794f076 Fix CI: recover numbers with the string operations every string_t has
recover_number() used back(), pop_back(), front(), and
find_first_of(const char*), which the minimal alt_string of
unit-alt-string.cpp does not provide. Since the binary readers recover
UBJSON/BJData high-precision numbers with it, from_ubjson() instantiated
it, too, and the test no longer compiled. It now uses only size(),
operator[], and resize(), and unit-alt-string.cpp recovers from errors
in JSON text, UBJSON, and CBOR.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-28 12:17:11 +02:00
Niels Lohmann 437a95cfdb Repair complete items in binary formats when parse_error() returns true (#3989)
When the SAX parser asks to recover, the binary readers now repair an
item whose end is known and read on after it, as RFC 8949, Section 5.3
describes for CBOR:

- CBOR: tags are ignored, and simple values other than false, true, and
  null become null (RFC 8949, Section 6.1); a negative integer below the
  range of number_integer_t becomes the nearest floating-point number.
- Strings that are not valid UTF-8 get U+FFFD for each ill-formed
  sequence, as in JSON text; so does a UBJSON/BJData char above 0x7F.
- UBJSON/BJData high-precision numbers keep their longest valid
  beginning (via the lexer's recover_token()), or become infinity.
- Members whose key is not a string are skipped (CBOR, MessagePack,
  BON8), like members without a key in JSON text.
- BSON elements of types the library does not read (ObjectId, datetime,
  decimal128, ...) become null; a string without its terminator and a
  document whose size does not match are kept.

Where the end of an item is unknown, reading stops as before, except
that BSON skips to the end of the document, whose size it knows.

The value read before such an error is now completed by the reader from
its container stack, as the JSON parser does, instead of by a proxy SAX
parser, which is removed. Like the parser, binary_reader gets an
AllowRecovery template parameter, so that from_*() compile without the
new code.

Tests: a table of repairs, numbers out of range, errors that stop, and
a sweep over changed and removed bytes of eight encodings that checks
balanced events and that the first error is the one from_*() reports.
All fuzzers now run a recovering checker; the binary ones also check
that it reports an error exactly when from_*() fails.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 21:40:59 +02:00
Niels Lohmann c8735246d0 Merge remote-tracking branch 'origin/develop' into claude/fix-issue-3989-db7e45
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 21:02:38 +02:00
Niels Lohmann 9adb510a0d Recover from parse errors when parse_error() returns true (#3989)
The return value of json_sax::parse_error() was documented both as "must
return false" and as "whether the parsing should continue", and the code
just passed it on. For JSON text, parsing stopped anyway, but sax_parse()
could report success for invalid input. The binary readers read on after
the error, looping forever on a CBOR indefinite-length array without its
end.

Now false stops parsing, and true recovers from the error:

- JSON text is repaired with the smallest local edit (insert a missing
  ',' or ':', remove a stray token, keep the readable part of a broken
  string or number, null for a value that cannot be read, close the
  innermost container at a wrong closing bracket and all of them at the
  end of the input), and parsing continues. The SAX events stay balanced,
  every key is followed by exactly one value, and each token is reported
  at most once.
- The binary formats cannot resynchronize, so they stop, but complete
  the value read so far.

sax_parse() returns false after any error. parse(), accept(), and the
from_*() functions never recover and compile to the same code as before.

Supersedes #4522.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 20:16:42 +02:00
253 changed files with 11604 additions and 81651 deletions

No files matched your search

+2
View File
@@ -111,6 +111,8 @@ context which existing file needs to be extended, and only very few cases requir
When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue.
`unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the
MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing the larger one.
Tests that call `sax_parse` go into `unit-sax_parse.cpp` rather than into a large test file: every call instantiates
the parser and the binary reader that recover from errors, which grows a test file considerably.
#### Exceptions
+2 -2
View File
@@ -67,10 +67,10 @@ jobs:
- name: Install astyle
run: |
python3 -mvenv venv
venv/bin/pip3 install --require-hashes -r $MAIN_DIR/tools/astyle/requirements.txt
venv/bin/pip3 install -r $MAIN_DIR/tools/astyle/requirements.txt
- name: Install generate_natvis dependencies
run: pip3 install --require-hashes -r $NATVIS_TOOL_DIR/requirements.txt
run: pip3 install -r $NATVIS_TOOL_DIR/requirements.txt
- name: Regenerate the tools/macro_builder tables in macro_scope.hpp
run: |
+7 -6
View File
@@ -38,13 +38,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
languages: c-cpp
# Analyze the sources without compiling them. Autobuild compiled the
# whole test suite and the resulting database was so large that the
# runner often lost communication with the server while importing it.
build-mode: none
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
+1 -1
View File
@@ -48,6 +48,6 @@ jobs:
output: 'flawfinder_results.sarif'
- name: Upload analysis results to GitHub Security tab
uses: github/codeql-action/upload-sarif@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
+1 -1
View File
@@ -80,6 +80,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
sarif_file: results.sarif
+2 -6
View File
@@ -52,12 +52,8 @@ jobs:
# the old action fell back to, running community rules with no token.
# SEMGREP_APP_TOKEN is still passed through so registry auth works if a
# token is ever added.
# The version is pinned, but not by hash: Semgrep pins its own dependencies
# to exact or compatible-release versions, so a hash-pinned lock file broke
# on almost every Dependabot bump of a transitive dependency. Dependabot
# only bumps the semgrep line; pip resolves the rest.
- name: Install Semgrep
run: python3 -m pip install --user -r cmake/requirements/requirements-semgrep.txt
run: python3 -m pip install --user semgrep==1.168.0
# `semgrep scan --sarif` always exits 0 even with findings; continue-on-error
# is a safety net so the SARIF upload still runs if the scan itself errors.
@@ -69,7 +65,7 @@ jobs:
# Upload SARIF file generated in previous step
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@33d7b85da1a04171e211befe2c739303b9a4c066 # v4.39.3
uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
with:
sarif_file: semgrep.sarif
if: always()
+2 -2
View File
@@ -42,7 +42,7 @@ jobs:
- name: Check that Meson and pkg-config offer the CMake options
run: make check_build_options
- name: Install Meson
run: pip install --require-hashes -r cmake/requirements/requirements-meson.txt
run: pip install meson
- name: Install with Meson
run: |
meson setup build-meson --prefix=${{ github.workspace }}/install
@@ -153,7 +153,7 @@ jobs:
container: ubuntu:24.04
strategy:
matrix:
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local, ci_test_recursion_depth_limit_0]
target: [ci_cmake_flags, ci_test_diagnostics, ci_test_diagnostic_positions, ci_test_noexceptions, ci_test_noimplicitconversions, ci_test_legacycomparison, ci_test_noglobaludls, ci_test_disableenumserialization, ci_test_disabletuplereferenceconversion, ci_test_skiplibraryversioncheck, ci_test_simdutf, ci_test_strict_nul_handling, ci_test_delete_deprecated_functions, ci_test_no_thread_local]
steps:
- name: Install build-essential
run: apt-get update ; apt-get install -y build-essential unzip wget git
-1
View File
@@ -30,7 +30,6 @@ cc_library(
"include/nlohmann/detail/exceptions.hpp",
"include/nlohmann/detail/hash.hpp",
"include/nlohmann/detail/input/binary_reader.hpp",
"include/nlohmann/detail/input/contiguous_parser.hpp",
"include/nlohmann/detail/input/input_adapters.hpp",
"include/nlohmann/detail/input/json_sax.hpp",
"include/nlohmann/detail/input/lexer.hpp",
+3272
View File
File diff suppressed because it is too large. Load diff
+18 -1
View File
@@ -1,4 +1,4 @@
.PHONY: all amalgamate check-amalgamation check_build_options pretty install_astyle clean release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check run_benchmarks pvs_studio serve_header reuse spdx
.PHONY: all amalgamate check-amalgamation check_build_options pretty install_astyle clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check run_benchmarks pvs_studio serve_header reuse spdx
##########################################################################
# configuration
@@ -40,6 +40,7 @@ MACRO_SCOPE_HPP=include/nlohmann/detail/macro_scope.hpp
all:
@echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd,_literals,_view}.hpp from the include/nlohmann sources"
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
@echo "ChangeLog.md - generate ChangeLog file"
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
@echo "check_build_options - check that the Meson build and the pkg-config files offer the options of the CMake target"
@echo "clean - remove built files"
@@ -174,6 +175,22 @@ check-amalgamation:
BUILD.bazel:
cmake -P cmake/scripts/gen_bazel_build_file.cmake
##########################################################################
# ChangeLog
##########################################################################
# Create a ChangeLog based on the git log using the GitHub Changelog Generator
# (<https://github.com/github-changelog-generator/github-changelog-generator>).
# variable to control the diffs between the last released version and the current repository state
NEXT_VERSION ?= "unreleased"
ChangeLog.md:
github_changelog_generator -o ChangeLog.md --user nlohmann --project json --simple-list --release-url https://github.com/nlohmann/json/releases/tag/%s --future-release $(NEXT_VERSION)
$(SED) -i 's|https://github.com/nlohmann/json/releases/tag/HEAD|https://github.com/nlohmann/json/tree/HEAD|' ChangeLog.md
$(SED) -i '2i All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).' ChangeLog.md
##########################################################################
# Release files
##########################################################################
+11 -36
View File
@@ -1,9 +1,4 @@
<a href="https://github.com/nlohmann/json/releases">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/mkdocs/docs/images/json-dark.svg">
<img src="docs/mkdocs/docs/images/json-light.svg" alt="JSON for Modern C++: animated feature slideshow">
</picture>
</a>
[![JSON for Modern C++](docs/mkdocs/docs/images/json.gif)](https://github.com/nlohmann/json/releases)
[![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk/branch/develop?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
[![Ubuntu](https://github.com/nlohmann/json/workflows/Ubuntu/badge.svg)](https://github.com/nlohmann/json/actions?query=workflow%3AUbuntu)
@@ -44,7 +39,6 @@
- [Conversions to/from arbitrary types](#arbitrary-types-conversions)
- [Specializing enum conversion](#specializing-enum-conversion)
- [Binary formats (BSON, CBOR, MessagePack, UBJSON, BJData, and BON8)](#binary-formats-bson-cbor-messagepack-ubjson-bjdata-and-bon8)
- [Zero-copy views](#zero-copy-views)
- [Customers](#customers)
- [Ecosystem](#ecosystem)
- [Supported compilers](#supported-compilers)
@@ -74,7 +68,7 @@ Other aspects were not so important to us:
- **Memory efficiency**. Each JSON object has an overhead of one pointer (the maximal size of a union) and one enumeration element (1 byte). The default generalization uses the following C++ data types: `std::string` for strings, `int64_t`, `uint64_t` or `double` for numbers, `std::map` for objects, `std::vector` for arrays, and `bool` for Booleans. However, you can template the generalized class `basic_json` to your needs.
- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set. The [performance](https://json.nlohmann.me/features/performance/) page shows how to get the most out of the library.
- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set.
See the [contribution guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#please-dont) for more information.
@@ -501,7 +495,7 @@ bool key(string_t& val);
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
```
The return value of each function determines whether parsing should proceed.
The return value of each function determines whether parsing should proceed. For `parse_error`, returning `true` [recovers from the error](https://json.nlohmann.me/features/parsing/error_recovery/): the parser repairs the input and continues.
To implement your own SAX handler, proceed as follows:
@@ -509,7 +503,7 @@ To implement your own SAX handler, proceed as follows:
2. Create an object of your SAX interface class, e.g. `my_sax`.
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
Note the `sax_parse` function only returns a `bool` indicating whether the input was parsed without errors and no SAX event returned `false`. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
### STL-like access
@@ -1058,7 +1052,7 @@ struct less_than_32_serializer {
Be **very** careful when reimplementing your serializer, you can stack overflow if you don't pay attention:
```cpp
template <typename T, typename = void>
template <typename T, void>
struct bad_serializer
{
template <typename BasicJsonType>
@@ -1069,10 +1063,10 @@ struct bad_serializer
}
template <typename BasicJsonType>
static void from_json(const BasicJsonType& j, T& value) {
static void to_json(const BasicJsonType& j, T& value) {
// this calls BasicJsonType::json_serializer<T>::from_json(j, value)
// if BasicJsonType::json_serializer == bad_serializer ... oops!
value = j.template get<T>(); // oops!
value = j.get<T>(); // oops!
}
};
```
@@ -1775,7 +1769,7 @@ I deeply appreciate the help of the following people.
330. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation.
331. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example.
332. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test.
333. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation and avoided temporary `basic_json` values for CBOR and MessagePack object keys.
333. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation.
334. [laterlaugh](https://github.com/laterlaugh) fixed some typos.
335. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer.
336. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer.
@@ -1853,7 +1847,7 @@ I deeply appreciate the help of the following people.
408. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation.
409. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions.
410. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads.
411. [hum4nBeing](https://github.com/hum4nBeing) and [AJ369ninja](https://github.com/AJ369ninja) fixed the overflow handling of high-precision numbers in UBJSON.
411. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON.
412. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes.
413. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability.
414. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`.
@@ -1867,26 +1861,6 @@ I deeply appreciate the help of the following people.
422. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks.
423. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output.
424. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include.
425. [Alexander Lanin](https://github.com/AlexanderLanin) improved the error message of `get_to()` for `const` arguments.
426. [satelliteprogrammer](https://github.com/satelliteprogrammer) added `merge_diff()` to create JSON Merge Patches (RFC 7396).
427. [Jeremy Nimmer](https://github.com/jwnimmer-tri) removed `alwayslink` from the Bazel build.
428. [Dadi Reddy Sai Praneeth Reddy](https://github.com/dsp0redy) made `to_msgpack()` throw when a size exceeds the MessagePack limit.
429. [ayush-singh-0601](https://github.com/ayush-singh-0601) reduced the runtime of the test suite.
430. [Kartikey Negi](https://github.com/ReturnKartikey) fixed the CBOR tag handling of tagged values that are not byte strings.
431. [Michiel van Slobbe](https://github.com/mvanslobbe) sped up the parser by checking for the expected separator first.
432. [Mohd Quamar Tyagi](https://github.com/Tyagiquamar) allowed SAX parsing of tagged CBOR.
433. [fhgffy](https://github.com/fhgffy) rejected NUL bytes in UBJSON and BJData high-precision numbers and fixed a `-Wswitch-enum` warning.
434. [Suyog Verma](https://github.com/commit-msuyog) used MSVC intrinsics for the 128-bit multiplication in the floating-point serialization.
435. [Bas van Dijk](https://github.com/basdedas) fixed a TU-local exposure of `UTF8_ACCEPT` and `UTF8_REJECT` in C++20 modules.
436. [Christoph Grüninger](https://github.com/gruenich) fixed a leak and a crash when an allocation throws during parsing.
437. [Amelia Barnaby](https://github.com/ameliabarnabyhub) made serializing discarded values to binary formats throw instead of writing corrupt output.
438. [Vesko Karaganev](https://github.com/veselink1) made the destructor use the provided allocator.
439. [Michael Sam](https://github.com/michaelsam94) kept allocation failures from escaping the `noexcept` destructor.
440. [Mihnea Magheru](https://github.com/sakuntalle) added a natvis visualizer for `detail::json_default_base`.
441. [Joseph Demarest](https://github.com/JosephDemarest) rejected nested indefinite-length CBOR strings.
442. [Afonso Januário](https://github.com/afonsojanu) made `std::hash<basic_json>` consistent with `operator==` for numbers.
443. [Kartik Gupta](https://github.com/KartikeyG-world) limited the natvis namespace variants to two ABI tags.
444. [manop55555](https://github.com/manop55555) reported a buffer over-read in `dump()` with large indentation values.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
@@ -1909,6 +1883,7 @@ The library itself consists of a single header file licensed under the MIT licen
- [**cpplint**](https://github.com/cpplint/cpplint) for style checks
- [**doctest**](https://github.com/doctest/doctest) for the unit tests
- [**Flawfinder**](https://dwheeler.com/flawfinder/) for static analysis
- [**GitHub Changelog Generator**](https://github.com/github-changelog-generator/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks
- [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros
- [**include-what-you-use**](https://include-what-you-use.org) to check the included headers
@@ -1975,7 +1950,7 @@ For more information, see [JSON With Commas and Comments (JWCC)](https://nigelta
By default, the library does not preserve the **insertion order of object elements**. This is standards-compliant, as the [JSON standard](https://tools.ietf.org/html/rfc8259.html) defines objects as "an unordered collection of zero or more name/value pairs".
If you do want to preserve the insertion order, you can try the type [`nlohmann::ordered_json`](https://json.nlohmann.me/api/ordered_json/). Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
If you do want to preserve the insertion order, you can try the type [`nlohmann::ordered_json`](https://github.com/nlohmann/json/issues/2179). Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
See the [**documentation on object order**](https://json.nlohmann.me/features/object_order/) for more information.
-21
View File
@@ -342,27 +342,6 @@ add_custom_target(ci_test_no_thread_local
COMMENT "Compile and test without thread-local storage"
)
###############################################################################
# Change the number of levels the walks over a value recurse into.
###############################################################################
# JSON_RECURSION_DEPTH_LIMIT is 128 by default, which is what the whole test
# suite otherwise runs with. 0 sends every walk to its explicit stack at once,
# so this target runs the whole suite through the walks that the default only
# uses for values nested more deeply than 128 levels: results must not depend on
# the limit. The largest limit, 254, is covered by a build of
# tests/src/unit-recursion_depth_limit.cpp (see tests/CMakeLists.txt).
add_custom_target(ci_test_recursion_depth_limit_0
COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON
-DCMAKE_CXX_FLAGS=-DJSON_RECURSION_DEPTH_LIMIT=0
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_recursion_depth_limit_0
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_recursion_depth_limit_0
COMMAND cd ${PROJECT_BINARY_DIR}/build_recursion_depth_limit_0 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMENT "Compile and test with JSON_RECURSION_DEPTH_LIMIT=0"
)
###############################################################################
# Coverage.
###############################################################################
-5
View File
@@ -9,11 +9,6 @@
# __n2)`, which stores `__n1 - __n2` in a signed difference_type and clamps it to int. Any comparison with a shorter
# view wraps that subtraction on purpose, and -fsanitize=integer reports it as implicit-integer-sign-change. json_view
# compares keys as string views, so the same check is excluded for <string_view> as well.
# std::filesystem::path::u8string() in libstdc++ 14 builds a std::u8string from the native char sequence, so
# basic_string's range copy (_S_copy_chars) converts every char >= 0x80 to char8_t, which -fsanitize=integer reports as
# implicit-integer-sign-change (test-conversions2_cpp20/cpp23, non-ASCII path). The conversion is intended and happens
# inside the standard library, not in this library.
[implicit-integer-sign-change]
src:*/include/c++/*/format
src:*/include/c++/*/string_view
src:*/include/c++/*/bits/basic_string.h
@@ -1,3 +0,0 @@
meson==1.12.1 \
--hash=sha256:930bc7542cbd9f57009e182fd014eba48cf1a0180a7b9006d2d32d8f168d8b02 \
--hash=sha256:ab0a6ca09f8ef70c564c8241fb5a23957886a0b53fb58412b5e07eaf07dba743
+1 -1
View File
@@ -1 +1 @@
reuse==6.3.0
reuse==6.2.0
@@ -1 +0,0 @@
semgrep==1.184.0
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

+5 -17
View File
@@ -67,11 +67,7 @@ basic_json(basic_json&& other) noexcept;
- **objects**: [`object_t`](object_t.md) and all kinds of compatible associative containers such as `std::map`,
`std::unordered_map`, `std::multimap`, and `std::unordered_multimap` with a `key_type` compatible to `string_t`
and a `value_type` from which a `basic_json` value can be constructed.
- **strings**: `string_t`, string literals, and all compatible string containers can be used. Since version 3.13.0,
this includes `#!cpp std::u8string` (C++20): its code units are stored as a JSON string and
[`get<std::u8string>()`](get.md) reads it back (in version 3.12.0, a `#!cpp std::u8string` was stored as an array
of numbers; `get<std::u8string>()` still accepts such an array). `#!cpp std::u8string_view` and `#!cpp char8_t`
arrays are not strings; they are stored as arrays of code units.
- **strings**: `string_t`, string literals, and all compatible string containers can be used.
!!! note "Rvalue containers are moved from"
@@ -97,18 +93,10 @@ basic_json(basic_json&& other) noexcept;
The constructor tries to convert the internal `m_value` of the parameter. Each member value (object, array, string,
etc.) is serialized via the corresponding `to_json()` overload. For objects and strings, the conversion requires
that the *target* `basic_json` type's `object_t::key_type` (or `string_t`) be directly constructible from the
*source* type's corresponding member type via `is_constructible`. If this requirement is not met, the result
depends on the *source* string type:
- If it is a range of characters (it has `begin()` and `end()`, as `std::string` does), the conversion falls back to
the array-conversion path, which represents objects as arrays of `[key, value]` pairs and strings as arrays of
character codes. This result has a different type than the source value, so the constructor's
[`JSON_ASSERT`](../macros/json_assert.md) fails; with its default definition, `assert`, the program aborts unless
`NDEBUG` is defined, in which case the array is returned.
- Otherwise, the conversion does not compile, even though `#!cpp std::is_constructible` reports `#!cpp true`; for
example, when converting a `basic_json` whose `string_t` is the `alt_string` of the unit tests to `json`.
This is a known limitation tracked in [issue #3425](https://github.com/nlohmann/json/issues/3425).
*source* type's corresponding member type via `is_constructible`. If this requirement is not met, the conversion
does not fail to compile; instead, it silently falls back to the array-conversion path, which represents objects
as arrays of `[key, value]` pairs and strings as arrays of character codes. This is a known limitation tracked in
[issue #3425](https://github.com/nlohmann/json/issues/3425).
5. Creates a JSON value of type array or object from the passed initializer list `init`. In case `type_deduction` is
`#!cpp true` (default), the type of the JSON value to be created is deducted from the initializer list `init`
-6
View File
@@ -130,12 +130,6 @@ overload (3).
`#!cpp json(true).get<double>()` throw. This is the behavior of all 3.x versions;
[`basic_json_view::get<T>()`](../basic_json_view/get.md) behaves the same.
!!! note "`std::u8string` conversions"
Since version 3.13.0, `#!cpp get<std::u8string>()` (C++20) reads a JSON string; it also accepts an array of numbers,
the form in which version 3.12.0 stored a `#!cpp std::u8string`. Any other JSON type throws
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) ("type must be string or array").
!!! note "`std::optional` conversions"
Prior to version 3.13.0, `#!cpp get<std::optional<T>>()` (and other conversions to `std::optional<T>`) failed to
+2 -2
View File
@@ -50,7 +50,7 @@ Besides `basic_json`, the library offers a second way to hold a parsed JSON text
[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the
values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document.
Both are defined in the header `<nlohmann/json_view.hpp>`; see
[Zero-copy JSON views](../../features/json_view/index.md) for the concept and the trade-offs.
[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs.
- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only
documents of the `basic_json` specialization they belong to
@@ -363,7 +363,7 @@ Access to the JSON value
specification of the JSON data interchange format
- [`basic_json_document`](../basic_json_document/index.md) - / [`basic_json_view`](../basic_json_view/index.md) - parse a JSON
text into a flat index and read it through a view
- [Zero-copy JSON views](../../features/json_view/index.md) - when to use a document and a view instead of a `basic_json`
- [Zero-copy JSON views](../../features/json_view.md) - when to use a document and a view instead of a `basic_json`
- [Types](../../features/types/index.md) - how JSON values are stored and which types exist
- [Converting values](../../features/conversions.md) - get values out of a JSON value and put values in
- [Creating JSON values](../../features/creating_values.md) - all the ways to build a JSON value
+3 -6
View File
@@ -102,12 +102,9 @@ objects.
When converting an object from one `basic_json` specialization to another via the
[converting constructor](basic_json.md) (overload 4), the target `object_t`'s `key_type` must be
directly constructible from the source object's key type (with the default `std::map`, the source `basic_json`'s
`string_t`). If this requirement is not met and the source string type is a range of characters, the
object is converted as an array of key-value pairs, which is incorrect: the converting constructor's
[`JSON_ASSERT`](../macros/json_assert.md) fails; with its default definition, `assert`, the program aborts unless
`NDEBUG` is defined, in which case the array is returned. If the source string type is not a range of characters, the
conversion does not compile at all. See [`basic_json`](basic_json.md) and
directly constructible from the source `basic_json`'s `string_t` type (or more generally, from the
source object's key type). If this requirement is not met, the conversion does not fail; instead,
the object is silently converted as an array of key-value pairs, which is incorrect. See
[issue #3425](https://github.com/nlohmann/json/issues/3425) for details and an example.
## Examples
@@ -27,16 +27,6 @@ reference operator+=(initializer_list_t init);
`init` is converted into an object element and added using `operator+=(const typename object_t::value_type&)`.
Otherwise, `init` is converted to a JSON value and added using `operator+=(basic_json&&)`.
!!! note "Single braced JSON value"
For a call such as `#!cpp arr += {obj}`, where the initializer list contains a single JSON value `obj`,
compilers may disagree unless
[`JSON_BRACE_INIT_COPY_SEMANTICS`](../macros/json_brace_init_copy_semantics.md) is defined to `1`: GCC, for
example, selects this overload and appends a single-element array `[obj]`, whereas Clang selects the
`basic_json` overload and appends a copy of `obj`. With the macro, GCC and Clang both append a copy of `obj`.
To get a portable result without the macro, write `#!cpp arr += obj` to append a copy, or
`#!cpp arr += json::array({obj})` to append a single-element array.
## Iterator invalidation
For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators (including
@@ -60,14 +60,6 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -60,14 +60,6 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -61,14 +61,6 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -71,14 +71,6 @@ Linear.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result depends on the language standard. Before C++20, the pair
counts as equivalent and the following elements (or the sizes) decide: for instance, `[NaN] < [1, NaN]` and
`[1] <= [NaN]` are `#!cpp true`. Since C++20, the whole comparison is unordered, and `<`, `<=`, `>`, and `>=` all
yield `#!cpp false`. Otherwise (for example `[1, NaN] < [2, NaN]`, or objects whose keys differ), the result is the
same for all language standards. The same program can therefore order such values differently when it is compiled
with a different language standard.
!!! note "Operator overload resolution"
Since C++20 overload resolution will consider the _rewritten candidate_ generated from
@@ -55,10 +55,6 @@ Linear.
1. Comparing a `NaN` with itself.
2. Comparing a `NaN` with another `NaN`.
3. Comparing a `NaN` and any other number.
- For arrays and objects, the elements are compared in order. If the comparison reaches a `NaN` and a number (or
another `NaN`) before any other difference, the result is `std::partial_ordering::unordered`. (Without C++20, the
comparison operators `<`, `<=`, `>`, and `>=` use the comparison of the containers, which treats such a pair as
equivalent; see [**operator<**](operator_lt.md).)
## Examples
+6 -12
View File
@@ -124,19 +124,17 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
[`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but
keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses
less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string,
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view/index.md).
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view.md).
!!! tip "Performance"
Contiguous inputs (strings, string views, vectors, arrays, pointer ranges) are the fastest to parse. A
`#!cpp std::istream` comes close: the parser scans the characters its stream buffer holds in bulk, without reading
ahead, so the stream is left exactly where it would be after reading one character at a time. With glibc
and the Apple C library, the characters the `#!cpp FILE` holds in its buffer are scanned in bulk like a stream's;
elsewhere a `#!cpp FILE*` is read one character at a time. Where the C library has unlocked stdio functions, the
file is locked only once, so other threads that use the same `FILE` object wait until parsing is done, unless
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) is defined to `1`. See
[input sources and performance](../../features/parsing/index.md#input-sources-and-performance) and
[`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md).
ahead, so the stream is left exactly where it would be after reading one character at a time. A `#!cpp FILE*` is
read one character at a time, but the file is locked only once, so other threads that use the same `FILE` object
wait until parsing is done. See [input sources and performance](../../features/parsing/index.md#input-sources-and-performance),
[`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md), and
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md).
## Examples
@@ -313,10 +311,6 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
it as end of input; planned to become the default in version 4.0.0.
- Extended empty-input detection to also cover an `std::istream` without a stream buffer, and fixed a crash
(`std::terminate`) when parsing from an `std::istream` with `eofbit` in its exception mask, in version 3.13.0.
- Fixed the position that a [`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) reports for an
unexpected number that is followed by a line feed in version 3.13.0: for the input `1`, line feed, `2`, line feed,
version 3.12.0 reported "line 2, column 0" and version 3.13.0 reports "line 2, column 1"; for `1 2` and a line feed,
the report was "line 1, column 0" and is now "line 1, column 3".
- Parsing from an `std::istream` scans the characters in its stream buffer in bulk in version 3.13.0
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md) restores reading one character at a
time).
@@ -27,16 +27,6 @@ void push_back(initializer_list_t init);
`init` is converted into an object element and added using `push_back(const typename object_t::value_type&)`.
Otherwise, `init` is converted to a JSON value and added using `push_back(basic_json&&)`.
!!! note "Single braced JSON value"
For a call such as `#!cpp arr.push_back({obj})`, where the initializer list contains a single JSON value `obj`,
compilers may disagree unless
[`JSON_BRACE_INIT_COPY_SEMANTICS`](../macros/json_brace_init_copy_semantics.md) is defined to `1`: GCC, for
example, selects this overload and appends a single-element array `[obj]`, whereas Clang selects the
`basic_json` overload and appends a copy of `obj`. With the macro, GCC and Clang both append a copy of `obj`.
To get a portable result without the macro, write `#!cpp arr.push_back(obj)` to append a copy, or
`#!cpp arr.push_back(json::array({obj}))` to append a single-element array.
## Iterator invalidation
For all cases where an element is added to an **array**, a reallocation can happen, in which case all iterators (including
+7 -5
View File
@@ -104,7 +104,9 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
## Return value
return value of the last processed SAX event
`#!cpp true` if the input was parsed without errors and no SAX event returned `#!cpp false`; `#!cpp false` otherwise.
In particular, the result is `#!cpp false` for input with errors, even if the SAX parser recovered from all of them
(see [error recovery](../../features/parsing/error_recovery.md)).
## Exception safety
@@ -124,10 +126,9 @@ consumer `sax` has a super-linear complexity.
A UTF-8 byte order mark is silently ignored.
When reading from a `#!c FILE*` where the C library has unlocked stdio functions, the file is locked while it is parsed,
unless [`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) is defined to `1`.
The lock is recursive, so the SAX handler may use the same file with stdio functions, but it must not wait for another
thread that uses the same `FILE` object.
When reading from a `#!c FILE*`, the file is locked while it is parsed. The lock is recursive, so the SAX handler may
use the same file with stdio functions, but it must not wait for another thread that uses the same `FILE` object; see
[`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md).
## Examples
@@ -164,6 +165,7 @@ thread that uses the same `FILE` object.
- Added `ignore_trailing_commas` in version 3.13.0.
- Added `tag_handler` 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.
- Recovering from parse errors (see [`parse_error`](../json_sax/parse_error.md)) added in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
after the parsed value when `strict` is `#!cpp false`.
@@ -67,7 +67,3 @@ Constant.
## Version history
- Added in version 3.12.0.
- Fixed in version 3.13.0: in version 3.12.0, the start position of a string that contains escape sequences (for
example `#!cpp "\n"` or `#!cpp "\u00e4"`) was too large, by the number of bytes the escape sequences add to the
source text. The position is now always that of the opening `"`. The "(bytes a-b)" range in the messages of
[`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) was wrong in the same way.
+3 -5
View File
@@ -60,11 +60,9 @@ String values are stored as pointers in a `basic_json` type. That is, for any ac
When converting a string value from one `basic_json` specialization to another via the
[converting constructor](basic_json.md) (overload 4), the target `string_t` must be directly
constructible from the source `basic_json`'s `string_t` type. If this requirement is not met, the
conversion depends on the source string type. If it is a range of characters, the string is converted as an array of
character codes, which is incorrect: the converting constructor's [`JSON_ASSERT`](../macros/json_assert.md) fails; with
its default definition, `assert`, the program aborts unless `NDEBUG` is defined, in which case the array is returned.
If the source string type is not a range of characters, the conversion does not compile at all. See
[issue #3425](https://github.com/nlohmann/json/issues/3425) for details and an example.
conversion does not fail; instead, the string is silently converted as an array of character codes,
which is incorrect. See [issue #3425](https://github.com/nlohmann/json/issues/3425) for details
and an example.
## Examples
@@ -77,7 +77,7 @@ compile; see [`parse`](parse.md#notes).
- [`parse`](parse.md) - deserialize from a compatible input
- [`BasicJsonType::accept`](../basic_json/accept.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how parsing a document differs from, and matches, basic_json::parse
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how parsing a document differs from, and matches, basic_json::parse
## Version history
@@ -52,7 +52,7 @@ Constant.
- [`parse`](parse.md) - deserialize from a compatible input
- [`is_discarded`](is_discarded.md) - return whether the last parse failed
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
## Version history
@@ -123,7 +123,7 @@ by an erase, not only those at or after the removed element, since the container
- [`root`](root.md) - the view of the root value, the starting point of overload 3
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
## Version history
@@ -109,17 +109,16 @@ points apply to every edit:
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
number that was not itself edited — see [Editing a document](../../features/json_view/editing.md) for
number that was not itself edited — see [Editing a document](../../features/json_view.md#editing-a-document) for
why this matters.
- [`read()`](read.md) discards all edits (so its `input` must not be a string obtained from this document),
[`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once there are edits, and
[`memory_usage()`](memory_usage.md) includes the memory edits use.
- [`read()`](read.md) discards all edits, [`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once
there are edits, and [`memory_usage()`](memory_usage.md) includes the memory edits use.
[`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is
`#!cpp static_cast<std::size_t>(-1)`, the same value it reports for a decoded string.
## See also
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
- [`basic_json_view`](../basic_json_view/index.md) - the non-owning handle to a value in a document
- [`json_document`](../json_document.md) - and [`json_editable_document`](../json_editable_document.md) - the default specializations
- [`materialize`](../basic_json_view/materialize.md) - build a `basic_json` value from a view
@@ -108,7 +108,7 @@ an edit in general.
- [`set`](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
## Version history
@@ -43,7 +43,7 @@ When the document is discarded, [`root()`](root.md) returns a discarded view (it
- [`parse`](parse.md) - deserialize from a compatible input
- [`basic_json_view::is_discarded`](../basic_json_view/is_discarded.md) - return whether a view is invalid
- [Zero-copy JSON views](../../features/json_view/differences.md#what-is-the-same) - how a discarded document arises
- [Zero-copy JSON views](../../features/json_view.md#what-is-the-same-as-parse) - how a discarded document arises
## Version history
@@ -24,7 +24,7 @@ static basic_json_document load(std::vector<std::uint8_t>&& image,
In every overload, the node index is copied into storage the document itself owns — so that it is properly aligned,
and, for an [editable](index.md#edits) document, can be edited — while the text and the decoded strings stay in
`image`. The hash indexes [large objects](../../features/json_view/index.md) use for lookup are rebuilt, exactly as after
`image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after
parsing.
## Parameters
@@ -113,12 +113,11 @@ How thoroughly `load()` validates `image` before trusting it.
`full` (the default)
: Checks everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a
string still in the source text, that it contains no quote, backslash, or control character); and that every number
token is well-formed and matches the value stored for it, and has the type the parser gives it (a float node only
for a token with a fraction or an exponent, or an integer that does not fit 64 bits). Strings and float tokens may
share a range only if the ranges are identical (as nodes that share a value do), and never overlap otherwise.
Reading and serializing a checked image is safe and always produces valid JSON, exactly as for a parsed document.
This is the right choice for an image from anything you do not fully control — a file, a cache shared with other
processes, a peer on the network.
token is well-formed and matches the value stored for it. Strings and float tokens may share a range only if the
ranges are identical (as nodes that share a value do), and never overlap otherwise. Reading and serializing a
checked image is safe and always produces valid JSON, exactly as for a parsed document. This is the right choice for
an image from anything you do not fully control — a file, a cache shared with other processes, a peer on the
network.
`bounds`
: Checks structure and bounds only: that every offset and count in the node index stays inside the image. So
@@ -173,7 +172,7 @@ returned document, and any [view](../basic_json_view/index.md) taken from it, is
- [`save`](save.md) - write the document as an image
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [`parse`](parse.md) - deserialize from JSON text instead of an image
- [Images](../../features/json_view/images.md) - why and when to use images
- [Images](../../features/json_view.md#images) - why and when to use images
## Version history
@@ -44,7 +44,7 @@ to observe the effect of [`shrink_to_fit()`](shrink_to_fit.md).
- [`node_count`](node_count.md) - the number of index entries
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
## Version history
@@ -43,7 +43,7 @@ strings and, for an owned document, the text).
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
- [`shrink_to_fit`](shrink_to_fit.md) - release unused index capacity
- [Zero-copy JSON views](../../features/json_view/index.md#the-idea) - the flat node index and its memory
- [Zero-copy JSON views](../../features/json_view.md#the-idea) - the flat node index and its memory
## Version history
@@ -43,7 +43,7 @@ document (`#!cpp owns_source() == false`) is only valid while the buffer it was
- [`parse`](parse.md) - deserialize from a compatible input
- [`parse_copy`](parse_copy.md) - deserialize a copy of a compatible input, always owned
- [`source`](source.md) - the parsed text
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
## Version history
@@ -112,7 +112,7 @@ told apart from other class-type iterators, so their range is read into an owned
range (e.g. of a `#!cpp std::list<char>`).
See [`owns_source`](owns_source.md) to check which happened after a call, and the
[feature page](../../features/json_view/index.md) for the reasoning.
[feature page](../../features/json_view.md) for the reasoning.
**Numbers.** As for [`BasicJsonType::parse()`](../basic_json/parse.md), an integer literal too large for the 64-bit
integer type becomes a floating-point value.
@@ -74,7 +74,7 @@ compile; see [`parse`](parse.md#notes).
- [`parse`](parse.md) - deserialize from a compatible input, borrowing it where possible
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - borrowing or copying the source text
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - borrowing or copying the source text
## Version history
@@ -96,7 +96,7 @@ Like [`set`](set.md) on a member or an element, `push_back` never moves an exist
- [`root`](root.md) - the view of the root value
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Zero-copy JSON views](../../features/json_view/editing.md) - what an edit guarantees and costs
- [Zero-copy JSON views](../../features/json_view.md#editing-a-document) - what an edit guarantees and costs
## Version history
@@ -46,34 +46,6 @@ Linear in the length of the input.
Every view taken from `#!cpp *this` before the call — including the previous [`root()`](root.md) — is invalidated,
whether or not the new parse succeeds; take fresh views from [`root()`](root.md) afterward.
!!! warning "`input` must not point into the document itself"
`read()` releases the document's previous text, its decoded strings, and (for an editable document) its edits
*before* it parses `input`. An `input` that is, or points into, any of them violates a precondition: it may seem to
work, parse empty or garbled text, be reported by a sanitizer, or read freed memory (undefined behavior).
This applies to every `string_view` obtained from the document whose characters live in memory that `read()`
releases:
- [`source()`](source.md) of a document that owns its text;
- [`get_string()`](../basic_json_view/get_string.md) (also as `get<string_view_t>()` or a `string_view` member of a
decoded struct) of a string with escapes, of a string of a document that owns its text, and of an edited string;
- [`number_token()`](../basic_json_view/number_token.md) of a number of a document that owns its text, and of a
number written by an edit;
- the key of an object member (`key()`) if it has escapes, was written by an edit, or the document owns its text.
It does not apply to a text that the caller owns and the document borrows, nor to the strings, keys, and tokens that
are read from such a text unchanged, nor to the decoded strings of a borrowed [image](load.md), which live in the
caller's image: `read()` never touches them. To parse JSON that a document holds in a string of its own (for
example, JSON embedded in a string value), copy the string first, for example with
`#!cpp std::string inner(doc.root()["payload"].get_string()); doc.read(inner);`.
A debug build (one without `NDEBUG`) checks this with `assert()`: an `input` in the document's text, decoded
strings, or image, or in the most recent buffer of strings written by edits, ends the program. (Strings written
by earlier edits are not covered.) A release build does not check, to keep the cost of reusing a document at a
minimum. The check uses `assert()` directly, not [`JSON_ASSERT`](../macros/json_assert.md), which `json.hpp`
undefines at its end.
`input` is borrowed or owned by the same rules as [`parse()`](parse.md#notes); a document can borrow on one call and
own on the next, since ownership is decided freshly each time. An input that is copied (see the table in
[`parse()`](parse.md#notes)) is written into the document's previous owned copy, reusing its memory; a moved-in
@@ -109,7 +81,7 @@ compile; see [`parse`](parse.md#notes).
- [`parse`](parse.md) - deserialize from a compatible input
- [`root`](root.md) - the view of the root value
- [`load`](load.md) - read a document from an image instead of parsing JSON text
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - reusing a document and what it invalidates
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - reusing a document and what it invalidates
## Version history
@@ -40,7 +40,7 @@ auto v = doc.root();
returned view is valid under the same conditions as any other view of the document — see
[Object inspection](../basic_json_view/index.md) — in particular, it is invalidated by the next
[`read()`](read.md) or [`shrink_to_fit()`](shrink_to_fit.md) on this document. A view can dangle, like a
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view/lifetime.md).
`#!cpp std::string_view`; see [Ownership and lifetime](../../features/json_view.md#ownership-and-lifetime).
## Examples
@@ -91,7 +91,7 @@ big-endian reader could not interpret correctly.
- [`load`](load.md) - read an image written by `save()`
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
- [Images](../../features/json_view/images.md) - why and when to use images
- [Images](../../features/json_view.md#images) - why and when to use images
## Version history
@@ -195,7 +195,7 @@ one-node scalar.
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
spelling with `#!cpp number_format::source`
- [Edits](index.md#edits) - what an edit guarantees, for every overload
- [Editing a document](../../features/json_view/editing.md) - why editable documents keep the source
- [Editing a document](../../features/json_view.md#editing-a-document) - why editable documents keep the source
text's order and number spelling
## Version history
@@ -56,7 +56,7 @@ possible would be an internal optimization only, not a guarantee to rely on.
- [`node_count`](node_count.md) - the number of index entries
- [`memory_usage`](memory_usage.md) - the number of bytes held by the document
- [`root`](root.md) - the view of the root value
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - what releasing memory invalidates
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - what releasing memory invalidates
## Version history
@@ -24,9 +24,6 @@ Constant.
For a borrowed document, `source()` points directly into the caller's buffer, so it is only valid while that buffer
is; see [`owns_source`](owns_source.md).
The text of a document that owns it must not be passed to [`read()`](read.md) of the same document: `read()` releases
an owned text before it parses its input. Copy it first. (A borrowed text belongs to the caller and is not affected.)
## Examples
??? example
@@ -45,7 +42,7 @@ an owned text before it parses its input. Copy it first. (A borrowed text belong
- [`owns_source`](owns_source.md) - return whether the document holds its own copy of the text
- [`source_offset`](../basic_json_view/source_offset.md) - byte offset of a value in the source text
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - who owns the source text and for how long
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - who owns the source text and for how long
## Version history
+1 -1
View File
@@ -92,7 +92,7 @@ Pointer path of the value within the document, and with
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
the last value.
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
@@ -58,7 +58,7 @@ placeholder for "no value yet" and later be assigned a real view.
- [`is_discarded`](is_discarded.md) - return whether the view is invalid
- [`root`](../basic_json_document/root.md) - the view of a document's root value
- [`view_mode`](../view_mode.md) - whether a document and its views can be edited
- [Zero-copy JSON views](../../features/json_view/lifetime.md) - how long a view stays valid
- [Zero-copy JSON views](../../features/json_view.md#ownership-and-lifetime) - how long a view stays valid
## Version history
@@ -47,7 +47,7 @@ No-throw guarantee: this function never throws exceptions.
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
the last value.
Overload 1 always returns `#!cpp false` when the value is not an object — including a [discarded](is_discarded.md)
+4 -7
View File
@@ -48,11 +48,8 @@ Strong exception safety: if an exception is thrown, there are no changes to the
May throw `#!cpp std::bad_alloc` if allocating the output string fails. Unlike
[`BasicJsonType::dump`](../basic_json/dump.md), there is no `error_handler` parameter and no
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316): the parser validated the text the view
holds as UTF-8, so there is nothing to replace or ignore. Only a document loaded with
[`image_check::bounds` or `image_check::none`](../basic_json_document/load.md#image_check) can still throw it: these do
not check the strings, so `dump()` writes such a string as it is, which can be invalid UTF-8 or invalid JSON, and
`dump()` with `ensure_ascii` set to `#!cpp true` throws `type_error.316` for invalid UTF-8.
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316): the view only ever holds text the parser
already validated as UTF-8, so there is nothing to replace or ignore.
## Complexity
@@ -73,7 +70,7 @@ except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
for `#!cpp -0` — `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
digits than any number type holds (such a literal is itself classified as a float, see
[What is different](../../features/json_view/differences.md)) — something `BasicJsonType` cannot do, since
[What is different](../../features/json_view.md#what-is-different)) — something `BasicJsonType` cannot do, since
parsing already reduces every number to its parsed value.
## Examples
@@ -101,7 +98,7 @@ parsing already reduces every number to its parsed value.
- [`operator<<`](operator_ltlt.md) - serialize this value to a stream
- [`materialize`](materialize.md) - build a `BasicJsonType` value, e.g. to use `BasicJsonType::dump`'s `error_handler`
- [`BasicJsonType::dump`](../basic_json/dump.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
## Version history
+1 -1
View File
@@ -37,7 +37,7 @@ constant time on average.
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
the last value.
Unlike [`BasicJsonType::find`](../basic_json/find.md), which always returns `#!cpp end()` for a non-object type, this
+3 -3
View File
@@ -39,14 +39,14 @@ as [`BasicJsonType::parse()`](../basic_json/parse.md) does), as well as the type
accept views, e.g. with [`is_json_view`](../is_json_view.md). A `from_json()` that also accepts types that are no JSON
values at all, such as the unconstrained `#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType&,
T&)` of [arbitrary type conversions](../../features/arbitrary_types.md), is not called with a view: it may use
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view/generic_code.md).
`BasicJsonType` functions a view does not have. See [Writing generic code](../../features/json_view.md#writing-generic-code).
!!! warning "Duplicate keys"
Such a `from_json()` looks members up in the view, where a repeated key finds the **first** member, while
`#!cpp materialize().get<T>()` sees the **last** one, as [`BasicJsonType::parse()`](../basic_json/parse.md)
keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get<point>()` reads `#!cpp x == 1`. See
[Duplicate keys](../../features/json_view/differences.md#duplicate-keys).
[Duplicate keys](../../features/json_view.md#duplicate-keys).
Every other `T` — user types whose `from_json()` is written for `BasicJsonType` only, types without a default
constructor, `#!cpp std::tuple`s holding references, ... — is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
@@ -152,7 +152,7 @@ instead: its path is relative to the root of the view's subtree, because `materi
- [`number_token`](number_token.md) - a number's token text, without a copy
- [`materialize`](materialize.md) - build the `BasicJsonType` value of this subtree
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
## Version history
@@ -46,10 +46,6 @@ The returned [`string_view_t`](index.md#member-types) is valid exactly as long a
source text, which for a borrowed document must remain valid **and unmodified** for as long as the document exists,
since floating-point values and unescaped strings are read from the source lazily.
A string with escapes, a string of a document that owns its text, and an edited string must not be passed to
[`read()`](../basic_json_document/read.md) of the document it came from: `read()` releases them before it parses its
input. Copy the string first. (A string read unchanged from a borrowed text is not affected.)
## Examples
??? example
@@ -74,7 +70,7 @@ input. Copy the string first. (A string read unchanged from a borrowed text is n
- [`get`](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
- [`number_token`](number_token.md) - a number's token text, without a copy
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - borrowing strings instead of copying them
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - borrowing strings instead of copying them
## Version history
@@ -75,7 +75,7 @@ them with those of a default-constructed `T`.
- [`get`](get.md) - convert the value to a given type
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - which conversions avoid building a basic_json value
## Version history
@@ -147,7 +147,7 @@ still refers to it — including ones taken before the change — reads the new
## See also
- [Zero-copy JSON views](../../features/json_view/index.md) - guide to documents and views
- [Zero-copy JSON views](../../features/json_view.md) - guide to documents and views
- [`basic_json_document`](../basic_json_document/index.md) - the document that owns the parsed data
- [`json_view`](../json_view.md) - and [`json_editable_view`](../json_editable_view.md) - the default specializations
- [`materialize`](materialize.md) - build a `basic_json` value from a view
@@ -54,7 +54,7 @@ A `basic_json_view` is not convertible to `#!cpp bool`: such a conversion would
- [`(constructor)`](basic_json_view.md) - the default constructor creates a discarded view
- [`basic_json_document::is_discarded`](../basic_json_document/is_discarded.md) - return whether the last parse failed
- [`BasicJsonType::is_discarded`](../basic_json/is_discarded.md) - the corresponding function of `basic_json`
- [Zero-copy JSON views](../../features/json_view/differences.md) - how views differ from basic_json
- [Zero-copy JSON views](../../features/json_view.md#what-is-different) - how views differ from basic_json
## Version history
@@ -78,7 +78,7 @@ never caches the result.
- [`root`](../basic_json_document/root.md) - the view of a document's root value
- [`BasicJsonType::parse`](../basic_json/parse.md) - build a `BasicJsonType` value directly from a JSON text
- [`JSON_DIAGNOSTIC_POSITIONS`](../macros/json_diagnostic_positions.md) - source positions on parsed values
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - when to build a basic_json value from a view
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - when to build a basic_json value from a view
## Version history
@@ -45,7 +45,7 @@ source
- [`number_token`](number_token.md) - get a single number's token text without dumping the whole value
- [`BasicJsonType::error_handler_t`](../basic_json/error_handler_t.md) - the analogous enumeration for
`BasicJsonType::dump`'s decoding-error behavior
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing numbers as they appeared in the source
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing numbers as they appeared in the source
## Version history
@@ -38,14 +38,9 @@ by the grammar; trailing zeros in the fraction; the case and sign of the exponen
useful precisely where that form must survive — a price or an identifier that must be reproduced exactly, or a
number too large for any of `BasicJsonType`'s number types to hold without loss.
The returned [`string_view_t`](index.md#member-types) points into the document's
[`source()`](../basic_json_document/source.md) text or, for a number written by an
[edit](../basic_json_document/index.md#edits), into storage owned by the document (the token of an edited `NaN` is
`nan`). It is valid exactly as long as that text or storage is, see the [validity rules](index.md) of
`basic_json_view`. Numbers are never decoded into the document's separate string buffer.
For the same reason, the token must not be passed to [`read()`](../basic_json_document/read.md) of the document it came
from if that document owns its text or the number was written by an edit: `read()` releases both before it parses.
The returned [`string_view_t`](index.md#member-types) always points into the document's
[`source()`](../basic_json_document/source.md) text — numbers are never decoded into the document's separate string
buffer — and is valid exactly as long as that text is, see the [validity rules](index.md) of `basic_json_view`.
## Examples
@@ -73,7 +68,7 @@ from if that document owns its text or the number was written by an edit: `read(
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
number types `#!cpp get<T>()` and `materialize()` convert into
- [Zero-copy JSON views](../../features/json_view/reading.md#getting-values-out-without-copying) - reading a number exactly as written
- [Zero-copy JSON views](../../features/json_view.md#getting-values-out-without-copying) - reading a number exactly as written
## Version history
@@ -120,7 +120,7 @@ document.
[`materialize()`](materialize.md) or iterate the members with [`items()`](items.md) and keep the last match.
The key hash index of a larger object (128 members or more) leads to the first member of a key as well.
[`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
duplicates, in document order. See [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) and
duplicates, in document order. See [Duplicate keys](../../features/json_view.md#duplicate-keys) and
[`size()`](size.md#notes).
!!! info "JSON pointer resolution"
@@ -19,7 +19,7 @@ Neither overload builds a `BasicJsonType` value for a view to do the comparison
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
resolved exactly as `parse()` resolves them — the last value, at the position of the first occurrence of the key.
This is not what [`operator[]`](operator[].md) and the other lookups return for a duplicate key: they find the *first*
member (see [Duplicate keys](../../features/json_view/differences.md#duplicate-keys)).
member (see [Duplicate keys](../../features/json_view.md#duplicate-keys)).
## Parameters
@@ -57,9 +57,7 @@ compared. Two arrays of different [`size()`](size.md) are rejected without visit
Only a single number, boolean, or `#!cpp null` value is ever materialized into a `BasicJsonType`, to reuse its
`operator==` — for numbers, so that values written differently in the source text but equal in value (e.g. an
integer and a floating-point literal) still compare equal, following the same rules `BasicJsonType` does for special
values such as `#!cpp NaN`: a `#!cpp NaN` that an [edit](../basic_json_document/set.md) wrote is not equal to itself, so
a view of it is not equal to itself either, nor is a view of an array or object that contains one, as for
`BasicJsonType`. Constructing one of these scalars never allocates. Strings are compared directly, without
values such as `#!cpp NaN`. Constructing one of these scalars never allocates. Strings are compared directly, without
allocating, either from the source text on both sides or, for overload 2, against `BasicJsonType`'s own string.
Arrays and objects are never materialized at all; only their elements or members are visited, one pair at a time.
@@ -68,7 +68,7 @@ Linear, as [`dump`](dump.md#complexity).
- [`dump`](dump.md) - serialize to a JSON-formatted string
- [`operator<<(std::ostream&)`](../operator_ltlt.md) - the corresponding operator for `basic_json`
- [`JSON_NO_IO`](../macros/json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [Zero-copy JSON views](../../features/json_view/reading.md#writing-a-view-back) - writing a view back without building a tree
- [Zero-copy JSON views](../../features/json_view.md#writing-a-view-back) - writing a view back without building a tree
## Version history
@@ -49,7 +49,7 @@ string, whose token length is not the same as its decoded [`size()`](size.md).
- [`source`](../basic_json_document/source.md) - the parsed text
- [`is_string`](is_string.md) - return whether the value is a string
- [Zero-copy JSON views](../../features/json_view/images.md) - images and the source text
- [Zero-copy JSON views](../../features/json_view.md#images) - images and the source text
## Version history
@@ -84,7 +84,7 @@ Pointer path of the value within the document, and with
If the source text repeats a key, this resolves to the *first* member with it, not to the last one that
[`materialize()`](materialize.md) and `parse()` keep. See the [Notes on duplicate keys](operator[].md#notes) of
`operator[]` and [Duplicate keys](../../features/json_view/differences.md#duplicate-keys) for the reasons and for how to get
`operator[]` and [Duplicate keys](../../features/json_view.md#duplicate-keys) for the reasons and for how to get
the last value.
!!! info "Differences to `at` and `operator[]`"
+1 -1
View File
@@ -49,7 +49,7 @@ view class.
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
- [get](basic_json_view/get.md) - which conversions read the view directly
- [view_mode](view_mode.md) - read-only or editable
- [Writing generic code](../features/json_view/generic_code.md) - functions and `from_json()` for views
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions and `from_json()` for views
## Version history
+1 -1
View File
@@ -29,7 +29,7 @@ specialization.
- [`json_view`](json_view.md) - a view of a value of a `json_document`
- [`ordered_json_document`](ordered_json_document.md) - the corresponding document for `ordered_json`
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
## Version history
@@ -10,7 +10,7 @@ This type is an **editable** [`basic_json_document`](basic_json_document/index.m
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
parsing, without ever rewriting the source text — see [Edits](basic_json_document/index.md#edits) and
[Editing a document](../features/json_view/editing.md).
[Editing a document](../features/json_view.md#editing-a-document).
## Examples
+1 -1
View File
@@ -35,7 +35,7 @@ still referring to it seeing the change, see [Edits](basic_json_document/index.m
- [`json_view`](json_view.md) - the corresponding read-only view
- [`ordered_json_editable_view`](ordered_json_editable_view.md) - the corresponding view for
`ordered_json_editable_document`
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
## Version history
+2 -1
View File
@@ -7,7 +7,8 @@ struct json_sax;
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
different situations while the input is parsed. The boolean return value informs the parser whether to continue
processing the input.
processing the input; for [`parse_error`](parse_error.md), it decides whether to
[recover from the error](../../features/parsing/error_recovery.md).
For instance, parsing the JSON text `{"a": [1, true]}` triggers the following callbacks, in order:
+26 -2
View File
@@ -21,11 +21,18 @@ A parse error occurred.
## Return value
Whether parsing should proceed (**must return `#!cpp false`**).
Whether to recover from the error:
- `#!cpp false` stops parsing.
- `#!cpp true` recovers from the error: the error is repaired and parsing continues. If that is not possible, which
happens in the binary formats when the end of the item with the error is unknown, the value read so far is completed
and parsing stops. See [error recovery](../../features/parsing/error_recovery.md) for how errors are repaired.
Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
## Examples
??? example
??? example "Example: (1) the SAX interface"
The example below shows how the SAX interface is used.
@@ -39,13 +46,30 @@ Whether parsing should proceed (**must return `#!cpp false`**).
--8<-- "examples/sax_parse.output"
```
??? example "Example: (2) recovering from errors"
The example below shows how a SAX parser recovers from errors.
```cpp
--8<-- "examples/sax_parse__error_recovery.cpp"
```
Output:
```
--8<-- "examples/sax_parse__error_recovery.output"
```
## See also
- [`sax_parse`](../basic_json/sax_parse.md) - SAX parser
- [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without
exceptions
- [Error Recovery](../../features/parsing/error_recovery.md) - the article on recovering from parse errors
- [SAX Interface](../../features/parsing/sax_interface.md) - guide to event-based parsing
## Version history
- Added in version 3.2.0.
- Returning `#!cpp true` recovers from the error since version 3.13.0; before, parsing stopped, but the result of
[`sax_parse`](../basic_json/sax_parse.md) could be wrong.
+1 -1
View File
@@ -28,7 +28,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`j
- [`json_document`](json_document.md) - the document type this view refers into
- [`ordered_json_view`](ordered_json_view.md) - the corresponding view for `ordered_json_document`
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
## Version history
-2
View File
@@ -40,8 +40,6 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [**JSON_NO_SIMD**](json_no_simd.md) - use only portable code instead of vector instructions (SSE2, SSSE3, NEON)
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
- [**JSON_RECURSION_DEPTH_LIMIT**](json_recursion_depth_limit.md) - set how many nesting levels the operations on a value
recurse into on the call stack (default 128)
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
@@ -27,13 +27,9 @@ The default value is `0` (disabled — existing behavior is preserved).
json j{obj};
```
creates a single-element **array** `[{"key":"value"}]` instead of a copy of `obj`. For this constructor, the
behavior was compiler-dependent for older compilers (GCC wrapped, Clang did not), but starting from Clang 20, both
compilers behave the same way.
The calls `#!cpp arr.push_back({obj})` and `#!cpp arr += {obj}` are still compiler-dependent without the macro,
even with Clang 20 and later: GCC converts `{obj}` to a single-element array and appends that array, whereas Clang
appends a copy of `obj`. With the macro defined to `1`, both compilers append a copy of `obj`.
creates a single-element **array** `[{"key":"value"}]` instead of a copy of `obj`. This behavior is
compiler-dependent for older compilers (GCC wrapped, Clang did not), but starting from Clang 20, both compilers
behave the same way.
Enabling this macro opts into copy/move semantics for this case
(see [#5074](https://github.com/nlohmann/json/issues/5074)).
@@ -34,9 +34,6 @@ or quotes.
Note that enabling this macro increases the size of every JSON value by two `std::size_t` fields and adds slight runtime
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
these values to be reported in those error messages.
In addition, `parse()`, `accept()`, and `sax_parse()` no longer use the
[faster path for input in one block of memory](../../features/parsing/index.md#input-sources-and-performance), so
parsing a string or a buffer is slower than without the macro.
If [`JSON_DIAGNOSTICS`](json_diagnostics.md) is also set, the message contains the path and the byte range, such as
`(/1) (bytes 4-8)`. An exception about the root value has no path and only the range, such as `(bytes 0-4)`.
@@ -125,5 +122,3 @@ When the macro is not defined, the library will define it to its default value.
## Version history
- Added in version 3.12.0.
- Fixed in version 3.13.0: in version 3.12.0, [`start_pos()`](../basic_json/start_pos.md) of a string that contains
escape sequences was too large; it is now the position of the opening `"`.
@@ -48,8 +48,8 @@ The default value is `0`: streams are scanned in bulk.
!!! note "Performance"
Parsing from a `std::ifstream` or `std::istringstream` is typically 1.5 to 2 times as fast as before, and reading
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream then takes about
1.15 to 1.45 times as long as first reading the whole file into a `std::string` and parsing that.
many small values from one stream with `operator>>` 1.4 to 4 times as fast. Parsing from a stream is then within
about 10 to 20 % of first reading the whole file into a `std::string` and parsing that.
A stream buffer without a get area (one that only overrides `underflow()` and `uflow()` and hands out every
character from there) offers nothing to scan in bulk, so it is read one character at a time as before.
+1 -1
View File
@@ -80,7 +80,7 @@ By default, `#!cpp JSON_NO_SIMD` is not defined, and vector instructions are use
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance) - how input
sources affect parsing performance
- [Serialization](../../features/serialization.md) - pretty-printing, UTF-8 handling, and number output
- [Zero-copy JSON views](../../features/json_view/index.md) - the zero-copy view, whose parser the macro also affects
- [Zero-copy JSON views](../../features/json_view.md) - the zero-copy view, whose parser the macro also affects
## Version history
@@ -45,7 +45,6 @@ Copying and comparing fall back to working without the call stack there, as they
## See also
- [`JSON_RECURSION_DEPTH_LIMIT`](json_recursion_depth_limit.md) - set how many nesting levels the operations recurse into
- [FAQ: Thread safety](../../home/faq.md#thread-safety) - thread-safety guarantees of the library
- [Supported Macros](../../features/macros.md) - guide to all supported macros
@@ -1,307 +0,0 @@
# JSON_RECURSION_DEPTH_LIMIT
```cpp
#define JSON_RECURSION_DEPTH_LIMIT /* value */
```
This macro sets how many nesting levels the operations that walk a value recurse into on the call stack. Beyond that,
they continue with an explicit stack on the heap. The value is an integer constant from `0` to `254`; the default is
`128`.
The operations are copying a value, comparing two values, serializing with [`dump`](../basic_json/dump.md) or
[`operator<<`](../operator_ltlt.md), hashing with `std::hash`, [`merge_patch`](../basic_json/merge_patch.md),
[`update`](../basic_json/update.md) with `merge_objects`, [`diff`](../basic_json/diff.md),
[`merge_diff`](../basic_json/merge_diff.md), converting a value to another specialization of `basic_json`, destroying a
value, and the binary writers [`to_cbor`](../basic_json/to_cbor.md),
[`to_msgpack`](../basic_json/to_msgpack.md), [`to_ubjson`](../basic_json/to_ubjson.md),
[`to_bjdata`](../basic_json/to_bjdata.md), and [`to_bon8`](../basic_json/to_bon8.md). They share this one limit; none
has
a limit of its own. Recursing is the fastest way to walk a value, but a value nested deeply enough would exhaust the
call
stack, so the library recurses only as far as the limit and finishes the levels below with an explicit stack. This is
what makes all of them safe for untrusted input
(see [Nesting depth](../../features/parsing/untrusted_input.md#nesting-depth)). Parsing, the binary readers,
[`flatten`](../basic_json/flatten.md), [`unflatten`](../basic_json/unflatten.md), and
[`to_bson`](../basic_json/to_bson.md) always use an explicit stack and do not depend on the macro. The lockstep
comparison of two [`json_view`](../basic_json_view/index.md) documents has a fixed bound of its own, 48 levels,
and does not depend on it either.
The call stack that these operations need grows with the limit: roughly *a few kilobytes plus a cost per level times the
limit*, where the cost per level is up to about 2.3 KiB in an unoptimized and up to about 0.5 KiB in an optimized build
(see [Stack needed](#stack-needed)). The default of `128` levels is tuned for threads with ordinary stacks of a few
hundred kilobytes or more. Set a lower limit if your threads have small stacks (RTOS tasks, fibers, threads of the musl
C
library, which has a default stack of 128 KiB), or a higher limit to trade stack for speed on deeply nested values.
The macro changes only how much stack is used and how fast the operations are. It never changes a result: every
operation returns the same value for every setting of the limit, for values nested less deeply than the limit as well as
for values nested more deeply.
## Default definition
The default value is `128`.
```cpp
#define JSON_RECURSION_DEPTH_LIMIT 128
```
When the macro is not defined, the library defines it to its default value.
## Notes
!!! note "Allowed values"
The value must be an integer constant expression from `0` to `254`; any other value (a negative number, a number
above `254`, a floating-point number such as `254.0`, or `true` or `false`) is rejected at compile time with an
error message that names the macro. A character of any character type (`'a'`, `L'a'`, `u8'a'`, ...) is rejected,
too, although it is an integer.
- `0` is allowed, and means that every operation uses its explicit stack from the first level on and no operation
recurses over the nesting of a value at all. That needs a constant amount of call stack (at most 16 KiB in the
measurements below, for a value nested 20,000 levels deep) and is the setting for tiny stacks, at the price of
speed (see [Speed](#speed)).
- `254` is the largest allowed value. Copying a value and comparing two values (with `<=>`, or with the operators on
objects and arrays of a custom type) cannot pass the depth down as an argument, because the signatures of the
copy constructor and the operators are fixed. They count the levels they have descended into in a variable of type
`#!cpp std::uint8_t` that exists once per thread (see [`JSON_NO_THREAD_LOCAL`](json_no_thread_local.md)). Only
the levels that are descended into are counted, so the count never exceeds the limit, not even when user code (a
copy constructor of a custom base class, say) copies a value while a copy is going on. A byte holds `255` at
most, and the limit stays below that, so that the count cannot wrap around. (More levels would hardly be
useful: `254` levels need up to 576 KiB of stack in an unoptimized build already, twice as much as the
default.)
### Stack needed
The tables show the smallest stack that an operation needs, in KiB, at several limits, on a value that is nested
more deeply than the limit (limit + 10 levels). Each cell shows the result for Clang (Apple clang 21) and for
GCC 16, separated by a slash; both measured on macOS with arm64, compiling in C++20 mode. The operations run in a
thread whose stack has the size given, and are not given any stack that they do not need: the value is built, and
the result destroyed, outside of that thread. Stacks can only be set in steps of 16 KiB (the page size), so `16`
means "16 KiB or less". A row stands for all operations listed; the table shows the largest value among them.
Unoptimized (`-O0`):
| Operation | limit 16 | limit 32 | limit 64 | limit 128 | limit 254 |
|---|---:|---:|---:|---:|---:|
| `dump()` and `dump(indent)`, `operator<<` | 48 / 32 | 80 / 64 | 144 / 128 | 288 / 256 | 576 / 512 |
| copy construction and assignment | 32 / 16 | 48 / 32 | 112 / 80 | 224 / 144 | 432 / 304 |
| `==` | 16 / 16 | 16 / 16 | 16 / 16 | 48 / 32 | 96 / 48 |
| `<=>` (C++20) | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 64 | 144 / 128 |
| `std::hash` | 16 / 16 | 16 / 16 | 32 / 32 | 80 / 64 | 160 / 128 |
| `merge_patch`, `update(j, true)`, `patch` | 32 / 16 | 48 / 32 | 112 / 80 | 224 / 144 | 432 / 304 |
| `diff` | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 48 | 144 / 96 |
| `merge_diff` | 16 / 16 | 48 / 32 | 80 / 48 | 176 / 112 | 352 / 224 |
| `to_cbor`, `to_msgpack` | 16 / 16 | 32 / 16 | 64 / 32 | 112 / 80 | 224 / 160 |
| `to_ubjson`, `to_bjdata` | 16 / 16 | 16 / 16 | 48 / 32 | 96 / 64 | 176 / 112 |
| `to_bon8` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 16 | 64 / 48 |
| conversion to another `basic_json` type | 32 / 16 | 64 / 32 | 112 / 80 | 224 / 144 | 448 / 304 |
| destruction | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 |
Optimized (`-O2`):
| Operation | limit 16 | limit 32 | limit 64 | limit 128 | limit 254 |
|---|---:|---:|---:|---:|---:|
| `dump()` and `dump(indent)`, `operator<<` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 |
| copy construction and assignment | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 48 |
| `==` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 |
| `<=>` (C++20) | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 48 / 16 |
| `std::hash` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 48 | 80 / 80 |
| `merge_patch`, `update(j, true)`, `patch` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 48 |
| `diff` | 16 / 16 | 16 / 16 | 16 / 32 | 48 / 48 | 96 / 112 |
| `merge_diff` | 16 / 16 | 16 / 16 | 16 / 16 | 48 / 32 | 96 / 64 |
| `to_cbor`, `to_msgpack` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 |
| `to_ubjson`, `to_bjdata` | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 32 | 64 / 64 |
| `to_bon8` | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 32 | 32 / 64 |
| conversion to another `basic_json` type | 16 / 16 | 16 / 16 | 16 / 16 | 32 / 48 | 64 / 96 |
| destruction | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 | 16 / 16 |
With a limit of `0`, every operation in the tables ran in a 16 KiB stack at `-O0` (Clang and GCC), on a value nested
20,000 levels deep. The operations that do not depend on the macro (`flatten`, `unflatten`, `to_bson`) needed
16 KiB or less at every limit.
The rule of thumb is
> stack needed ≈ a few KiB + cost per level × limit
with a cost per level of up to 2.3 KiB (`dump` at `-O0` with Clang; 2.1 KiB with GCC), 1.7 KiB for copying and merging
(1.3 KiB with GCC), and 0.1 to 0.5 KiB for most other operations at `-O0`; and 0.5 KiB or less at `-O2`. The numbers
come from the difference between the limits `128` and `254` in the tables, so they are rough: other compilers,
other targets (the frames of x86-64 code are about as large), other standard libraries, `JSON_DIAGNOSTICS`, and
sanitizers change them. Measure with your own tool chain (see the example below) rather than relying on the tables.
The stack that your own code and the operating system need comes on top of these numbers.
Which limit to choose, for the stack that you can spare for the library (the largest limit of those measured
for which the slowest operation needs at most that much):
| Stack for the library | optimized (`-O2`) | unoptimized (`-O0`) |
|---|---:|---:|
| 32 KiB | 64 | 0 |
| 64 KiB | 128 | 16 |
| 128 KiB (the default stack of threads on musl) | 254 | 32 |
| 256 KiB | 254 | 64 |
| 512 KiB | 254 | 128 |
| 1 MiB or more | 254 | 254 |
### Speed
Operations are faster the more of a value they handle with plain recursion. The levels below the limit are handled with
an explicit stack, which has to be kept on the heap and costs more per level. The macro therefore trades speed against
stack.
The table shows the time of copying a value (including destroying the copy), of `==` on two equal values, of `dump`, and
of `std::hash` for several limits, relative to the default of `128` (`1.00` means the same time; more is slower). It was
measured with Clang 21 at `-O2` in C++17 mode on macOS with arm64, as the best of seven runs of each setting (two
rounds, best of both). The documents are ordinary JSON files that are nested 7 (`canada.json`), 8
(`citm_catalog.json`), 10 (`twitter.json`), and 2 levels deep (`jeopardy.json`); the other values are one array or object
with one member per level, nested 1,000 and 100,000 levels deep. The absolute time at the limit `128` is given in the
third column. Differences of up to about 0.1 are noise: a different limit changes the layout of the compiled code, and
timings of the same binary vary by about as much. The slow columns (limits 0 and 1) vary more: repeating `std::hash` on
`canada.json` gave 1.27 to 1.30 for limit 0 and 1.4 to 1.9 for limit 1, so read the cells of those two columns as
accurate to about 0.3.
| Value | Operation | limit 128 (absolute) | limit 0 | limit 1 | limit 16 | limit 254 |
|---|---|---:|---:|---:|---:|---:|
| `canada.json` (2.1 MB) | copy | 3.5 ms | 1.80 | 1.80 | 0.99 | 1.00 |
| `canada.json` (2.1 MB) | `==` | 350.5 µs | 2.36 | 2.38 | 1.00 | 0.99 |
| `canada.json` (2.1 MB) | `dump` | 2.4 ms | 1.11 | 1.11 | 1.00 | 1.00 |
| `canada.json` (2.1 MB) | `std::hash` | 920.2 µs | 1.33 | 1.59 | 1.00 | 1.00 |
| `citm_catalog.json` (1.7 MB) | copy | 1.8 ms | 1.38 | 1.36 | 1.00 | 0.99 |
| `citm_catalog.json` (1.7 MB) | `==` | 275.5 µs | 1.31 | 1.31 | 0.99 | 1.00 |
| `citm_catalog.json` (1.7 MB) | `dump` | 314.6 µs | 1.24 | 1.23 | 1.00 | 1.00 |
| `citm_catalog.json` (1.7 MB) | `std::hash` | 309.1 µs | 1.77 | 1.42 | 1.06 | 1.05 |
| `twitter.json` (0.6 MB) | copy | 865.7 µs | 1.45 | 1.47 | 1.01 | 1.00 |
| `twitter.json` (0.6 MB) | `==` | 135.8 µs | 1.26 | 1.23 | 1.00 | 1.00 |
| `twitter.json` (0.6 MB) | `dump` | 224.6 µs | 1.12 | 1.12 | 1.01 | 1.00 |
| `twitter.json` (0.6 MB) | `std::hash` | 132.9 µs | 1.28 | 1.27 | 1.03 | 1.04 |
| `jeopardy.json` (53 MB) | copy | 111.1 ms | 1.22 | 1.25 | 1.00 | 1.00 |
| `jeopardy.json` (53 MB) | `==` | 23.5 ms | 1.29 | 1.53 | 1.07 | 1.00 |
| `jeopardy.json` (53 MB) | `dump` | 33.2 ms | 1.14 | 1.34 | 1.03 | 1.01 |
| `jeopardy.json` (53 MB) | `std::hash` | 19.9 ms | 1.17 | 1.52 | 1.05 | 1.00 |
| arrays, 1,000 levels | copy | 69.9 µs | 0.95 | 0.98 | 0.92 | 0.99 |
| arrays, 1,000 levels | `==` | 10.4 µs | 1.02 | 1.01 | 0.96 | 1.00 |
| arrays, 1,000 levels | `dump` | 9.6 µs | 0.97 | 0.96 | 0.97 | 1.06 |
| arrays, 1,000 levels | `std::hash` | 16.9 µs | 1.00 | 1.01 | 1.01 | 0.98 |
| objects, 1,000 levels | copy | 77.1 µs | 0.98 | 0.97 | 0.97 | 1.03 |
| objects, 1,000 levels | `==` | 12.0 µs | 0.96 | 0.95 | 0.95 | 1.07 |
| objects, 1,000 levels | `dump` | 12.6 µs | 0.97 | 0.95 | 0.98 | 1.01 |
| objects, 1,000 levels | `std::hash` | 17.3 µs | 0.99 | 1.01 | 0.98 | 1.00 |
| arrays, 100,000 levels | copy | 6.4 ms | 1.00 | 1.00 | 1.00 | 1.00 |
| arrays, 100,000 levels | `==` | 1.1 ms | 1.01 | 1.03 | 1.00 | 1.06 |
| arrays, 100,000 levels | `dump` | 917.9 µs | 1.01 | 1.01 | 1.02 | 1.01 |
| arrays, 100,000 levels | `std::hash` | 1.7 ms | 1.04 | 1.05 | 0.99 | 0.99 |
| objects, 100,000 levels | copy | 7.5 ms | 0.99 | 1.02 | 1.01 | 0.99 |
| objects, 100,000 levels | `==` | 1.3 ms | 1.02 | 1.09 | 1.08 | 0.96 |
| objects, 100,000 levels | `dump` | 1.2 ms | 1.06 | 0.99 | 1.01 | 0.99 |
| objects, 100,000 levels | `std::hash` | 1.8 ms | 1.00 | 1.00 | 1.00 | 1.00 |
The results are as follows.
- A limit of `16` or more does not change the speed of ordinary documents (differences are within noise), because they
are not nested more deeply than that anyway. A document nested more deeply than the limit is walked partly with the
explicit stack.
- A limit of `0` or `1` makes the operations on ordinary documents 1.1 to 2.4 times as slow, because even their first
levels are handled with the explicit stack. The slowdown is largest for `==` on a document of many small containers
(`canada.json`: 2.4 times) and for copying (up to 1.8 times).
- For values that are nested far more deeply than any limit (1,000 and 100,000 levels deep), the limit makes no
measurable difference: almost all of the levels are handled with the explicit stack for every setting.
Raising the limit gains nothing for them, and lowering it costs nothing.
!!! note "Other macros"
- With [`JSON_NO_THREAD_LOCAL`](json_no_thread_local.md) defined (or for Clang targeting MinGW, where the library
defines it by itself), copying and comparing never descend, so for them the limit does not matter. The other
operations still recurse as many levels as the limit says. The range of the limit is the same.
- The macro is not part of the [ABI tag](../../features/namespace.md), as `JSON_NO_THREAD_LOCAL` is not, and it is
not undefined at the end of `json.hpp`.
!!! warning "Use the same value in every translation unit"
All translation units of a program that include the library must define the same value. Otherwise, the program
violates the One Definition Rule: the function `nlohmann::detail::recursion_depth_limit()` and all the functions
that use it are defined differently in different translation units. Which limit a call uses then depends on which
functions the compiler inlined (an inlined copy uses the value of its own translation unit) and which out-of-line
copy of each remaining function the linker kept, which is not specified. A function compiled with one value can
call a function compiled with another, so a single operation can mix both limits.
Mixing values cannot corrupt the byte per thread that counts the levels of copying and comparing: each limit
is at most `254`, and all of them stop counting at their own limit, so the count never exceeds `254`, whichever
translation unit's definition runs. The counter exists once per thread and `basic_json` specialization, and is
shared by all translation units. Nor can mixing values change any result, as no result depends on the limit. What
changes is how much stack is used and how fast the operations are. Nevertheless, avoid it: the program has undefined
behavior as far as the C++ standard is concerned.
## Examples
??? example "Example: lower the limit"
The example nests an array 5,000 levels deep. With the limit set to 16, the library recurses over the first 16
levels
and over the rest uses an explicit stack. The results are those of the default limit.
```cpp
--8<-- "examples/json_recursion_depth_limit.cpp"
```
Output:
```
--8<-- "examples/json_recursion_depth_limit.output"
```
??? example "Example: find out how much stack an operation needs"
The program runs `dump()` on a value nested `depth` levels deep in a thread with a stack of `kib` KiB. Compile it
with the options and the limit that you use, e.g. `-DJSON_RECURSION_DEPTH_LIMIT=64`, run it with `depth` set to the
limit plus 10 and decreasing values of `kib` (the stack size must be a multiple of the page size, often 4 or 16
KiB),
and note the smallest stack with which it still finishes. That is how the tables above were measured.
```cpp
#include <cstdlib>
#include <iostream>
#include <string>
#include <utility>
#include <pthread.h>
#include <nlohmann/json.hpp>
static nlohmann::json value;
static void* run(void*)
{
std::cout << "dump() ok, " << value.dump().size() << " bytes\n";
return nullptr;
}
int main(int argc, char** argv)
{
const int depth = std::atoi(argv[1]);
const std::size_t kib = static_cast<std::size_t>(std::atoi(argv[2]));
// built on the main thread, without recursion
value = 1;
for (int i = 0; i < depth; ++i)
{
nlohmann::json outer = nlohmann::json::array();
outer.push_back(std::move(value));
value = std::move(outer);
}
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, kib * 1024);
pthread_t thread;
pthread_create(&thread, &attr, run, nullptr);
pthread_join(thread, nullptr);
}
```
## See also
- [Nesting depth](../../features/parsing/untrusted_input.md#nesting-depth) - how the library handles deeply nested
values
- [`JSON_NO_THREAD_LOCAL`](json_no_thread_local.md) - switch off the use of `thread_local` storage
- [Supported Macros](../../features/macros.md) - guide to all supported macros
## Version history
- Added in version 3.13.0.
@@ -93,7 +93,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i
## See also
- [Binary formats](../../features/binary_formats/index.md) - overview of the supported binary formats
- [`from_cbor`](../basic_json/from_cbor.md) - not affected by the macro; pass `error_handler` to validate CBOR strings
- [`from_cbor`](../basic_json/from_cbor.md) - also affected when parsing CBOR strings
- [`to_cbor`](../basic_json/to_cbor.md) - create a CBOR serialization of a JSON value
- [`to_ubjson`](../basic_json/to_ubjson.md) - create a UBJSON serialization of a JSON value
- [`to_bjdata`](../basic_json/to_bjdata.md) - create a BJData serialization of a JSON value
@@ -32,7 +32,7 @@ of object keys, instead of sorting them like [`json_document`](json_document.md)
- [`ordered_json_view`](ordered_json_view.md) - a view of a value of an `ordered_json_document`
- [`json_document`](json_document.md) - the corresponding document for the default `json` specialization
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
## Version history
@@ -41,7 +41,7 @@ members [`set`](basic_json_document/set.md) added — instead of sorting them li
specialization
- [Object Order](../features/object_order.md) - preserving the insertion order of object keys
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
## Version history
@@ -34,7 +34,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
- [`ordered_json_editable_document`](ordered_json_editable_document.md) - the document type this view refers into
- [`ordered_json_view`](ordered_json_view.md) - the corresponding read-only view
- [`json_editable_view`](json_editable_view.md) - the corresponding view for `json_editable_document`
- [Zero-copy JSON views](../features/json_view/editing.md) - editing a document in place and writing it back
- [Zero-copy JSON views](../features/json_view.md#editing-a-document) - editing a document in place and writing it back
## Version history
+1 -1
View File
@@ -29,7 +29,7 @@ This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
- [`ordered_json_document`](ordered_json_document.md) - the document type this view refers into
- [`json_view`](json_view.md) - the corresponding view for `json_document`
- [Zero-copy JSON views](../features/json_view/index.md) - how to parse, inspect and edit a document without building a tree
- [Zero-copy JSON views](../features/json_view.md) - how to parse, inspect and edit a document without building a tree
## Version history
+1 -1
View File
@@ -82,7 +82,7 @@ nor a [`json_editable_view`](json_editable_view.md). Views of the two modes comp
- [basic_json_view](basic_json_view/index.md) - a handle to one value of a document
- [json_editable_document](json_editable_document.md) - an editable document of `json`
- [(constructor)](basic_json_view/basic_json_view.md) - the conversion from a read-only to an editable view
- [Writing generic code](../features/json_view/generic_code.md) - functions that accept views
- [Writing generic code](../features/json_view.md#writing-generic-code) - functions that accept views
## Version history
+2 -5
View File
@@ -53,15 +53,12 @@ flowchart LR
only as bytes are actually read, arrays reserve at most a fixed number of elements up front, and sizes that no
container can hold are rejected.
- **Memory is owned by values.** Each `basic_json` value owns its content, and there is no manual memory management in
user code. The destructor recurses only over a bounded number of nesting levels and then continues without the call
stack, so destroying a deeply nested value does not exhaust the stack.
user code. The destructor does not recurse, so destroying a deeply nested value does not exhaust the stack.
- **Bounded recursion.** The JSON parser and the binary readers keep their state in explicit stacks instead of
recursing per nesting level. Operations that walk a value, such as [`dump`](../api/basic_json/dump.md), copying,
comparison, hashing, [`merge_patch`](../api/basic_json/merge_patch.md), [`diff`](../api/basic_json/diff.md),
[`flatten`](../api/basic_json/flatten.md), and the binary writers, recurse only up to a fixed depth and continue with
an explicit stack below it. The depth is 128 levels by default and can be changed with
[`JSON_RECURSION_DEPTH_LIMIT`](../api/macros/json_recursion_depth_limit.md), for example to run on a small stack.
Applications that process untrusted input can limit its nesting depth with a
an explicit stack below it. Applications that process untrusted input can limit its nesting depth with a
[parser callback](../features/parsing/parser_callbacks.md).
- **Invariants are checked.** The class invariant (for instance, that the pointer for the stored type is never null) is
checked with runtime assertions throughout the test suite.
+1 -25
View File
@@ -759,14 +759,9 @@ body:has(.nj-hero) .md-content {
}
}
/* blog posts: author and date right below the title */
.md-typeset .nj-byline { margin: -.8rem 0 1.6rem; font-size: .75rem; color: var(--md-default-fg-color--light); }
.md-typeset .nj-byline a { color: inherit; }
.md-typeset .nj-byline a:hover { color: var(--md-accent-fg-color); }
/* figure captions: "Figure N." in bold, close to the figure, set in from the text column; figures and tables are
numbered per page, each with its own counter */
.md-content__inner { counter-reset: nj-figure nj-table nj-listing; }
.md-content__inner { counter-reset: nj-figure nj-table; }
.md-typeset :is(.nj-caption, .nj-charts-caption) {
max-width: none;
margin: .3rem 2rem 1.5rem;
@@ -787,25 +782,6 @@ body:has(.nj-hero) .md-content {
.md-typeset :is(.nj-caption, .nj-charts-caption) { margin-inline: 1rem; }
}
/* code captions: "Listing N." in bold, directly below the code block, in the figure caption style */
.md-typeset .nj-code-caption {
max-width: none;
margin: -.4rem 2rem 1.5rem;
font-size: .75rem;
line-height: 1.5;
text-align: left;
color: var(--md-default-fg-color--light);
}
.md-typeset .nj-code-caption::before {
counter-increment: nj-listing;
content: "Listing " counter(nj-listing) ". ";
font-weight: 700;
color: var(--md-default-fg-color);
}
@media screen and (max-width: 44.9375em) {
.md-typeset .nj-code-caption { margin-inline: 1rem; }
}
/* table captions: "Table N." in bold, directly below the table, in the figure caption style */
.md-typeset .nj-table-caption {
max-width: none;
-44
View File
@@ -27,10 +27,6 @@
.md-typeset svg.nj-charts--compile, .md-typeset svg.nj-charts--scaling { max-width: 26rem; }
.md-typeset svg.nj-charts--sizes { max-width: 26rem; }
/* home/blog/how_3_13_0_got_faster.md (scripts/figures/perf_blog.py) */
.md-typeset svg.nj-charts--blog { max-width: 34rem; }
.md-typeset svg.nj-charts--swar { max-width: 34rem; }
.md-typeset svg.nj-charts--timeline { max-width: 22rem; }
.nj-charts text { fill: currentColor; font-size: 13px; }
.nj-charts .nj-charts-leg { font-size: 13.5px; }
@@ -47,34 +43,10 @@
paint-order: stroke; stroke: var(--md-default-bg-color); stroke-width: 4px; stroke-linejoin: round; }
.nj-charts .nj-charts-factor { fill: var(--nj-charts-ink2); font-size: 12px; font-weight: 600; text-anchor: middle; }
.nj-charts .nj-charts-note { fill: var(--nj-charts-ink2); font-size: 12px; }
/* heatmap cells (perf_blog_usecases_*.svg): green for the fastest, gray around twice the time, red for the slowest;
adjacent steps checked for CVD separation on the light and the slate surface; the numbers are in every cell */
.md-typeset svg.nj-charts--heat { max-width: 49rem; }
.nj-charts--heat { --nj-heat-0: #3fa557; --nj-heat-1: #8bcc95; --nj-heat-2: #ebeae6; --nj-heat-3: #f2ae9b;
--nj-heat-4: #dc5a43; --nj-heat-ink: #1b1b1b; --nj-heat-ink-strong: #1b1b1b; }
[data-md-color-scheme="slate"] .nj-charts--heat { --nj-heat-0: #3a9d50; --nj-heat-1: #2c6b3a; --nj-heat-2: #33353a;
--nj-heat-3: #8a3f2e; --nj-heat-4: #d95b3a; --nj-heat-ink: #e8e8e6; --nj-heat-ink-strong: #141414; }
.nj-charts .nj-heat-0 { fill: var(--nj-heat-0); }
.nj-charts .nj-heat-1 { fill: var(--nj-heat-1); }
.nj-charts .nj-heat-2 { fill: var(--nj-heat-2); }
.nj-charts .nj-heat-3 { fill: var(--nj-heat-3); }
.nj-charts .nj-heat-4 { fill: var(--nj-heat-4); }
.nj-charts .nj-heat-na { fill: none; stroke: var(--nj-charts-grid); stroke-width: 1; }
.nj-charts .nj-charts-cell { fill: var(--nj-heat-ink); font-size: 12px; font-variant-numeric: tabular-nums; }
.nj-charts :is(.nj-heat-0, .nj-heat-4) ~ .nj-charts-cell { fill: var(--nj-heat-ink-strong); }
.nj-charts .nj-charts-cell--small { font-size: 10.5px; }
.nj-charts .nj-heat-cell { cursor: default; outline: none; }
.nj-charts .nj-heat-cell:is(:hover, :focus-visible, .nj-heat-on) rect { stroke: var(--md-default-fg-color); stroke-width: 1.5; }
.nj-heat-tip { position: absolute; z-index: 5; max-width: 24rem; padding: .5rem .7rem; border-radius: 6px;
background: var(--md-default-bg-color); color: var(--md-default-fg-color);
box-shadow: 0 2px 10px rgba(0, 0, 0, .25); font-size: .68rem; line-height: 1.45; pointer-events: none; }
.nj-heat-tip strong { display: block; margin-bottom: .2rem; }
.nj-heat-tip[hidden] { display: none; }
.nj-charts .nj-charts-grid { stroke: var(--nj-charts-grid); stroke-width: 1; }
.nj-charts .nj-charts-zero, .nj-charts .nj-charts-axis { stroke: var(--nj-charts-base); stroke-width: 1.5; }
.nj-charts .nj-charts-ref { stroke: currentColor; stroke-width: 1.5; }
.nj-charts .nj-charts-head { fill: var(--nj-charts-base); }
.nj-charts .nj-charts-c1 { fill: var(--nj-charts-c1); }
.nj-charts .nj-charts-c2 { fill: var(--nj-charts-c2); }
@@ -168,19 +140,3 @@
/* direct line labels replace the legend */
.nj-charts .nj-charts-name-json { fill: var(--nj-charts-json-c); font-weight: 600; }
.nj-charts .nj-charts-name-ojson { fill: var(--nj-charts-ojson-c); font-weight: 600; }
/* theme badges of home/blog/how_3_13_0_got_faster.md: the number of one of the eight themes, as a digit in a blue
circle, in headings, text, tables and the timeline figures (nj-theme-dot); "U" and other marks are gray */
.md-typeset .nj-theme { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
min-width: 1.45em; height: 1.45em; padding: 0 .3em; border-radius: .725em; vertical-align: .08em;
background: var(--nj-blue); color: var(--md-default-bg-color); font-size: .78em; font-weight: 700; line-height: 1;
font-variant-numeric: tabular-nums; text-decoration: none; }
.md-typeset a.nj-theme:is(:hover, :focus-visible) { background: var(--nj-blue-deep); color: var(--md-default-bg-color); }
.md-typeset .nj-theme--other { background: var(--md-default-fg-color--light); }
.md-typeset h2 .nj-theme { margin-right: .35em; vertical-align: .12em; }
.md-typeset .nj-theme-list > ul { list-style: none; margin-left: 0; }
.md-typeset .nj-theme-list > ul > li { margin-left: 0; padding-left: 2em; text-indent: -2em; }
.md-typeset .nj-theme-list > ul > li > .nj-theme { margin-right: .55em; text-indent: 0; }
.nj-charts .nj-theme-dot { fill: var(--nj-blue); }
.nj-charts .nj-theme-dot--other { fill: var(--nj-charts-ink2); }
.nj-charts .nj-theme-num { fill: var(--md-default-bg-color); font-size: 11px; font-weight: 700; }
+1 -1
View File
@@ -1,4 +1,4 @@
/* figures: jsonview (features/json_view/*.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
/* figures: jsonview (features/json_view.md; SVGs from scripts/figures/json_view_layout.py, stepper in js/fig-jsonview.js) */
/* one colour per concept, the same in every figure: structure (object/array, views) blue, keys navy, strings green,
numbers orange, decoded strings purple, owned edit storage teal, dangling/invalid red */
@@ -1,29 +0,0 @@
#include <functional>
#include <iostream>
#include <utility>
// walk over at most 16 nesting levels on the call stack, and over the rest with an explicit stack
#define JSON_RECURSION_DEPTH_LIMIT 16
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// an array nested 5,000 levels deep, built without recursion
json j = 1;
for (int i = 0; i < 5000; ++i)
{
json outer = json::array();
outer.push_back(std::move(j));
j = std::move(outer);
}
// the results are those of any other limit
const json copy = j;
std::cout << std::boolalpha;
std::cout << "limit: " << JSON_RECURSION_DEPTH_LIMIT << '\n';
std::cout << "copy equals original: " << (copy == j) << '\n';
std::cout << "length of dump(): " << j.dump().size() << '\n';
std::cout << "same hash: " << (std::hash<json> {}(copy) == std::hash<json> {}(j)) << '\n';
}
@@ -1,4 +0,0 @@
limit: 16
copy equals original: true
length of dump(): 10001
same hash: true
@@ -0,0 +1,43 @@
#include <iostream>
#include <iomanip>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// a SAX parser that creates a JSON value like json::parse does, but that
// recovers from parse errors instead of stopping at the first one
class recovering_parser : public nlohmann::detail::json_sax_dom_parser<json>
{
public:
explicit recovering_parser(json& result)
: nlohmann::detail::json_sax_dom_parser<json>(result, false)
{}
bool parse_error(std::size_t position,
const std::string& /*last_token*/,
const json::exception& ex)
{
std::cout << "byte " << position << ": " << ex.what() << '\n';
// repair the input and continue
return true;
}
};
int main()
{
// JSON text with several mistakes that ends too early
const std::string text = R"({
"name": "Hello World",
"tags": ["a" "b",],
"valid": tru,
"size": 1.,
"nested": {"x": 1)";
json result;
recovering_parser sax(result);
const bool valid = json::sax_parse(text, &sax);
std::cout << "\nvalid JSON: " << std::boolalpha << valid << '\n'
<< std::setw(4) << result << std::endl;
}
@@ -0,0 +1,19 @@
byte 49: [json.exception.parse_error.101] parse error at line 3, column 20: syntax error while parsing array - unexpected string literal; expected ']'
byte 51: [json.exception.parse_error.101] parse error at line 3, column 22: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
byte 70: [json.exception.parse_error.101] parse error at line 4, column 17: syntax error while parsing value - invalid literal; last read: '"valid": tru,'
byte 86: [json.exception.parse_error.101] parse error at line 5, column 15: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.,'
byte 109: [json.exception.parse_error.101] parse error at line 6, column 22: syntax error while parsing object - unexpected end of input; expected '}'
valid JSON: false
{
"name": "Hello World",
"nested": {
"x": 1
},
"size": 1,
"tags": [
"a",
"b"
],
"valid": null
}
@@ -88,6 +88,7 @@ The library uses the following mapping from JSON values types to BJData types ac
| number_integer | 65536..2147483647 | int32 | `l` |
| number_integer | 2147483648..4294967295 | uint32 | `m` |
| number_integer | 4294967296..9223372036854775807 | int64 | `L` |
| number_integer | 9223372036854775808..18446744073709551615 | uint64 | `M` |
| number_unsigned | 0..127 | int8 | `i` |
| number_unsigned | 128..255 | uint8 | `U` |
| number_unsigned | 256..32767 | int16 | `I` |
@@ -61,7 +61,7 @@ specification:
| number_integer | 128..255 | uint 8 | 0xCC |
| number_integer | 256..65535 | uint 16 | 0xCD |
| number_integer | 65536..4294967295 | uint 32 | 0xCE |
| number_integer | 4294967296..9223372036854775807 | uint 64 | 0xCF |
| number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF |
| number_unsigned | 0..127 | positive fixint | 0x00..0x7F |
| number_unsigned | 128..255 | uint 8 | 0xCC |
| number_unsigned | 256..65535 | uint 16 | 0xCD |
+2 -2
View File
@@ -48,7 +48,7 @@ which avoids repeating it:
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
type is obtained from a subtree that is built on demand with
[`materialize()`](../api/basic_json_view/materialize.md). See
[Zero-copy JSON views](json_view/reading.md#getting-values-out-without-copying).
[Zero-copy JSON views](json_view.md#getting-values-out-without-copying).
The library already knows how to convert to and from the scalar types and the STL containers (such as
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
@@ -269,7 +269,7 @@ The conversions above are built in for standard types. To make the same syntax w
- [`get_ref`](../api/basic_json/get_ref.md) - / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
- [Zero-copy JSON views](json_view/index.md) - get values out of a document or a view
- [Zero-copy JSON views](json_view.md) - get values out of a document or a view
- [`adl_serializer`](../api/adl_serializer/index.md) - the default serializer behind the conversions
- [`type`](../api/basic_json/type.md) - query the type of a value
- [`JSON_DISABLE_TUPLE_REFERENCE_CONVERSION`](../api/macros/json_disable_tuple_reference_conversion.md) - toggle conversion to tuples of references
Loaded 100 of 253 files, more files were not shown because too many files have changed in this diff. Show more