Compare commits

..
Author SHA1 Message Date
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
167 changed files with 11752 additions and 5622 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. 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 `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. 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 #### Exceptions
+3 -3
View File
@@ -163,9 +163,9 @@ jobs:
python3 $GENERATOR --list-tags python3 $GENERATOR --list-tags
TAG_COUNT=$(python3 $GENERATOR --list-tags | wc -l) TAG_COUNT=$(python3 $GENERATOR --list-tags | wc -l)
python3 $GENERATOR --abi-tags diag,_dp,_ns --abi-tags _diag --output $RUNNER_TEMP/abi_tags.natvis python3 $GENERATOR --abi-tags diag,_dp,_vns --abi-tags _diag --output $RUNNER_TEMP/abi_tags.natvis
grep -q '<!-- Namespace nlohmann::json_abi_diag_dp_ns -->' $RUNNER_TEMP/abi_tags.natvis grep -q '<!-- Namespace nlohmann::json_abi_diag_dp_vns -->' $RUNNER_TEMP/abi_tags.natvis
grep -q '<!-- Namespace nlohmann::json_abi_diag_dp_ns_v[0-9_]* -->' $RUNNER_TEMP/abi_tags.natvis grep -q '<!-- Namespace nlohmann::json_abi_diag_dp_vns_v[0-9_]* -->' $RUNNER_TEMP/abi_tags.natvis
test $(grep -c '<!-- Namespace ' $RUNNER_TEMP/abi_tags.natvis) -eq $(( $(grep -c '<!-- Namespace ' nlohmann_json.natvis) + 2 )) test $(grep -c '<!-- Namespace ' $RUNNER_TEMP/abi_tags.natvis) -eq $(( $(grep -c '<!-- Namespace ' nlohmann_json.natvis) + 2 ))
python3 $GENERATOR --all --output $RUNNER_TEMP/all.natvis python3 $GENERATOR --all --output $RUNNER_TEMP/all.natvis
+2 -2
View File
@@ -43,8 +43,8 @@ jobs:
- name: flawfinder_scan - name: flawfinder_scan
uses: david-a-wheeler/flawfinder@c4216b74cf2639ffa98503768bd6e4299b5440c9 # v2.0.20 uses: david-a-wheeler/flawfinder@c4216b74cf2639ffa98503768bd6e4299b5440c9 # v2.0.20
with: with:
# single_include/ is generated from include/ and would report every finding twice # single_include/ is omitted: it is generated from include/ and would report every finding twice
arguments: "--sarif --exclude '*/single_include/*' ./" arguments: '--sarif ./cmake ./docs ./include ./src ./tests ./tools'
output: 'flawfinder_results.sarif' output: 'flawfinder_results.sarif'
- name: Upload analysis results to GitHub Security tab - name: Upload analysis results to GitHub Security tab
-1
View File
@@ -90,7 +90,6 @@ cc_library(
"include/nlohmann/detail/view/simd.hpp", "include/nlohmann/detail/view/simd.hpp",
"include/nlohmann/detail/view/string_ref.hpp", "include/nlohmann/detail/view/string_ref.hpp",
"include/nlohmann/detail/view/value.hpp", "include/nlohmann/detail/view/value.hpp",
"include/nlohmann/detail/view/view_mode.hpp",
"include/nlohmann/json.hpp", "include/nlohmann/json.hpp",
"include/nlohmann/json_fwd.hpp", "include/nlohmann/json_fwd.hpp",
"include/nlohmann/json_literals.hpp", "include/nlohmann/json_literals.hpp",
+1 -1
View File
@@ -309,7 +309,7 @@ The "Check amalgamation" workflow fails if the file is out of date.
The file covers the namespace variants with up to two ABI tags. A Natvis file for other configurations can be The file covers the namespace variants with up to two ABI tags. A Natvis file for other configurations can be
generated with the `--abi-tags` or `--all` options, for example generated with the `--abi-tags` or `--all` options, for example
`make natvis NATVIS_ARGS="--abi-tags _diag,_dp,_ns --output my_nlohmann_json.natvis"`; see `make natvis NATVIS_ARGS="--abi-tags _diag,_dp,_vns --output my_nlohmann_json.natvis"`; see
[`tools/generate_natvis`](tools/generate_natvis/README.md). With MSVC, CMake adds the file to the `nlohmann_json` [`tools/generate_natvis`](tools/generate_natvis/README.md). With MSVC, CMake adds the file to the `nlohmann_json`
target and installs it. target and installs it.
+1 -1
View File
@@ -120,7 +120,7 @@ $(AMALGAMATED_VIEW_FILE): $(SRCS)
tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_view.json -s . --verbose=yes tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_view.json -s . --verbose=yes
NATVIS_VENV=tools/generate_natvis/venv NATVIS_VENV=tools/generate_natvis/venv
# extra options for generate_natvis.py, e.g. NATVIS_ARGS="--abi-tags _diag,_dp,_ns --output my.natvis" # extra options for generate_natvis.py, e.g. NATVIS_ARGS="--abi-tags _diag,_dp,_vns --output my.natvis"
NATVIS_ARGS= NATVIS_ARGS=
# regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp # regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp
+2 -2
View File
@@ -495,7 +495,7 @@ bool key(string_t& val);
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex); 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: To implement your own SAX handler, proceed as follows:
@@ -503,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`. 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. 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 ### STL-like access
+2 -3
View File
@@ -90,9 +90,8 @@ type `#!cpp binary_t*` must be dereferenced.
- MessagePack - MessagePack
- If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1,
fixext2, fixext4, fixext8, fixext16) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is
subtype is then written as the signed 8-bit ext type: subtypes 128 to 255 map to the negative (reserved) ext then added as a signed 8-bit integer.
types, and on reading the ext type is exposed as an unsigned subtype from 0 to 255.
- If no subtype is given, the bin family (bin8, bin16, bin32) is used. - If no subtype is given, the bin family (bin8, bin16, bin32) is used.
- BSON - BSON
@@ -1,48 +0,0 @@
# <small>nlohmann::basic_json::</small>bjdata_version_t
```cpp
enum class bjdata_version_t
{
draft2,
draft3
};
```
This enumeration is used in [`to_bjdata`](to_bjdata.md) to choose the version of the BJData specification to write:
draft2
: BJData Draft 2 (the default). Binary values are written as arrays of `U` integers, and ND-arrays with
`"_ArrayType_": "byte"` are written as regular objects, since the `B` marker is not part of Draft 2.
draft3
: BJData Draft 3. Binary values are written as `B` elements: as an optimized binary array (`[$B#`) if both `use_size`
and `use_type` are set, and as an array of `B`-prefixed elements otherwise. ND-arrays with
`"_ArrayType_": "byte"` are written as packed arrays (`[$B#[...]`).
Only the writer is affected by the version. [`from_bjdata`](from_bjdata.md) reads the markers of both drafts.
## Examples
??? example
The example below shows how the different values of the `bjdata_version_t` influence the serialization of a binary
value with [`to_bjdata`](to_bjdata.md).
```cpp
--8<-- "examples/bjdata_version_t.cpp"
```
Output:
```json
--8<-- "examples/bjdata_version_t.output"
```
## See also
- [to_bjdata](to_bjdata.md) serializes a JSON value to BJData
- [BJData](../../features/binary_formats/bjdata.md) - the article on the BJData format
## Version history
- Added in version 3.12.0.
@@ -15,13 +15,11 @@ error
: report a parse error in case of a tag (the `from_cbor` overloads throw a `parse_error` exception by default) : report a parse error in case of a tag (the `from_cbor` overloads throw a `parse_error` exception by default)
ignore ignore
: ignore tags (also in front of a text string map key) : ignore tags
store store
: store tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are : store tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are
read as if the tag were ignored. If several tags precede a byte string, only the tag directly in front of it is read as if the tag were ignored. If several tags precede a byte string, only the innermost one is stored.
stored, and only when it has the form 0xd8..0xdb (a tag 0..23 written as 0xc0..0xd7 is skipped). Tags in front of a
text string map key are skipped.
## Examples ## Examples
+2 -9
View File
@@ -61,18 +61,11 @@ Strong exception safety: if an exception occurs, the original value stays intact
## Complexity ## Complexity
1. Logarithmic in the size of the JSON object. Logarithmic in the size of the JSON object.
2. Logarithmic in the size of the JSON object.
3. At most one lookup per reference token of `ptr`, each logarithmic in the size of the container at that level:
O(*n* log *m*) for *n* reference tokens and containers of size at most *m*.
4. Deleted; a call with an integral argument does not compile.
## Notes ## Notes
- Overloads (1) and (2) always return `#!cpp false` when executed on a JSON type that is not an object. Overload (3) - This method always returns `#!cpp false` when executed on a JSON type that is not an object.
also works on arrays and on primitive values: the empty JSON pointer resolves in every JSON value (so
`#!cpp json(1).contains(""_json_pointer)` returns `#!cpp true`), and `#!cpp json({1, 2}).contains("/1"_json_pointer)`
returns `#!cpp true`.
- This method can be executed on any JSON value type. - This method can be executed on any JSON value type.
- Calling this function with an integer argument (for example, `#!cpp contains(0)`) does not compile: such an argument - Calling this function with an integer argument (for example, `#!cpp contains(0)`) does not compile: such an argument
would otherwise implicitly convert to a null `#!cpp const char*` and, from there, cause undefined behavior when would otherwise implicitly convert to a null `#!cpp const char*` and, from there, cause undefined behavior when
+4 -41
View File
@@ -2,27 +2,17 @@
```cpp ```cpp
static basic_json diff(const basic_json& source, static basic_json diff(const basic_json& source,
const basic_json& target, const basic_json& target);
const string_t& path = "");
``` ```
Creates a [JSON Patch](http://jsonpatch.com) so that value `source` can be changed into the value `target` by calling Creates a [JSON Patch](http://jsonpatch.com) so that value `source` can be changed into the value `target` by calling
[`patch`](patch.md) function. [`patch`](patch.md) function.
For two JSON values `source` and `target`, the following code yields `#!cpp true`: For two JSON values `source` and `target`, the following code always yields `#!cpp true`:
```cpp ```cpp
source.patch(diff(source, target)) == target; source.patch(diff(source, target)) == target;
``` ```
The equality holds up to [`operator==`](operator_eq.md), so it has the following exceptions:
- A value that is not equal to itself cannot round-trip. This applies to `NaN` (also nested in an array or object) and
to [discarded](is_discarded.md) values: `diff` emits a `replace` operation for them. A `NaN` in the patch is
serialized as `#!json null`, so a patch that was dumped and parsed again would set `#!json null`.
- Numbers that compare equal produce no operation, even if their types or signs differ: `diff(1, 1.0)`,
`diff(1u, 1)` (unsigned and signed integer) and `diff(0.0, -0.0)` all return `#!json []`. The patched value is then
the `source` and keeps its number type.
## Parameters ## Parameters
`source` (in) `source` (in)
@@ -31,12 +21,6 @@ The equality holds up to [`operator==`](operator_eq.md), so it has the following
`target` (in) `target` (in)
: JSON value to compare against : JSON value to compare against
`path` (in)
: a prefix that is put verbatim in front of the `path` member of every operation (default: `""`, which addresses the
root of the value). The prefix is neither escaped nor validated: `#!cpp diff(1, 2, "abc")` yields an operation with
the `path` `abc`, which is not a valid JSON Pointer. Use it with a valid JSON Pointer such as `/root` and escape
`~` and `/` in its reference tokens (see [JSON Pointer](../../features/json_pointer.md)).
## Return value ## Return value
a JSON patch to convert the `source` to `target` a JSON patch to convert the `source` to `target`
@@ -47,32 +31,11 @@ Strong guarantee: `source` and `target` are never modified.
## Complexity ## Complexity
Linear in the total number of values (including nested ones) in `source` and `target`, times the cost of a key-based Linear in the lengths of `source` and `target`.
object operation (looking up a key in an object): logarithmic for [`json`](../json.md), and linear for
[`ordered_json`](../ordered_json.md) (see [`ordered_map` complexity](../ordered_map.md#complexity)). For
`ordered_json`, diffing two objects with `n` and `m` members therefore takes O((n+m)²) when their keys do not line up
(e.g., a key was added or the keys are in a different order). The size of the patch is proportional to the number of
operations times the length of their paths, so a small change deep inside a value yields a long `path`.
## Notes ## Notes
- Currently, only `remove`, `add`, and `replace` operations are generated. Currently, only `remove`, `add`, and `replace` operations are generated.
- The patch is correct, but not guaranteed to be minimal. For instance, `diff([1,2,3], [0,1,2,3])` yields three
`replace` operations and one `add` operation instead of a single insertion.
- Differences between two arrays are reported index by index. Elements that were added at the end of the array are added
with `add` operations on the path `/-` (appended in order), and elements that were removed are removed with `remove`
operations from the highest index down to the lowest, so that the remaining indices stay valid.
- For objects, the operations for the members of `source` come first (`replace`, `remove`, or the operations for nested
values, in the iteration order of `source`), followed by `add` operations for the members that only `target` has.
- For [`ordered_json`](../ordered_json.md), objects with the same keys in a different order are different. As a patch
cannot reorder members, `diff` removes all keys of the `source` object and adds them again in the order of the
`target`.
- If `source` and `target` differ as a whole (e.g., different types), the patch consists of a single `replace`
operation for the root of the value, i.e., with the `path` `""` (or the given `path` prefix).
- The `value` members of the patch hold copies of values of `target`. A [binary](../../features/binary_values.md) value
has no JSON representation: when a patch that contains one is serialized, it becomes an object such as
`#!json {"bytes":[1,3],"subtype":null}`, so a parsed patch no longer holds a binary value (and would set that object
instead).
## Examples ## Examples
@@ -82,11 +82,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
successfully, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict` successfully, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict`
- Throws [parse_error.115](../../home/exceptions.md#jsonexceptionparse_error115) if the text of a high-precision
number (`H`) is not a valid number
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container - Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
cannot be represented by `std::size_t` or exceeds the container's `max_size()`, or if the product of the dimensions of or n-dimensional array cannot be represented by `std::size_t`
an n-dimensional array overflows
## Complexity ## Complexity
@@ -74,11 +74,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs, for instance - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs, for instance
an invalid byte, a string that is not valid UTF-8, or an object key that is not a string an invalid byte, a string that is not valid UTF-8, or an object key that is not a string
The UTF-8 validity of strings is always checked, independent of
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md), because the end of a BON8 string is determined from
its encoding. Duplicate object keys are accepted; the last value wins (see
[BON8](../../features/binary_formats/bon8.md)).
## Complexity ## Complexity
Linear in the size of the input. Linear in the size of the input.
+1 -2
View File
@@ -80,8 +80,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [`parse_error.110`](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or - Throws [`parse_error.110`](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or
the end of the input was not reached when `strict` was set to true the end of the input was not reached when `strict` was set to true
- Throws [`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs (e.g., an - Throws [`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs (e.g., an
invalid string or byte array length, an invalid inner length of a binary value with subtype `0x02`, or a document size invalid string or byte array length)
that does not match the content; `strict` does not affect the document size checks)
- Throws [`parse_error.114`](../../home/exceptions.md#jsonexceptionparse_error114) if an unsupported BSON record type is - Throws [`parse_error.114`](../../home/exceptions.md#jsonexceptionparse_error114) if an unsupported BSON record type is
encountered encountered
- Throws [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) if a string value or object key is - Throws [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) if a string value or object key is
+4 -12
View File
@@ -32,10 +32,8 @@ The exact mapping and its limitations are described on a [dedicated page](../../
- an `std::istream` object - an `std::istream` object
- a `FILE` pointer - a `FILE` pointer
- a C-style array of characters (its terminating NUL is read as trailing data: `parse_error.110` unless `strict` is - a C-style array of characters
`false`) - a pointer to a null-terminated string of single byte characters
- a pointer to a null-terminated string of single byte characters (read up to the first 0x00 byte, which is a valid
CBOR byte)
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators - a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`) (as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
@@ -91,14 +89,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
used in the given input or if the input is not valid CBOR used in the given input or if the input is not valid CBOR
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of
other types are not supported, as JSON object keys are always strings), if the head of a string or byte string is other types are not supported, as JSON object keys are always strings), or if a string value or object key is not
malformed (including a chunk of the wrong type or an indefinite-length string nested inside an indefinite-length valid UTF-8 and `error_handler` is `strict`
string), or if a string value or object key (or one chunk of an indefinite-length string) is not valid UTF-8 and
`error_handler` is `strict`
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if an array or map announces
a length that `std::size_t` cannot hold, or that equals `std::size_t(-1)` (for example `9B FF FF FF FF FF FF FF FF`)
- Throws [out_of_range.406](../../home/exceptions.md#jsonexceptionout_of_range406) if a floating-point number does not
fit into `number_float_t`
## Complexity ## Complexity
+4 -11
View File
@@ -79,18 +79,11 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or - Throws [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110) if the given input ends prematurely or
the end of the file was not reached when `strict` was set to true the end of the file was not reached when `strict` was set to true
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if an invalid type marker is read - Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
(including a type marker after `$` of an optimized container, even if its count is 0), or if `$` is not followed by `#`
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed - Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
successfully (negative or non-integer length, `C` followed by a byte above 0x7F), if the count of an optimized successfully, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict`
container is negative, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict` - Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
- Throws [parse_error.115](../../home/exceptions.md#jsonexceptionparse_error115) if the text of a high-precision number or n-dimensional array cannot be represented by `std::size_t`
(`H`) is not a valid JSON number
- Throws [out_of_range.406](../../home/exceptions.md#jsonexceptionout_of_range406) if a high-precision number, or a
float64 when `number_float_t` is `float`, does not fit into `number_float_t`
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the count of an optimized
container exceeds the capacity of the container (or of `std::size_t`), or if an optimized array of `Z`, `T`, or `F` has
more than 1,048,576 elements
## Complexity ## Complexity
-1
View File
@@ -121,7 +121,6 @@ The class satisfies the following concept requirements:
- [**json_pointer**](../json_pointer/index.md) - JSON Pointer implementation - [**json_pointer**](../json_pointer/index.md) - JSON Pointer implementation
- [**json_serializer**](json_serializer.md) - type of the serializer to for conversions from/to JSON - [**json_serializer**](json_serializer.md) - type of the serializer to for conversions from/to JSON
- [**error_handler_t**](error_handler_t.md) - type to choose behavior on decoding errors - [**error_handler_t**](error_handler_t.md) - type to choose behavior on decoding errors
- [**bjdata_version_t**](bjdata_version_t.md) - type to choose the BJData version to write
- [**cbor_tag_handler_t**](cbor_tag_handler_t.md) - type to choose how to handle CBOR tags - [**cbor_tag_handler_t**](cbor_tag_handler_t.md) - type to choose how to handle CBOR tags
- **initializer_list_t** - type for initializer lists of `basic_json` values - **initializer_list_t** - type for initializer lists of `basic_json` values
- [**input_format_t**](input_format_t.md) - type to choose the format to parse - [**input_format_t**](input_format_t.md) - type to choose the format to parse
+4 -24
View File
@@ -39,33 +39,13 @@ Basic guarantee: if an exception is thrown during the operation, the JSON value
## Complexity ## Complexity
Linear in the total number of values (including nested ones) in `apply_patch`, times the cost of a key-based object Linear in the lengths of `apply_patch`.
operation (looking up, adding, or erasing a member): logarithmic for [`json`](../json.md), and linear for
[`ordered_json`](../ordered_json.md) (see [`ordered_map` complexity](../ordered_map.md#complexity)). For
`ordered_json`, patching an object that has `n` members with a patch that has `m` members therefore takes O(n·m). The
cost also includes one copy of `apply_patch` (see the notes below).
## Notes ## Notes
- `apply_patch` may be `#!cpp *this` itself or refer to a value contained in `#!cpp *this` (for example, a subobject `apply_patch` may be `#!cpp *this` itself or refer to a value contained in `#!cpp *this` (for example, a subobject
returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was called, before any modification of returned by `#!cpp (*this)[key]`); it is read as it was when `merge_patch()` was called, before any modification of
`#!cpp *this`. To make this possible, the function copies `apply_patch` before it modifies `#!cpp *this`, so the cost `#!cpp *this`.
includes one copy of the patch.
- A merge patch uses `#!json null` to remove a member, so it cannot set a member to `#!json null`: applying
`#!json {"a":null}` to `#!json {"a":1}` gives `#!json {}`.
- Arrays are not merged: an array in the patch replaces the target value as a whole, and a `#!json null` inside an
array is kept as an element. `#!json null` members of the target that the patch does not mention are preserved.
- A `#!json null` in the patch only removes a member; it does not prevent the creation of the enclosing objects.
Applying `#!json {"a":{"b":null}}` to `#!json {}` gives `#!json {"a":{}}` (see example 15 in Appendix A of the RFC).
- If `apply_patch` is not an object, it replaces the target as a whole. In particular, a `#!json null` patch turns the
target into `#!json null`. If `apply_patch` is an object and the target is not, the target is first replaced by an
empty object, so applying `#!json {}` to `#!json 1` gives `#!json {}`.
- A merge patch cannot express every change; for example, it cannot reorder array elements or remove a specific array
element. Use [JSON Patch](../../features/json_patch.md) ([RFC 6902](https://tools.ietf.org/html/rfc6902)) for that.
[`merge_diff`](merge_diff.md) has a matching limitation, see its notes.
- A [discarded](is_discarded.md) value is treated like any other non-object value: a discarded
`apply_patch` turns the target into a discarded value, and an object patch replaces a discarded target by an object
as described above.
## Examples ## Examples
@@ -137,13 +137,10 @@ Strong exception safety: if an exception occurs, the original value stays intact
When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing
leaf), each missing level is created as an array or an object depending on whether the corresponding leaf), each missing level is created as an array or an object depending on whether the corresponding
pointer token is a valid array index: the token `0`, a sequence of digits that does not begin with `0`, pointer token parses as a non-negative integer: a numeric token creates an array, a non-numeric token
or the token `-` creates an array, and every other token creates an object. For example, on an creates an object. For example, on an initially `#!json null` value, `/foo/0/0/0` creates nested arrays,
initially `#!json null` value, `/foo/0/0/0` creates nested arrays, while `/foo/one/one/one` creates while `/foo/one/one/one` creates nested objects. This is not specified by the JSON Pointer RFC; it is
nested objects. Tokens such as `01` or the empty token cannot be array indices (cf. RFC 6901, Sect. 4) this library's own, intentional disambiguation rule. See also [JSON Pointer](../../features/json_pointer.md).
and therefore create objects, just as they would if the level already existed as an object. This is not
specified by the JSON Pointer RFC; it is this library's own, intentional disambiguation rule. See also
[JSON Pointer](../../features/json_pointer.md).
!!! warning "Deprecation" !!! warning "Deprecation"
+2 -10
View File
@@ -40,7 +40,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
where an existing element is required (the "path" of "replace", the "from" of "move" and "copy"); example: where an existing element is required (the "path" of "replace", the "from" of "move" and "copy"); example:
`"array index '-' (3) is out of range"`. `"array index '-' (3) is out of range"`.
- Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch
could not be resolved successfully in the current JSON value; example: `"key 'baz' not found"`. could not be resolved successfully in the current JSON value; example: `"key baz not found"`.
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token of a JSON - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token of a JSON
pointer inside the patch cannot be resolved, e.g., `-` in a "remove" operation or `1a` for an array; example: pointer inside the patch cannot be resolved, e.g., `-` in a "remove" operation or `1a` for an array; example:
`"unresolved reference token '-'"`. `"unresolved reference token '-'"`.
@@ -53,10 +53,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
- Throws [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) if a "move" operation's "from" - Throws [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) if a "move" operation's "from"
location is a proper prefix of its "path" location. location is a proper prefix of its "path" location.
- Throws [`other_error.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was - Throws [`other_error.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was
unsuccessful. A "test" operation whose "path" does not exist is unsuccessful, too: the failed lookup is reported as a unsuccessful.
failed test, even if the operation has no "value" member (which would otherwise be
[`parse_error.105`](../../home/exceptions.md#jsonexceptionparse_error105)). Which of the two errors is reported for
this combination depends on the order in which the operands of a comparison are evaluated, so do not rely on it.
## Complexity ## Complexity
@@ -68,10 +65,6 @@ the JSON value, the complexity can usually be neglected.
The application of a patch is atomic: Either all operations succeed and the patched document is returned or an exception The application of a patch is atomic: Either all operations succeed and the patched document is returned or an exception
is thrown. In any case, the original value is not changed: the patch is applied to a copy of the value. is thrown. In any case, the original value is not changed: the patch is applied to a copy of the value.
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
`remove` operation.
## Examples ## Examples
??? example "Example: apply a JSON patch" ??? example "Example: apply a JSON patch"
@@ -115,7 +108,6 @@ is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op":
- Added in version 2.0.0. - Added in version 2.0.0.
- Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's - Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's
target location has a non-object/non-array parent in version 3.13.0. target location has a non-object/non-array parent in version 3.13.0.
This includes a `null` parent, which was previously silently converted to an object, in version 3.13.0.
- Added [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) and stopped silently ignoring a "remove" operation whose target - Added [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) and stopped silently ignoring a "remove" operation whose target
location has a non-object/non-array parent in version 3.13.0. location has a non-object/non-array parent in version 3.13.0.
- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper - Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper
@@ -36,7 +36,7 @@ No guarantees, value may be corrupted by an unsuccessful patch operation.
where an existing element is required (the "path" of "replace", the "from" of "move" and "copy"); example: where an existing element is required (the "path" of "replace", the "from" of "move" and "copy"); example:
`"array index '-' (3) is out of range"`. `"array index '-' (3) is out of range"`.
- Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch - Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a JSON pointer inside the patch
could not be resolved successfully in the current JSON value; example: `"key 'baz' not found"`. could not be resolved successfully in the current JSON value; example: `"key baz not found"`.
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token of a JSON - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token of a JSON
pointer inside the patch cannot be resolved, e.g., `-` in a "remove" operation or `1a` for an array; example: pointer inside the patch cannot be resolved, e.g., `-` in a "remove" operation or `1a` for an array; example:
`"unresolved reference token '-'"`. `"unresolved reference token '-'"`.
@@ -49,10 +49,7 @@ No guarantees, value may be corrupted by an unsuccessful patch operation.
- Throws [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) if a "move" operation's "from" - Throws [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) if a "move" operation's "from"
location is a proper prefix of its "path" location. location is a proper prefix of its "path" location.
- Throws [`other_error.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was - Throws [`other_error.501`](../../home/exceptions.md#jsonexceptionother_error501) if "test" operation was
unsuccessful. A "test" operation whose "path" does not exist is unsuccessful, too: the failed lookup is reported as a unsuccessful.
failed test, even if the operation has no "value" member (which would otherwise be
[`parse_error.105`](../../home/exceptions.md#jsonexceptionparse_error105)). Which of the two errors is reported for
this combination depends on the order in which the operands of a comparison are evaluated, so do not rely on it.
## Complexity ## Complexity
@@ -65,20 +62,6 @@ Unlike [`patch`](patch.md), `patch_inplace` applies the operation "in place" and
That makes it faster for large documents by avoiding the copy. However, the JSON value might be corrupted if the That makes it faster for large documents by avoiding the copy. However, the JSON value might be corrupted if the
function throws an exception. function throws an exception.
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last one
is kept, see [`object_t`](object_t.md#behavior)), so `#!json {"op": "add", "op": "remove", "path": "/a"}` is a
`remove` operation.
The JSON patch may be the JSON value itself (`#!cpp j.patch_inplace(j)`): the operations are then read from a copy of
the patch made before the first operation is applied, so the result is the same as that of
`#!cpp j = j.patch(j)`, and the exception safety is the same as above.
!!! danger "Undefined behavior"
The JSON patch must not be a value stored inside the JSON value (e.g., `#!cpp j.patch_inplace(j["patch"])`), as the
operations may change or destroy it while it is read. Pass a copy (`#!cpp j.patch_inplace(json(j["patch"]))`) or use
[`patch`](patch.md) instead.
## Examples ## Examples
??? example "Example: apply a JSON patch in place" ??? example "Example: apply a JSON patch in place"
@@ -123,7 +106,6 @@ the patch made before the first operation is applied, so the result is the same
- Added in version 3.11.0. - Added in version 3.11.0.
- Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's - Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's
target location has a non-object/non-array parent in version 3.13.0. target location has a non-object/non-array parent in version 3.13.0.
This includes a `null` parent, which was previously silently converted to an object, in version 3.13.0.
- Added [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) and stopped silently ignoring a "remove" operation whose target - Added [`out_of_range.413`](../../home/exceptions.md#jsonexceptionout_of_range413) and stopped silently ignoring a "remove" operation whose target
location has a non-object/non-array parent in version 3.13.0. location has a non-object/non-array parent in version 3.13.0.
- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper - Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper
@@ -131,4 +113,3 @@ the patch made before the first operation is applied, so the result is the same
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones. not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
- Applying a JSON value as patch to itself no longer results in undefined behavior in version 3.13.0.
+4 -1
View File
@@ -104,7 +104,9 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
## Return value ## 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 ## Exception safety
@@ -161,6 +163,7 @@ use the same file with stdio functions, but it must not wait for another thread
- Added `ignore_trailing_commas` in version 3.13.0. - Added `ignore_trailing_commas` in version 3.13.0.
- Added `tag_handler` 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. - 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. - 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 - `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`. after the parsed value when `strict` is `#!cpp false`.
+2 -5
View File
@@ -43,8 +43,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
`#!cpp false` by default. `#!cpp false` by default.
`version` (in) `version` (in)
: which version of BJData to use, see [`bjdata_version_t`](bjdata_version_t.md) and the note on "Binary values" on : which version of BJData to use (see note on "Binary values" on [BJData](../../features/binary_formats/bjdata.md));
[BJData](../../features/binary_formats/bjdata.md);
optional, `#!cpp bjdata_version_t::draft2` by default. optional, `#!cpp bjdata_version_t::draft2` by default.
`error_handler` (in) `error_handler` (in)
@@ -65,9 +64,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
## Exceptions ## Exceptions
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size` - Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
is false, and `j` contains a non-empty array or object, or a non-empty binary value (with is false, and `j` contains a non-empty array, object, or binary value.
`version = bjdata_version_t::draft3`, any binary value, including an empty one).
Scalars, empty arrays, and empty objects do not throw.
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` (the default only if not valid UTF-8 and `error_handler` is `strict` (the default only if
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
+1 -1
View File
@@ -13,7 +13,7 @@ static void to_bson(const basic_json& j, detail::output_adapter<char> o,
``` ```
BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are stored as a single entity (a BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are stored as a single entity (a
so-called document). The members of an object are written in the order in which iteration visits them. so-called document).
1. Returns a byte vector containing the BSON serialization. 1. Returns a byte vector containing the BSON serialization.
2. Writes the BSON serialization to an output adapter. 2. Writes the BSON serialization to an output adapter.
+1 -2
View File
@@ -42,8 +42,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
## Exception safety ## Exception safety
Strong guarantee: if an exception is thrown, there are no changes in the JSON value. Bytes that were already written to Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
the output adapter are not taken back.
## Exceptions ## Exceptions
@@ -53,7 +53,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
value, array, or object exceeds 4294967295, the maximum MessagePack can store; example: value, array, or object exceeds 4294967295, the maximum MessagePack can store; example:
`"MessagePack length 4294967296 exceeds maximum of 4294967295"` `"MessagePack length 4294967296 exceeds maximum of 4294967295"`
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value - Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
exceeds 255, the largest value the library can store in the one-byte MessagePack ext type; example: exceeds 255, the maximum of the MessagePack ext type; example:
`"subtype 70000 is too large for the MessagePack ext type (max 255)"` `"subtype 70000 is too large for the MessagePack ext type (max 255)"`
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
not valid UTF-8 and `error_handler` is `strict` not valid UTF-8 and `error_handler` is `strict`
+5 -10
View File
@@ -36,9 +36,9 @@ The function can throw the following exceptions:
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a
number; example: `"array index 'one' is not a number"` number; example: `"array index 'one' is not a number"`
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array
(because one of its keys is `0`) and another key at that level is empty (as in `/a/`), is `-`, or begins with a digit (because one of its keys is `0`) and another key at that level begins with a digit but is not a valid array index
but is not a valid array index (such as `1a`); examples: `"unresolved reference token '-'"`, (such as `1a`), or is `-`; example:
`"unresolved reference token ''"` `"unresolved reference token '-'"`
## Complexity ## Complexity
@@ -46,7 +46,7 @@ Linear in the size of the JSON value.
## Notes ## Notes
Empty objects and arrays are flattened by [`flatten()`](flatten.md) to `#!json null` values and cannot be unflattened to Empty objects and arrays are flattened by [`flatten()`](flatten.md) to `#!json null` values and cannot unflattened to
their original type. their original type.
A flattened array and a flattened object whose keys are array indices are indistinguishable, because both are A flattened array and a flattened object whose keys are array indices are indistinguishable, because both are
@@ -54,12 +54,7 @@ described by the same JSON pointers. A value is therefore restored as an array i
reference token `0`, and as an object otherwise: `#!json {"2": 1}` is restored unchanged, whereas `#!json {"0": 1}` is reference token `0`, and as an object otherwise: `#!json {"2": 1}` is restored unchanged, whereas `#!json {"0": 1}` is
restored as `#!json [1]`. This decision does not depend on the order in which the flattened object is iterated. restored as `#!json [1]`. This decision does not depend on the order in which the flattened object is iterated.
A third case is an object that has the key `0` next to a key that is not a valid array index: `#!json {"0": 1, "b": 2}` Apart from these two cases, for a JSON value `j`, the following is always true:
is flattened to `#!json {"/0": 1, "/b": 2}`, and unflatten treats the level as an array and throws
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) because `b` is not a number. (For the keys
`-`, `1a`, or the empty key instead of `b`, it throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404).)
Apart from these three cases, for a JSON value `j`, the following is always true:
`#!cpp j == j.flatten().unflatten()`. `#!cpp j == j.flatten().unflatten()`.
## Examples ## Examples
-3
View File
@@ -45,9 +45,6 @@ This is equivalent to Python's `dict.get(key, default)`.
return default_value; return default_value;
} }
``` ```
with one exception: if the JSON value is neither an array nor an object, the function throws
[`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) before looking at `ptr`, even for the empty
pointer, whereas `at` returns the value itself for the empty pointer.
!!! note "Differences to `at` and `operator[]`" !!! note "Differences to `at` and `operator[]`"
@@ -12,7 +12,7 @@ void erase(view_type array, I idx);
std::size_t erase(const json_pointer& ptr); std::size_t erase(const json_pointer& ptr);
``` ```
Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
has `erase`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `erase`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many 1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
template<typename BasicJsonType, view_mode Mode = view_mode::read_only> template<typename BasicJsonType, bool Editable = false>
class basic_json_document; class basic_json_document;
``` ```
@@ -20,9 +20,9 @@ is move-only: a copy would duplicate the whole index, the decoded-string buffer,
enough in every use we know of. The index, the decoded strings, and the owned text live in one heap block that the enough in every use we know of. The index, the decoded strings, and the owned text live in one heap block that the
document holds through a `#!cpp std::unique_ptr`; this is also why views survive a move of the document. document holds through a `#!cpp std::unique_ptr`; this is also why views survive a move of the document.
With `#!cpp Mode == view_mode::editable`, the document also offers [`set`](set.md), [`push_back`](push_back.md), With `#!cpp Editable == true`, the document also offers [`set`](set.md), [`push_back`](push_back.md),
[`insert`](insert.md), and [`erase`](erase.md) to change values in place, see [Edits](#edits) below. The source text [`insert`](insert.md), and [`erase`](erase.md) to change values in place, see [Edits](#edits) below. The source text
itself is never written; a read-only document (`#!cpp view_mode::read_only`, the default) does not carry any of the itself is never written; a read-only document (`#!cpp Editable == false`, the default) does not carry any of the
bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp static_assert`). bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp static_assert`).
## Template parameters ## Template parameters
@@ -32,10 +32,9 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
[`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this [`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this
is checked with a `static_assert`. is checked with a `static_assert`.
`Mode` `Editable`
: a [`view_mode`](../view_mode.md): `#!cpp view_mode::editable` if the document supports [`set`](set.md), : whether the document supports [`set`](set.md), [`push_back`](push_back.md), [`insert`](insert.md), and
[`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md) (optional, `#!cpp view_mode::read_only` [`erase`](erase.md) (optional, `#!cpp false` by default). See [Edits](#edits) below.
by default). See [Edits](#edits) below.
## Specializations ## Specializations
@@ -48,7 +47,7 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
## Member types ## Member types
- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType, Mode>`) - **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType, Editable>`)
- **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md) - **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md)
- **image_check** - how [`load()`](load.md) validates an image (`full`, `bounds`, `none`), see [`image_check`](load.md#image_check) - **image_check** - how [`load()`](load.md) validates an image (`full`, `bounds`, `none`), see [`image_check`](load.md#image_check)
@@ -81,15 +80,15 @@ bookkeeping edits need, and calling any of them on one fails to compile (`#!cpp
### Edits ### Edits
- [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers - [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers
to (editable documents only) to (`#!cpp Editable` documents only)
- [**push_back**](push_back.md) - append to an array (editable documents only) - [**push_back**](push_back.md) - append to an array (`#!cpp Editable` documents only)
- [**insert**](insert.md) - insert an element into an array before a given position (editable documents only) - [**insert**](insert.md) - insert an element into an array before a given position (`#!cpp Editable` documents only)
- [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to - [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
(editable documents only) (`#!cpp Editable` documents only)
## Edits ## Edits
An editable document (`#!cpp view_mode::editable`) can be changed after parsing, with [`set`](set.md), An editable document (`#!cpp Editable == true`) can be changed after parsing, with [`set`](set.md),
[`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md); [`push_back`](push_back.md), [`insert`](insert.md), and [`erase`](erase.md);
[`json_editable_document`](../json_editable_document.md) and [`json_editable_document`](../json_editable_document.md) and
[`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few [`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few
@@ -5,7 +5,7 @@ template<typename I, typename V>
view_type insert(view_type array, I idx, V&& value); view_type insert(view_type array, I idx, V&& value);
``` ```
Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
has `insert`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `insert`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
Inserts `value` into `array` as a new element before position `idx`, which must not be past the end Inserts `value` into `array` as a new element before position `idx`, which must not be past the end
@@ -55,7 +55,10 @@ validate with overload 2 first, if the original vector must survive a failed loa
## Exceptions ## Exceptions
Throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if `image` is not one On a big-endian target, throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) -- the same
exception [`save()`](save.md#exceptions) throws there, since the image format is little-endian only.
Otherwise throws [`parse_error.116`](../../home/exceptions.md#jsonexceptionparse_error116) if `image` is not one
`save()` could have written, or fails the requested `check`: `save()` could have written, or fails the requested `check`:
| message | when | | message | when |
@@ -92,9 +95,6 @@ are checked once, however many nodes refer to it.
## Notes ## Notes
**Little-endian targets only.** On a big-endian target, `load()` does not exist, just like
[`save()`](save.md#notes): the image format is little-endian, so a call fails to compile.
### `image_check` { #image_check } ### `image_check` { #image_check }
```cpp ```cpp
@@ -13,7 +13,7 @@ editable, and it does not have to be `array`'s own document -- which is copied s
source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers, source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers,
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...). strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
has `push_back`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `push_back`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
## Template parameters ## Template parameters
@@ -12,7 +12,7 @@ An edited document is written in its *current* state, with its values in documen
parser would have produced them for that JSON text: a member [`set`](set.md) added goes at the end, an parser would have produced them for that JSON text: a member [`set`](set.md) added goes at the end, an
[`erase`](erase.md)d member leaves no trace, and a float that is not finite (NaN or positive/negative infinity) [`erase`](erase.md)d member leaves no trace, and a float that is not finite (NaN or positive/negative infinity)
becomes null, the same substitution [`dump()`](../basic_json_view/dump.md) makes. The same document always saves to becomes null, the same substitution [`dump()`](../basic_json_view/dump.md) makes. The same document always saves to
the same bytes -- also across `BasicJsonType` and the [`view_mode`](../view_mode.md), since the image reflects document order and the same bytes -- also across `BasicJsonType` and `#!cpp Editable`, since the image reflects document order and
values only, not which specialization produced them. values only, not which specialization produced them.
## Return value ## Return value
@@ -31,6 +31,9 @@ Throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) i
[discarded](is_discarded.md) -- a default-constructed document, or one a failed [`parse()`](parse.md)/ [discarded](is_discarded.md) -- a default-constructed document, or one a failed [`parse()`](parse.md)/
[`read()`](read.md) with `allow_exceptions == false` left discarded. [`read()`](read.md) with `allow_exceptions == false` left discarded.
On a big-endian target, throws `type_error.320` with a different message instead: the image format is little-endian
only (see [Notes](#notes)).
Throws [`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416) if the node count, the text, or Throws [`out_of_range.416`](../../home/exceptions.md#jsonexceptionout_of_range416) if the node count, the text, or
the decoded strings of the image would individually reach 4 GiB -- the same 32-bit offsets the decoded strings of the image would individually reach 4 GiB -- the same 32-bit offsets
[`parse()`](parse.md#exceptions) and, for edits, [`set`](set.md)/[`push_back`](push_back.md) are already limited to. [`parse()`](parse.md#exceptions) and, for edits, [`set`](set.md)/[`push_back`](push_back.md) are already limited to.
@@ -41,6 +44,9 @@ the decoded strings of the image would individually reach 4 GiB -- the same 32-b
[json.exception.type_error.320] cannot save a discarded json_document [json.exception.type_error.320] cannot save a discarded json_document
``` ```
``` ```
[json.exception.type_error.320] json_document images need a little-endian target
```
```
[json.exception.out_of_range.416] images of 4 GiB or more are not supported by json_document [json.exception.out_of_range.416] images of 4 GiB or more are not supported by json_document
``` ```
@@ -64,9 +70,9 @@ else -- see [`load`'s Exceptions](load.md#exceptions).
the *same* build on the *same* (little-endian) machine -- not as a long-term storage format. Keep the original the *same* build on the *same* (little-endian) machine -- not as a long-term storage format. Keep the original
JSON text if you need to read a saved document back with a future library version. JSON text if you need to read a saved document back with a future library version.
**Little-endian targets only.** The image is written as raw little-endian bytes, with no byte-swapping. On a **Little-endian only.** The image is written as raw little-endian bytes, with no byte-swapping. `save()` (and
big-endian target, `save()` and [`load`](load.md) do not exist: a call fails to compile rather than produce bytes a [`load`](load.md)) throw `type_error.320` on a big-endian target rather than silently produce bytes a big-endian
big-endian reader could not interpret correctly. reader could not interpret correctly.
## Examples ## Examples
@@ -18,7 +18,7 @@ template<typename V>
view_type set(const json_pointer& ptr, V&& value); view_type set(const json_pointer& ptr, V&& value);
``` ```
Only an **editable** document (`#!cpp view_mode::editable`, e.g. [`json_editable_document`](../json_editable_document.md)) Only an **editable** document (`#!cpp Editable == true`, e.g. [`json_editable_document`](../json_editable_document.md))
has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`). has `set`; calling it on a read-only `basic_json_document` fails to compile (`#!cpp static_assert`).
1. Replaces the value `target` refers to with `value`. 1. Replaces the value `target` refers to with `value`.
@@ -23,10 +23,6 @@ Linear in [`node_count()`](node_count.md) plus the length of the decoded strings
## Notes ## Notes
The node index of a small document (an estimate of up to 1024 nodes, which is up to about 4 KiB of minified text) is
allocated together with the document's header, so that parsing needs one allocation. That part cannot be released
separately: `shrink_to_fit()` only trims a node index that has its own allocation, and the decoded strings.
!!! warning "Invalidates views" !!! warning "Invalidates views"
Unlike moving the document, `shrink_to_fit()` **invalidates every view taken from this document before the Unlike moving the document, `shrink_to_fit()` **invalidates every view taken from this document before the
@@ -1,32 +1,20 @@
# <small>nlohmann::basic_json_view::</small>basic_json_view # <small>nlohmann::basic_json_view::</small>basic_json_view
```cpp ```cpp
// (1)
basic_json_view() noexcept = default; basic_json_view() noexcept = default;
// (2) only for Mode == view_mode::editable
basic_json_view(const basic_json_view<BasicJsonType, view_mode::read_only>& other) noexcept;
``` ```
1. Creates an invalid (discarded) view: [`type()`](type.md) is `#!cpp value_t::discarded` and Creates an invalid (discarded) view: [`type()`](type.md) is `#!cpp value_t::discarded` and
[`is_discarded()`](is_discarded.md) is `#!cpp true`. [`is_discarded()`](is_discarded.md) is `#!cpp true`.
2. Converts a view of a read-only document into a view of an editable document (implicitly), so that a function
taking a [`json_editable_view`](../json_editable_view.md) accepts both. The result is a view of the same value of
the same read-only document. See [`view_mode`](../view_mode.md) for why there is no conversion the other way.
## Parameters This is the only constructor a caller can use directly. Every other view is obtained from a
`other` (in)
: a view of a read-only document with the same `BasicJsonType`
Apart from these, a view is obtained from a
[`basic_json_document`](../basic_json_document/index.md), via [`root()`](../basic_json_document/root.md) or by [`basic_json_document`](../basic_json_document/index.md), via [`root()`](../basic_json_document/root.md) or by
navigating into a container with [`operator[]`](operator[].md), [`at`](at.md), [`front`](front.md), [`back`](back.md), navigating into a container with [`operator[]`](operator[].md), [`at`](at.md), [`front`](front.md), [`back`](back.md),
[`find`](find.md), or iteration. [`find`](find.md), or iteration.
## Exception safety ## Exception safety
No-throw guarantee: these constructors never throw exceptions. No-throw guarantee: this constructor never throws exceptions.
## Complexity ## Complexity
@@ -57,7 +45,6 @@ placeholder for "no value yet" and later be assigned a real view.
- [is_discarded](is_discarded.md) - return whether the view is invalid - [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 - [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
## Version history ## Version history
+12 -37
View File
@@ -19,36 +19,16 @@ For the types below, the conversion works directly on the flat index -- no `Basi
document's own buffer of decoded strings (see [`get_string()`](get_string.md)) document's own buffer of decoded strings (see [`get_string()`](get_string.md))
- `BasicJsonType` -- equivalent to [`materialize()`](materialize.md) - `BasicJsonType` -- equivalent to [`materialize()`](materialize.md)
- `basic_json_view` -- returns `#!cpp *this` - `basic_json_view` -- returns `#!cpp *this`
- `#!cpp std::vector<U, A>` of the types above -- from an array, element by element, each converted with - `#!cpp std::vector<U, A>` -- from an array, element by element, each converted with `#!cpp get<U>()`; `#!cpp
`#!cpp get<U>()`; `#!cpp std::vector<basic_json_view>` keeps a view of every element instead of a value std::vector<basic_json_view>` keeps a view of every element instead of a value
- `#!cpp std::map<K, basic_json_view, C, A>` and `#!cpp std::unordered_map<K, basic_json_view, H, E, A>`, if `K` is - `#!cpp std::map<K, V, C, A>` and `#!cpp std::unordered_map<K, V, H, E, A>`, if `K` is constructible from a `#!cpp
constructible from a `#!cpp (const char*, std::size_t)` pair -- views of the members of an object; with a repeated (const char*, std::size_t)` pair -- from an object, member by member, each value converted with `#!cpp get<V>()`; with a repeated
key, the view of the *last* member is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) keeps its value key, the *last* value is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) (and
[`materialize()`](materialize.md)) does; `#!cpp std::map<std::string, basic_json_view>` keeps views of the members
instead of values
A default-constructible `T` whose `from_json()` accepts views reads the view directly as well, without building a Every other `T` -- `#!cpp std::list`, `#!cpp std::pair`, `#!cpp std::array`, enumerations, user types with a
`BasicJsonType` value. This covers the library's own conversions of the standard library types -- all other `from_json()`, ... -- is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
`#!cpp std::vector`s, `#!cpp std::map`/`#!cpp std::unordered_map` (also with other key types), `#!cpp std::list`,
`#!cpp std::deque`, `#!cpp std::set`, `#!cpp std::forward_list`, `#!cpp std::valarray`, `#!cpp std::array`, C arrays,
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, enumerations, `#!cpp std::filesystem::path`, ... --
which give the same values as `#!cpp materialize().get<T>()` (from an object, a repeated key keeps the *last* value,
as [`BasicJsonType::parse()`](../basic_json/parse.md) does), as well as the types of the
[`NLOHMANN_DEFINE_TYPE_*`](../macros/nlohmann_define_type_intrusive.md) and
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../macros/nlohmann_define_derived_type.md) macros and the enumerations of
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../macros/nlohmann_json_serialize_enum.md), and any `from_json()` constrained to
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.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.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`
value first (as [`BasicJsonType::parse()`](../basic_json/parse.md) would), and converted from there exactly as value first (as [`BasicJsonType::parse()`](../basic_json/parse.md) would), and converted from there exactly as
[`BasicJsonType::get<T>()`](../basic_json/get.md) would convert it. [`BasicJsonType::get<T>()`](../basic_json/get.md) would convert it.
@@ -73,15 +53,12 @@ Strong guarantee: there are no changes to the view or the document it refers to.
throws for the same JSON type and `T`. throws for the same JSON type and `T`.
- For `#!cpp std::vector<U, A>`: throws `type_error.302` if the value is not an array; otherwise, whatever converting - For `#!cpp std::vector<U, A>`: throws `type_error.302` if the value is not an array; otherwise, whatever converting
an element to `U` throws. an element to `U` throws.
- For `#!cpp std::map`/`#!cpp std::unordered_map` of views: throws `type_error.302` if the value is not an object. - For `#!cpp std::map`/`#!cpp std::unordered_map`: throws `type_error.302` if the value is not an object; otherwise,
- For a `T` whose `from_json()` reads the view: whatever that `from_json()` throws; for the library's conversions and whatever converting a member to the mapped type throws.
the macros, the same exceptions with the same messages as [`BasicJsonType::get<T>()`](../basic_json/get.md) of the
same value.
- For every other `T`: whatever [`materialize().get<T>()`](../basic_json/get.md) throws -- typically `type_error.302`, - For every other `T`: whatever [`materialize().get<T>()`](../basic_json/get.md) throws -- typically `type_error.302`,
or whatever a user-provided `from_json()` throws. or whatever a user-provided `from_json()` throws.
None of the exceptions thrown directly by this function or by a `from_json()` that reads the view (the first four None of the exceptions thrown directly by this function (the first three bullets above) carry a
bullets above) carry a
[`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType` value to point at. An [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType` value to point at. An
exception thrown while converting through `materialize()` (the last bullet) is different: it is thrown by a real exception thrown while converting through `materialize()` (the last bullet) is different: it is thrown by a real
`BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled. `BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled.
@@ -99,8 +76,6 @@ document.
- `#!cpp std::map`/`#!cpp std::unordered_map`: linear in the number of members for walking them, plus the container's - `#!cpp std::map`/`#!cpp std::unordered_map`: linear in the number of members for walking them, plus the container's
own insertion cost per member (logarithmic for `#!cpp std::map`, amortized constant for `#!cpp own insertion cost per member (logarithmic for `#!cpp std::map`, amortized constant for `#!cpp
std::unordered_map`), times the complexity of converting one member to the mapped type. std::unordered_map`), times the complexity of converting one member to the mapped type.
- a `T` whose `from_json()` reads the view: the cost of that `from_json()`; for the macros, linear in the number of
members times the cost of a lookup, plus the cost of converting the members.
- every other `T`: the cost of [`materialize()`](materialize.md) and of destroying the `BasicJsonType` value - every other `T`: the cost of [`materialize()`](materialize.md) and of destroying the `BasicJsonType` value
afterwards, plus that of [`BasicJsonType::get<T>()`](../basic_json/get.md). afterwards, plus that of [`BasicJsonType::get<T>()`](../basic_json/get.md).
@@ -5,13 +5,12 @@ template<typename T>
T& get_to(T& v) const; T& get_to(T& v) const;
``` ```
Converts the value to `T` and stores it in `v`, in one of three ways: Converts the value to `T` and stores it in `v`, in one of two ways:
- For the types that [`get<T>()`](get.md) converts directly -- arithmetic types, `#!cpp bool`, - For the types that [`get<T>()`](get.md) converts directly -- arithmetic types, `#!cpp bool`,
`#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `#!cpp std::basic_string`, `BasicJsonType`, `#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `#!cpp std::basic_string`, `BasicJsonType`,
`basic_json_view`, vectors of these, and maps of views -- `v` is assigned: `#!cpp v = get<T>()`. `basic_json_view`, `#!cpp std::vector`, and `#!cpp std::map`/`#!cpp std::unordered_map` with string keys -- `v` is
- For a type whose `from_json()` accepts views (see [`get<T>()`](get.md)), that `from_json()` is called with the assigned: `#!cpp v = get<T>()`.
view and the existing `v`, without building a `BasicJsonType` value.
- For every other type, the subtree is built into a `BasicJsonType` value and `#!cpp materialize().get_to(v)` is - For every other type, the subtree is built into a `BasicJsonType` value and `#!cpp materialize().get_to(v)` is
called, i.e. `from_json()` is invoked with the existing `v`, exactly as called, i.e. `from_json()` is invoked with the existing `v`, exactly as
[`BasicJsonType::get_to()`](../basic_json/get_to.md) does. [`BasicJsonType::get_to()`](../basic_json/get_to.md) does.
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
template<typename BasicJsonType, view_mode Mode = view_mode::read_only> template<typename BasicJsonType, bool Editable = false>
class basic_json_view; class basic_json_view;
``` ```
@@ -37,7 +37,7 @@ Moving the document itself does not invalidate its views: the index is heap-allo
`#!cpp operator ValueType()`), and no `#!cpp get_ptr()` or `#!cpp get_ref()`; see [`get`](get.md). `#!cpp operator ValueType()`), and no `#!cpp get_ptr()` or `#!cpp get_ref()`; see [`get`](get.md).
`basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an `basic_json_view` itself is always read-only -- it never has a `set` or `push_back` of its own. A view of an
**editable** document (`#!cpp view_mode::editable`) sees every edit made through **editable** document (`#!cpp Editable == true`) sees every edit made through
[`basic_json_document::set`](../basic_json_document/set.md) and [`basic_json_document::set`](../basic_json_document/set.md) and
[`basic_json_document::push_back`](../basic_json_document/push_back.md): once a value is changed, every view that [`basic_json_document::push_back`](../basic_json_document/push_back.md): once a value is changed, every view that
still refers to it -- including ones taken before the change -- reads the new value. See still refers to it -- including ones taken before the change -- reads the new value. See
@@ -49,10 +49,9 @@ still refers to it -- including ones taken before the change -- reads the new va
: a specialization of [`basic_json`](../basic_json/index.md), matching the : a specialization of [`basic_json`](../basic_json/index.md), matching the
[`basic_json_document`](../basic_json_document/index.md) the view was taken from. [`basic_json_document`](../basic_json_document/index.md) the view was taken from.
`Mode` `Editable`
: a [`view_mode`](../view_mode.md): whether the view is of an editable document, matching the : whether the view is of an editable document, matching the [`basic_json_document`](../basic_json_document/index.md)
[`basic_json_document`](../basic_json_document/index.md) it was taken from (optional, `#!cpp view_mode::read_only` by it was taken from (optional, `#!cpp false` by default). See [Edits](../basic_json_document/index.md#edits).
default). See [Edits](../basic_json_document/index.md#edits).
## Specializations ## Specializations
@@ -64,7 +63,6 @@ still refers to it -- including ones taken before the change -- reads the new va
## Member types ## Member types
- **basic_json_type** - `BasicJsonType` (it also marks the class for [`is_json_view`](../is_json_view.md))
- **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md) - **value_t** - the JSON type enumeration, see [`basic_json::value_t`](../basic_json/value_t.md)
- **string_t**, **number_integer_t**, **number_unsigned_t**, **number_float_t**, **json_pointer** - the corresponding - **string_t**, **number_integer_t**, **number_unsigned_t**, **number_float_t**, **json_pointer** - the corresponding
member types of `BasicJsonType` member types of `BasicJsonType`
@@ -6,7 +6,7 @@ class byte_container_with_subtype : public BinaryType;
``` ```
This type extends the template parameter `BinaryType` provided to [`basic_json`](../basic_json/index.md) with a subtype This type extends the template parameter `BinaryType` provided to [`basic_json`](../basic_json/index.md) with a subtype
used by BSON, CBOR, and MessagePack. This type exists so that the user does not have to specify a type themselves with a used by BSON and MessagePack. This type exists so that the user does not have to specify a type themselves with a
specific naming scheme in order to override the binary type. specific naming scheme in order to override the binary type.
## Template parameters ## Template parameters
-56
View File
@@ -1,56 +0,0 @@
# <small>nlohmann::</small>is_json_view
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp
template<typename T>
struct is_json_view;
```
A type trait: `#!cpp is_json_view<T>::value` is `#!cpp true` if `T` is a [`basic_json_view`](basic_json_view/index.md)
of any `BasicJsonType` and any [`view_mode`](view_mode.md) -- [`json_view`](json_view.md),
[`json_editable_view`](json_editable_view.md), [`ordered_json_view`](ordered_json_view.md), ... -- and
`#!cpp false` otherwise, for [`basic_json`](basic_json/index.md) as well.
It is meant for constraining templates that accept views, in particular a `from_json()` that should read views
directly: [`get<T>()`](basic_json_view/get.md) on a view calls a `from_json()` with the view only if that `from_json()`
accepts views but not arbitrary types.
## Template parameters
`T`
: the type to check (without cv-qualifiers or references)
## Notes
A view is recognized by its member type `basic_json_type` (a [`basic_json`](basic_json/index.md) specialization) and
its [`materialize()`](basic_json_view/materialize.md) returning that type, so the trait needs no declaration of the
view class.
## Examples
??? example
The example below shows the trait, and a `from_json()` constrained to `json` values and views: `get<T>()` on a view
reads the view directly.
```cpp
--8<-- "examples/is_json_view.cpp"
```
Output:
```
--8<-- "examples/is_json_view.output"
```
## See also
- [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.md#writing-generic-code) - functions and `from_json()` for views
## Version history
Added in version 3.13.0.
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_document = basic_json_document<json, view_mode::read_only>; using json_document = basic_json_document<json>;
``` ```
This type is a [`basic_json_document`](basic_json_document/index.md) of the default [`json`](json.md) This type is a [`basic_json_document`](basic_json_document/index.md) of the default [`json`](json.md)
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_editable_document = basic_json_document<json, view_mode::editable>; using json_editable_document = basic_json_document<json, true>;
``` ```
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the default This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the default
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_editable_view = basic_json_view<json, view_mode::editable>; using json_editable_view = basic_json_view<json, true>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a
@@ -18,8 +18,7 @@ json_pointer& operator/=(std::size_t array_idx)
## Parameters ## Parameters
`ptr` (in) `ptr` (in)
: JSON pointer to append; may be the JSON pointer itself (`#!cpp p /= p`), which then appends a copy of its : JSON pointer to append
reference tokens
`token` (in) `token` (in)
: reference token to append : reference token to append
@@ -69,7 +68,6 @@ json_pointer& operator/=(std::size_t array_idx)
## Version history ## Version history
1. Added in version 3.6.0. Appending a JSON pointer to itself no longer results in undefined behavior in version 1. Added in version 3.6.0.
3.13.0.
2. Added in version 3.6.0. Changed type of `token` to `string_t` in version 3.11.0. 2. Added in version 3.6.0. Changed type of `token` to `string_t` in version 3.11.0.
3. Added in version 3.6.0. 3. Added in version 3.6.0.
@@ -12,8 +12,7 @@ Parent of this JSON pointer; in case this JSON pointer is the root, the root its
## Exception safety ## Exception safety
Strong exception safety: if an exception occurs, the original value stays intact. The function is not `#!cpp noexcept` No-throw guarantee: this function never throws exceptions.
and allocates a new JSON pointer, so it can throw `#!cpp std::bad_alloc`; it throws no library exceptions.
## Complexity ## Complexity
+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 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 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: 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 ## 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 ## Examples
??? example ??? example "Example: (1) the SAX interface"
The example below shows how the SAX interface is used. The example below shows how the SAX interface is used.
@@ -39,12 +46,29 @@ Whether parsing should proceed (**must return `#!cpp false`**).
--8<-- "examples/sax_parse.output" --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 ## See also
- [sax_parse](../basic_json/sax_parse.md) - SAX parser - [sax_parse](../basic_json/sax_parse.md) - SAX parser
- [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without - [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without
exceptions exceptions
- [Error Recovery](../../features/parsing/error_recovery.md) - the article on recovering from parse errors
## Version history ## Version history
- Added in version 3.2.0. - 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
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using json_view = basic_json_view<json, view_mode::read_only>; using json_view = basic_json_view<json>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`json_document`](json_document.md). This type is a [`basic_json_view`](basic_json_view/index.md) of a value of a [`json_document`](json_document.md).
+2
View File
@@ -43,6 +43,8 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers - [**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_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 - [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
- [**JSON_VIEW_NO_SIMD**](json_view_no_simd.md) - use only portable code in the parser of `json_view.hpp`
- [**JSON_VIEW_USE_SSSE3**](json_view_use_ssse3.md) - validate non-ASCII strings with SSSE3 in the parser of `json_view.hpp`
## Library version ## Library version
+1 -3
View File
@@ -5,8 +5,7 @@
``` ```
When defined, the library uses only portable C++ code where it would otherwise use vector instructions: when scanning When defined, the library uses only portable C++ code where it would otherwise use vector instructions: when scanning
strings during parsing, and when writing floating-point numbers during serialization. This includes the parser of strings during parsing, and when writing floating-point numbers during serialization.
[`basic_json_document`](../basic_json_document/index.md) (`<nlohmann/json_view.hpp>`).
The macro is an escape hatch, for example for toolchains that cannot compile the vector code, for sanitizers or The macro is an escape hatch, for example for toolchains that cannot compile the vector code, for sanitizers or
analyzers that do not understand it, or to compare the speed of both variants. It does not change any result: values, analyzers that do not understand it, or to compare the speed of both variants. It does not change any result: values,
@@ -78,7 +77,6 @@ By default, `#!cpp JSON_NO_SIMD` is not defined, and vector instructions are use
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation - [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
- [**JSON_DISABLE_STREAM_BULK_SCAN**](json_disable_stream_bulk_scan.md) - read a `std::istream` one character at a time - [**JSON_DISABLE_STREAM_BULK_SCAN**](json_disable_stream_bulk_scan.md) - read a `std::istream` one character at a time
- [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance) - [Parsing: input sources and performance](../../features/parsing/index.md#input-sources-and-performance)
- [json_view](../../features/json_view.md) - the zero-copy view, whose parser the macro also affects
## Version history ## Version history
@@ -0,0 +1,52 @@
# JSON_VIEW_NO_SIMD
```cpp
#define JSON_VIEW_NO_SIMD
```
When defined, the parser of [`basic_json_document`](../basic_json_document/index.md) (`<nlohmann/json_view.hpp>`)
uses only portable C++ to scan strings. By default, it scans long runs of string bytes 16 at a time with NEON on
AArch64 (with GCC and Clang) and SSE2 on x86-64, which are part of the baseline instruction sets of these
architectures, and validates non-ASCII text with NEON (or SSSE3, see
[`JSON_VIEW_USE_SSSE3`](json_view_use_ssse3.md)).
The same input is accepted or rejected either way, with the same values, and errors are reported the same way; only
the speed differs. The macro exists for platforms whose compilers lack the intrinsics headers, and to test the portable
code.
!!! note "ABI compatibility"
The macro selects between two definitions of the same inline functions. Whether it is defined is encoded in the
[namespace](../../features/namespace.md) (tag `_vns`), resulting in distinct symbol names. Translation units
compiled with and without it can therefore be linked into the same program without One Definition Rule (ODR)
violations, but they cannot exchange instances of library types.
## Default definition
By default, `#!cpp JSON_VIEW_NO_SIMD` is not defined, and the vector code is used where available.
```cpp
#undef JSON_VIEW_NO_SIMD
```
## Examples
??? example
The code below uses the portable string scanning of the view.
```cpp
#define JSON_VIEW_NO_SIMD
#include <nlohmann/json_view.hpp>
...
```
## See also
- [JSON_VIEW_USE_SSSE3](json_view_use_ssse3.md) - validate non-ASCII strings with SSSE3 on x86-64
- [json_view](../../features/json_view.md) - the zero-copy view
## Version history
- Added in version 3.13.0.
@@ -0,0 +1,53 @@
# JSON_VIEW_USE_SSSE3
```cpp
#define JSON_VIEW_USE_SSSE3
```
When defined on x86-64, the parser of [`basic_json_document`](../basic_json_document/index.md)
(`<nlohmann/json_view.hpp>`) validates non-ASCII text in strings with SSSE3 without asking the CPU first.
By default, the parser checks once at run time whether the CPU has SSSE3 (all x86-64 CPUs since about 2011 have it)
and then validates non-ASCII text 16 bytes at a time, using the "lookup4" algorithm of
[simdjson](https://github.com/simdjson/simdjson); on CPUs without SSSE3, it validates one UTF-8 sequence at a time.
The vector check is compiled for SSSE3 with a function attribute (GCC 4.9 and later, Clang), so this needs no compiler
option. With MSVC, the check uses `__cpuid`. On AArch64, the vector check uses NEON and is always on.
Define the macro only together with a compiler option that enables SSSE3 (e.g. `-mssse3`, or `-march=` with a CPU that
has it), and only for programs that run on such CPUs. It saves the check of the CPU, which costs little. The same
input is accepted or rejected either way; only the speed of non-ASCII text differs.
!!! note "ABI compatibility"
The macro selects between two definitions of the same inline functions. Whether it is defined is encoded in the
[namespace](../../features/namespace.md) (tag `_vssse3`, on every architecture), resulting in distinct symbol
names. Translation units compiled with and without it can therefore be linked into the same program without One
Definition Rule (ODR) violations, but they cannot exchange instances of library types.
## Default definition
By default, `#!cpp JSON_VIEW_USE_SSSE3` is not defined.
```cpp
#undef JSON_VIEW_USE_SSSE3
```
## Examples
??? example
With CMake, for a program that only runs on CPUs with SSSE3:
```cmake
target_compile_definitions(your_target PRIVATE JSON_VIEW_USE_SSSE3)
target_compile_options(your_target PRIVATE -mssse3)
```
## See also
- [JSON_VIEW_NO_SIMD](json_view_no_simd.md) - use only portable code in the view's parser
- [JSON_USE_SIMDUTF](json_use_simdutf.md) - validate UTF-8 with simdutf in `basic_json`'s parser
## Version history
- Added in version 3.13.0.
@@ -147,13 +147,6 @@ void to_json(BasicJsonType& j, const B& b) {
See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and See Implementation limits for [NLOHMANN_DEFINE_TYPE_INTRUSIVE](nlohmann_define_type_intrusive.md) and
[NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md), respectively. [NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE](nlohmann_define_type_non_intrusive.md), respectively.
!!! info "Views"
The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s
(`<nlohmann/json_view.hpp>`): [`get<T>()`](../basic_json_view/get.md) on a view looks the members up in the
view, without building a `json` value first. The base part is read from the view as well if the base's
`from_json()` accepts views, and from [`materialize()`](../basic_json_view/materialize.md) otherwise.
## Examples ## Examples
??? example "Example: (1) NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE" ??? example "Example: (1) NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE"
@@ -199,5 +192,3 @@ void to_json(BasicJsonType& j, const B& b) {
6. Added in version 3.12.0. 6. Added in version 3.12.0.
All six macros were changed to allow an empty member list in version 3.13.0. All six macros were changed to allow an empty member list in version 3.13.0.
The generated `from_json()` reads the views of `json_document`s since version 3.13.0.
@@ -81,12 +81,6 @@ See the examples below for the concrete generated code.
that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by
hand, building/reading a `json::array()` of the members in order. hand, building/reading a `json::array()` of the members in order.
!!! info "Views"
The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s
(`<nlohmann/json_view.hpp>`): [`get<T>()`](../basic_json_view/get.md) on a view looks the members up in the
view, without building a `json` value first.
## Examples ## Examples
??? example "Example: (1) NLOHMANN_DEFINE_TYPE_INTRUSIVE" ??? example "Example: (1) NLOHMANN_DEFINE_TYPE_INTRUSIVE"
@@ -195,5 +189,3 @@ See the examples below for the concrete generated code.
All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0, All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0,
and to allow an empty member list in version 3.13.0. and to allow an empty member list in version 3.13.0.
The generated `from_json()` reads the views of `json_document`s since version 3.13.0.
@@ -80,12 +80,6 @@ See the examples below for the concrete generated code.
that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by
hand, building/reading a `json::array()` of the members in order. hand, building/reading a `json::array()` of the members in order.
!!! info "Views"
The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s
(`<nlohmann/json_view.hpp>`): [`get<T>()`](../basic_json_view/get.md) on a view looks the members up in the
view, without building a `json` value first.
## Examples ## Examples
??? example "Example: (1) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE" ??? example "Example: (1) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE"
@@ -194,5 +188,3 @@ See the examples below for the concrete generated code.
All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0, All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0,
and to allow an empty member list in version 3.13.0. and to allow an empty member list in version 3.13.0.
The generated `from_json()` reads the views of `json_document`s since version 3.13.0.
@@ -39,6 +39,6 @@ the library.
## Version history ## Version history
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in - Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, `_ekmo`, `_dsbs`, `_dufio`, and `_ns` in version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, `_ekmo`, `_dsbs`, `_dufio`, `_ns`, `_vns`,
version 3.13.0; see and `_vssse3` in version 3.13.0; see
[`nlohmann` Namespace](../../features/namespace.md#structure). [`nlohmann` Namespace](../../features/namespace.md#structure).
@@ -45,12 +45,6 @@ inline void from_json(const BasicJsonType& j, type& e);
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
with the converted keys. Such maps can be read from both forms. with the converted keys. Such maps can be read from both forms.
!!! info "Views"
The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s
(`<nlohmann/json_view.hpp>`), comparing the view with the entries of the mapping, without building a `json` value
first.
## Examples ## Examples
??? example "Example: Basic usage" ??? example "Example: Basic usage"
@@ -94,5 +88,3 @@ inline void from_json(const BasicJsonType& j, type& e);
## Version history ## Version history
Added in version 3.4.0. Added in version 3.4.0.
The generated `from_json()` reads the views of `json_document`s since version 3.13.0.
@@ -48,12 +48,6 @@ inline void from_json(const BasicJsonType& j, type& e);
Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects Define [`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](json_use_objects_for_enum_keyed_maps.md) to store them as objects
with the converted keys. Such maps can be read from both forms. with the converted keys. Such maps can be read from both forms.
!!! info "Views"
The generated `from_json()` also reads the views of [`json_document`](../json_document.md)s
(`<nlohmann/json_view.hpp>`), comparing the view with the entries of the mapping, without building a `json` value
first.
## Examples ## Examples
??? example "Example: Basic usage" ??? example "Example: Basic usage"
+1 -2
View File
@@ -37,8 +37,7 @@ the stream `o`
1. Throws [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON 1. Throws [`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON
value is not UTF-8 encoded. Note that unlike the [`dump`](basic_json/dump.md) member functions, no `error_handler` value is not UTF-8 encoded. Note that unlike the [`dump`](basic_json/dump.md) member functions, no `error_handler`
can be set. can be set.
2. Throws no library exceptions. As the string representation is built with [`to_string`](json_pointer/to_string.md), 2. None.
`#!cpp std::bad_alloc` is possible.
## Complexity ## Complexity
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_document = basic_json_document<ordered_json, view_mode::read_only>; using ordered_json_document = basic_json_document<ordered_json>;
``` ```
This type is a [`basic_json_document`](basic_json_document/index.md) of the [`ordered_json`](ordered_json.md) This type is a [`basic_json_document`](basic_json_document/index.md) of the [`ordered_json`](ordered_json.md)
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_editable_document = basic_json_document<ordered_json, view_mode::editable>; using ordered_json_editable_document = basic_json_document<ordered_json, true>;
``` ```
This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the This type is an **editable** [`basic_json_document`](basic_json_document/index.md) of the
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_editable_view = basic_json_view<ordered_json, view_mode::editable>; using ordered_json_editable_view = basic_json_view<ordered_json, true>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
+1 -1
View File
@@ -3,7 +3,7 @@
<small>Defined in header `<nlohmann/json_view.hpp>`</small> <small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp ```cpp
using ordered_json_view = basic_json_view<ordered_json, view_mode::read_only>; using ordered_json_view = basic_json_view<ordered_json>;
``` ```
This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an This type is a [`basic_json_view`](basic_json_view/index.md) of a value of an
-89
View File
@@ -1,89 +0,0 @@
# <small>nlohmann::</small>view_mode
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
```cpp
enum class view_mode : std::uint8_t
{
read_only,
editable
};
```
The second template parameter of [`basic_json_document`](basic_json_document/index.md) and
[`basic_json_view`](basic_json_view/index.md): whether a document can be changed after parsing, and whether a view is
of such a document.
`read_only`
: the default: the document is parsed once and then only read. It carries none of the bookkeeping edits need, and
calling [`set`](basic_json_document/set.md), [`push_back`](basic_json_document/push_back.md),
[`insert`](basic_json_document/insert.md), or [`erase`](basic_json_document/erase.md) on it fails to compile.
`editable`
: the document also offers [`set`](basic_json_document/set.md), [`push_back`](basic_json_document/push_back.md),
[`insert`](basic_json_document/insert.md), and [`erase`](basic_json_document/erase.md); its views see every edit,
see [Edits](basic_json_document/index.md#edits).
The shortcuts name both modes: [`json_document`](json_document.md) and [`json_view`](json_view.md) are read-only,
[`json_editable_document`](json_editable_document.md) and [`json_editable_view`](json_editable_view.md) are editable
(and likewise for `ordered_json`).
## Notes
The mode is a compile-time property of the document's and the view's type, not a setting of a call. A read-only view
reads a container in place, while a view of an editable document follows the links that edits leave behind. A view of
a read-only document converts implicitly to a view of an editable document of the same `BasicJsonType`: a read-only
document has no edits, so the converted view reads it like an editable document before its first edit (at the cost of
one check per access). There is no conversion the other way, because a read-only view of an editable document would
read wrong values after the next edit.
A function that takes a [`json_editable_view`](json_editable_view.md) therefore accepts the views of both modes. A
function template deduces the mode from its argument instead, without conversions, so it names the modes it accepts in
its signature:
```cpp
// views of editable documents only
template<typename BasicJsonType>
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
// views of read-only documents only
template<typename BasicJsonType>
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
// views of either document type
template<typename BasicJsonType, view_mode Mode>
void f(basic_json_view<BasicJsonType, Mode> v);
```
A function that takes a [`json_view`](json_view.md) accepts neither an [`ordered_json_view`](ordered_json_view.md)
nor a [`json_editable_view`](json_editable_view.md). Views of the two modes compare with
[`operator==`](basic_json_view/operator_eq.md).
## Examples
??? example
The example below shows a function template that accepts the views of both modes, one that accepts only views of
editable documents, and a function taking a `json_editable_view` that also accepts a view of a read-only document.
```cpp
--8<-- "examples/view_mode.cpp"
```
Output:
```
--8<-- "examples/view_mode.output"
```
## See also
- [basic_json_document](basic_json_document/index.md) - a parsed JSON text
- [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.md#writing-generic-code) - functions that accept views
## Version history
Added in version 3.13.0.
@@ -1,33 +0,0 @@
#include <iomanip>
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
void print_hex(const char* label, const std::vector<std::uint8_t>& v)
{
std::cout << label << ':';
for (auto b : v)
{
std::cout << ' ' << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(b);
}
std::cout << std::dec << std::setfill(' ') << '\n';
}
int main()
{
// a binary value
json j = json::binary({0xCA, 0xFE, 0xBA, 0xBE});
// Draft 2 (the default) writes an array of integers
auto v2 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft2);
print_hex("draft2", v2);
// Draft 3 writes an optimized binary array
auto v3 = json::to_bjdata(j, true, true, json::bjdata_version_t::draft3);
print_hex("draft3", v3);
// reading yields an array for Draft 2 and a binary value for Draft 3
std::cout << json::from_bjdata(v2) << '\n';
std::cout << json::from_bjdata(v3) << '\n';
}
@@ -1,4 +0,0 @@
draft2: 5b 24 55 23 69 04 ca fe ba be
draft3: 5b 24 42 23 69 04 ca fe ba be
[202,254,186,190]
{"bytes":[202,254,186,190],"subtype":null}
@@ -1,34 +0,0 @@
#include <iostream>
#include <type_traits>
#include <nlohmann/json_view.hpp>
using nlohmann::json;
namespace ns
{
struct person
{
std::string name;
int age = 0;
};
// reads json values and views (of any document type), but nothing else
template < typename J, typename std::enable_if < nlohmann::is_json_view<J>::value || std::is_same<J, json>::value, int >::type = 0 >
void from_json(const J& j, person& p)
{
p.name = j.at("name").template get<std::string>();
p.age = j.at("age").template get<int>();
}
} // namespace ns
int main()
{
std::cout << std::boolalpha
<< nlohmann::is_json_view<nlohmann::json_view>::value << ' '
<< nlohmann::is_json_view<nlohmann::ordered_json_editable_view>::value << ' '
<< nlohmann::is_json_view<json>::value << '\n';
const nlohmann::json_document doc = nlohmann::json_document::parse(R"({"name": "Ada", "age": 36})");
const auto p = doc.root().get<ns::person>(); // reads the view, no json value is built
std::cout << p.name << ' ' << p.age << '\n';
}
@@ -1,2 +0,0 @@
true true false
Ada 36
@@ -5,7 +5,7 @@ using nlohmann::ordered_json_editable_document;
int main() int main()
{ {
// ordered_json_editable_document is basic_json_document<nlohmann::ordered_json, nlohmann::view_mode::editable> // ordered_json_editable_document is basic_json_document<nlohmann::ordered_json, true>
ordered_json_editable_document doc = ordered_json_editable_document::parse(R"({"z": 1, "a": 2, "m": 3})"); ordered_json_editable_document doc = ordered_json_editable_document::parse(R"({"z": 1, "a": 2, "m": 3})");
doc.set(doc.root(), "b", 4); // set() always appends a new member at the end doc.set(doc.root(), "b", 4); // set() always appends a new member at the end
@@ -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
}
-36
View File
@@ -1,36 +0,0 @@
#include <iostream>
#include <nlohmann/json_view.hpp>
using nlohmann::basic_json_view;
using nlohmann::view_mode;
// accepts the views of read-only and of editable documents, of json and of ordered_json
template<typename BasicJsonType, view_mode Mode>
std::size_t count_members(basic_json_view<BasicJsonType, Mode> v)
{
return v.is_object() ? v.size() : 0;
}
// accepts only the views of editable documents
template<typename BasicJsonType>
bool is_edited(basic_json_view<BasicJsonType, view_mode::editable> v)
{
return v.is_string() && v.get_string() == "edited";
}
// a function (no template) taking a json_editable_view also accepts the view of a read-only document
std::size_t size_of(nlohmann::json_editable_view v)
{
return v.size();
}
int main()
{
const nlohmann::json_document doc = nlohmann::json_document::parse(R"({"a": 1, "b": 2})");
nlohmann::json_editable_document edoc = nlohmann::json_editable_document::parse(R"({"a": 1})");
edoc.set(edoc.root()["a"], "edited");
std::cout << count_members(doc.root()) << ' ' << count_members(edoc.root()) << '\n';
std::cout << std::boolalpha << is_edited(edoc.root()["a"]) << '\n';
std::cout << size_of(doc.root()) << ' ' << size_of(edoc.root()) << '\n';
}
@@ -1,3 +0,0 @@
2 1
true
2 1
@@ -2,9 +2,9 @@
The [BJData format](https://neurojson.org) was derived from and improved upon The [BJData format](https://neurojson.org) was derived from and improved upon
[Universal Binary JSON(UBJSON)](https://ubjson.org) specification (Draft 12). Specifically, it introduces an optimized [Universal Binary JSON(UBJSON)](https://ubjson.org) specification (Draft 12). Specifically, it introduces an optimized
array container for efficient storage of N-dimensional packed arrays (**ND-arrays**); it also adds four new type markers - array container for efficient storage of N-dimensional packed arrays (**ND-arrays**); it also adds 5 new type markers -
`[u] - uint16`, `[m] - uint32`, `[M] - uint64`, and `[h] - float16` - to unambiguously map common binary numeric types `[u] - uint16`, `[m] - uint32`, `[M] - uint64`, `[h] - float16` and `[B] - byte` - to unambiguously map common binary
(Draft 3 adds `[B] - byte`); furthermore, it uses little-endian (LE) to store all numerics instead of big-endian (BE) as in UBJSON to numeric types; furthermore, it uses little-endian (LE) to store all numerics instead of big-endian (BE) as in UBJSON to
avoid unnecessary conversions on commonly available platforms. avoid unnecessary conversions on commonly available platforms.
Compared to other binary JSON-like formats such as MessagePack and CBOR, both BJData and UBJSON demonstrate a rare Compared to other binary JSON-like formats such as MessagePack and CBOR, both BJData and UBJSON demonstrate a rare
@@ -15,30 +15,11 @@ simple processing.
!!! abstract "References" !!! abstract "References"
- [BJData Specification (Draft 3)](https://neurojson.org/bjdata/draft3) - [BJData Specification](https://neurojson.org/bjdata/draft2)
- [BJData Specification (Draft 2)](https://neurojson.org/bjdata/draft2)
!!! info "Supported drafts"
The library supports BJData Draft 2 (the default) and Draft 3, except for column-major N-dimensional arrays (see
below). The draft used for writing is selected with the
[`version` parameter](../../api/basic_json/bjdata_version_t.md) of
[`to_bjdata`](../../api/basic_json/to_bjdata.md); [`from_bjdata`](../../api/basic_json/from_bjdata.md) has no such
parameter and reads the markers of both drafts.
!!! warning "Draft 4 and column-major ND-arrays are not supported"
BJData Draft 4 (April 2026) adds structure-of-arrays (SoA) containers (`[$` or `{$` followed by a schema object)
and the extension type `E`. `from_bjdata` does not implement them and throws
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) for such input; `to_bjdata` never writes
them.
Likewise, column-major ND-arrays of Draft 3 (the `#` marker followed by an array that wraps the dimension vector)
are rejected with [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
## Byte layout ## Byte layout
BJData follows the structure of UBJSON: a one-byte, human-readable ASCII *type marker* followed by the payload of that type, with strings prefixed by a length that is itself an integer value. In contrast to UBJSON, numbers are stored in little-endian byte order, and additional markers cover unsigned 16/32/64-bit integers, half-precision floats, and bytes. Containers can be written unoptimized (opened with `[` or `{`, closed with `]` or `}`) or optimized with a count (`#`), optionally preceded by a declared element type (`$`). BJData extends the optimized form: the count can be a list of dimensions, which stores an N-dimensional packed array as a flat sequence of values without any per-element markers. BJData follows the structure of UBJSON: a one-byte, human-readable ASCII *type marker* followed by the payload of that type, with strings prefixed by a length that is itself an integer value. In contrast to UBJSON, numbers are stored in little-endian byte order, and additional markers cover unsigned 16/32/64-bit integers, half-precision floats, and bytes. Containers can be written unoptimized (opened with `[` or `{`, closed with `]` or `}`) or optimized with a declared element type (`$`) and/or count (`#`). BJData extends the optimized form: the count can be a list of dimensions, which stores an N-dimensional packed array as a flat sequence of values without any per-element markers.
Consider the following JSON value (shown formatted for readability): Consider the following JSON value (shown formatted for readability):
@@ -74,7 +55,7 @@ Consider the following JSON value (shown formatted for readability):
The library uses the following mapping from JSON values types to BJData types according to the BJData specification: The library uses the following mapping from JSON values types to BJData types according to the BJData specification:
| JSON value type | value/range | BJData type | marker | | JSON value type | value/range | BJData type | marker |
|-----------------|-------------------------------------------|-------------|-------------------------------------------------------------------------| |-----------------|-------------------------------------------|----------------|--------|
| null | `null` | null | `Z` | | null | `null` | null | `Z` |
| boolean | `true` | true | `T` | | boolean | `true` | true | `T` |
| boolean | `false` | false | `F` | | boolean | `false` | false | `F` |
@@ -101,7 +82,7 @@ The library uses the following mapping from JSON values types to BJData types ac
| string | *with shortest length indicator* | string | `S` | | string | *with shortest length indicator* | string | `S` |
| array | *see notes on optimized format/ND-array* | array | `[` | | array | *see notes on optimized format/ND-array* | array | `[` |
| object | *see notes on optimized format* | map | `{` | | object | *see notes on optimized format* | map | `{` |
| binary | *see notes on binary values* | array | `[$B` (Draft 3 with `use_size` and `use_type`), `[U`, or `[B` otherwise | | binary | *see notes on binary values* | array | `[$B` |
!!! success "Complete mapping" !!! success "Complete mapping"
@@ -126,16 +107,10 @@ The library uses the following mapping from JSON values types to BJData types ac
!!! info "Unused BJData markers" !!! info "Unused BJData markers"
The following markers are not written: The following markers are not used in the conversion:
- `N`: no-op values are not created (they are skipped when reading). - `Z`: no-op values are not created.
- `C`: single-character strings are serialized with `S` markers (`C` is read, and written for the elements of - `C`: single-byte strings are serialized with `S` markers.
ND-arrays with `"_ArrayType_": "char"`).
- `h`: floating-point numbers are written as `D`, not as float16 (`h` is read).
- `B`: only written in Draft 3 mode.
- `d` is only written for `single` ND-arrays or if `number_float_t` is `float`; `H` is only written for integers
wider than 64 bits (custom `number_integer_t`/`number_unsigned_t`). An unsigned integer above 9223372036854775807
is written as `M`.
!!! info "NaN/infinity handling" !!! info "NaN/infinity handling"
@@ -167,7 +142,7 @@ The library uses the following mapping from JSON values types to BJData types ac
BJData extends UBJSON's optimized array **size** marker to support ND-arrays of uniform numerical data types BJData extends UBJSON's optimized array **size** marker to support ND-arrays of uniform numerical data types
(referred to as *packed arrays*). For example, the 2-D `uint8` integer array `[[1,2],[3,4],[5,6]]`, stored as nested (referred to as *packed arrays*). For example, the 2-D `uint8` integer array `[[1,2],[3,4],[5,6]]`, stored as nested
optimized array in UBJSON `[ [$U#i2 1 2 [$U#i2 3 4 [$U#i2 5 6 ]`, can be further compressed in BJData to optimized array in UBJSON `[ [$U#i2 1 2 [$U#i2 3 4 [$U#i2 5 6 ]`, can be further compressed in BJData to
`[$U#[$i#i2 3 2 1 2 3 4 5 6` or `[$U#[i3 i2] 1 2 3 4 5 6`. `[$U#[$i#i2 2 3 1 2 3 4 5 6` or `[$U#[i2 i3] 1 2 3 4 5 6`.
To maintain type and size information, ND-arrays are converted to JSON objects following the **annotated array To maintain type and size information, ND-arrays are converted to JSON objects following the **annotated array
format** (defined in the [JData specification (Draft 3)][JDataAAFmt]), when parsed using format** (defined in the [JData specification (Draft 3)][JDataAAFmt]), when parsed using
@@ -177,14 +152,11 @@ The library uses the following mapping from JSON values types to BJData types ac
```json ```json
{ {
"_ArrayType_": "uint8", "_ArrayType_": "uint8",
"_ArraySize_": [3,2], "_ArraySize_": [2,3],
"_ArrayData_": [1,2,3,4,5,6] "_ArrayData_": [1,2,3,4,5,6]
} }
``` ```
ND-arrays with the element type `h` (float16) are returned with `"_ArrayType_": "half"`. `to_bjdata` does not pack
`"half"` objects; they are written as regular objects, which read back as an equal value.
Likewise, when a JSON object in the above form is serialized using Likewise, when a JSON object in the above form is serialized using
[`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array. [`to_bjdata`](../../api/basic_json/to_bjdata.md), it is automatically converted into a compact BJData ND-array.
@@ -195,40 +167,34 @@ The library uses the following mapping from JSON values types to BJData types ac
object; otherwise it is serialized as a regular JSON object, so the annotation is never lost in a round trip. This requires object; otherwise it is serialized as a regular JSON object, so the annotation is never lost in a round trip. This requires
all of the following: all of the following:
- the object has exactly the three keys `"_ArrayType_"`, `"_ArraySize_"`, and `"_ArrayData_"` (any other key, e.g.,
`"_ArrayOrder_"`, makes it a regular object),
- `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`, - `"_ArrayType_"` is one of `uint8`, `int8`, `uint16`, `int16`, `uint32`, `int32`, `uint64`, `int64`, `single`,
`double`, `char`, or (only with `version = bjdata_version_t::draft3`, since the `B` marker is not part of Draft 2) `double`, `char`, or `byte`,
`byte`; the names are case-sensitive,
- `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length, - `"_ArraySize_"` is an array, since the dimensions are written as the ND-array header's length,
- `"_ArraySize_"` has at least two entries and is not a two-entry row vector (`[1,N]`), since other shapes are - `"_ArraySize_"` has at least two entries and is not a 1×N row vector (first entry 1), since other shapes are
parsed back as a regular array (`[1,2,2]` is packed, `[1,4]` is not), parsed back as a regular array,
- every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`, - every entry of `"_ArraySize_"` is a positive integer, and their product is representable as a `std::size_t`,
- `"_ArrayData_"` is an array holding exactly that many elements, and - `"_ArrayData_"` is an array holding exactly that many elements, and
- every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, an - every element of `"_ArrayData_"` is a number of the kind named by `"_ArrayType_"`: for the integer types, a
integer (a floating-point number such as `1.0` does not qualify) that fits the named width (for `char`, 0..127); value that fits the named width; for `double`, any value; for `single`, a value that survives narrowing to
for `double`, a floating-point number (an integer such as `1` does not qualify); for `single`, a floating-point `float` and back without change (for instance, `0.1` does not, since it is not exactly representable as
number that survives narrowing to `float` and back without change (for instance, `0.1` does not, since it is not `float`).
exactly representable as `float`).
With `ordered_json`, an annotated object read from BJData has its keys in the order `"_ArrayType_"`, An annotated object is always read back with its keys in the order shown above, `"_ArrayType_"`, `"_ArraySize_"`,
`"_ArraySize_"`, `"_ArrayData_"`; with `json`, they are sorted like those of any other object. `"_ArrayData_"`, regardless of the order the ND-array's header stores them in on the wire. This matters for
`ordered_json`, whose comparison takes key order into account.
The current version of this library does not yet support automatic detection of and conversion from a nested JSON The current version of this library does not yet support automatic detection of and conversion from a nested JSON
array input to a BJData ND-array. array input to a BJData ND-array.
[JDataAAFmt]: https://github.com/NeuroJSON/jdata/blob/Draft-3/JData_specification.md#annotated-storage-of-n-d-arrays [JDataAAFmt]: https://github.com/NeuroJSON/jdata/blob/master/JData_specification.md#annotated-storage-of-n-d-arrays
!!! info "Restrictions in optimized data types for arrays and objects" !!! info "Restrictions in optimized data types for arrays and objects"
Due to diminished space saving, hampered readability, and increased security risks, in BJData, the Due to diminished space saving, hampered readability, and increased security risks, in BJData, the allowed data
allowed data types following the `$` marker in an optimized array and object container are restricted to types following the `$` marker in an optimized array and object container are restricted to
**non-zero-fixed-length** data types. Therefore, the valid optimized type markers can only be one of **non-zero-fixed-length** data types. Therefore, the valid optimized type markers can only be one of
`UiuImlMLhdDC`, plus `B` since Draft 3 (the reader accepts `B` in both drafts). This also means other variable (`[{SH`) or zero-length types (`TFZN`) can not be used in an `UiuImlMLhdDCB`. This also means other variable (`[{SH`) or zero-length types (`TFN`) can not be used in an
optimized array or object in BJData. The library enforces this restriction for both drafts. A `C` (char) element above 127 is rejected on read with optimized array or object in BJData.
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113), in 1-D and in N-dimensional arrays. With `use_type`, the
elements must have the same marker (`[1,200,3]` is not optimized, since it mixes `i` and `U`); booleans, null,
strings, arrays, and objects are never optimized with `$`.
!!! info "Binary values" !!! info "Binary values"
@@ -236,16 +202,14 @@ The library uses the following mapping from JSON values types to BJData types ac
in optimized arrays to designate binary data. This means that, unlike UBJSON, binary data can be both serialized and in optimized arrays to designate binary data. This means that, unlike UBJSON, binary data can be both serialized and
deserialized. deserialized.
To preserve compatibility with BJData Draft 2, the Draft 3 optimized binary array must be explicitly enabled: pass To preserve compatibility with BJData Draft 2, the Draft 3 optimized binary array must be explicitly enabled using
`version = bjdata_version_t::draft3` together with `use_size = true` and `use_type = true` to the `version` parameter of [`to_bjdata`](../../api/basic_json/to_bjdata.md).
[`to_bjdata`](../../api/basic_json/to_bjdata.md). With Draft 3 and only `use_size` (or neither), a binary value is
written as an array of `B` elements.
In Draft 2 mode (default), if the JSON data contains the binary type, the value is stored as a list of integers, as In Draft2 mode (default), if the JSON data contains the binary type, the value stored as a list of integers, as
suggested by the BJData documentation. In particular, this means that the serialization and the deserialization of suggested by the BJData documentation. In particular, this means that the serialization and the deserialization of
JSON containing binary values into BJData and back will result in a different JSON object. JSON containing binary values into BJData and back will result in a different JSON object.
[BJDataBinArr]: https://github.com/NeuroJSON/bjdata/blob/Draft-3/Binary_JData_Specification.md#optimized-binary-array [BJDataBinArr]: https://github.com/NeuroJSON/bjdata/blob/master/Binary_JData_Specification.md#optimized-binary-array
??? example "Example: serialize JSON values to BJData, with and without size/type optimization" ??? example "Example: serialize JSON values to BJData, with and without size/type optimization"
@@ -264,7 +228,7 @@ The library uses the following mapping from JSON values types to BJData types ac
The library maps BJData types to JSON value types as follows: The library maps BJData types to JSON value types as follows:
| BJData type | JSON value type | marker | | BJData type | JSON value type | marker |
|-----------------------|--------------------------------------------------|----------| |-------------|------------------------------------------|----------|
| no-op | *no value, next value is read* | `N` | | no-op | *no value, next value is read* | `N` |
| null | `null` | `Z` | | null | `null` | `Z` |
| false | `false` | `F` | | false | `false` | `F` |
@@ -272,7 +236,6 @@ The library maps BJData types to JSON value types as follows:
| float16 | number_float | `h` | | float16 | number_float | `h` |
| float32 | number_float | `d` | | float32 | number_float | `d` |
| float64 | number_float | `D` | | float64 | number_float | `D` |
| high-precision number | number_integer, number_unsigned, or number_float | `H` |
| uint8 | number_unsigned | `U` | | uint8 | number_unsigned | `U` |
| int8 | number_integer | `i` | | int8 | number_integer | `i` |
| uint16 | number_unsigned | `u` | | uint16 | number_unsigned | `u` |
@@ -291,18 +254,7 @@ The library maps BJData types to JSON value types as follows:
!!! success "Complete mapping" !!! success "Complete mapping"
The mapping is **complete** for the BJData types of Draft 2 and Draft 3 with one exception: column-major The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
N-dimensional arrays are rejected with [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
The Draft 4 constructs (structure-of-arrays containers and the extension type `E`) are not supported either.
The digits of an `H` value are parsed like a JSON number: integers that fit 64 bits become `number_integer` or
`number_unsigned`, anything else a `number_float`; invalid text throws
[`parse_error.115`](../../home/exceptions.md#jsonexceptionparse_error115).
!!! info "Duplicate object keys"
If an object contains the same key more than once, `from_bjdata` accepts it and keeps the last value; with
`ordered_json`, the key keeps the position of its first occurrence.
!!! warning "Ill-formed UTF-8 in string values and object keys" !!! warning "Ill-formed UTF-8 in string values and object keys"
@@ -1,6 +1,6 @@
# BON8 # BON8
BON8 (Binary Object Notation 8) is a compact binary serialization format for JSON values. It uses byte values that BON8 (Binary Object Notation 8) is a compact binary serialization format for JSON values. It uses the byte values that
cannot begin a UTF-8 character as type markers, so strings are stored as plain UTF-8 without a length prefix: a string cannot begin a UTF-8 character as type markers, so strings are stored as plain UTF-8 without a length prefix: a string
ends at the first byte that cannot continue it. Integers from -10 to 39, `true`, `false`, `null`, and the floating-point ends at the first byte that cannot continue it. Integers from -10 to 39, `true`, `false`, `null`, and the floating-point
values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to four elements need no terminator. values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to four elements need no terminator.
@@ -12,7 +12,7 @@ values -1.0, 0.0, and 1.0 take a single byte, and arrays and objects with up to
## Byte layout ## Byte layout
BON8 exploits the fact that some byte values can never start a UTF-8 character. These bytes serve as *type markers*, so strings can be stored as plain UTF-8 without a length prefix: a string ends at the first byte that cannot continue it: a marker byte, or a lead byte 0xC2..0xF7 followed by a byte outside 0x80..0xBF. The ending byte is often the marker of the next value. Small integers, `true`, `false`, `null`, and a few common floats are a single byte. Arrays and objects are introduced by a marker; those with up to four elements encode their size in the marker and need no terminator, while larger ones are closed by the end marker `0xFE`. BON8 exploits the fact that some byte values can never start a UTF-8 character. These bytes serve as *type markers*, so strings can be stored as plain UTF-8 without a length prefix: a string ends at the first byte that cannot continue it (often the marker of the next value). Small integers, `true`, `false`, `null`, and a few common floats are a single byte. Arrays and objects are introduced by a marker; those with up to four elements encode their size in the marker and need no terminator, while larger ones are closed by the end marker `0xFE`.
Consider the following JSON value (shown formatted for readability): Consider the following JSON value (shown formatted for readability):
@@ -33,7 +33,7 @@ Consider the following JSON value (shown formatted for readability):
<div class="byte-layout" markdown="0"><table> <div class="byte-layout" markdown="0"><table>
<thead><tr><th>JSON</th><th>Bytes</th><th>Meaning</th></tr></thead><tbody> <thead><tr><th>JSON</th><th>Bytes</th><th>Meaning</th></tr></thead><tbody>
<tr><td><code>{</code></td><td><span class="bl bl-t">8A</span></td><td>object with 4 members</td></tr> <tr><td><code>{</code></td><td><span class="bl bl-t">8A</span></td><td>object with 4 members</td></tr>
<tr><td><code>&quot;id&quot;: 300,</code></td><td><span class="bl bl-d">69 64</span> <span class="bl bl-t">C4</span> <span class="bl bl-d">04</span></td><td>key: UTF-8 bytes; the string ends at 0xC4, because the byte after it (0x04) is not a UTF-8 continuation byte, so 0xC4 starts an integer; value: 2-byte positive integer (lead byte, then a byte below 0x80)</td></tr> <tr><td><code>&quot;id&quot;: 300,</code></td><td><span class="bl bl-d">69 64</span> <span class="bl bl-t">C4</span> <span class="bl bl-d">04</span></td><td>key: UTF-8 bytes; the string ends at the next byte, which cannot continue a UTF-8 character; value: 2-byte positive integer (lead byte, then a byte below 0x80)</td></tr>
<tr><td><code>&quot;name&quot;: &quot;json&quot;,</code></td><td><span class="bl bl-d">6E 61 6D 65</span> <span class="bl bl-l">FF</span> <span class="bl bl-d">6A 73 6F 6E</span> <span class="bl bl-l">FF</span></td><td>key: UTF-8 bytes; 0xFF terminates the string as another string follows; value: UTF-8 bytes; terminated as a string key follows</td></tr> <tr><td><code>&quot;name&quot;: &quot;json&quot;,</code></td><td><span class="bl bl-d">6E 61 6D 65</span> <span class="bl bl-l">FF</span> <span class="bl bl-d">6A 73 6F 6E</span> <span class="bl bl-l">FF</span></td><td>key: UTF-8 bytes; 0xFF terminates the string as another string follows; value: UTF-8 bytes; terminated as a string key follows</td></tr>
<tr><td><code>&quot;pi&quot;: 3.14,</code></td><td><span class="bl bl-d">70 69</span> <span class="bl bl-t">8F</span> <span class="bl bl-d">40 09 1E B8 51 EB 85 1F</span></td><td>key: UTF-8 bytes; value: binary64 (IEEE 754, big-endian); its marker also ends <code>&quot;pi&quot;</code></td></tr> <tr><td><code>&quot;pi&quot;: 3.14,</code></td><td><span class="bl bl-d">70 69</span> <span class="bl bl-t">8F</span> <span class="bl bl-d">40 09 1E B8 51 EB 85 1F</span></td><td>key: UTF-8 bytes; value: binary64 (IEEE 754, big-endian); its marker also ends <code>&quot;pi&quot;</code></td></tr>
<tr><td><code>&quot;tags&quot;: [</code></td><td><span class="bl bl-d">74 61 67 73</span> <span class="bl bl-t">82</span></td><td>key: UTF-8 bytes; value: array with 2 elements; its marker also ends <code>&quot;tags&quot;</code></td></tr> <tr><td><code>&quot;tags&quot;: [</code></td><td><span class="bl bl-d">74 61 67 73</span> <span class="bl bl-t">82</span></td><td>key: UTF-8 bytes; value: array with 2 elements; its marker also ends <code>&quot;tags&quot;</code></td></tr>
@@ -100,8 +100,7 @@ serialized as 0x81 0x65 0xFF, but `[1,2,3,4,"e"]` as 0x85 0x91 0x92 0x93 0x94 0x
The following values can **not** be converted to a BON8 value: The following values can **not** be converted to a BON8 value:
- integers outside the int64 range (unsigned integers above 9223372036854775807 and, for number types wider than - unsigned integers above 9223372036854775807, because BON8 has no unsigned 64-bit integer type
64 bits, any larger or smaller integer), because BON8 integers are limited to int64
([out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407)) ([out_of_range.407](../../home/exceptions.md#jsonexceptionout_of_range407))
- strings that are not valid UTF-8, because the end of a string is determined from its encoding - strings that are not valid UTF-8, because the end of a string is determined from its encoding
([type_error.316](../../home/exceptions.md#jsonexceptiontype_error316)) ([type_error.316](../../home/exceptions.md#jsonexceptiontype_error316))
@@ -169,13 +168,7 @@ The library maps BON8 types to JSON value types as follows:
Non-negative integers are read as number_unsigned, negative integers as number_integer. Non-negative integers are read as number_unsigned, negative integers as number_integer.
A byte 0xF5..0xF7 followed by 0x80..0xBF is neither a string nor an integer and is rejected with !!! info
[parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) (invalid UTF-8 byte). The same error is
reported for any other invalid byte (such as a stray 0xFE), for ill-formed UTF-8, and for an object key that is not a
string. Input that ends prematurely, trailing data after the value, and a missing final 0xFF after a string at the end
of the message are reported as [parse_error.110](../../home/exceptions.md#jsonexceptionparse_error110).
!!! info "Non-canonical input"
Values that do not use the canonical representation, such as integers with a longer encoding than necessary, Values that do not use the canonical representation, such as integers with a longer encoding than necessary,
arrays and objects with up to four elements that are terminated by 0xFE, unsorted object keys, or a 0xFF after a arrays and objects with up to four elements that are terminated by 0xFE, unsorted object keys, or a 0xFF after a
@@ -183,10 +176,7 @@ of the message are reported as [parse_error.110](../../home/exceptions.md#jsonex
Strings must be valid UTF-8, and a string at the very end of a message must be terminated by 0xFF. Strings must be valid UTF-8, and a string at the very end of a message must be terminated by 0xFF.
Duplicate object keys are accepted; the last value wins (`ordered_json` keeps the position of the first occurrence). !!! info
The specification does not say how to treat them; the HikoGUI reference decoder keeps the first value.
!!! info "Round trips"
Any BON8 output created by `to_bon8` can be successfully parsed by `from_bon8`. Any BON8 output created by `to_bon8` can be successfully parsed by `from_bon8`.
@@ -66,12 +66,7 @@ The library uses the following mapping from JSON values types to BSON types:
!!! warning "Incomplete mapping" !!! warning "Incomplete mapping"
The mapping is **incomplete**, since only JSON-objects (and things contained therein) can be serialized to BSON. The mapping is **incomplete**, since only JSON-objects (and things contained therein) can be serialized to BSON.
Also, keys may not contain U+0000, since they are serialized as zero-terminated C strings. Also, keys may not contain U+0000, since they are serialized a zero-terminated c-strings.
!!! info "Order of object members"
The members of an object are written in the order in which iteration visits them: sorted by key for
[`json`](../../api/json.md), in insertion order for [`ordered_json`](../../api/ordered_json.md).
!!! warning "BSON type 0x11 interoperability" !!! warning "BSON type 0x11 interoperability"
@@ -86,19 +81,6 @@ The library uses the following mapping from JSON values types to BSON types:
As a result, serializing and deserializing a JSON object containing such a value produces a different JSON object, As a result, serializing and deserializing a JSON object containing such a value produces a different JSON object,
even though the binary data is unchanged. even though the binary data is unchanged.
!!! info "Binary subtype 0x02 (old binary)"
The BSON specification defines the payload of the "binary (old)" subtype `0x02` as an int32 followed by that many
bytes, so the value is preceded by two lengths: the length of the binary element and, as the first four bytes of
the payload, the number of bytes that follow. A binary value with subtype `2` is therefore written with the outer
length 4 bytes larger than its size and the size as inner length: `binary({1, 2, 3}, 2)` becomes
`07 00 00 00 02 03 00 00 00 01 02 03`. When reading, the inner length is checked and removed, so the value
contains only the bytes that follow it. `from_bson()` throws
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) if the outer length is less than 4 or the
inner length is not the outer length minus 4. Versions before 3.13.0 did not write the inner length and returned
it as part of the payload; BSON data written by those versions with subtype 2 is not readable any more and must be
written again.
??? example "Example: serialize a JSON value to BSON" ??? example "Example: serialize a JSON value to BSON"
```cpp ```cpp
@@ -153,29 +135,12 @@ The library maps BSON record types to JSON value types as follows:
The BSON reader is lenient in a few areas where the BSON specification is more restrictive: The BSON reader is lenient in a few areas where the BSON specification is more restrictive:
- array element keys are not checked against the required decimal sequence (`0`, `1`, `2`, ...), - array element keys are not checked against the required decimal sequence (`0`, `1`, `2`, ...),
- any non-zero byte is accepted as `true` for the boolean type, - any non-zero byte is accepted as `true` for the boolean type, and
- the payloads of the binary subtypes `0x03` (UUID, old), `0x04` (UUID), and `0x05` (MD5) are not checked to have - the payload for binary subtype `0x02` is returned as-is, including its inner length prefix.
16 bytes,
- duplicate keys within a document are accepted; the last value wins (`{"a": 1, "a": 2}` is read as `{"a": 2}`),
while a SAX handler receives every occurrence,
- a `string` value may contain embedded `0x00` bytes, and
- with `strict` set to `false`, bytes after the end of the document are ignored.
If BSON input must be validated for strict specification compliance, validate it separately before passing it to If BSON input must be validated for strict specification compliance, validate it separately before passing it to
`from_bson()`. `from_bson()`.
!!! note "Document size"
Every document starts with its size. `from_bson()` rejects a document with
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) if the size is less than 5 (the size prefix and the terminator), if an embedded document does not end before the
end of the document that contains it, if the elements do not fit into the declared size, or if the size does not
match the number of bytes read when the terminating `0x00` byte is reached. A size that is too small is detected
before an element is read whose type byte and name no longer fit; an element that starts inside the declared size
but extends past it is read, and reported to a SAX handler, before the mismatch is found at the terminator. A size
that is too large is likewise found only at the terminator. Passing `strict` as `false` skips only the check for bytes after
the end of the document ([`parse_error.110`](../../home/exceptions.md#jsonexceptionparse_error110)), not the
document size checks.
!!! warning "Ill-formed UTF-8 in string values" !!! warning "Ill-formed UTF-8 in string values"
The BSON specification requires `string` values (type `0x02`) to be valid UTF-8, but this is not required of a The BSON specification requires `string` values (type `0x02`) to be valid UTF-8, but this is not required of a
@@ -189,9 +154,8 @@ The library maps BSON record types to JSON value types as follows:
ill-formed bytes. `to_bson()`'s own `error_handler` parameter defaults to `keep`, so such a string value or element ill-formed bytes. `to_bson()`'s own `error_handler` parameter defaults to `keep`, so such a string value or element
(key) name is written unchanged; with `strict` (the default if (key) name is written unchanged; with `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception
instead. Element (key) names are treated like string values: with `keep` they are read unchanged, with `strict`, instead. Element (key) names are never validated on read, since they are read byte-by-byte as a C string. `binary`
`replace`, or `ignore` they are checked and handled in the same way. `binary` values (type `0x05`) are unaffected, values (type `0x05`) are unaffected, since they are not required to hold text.
since they are not required to hold text.
??? example "Example: deserialize a JSON value from BSON" ??? example "Example: deserialize a JSON value from BSON"
@@ -11,7 +11,7 @@ extremely small code sizes, fairly small message size, and extensibility without
## Byte layout ## Byte layout
CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly. A reader of definite-length items knows how many items to expect without a terminator; indefinite-length items (additional information 31) are instead closed by the "break" byte 0xFF, which `from_cbor` reads but `to_cbor` never writes. CBOR encodes every data item as a *head* followed by optional content. The first byte of the head holds the *major type* in its upper three bits (unsigned integer, negative integer, byte string, text string, array, map, tag, or simple/float) and an *additional information* value in its lower five bits. Additional information below 24 is the argument itself (for example, a small integer or a short length); the values 24 to 27 announce that the argument follows in 1, 2, 4, or 8 big-endian bytes. Strings, arrays, and maps are prefixed with their length (or count of elements/pairs), and content follows directly, so a reader always knows how many items to expect without a terminator.
Consider the following JSON value (shown formatted for readability): Consider the following JSON value (shown formatted for readability):
@@ -52,16 +52,16 @@ The library uses the following mapping from JSON values types to CBOR types acco
| null | `null` | Null | 0xF6 | | null | `null` | Null | 0xF6 |
| boolean | `true` | True | 0xF5 | | boolean | `true` | True | 0xF5 |
| boolean | `false` | False | 0xF4 | | boolean | `false` | False | 0xF4 |
| number_integer | -9223372036854775808..-4294967297 | Negative integer (8 bytes follow) | 0x3B | | number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B |
| number_integer | -4294967296..-65537 | Negative integer (4 bytes follow) | 0x3A | | number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A |
| number_integer | -65536..-257 | Negative integer (2 bytes follow) | 0x39 | | number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 |
| number_integer | -256..-25 | Negative integer (1 byte follow) | 0x38 | | number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 |
| number_integer | -24..-1 | Negative integer | 0x20..0x37 | | number_integer | -24..-1 | Negative integer | 0x20..0x37 |
| number_integer | 0..23 | Integer | 0x00..0x17 | | number_integer | 0..23 | Integer | 0x00..0x17 |
| number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 | | number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 |
| number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 | | number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 |
| number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A | | number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A |
| number_integer | 4294967296..9223372036854775807 | Unsigned integer (8 bytes follow) | 0x1B | | number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B |
| number_unsigned | 0..23 | Integer | 0x00..0x17 | | number_unsigned | 0..23 | Integer | 0x00..0x17 |
| number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 | | number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 |
| number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 | | number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 |
@@ -95,24 +95,7 @@ see "binary" cells in the table above.
!!! success "Complete mapping" !!! success "Complete mapping"
The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value, with one The mapping is **complete** in the sense that any JSON value type can be converted to a CBOR value.
exception: an integer outside the range of CBOR integers (only possible with a number type wider than 64 bits)
throws [`out_of_range.407`](../../home/exceptions.md#jsonexceptionout_of_range407).
!!! info "Not a deterministic encoding"
`to_cbor` does not produce the deterministic encoding of
[RFC 8949, Section 4.2.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-4.2.1), and not always the preferred
serialization of Section 4.1:
- floating-point numbers are written as single precision (0xFA) when that is exact and as double precision (0xFB)
otherwise; half precision (0xF9) is used only for `NaN` and `Infinity`/`-Infinity` (`1.0` is `fa 3f 80 00 00`);
- a binary subtype 0..23 is written as `d8 xx` instead of the one-byte tag 0xC0..0xD7 (`d8 01 41 01`);
- object members are written in the order of `object_t` (for the default `std::map`: sorted by the key string, not
by the encoded key bytes).
Integers and lengths use the shortest form, and indefinite-length items are never written. Decoders must accept all
of this; the RFC does not require the preferred serialization.
!!! info "NaN/infinity handling" !!! info "NaN/infinity handling"
@@ -122,8 +105,9 @@ see "binary" cells in the table above.
!!! note !!! note
Prior to version 3.8.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB, Prior to version 3.13.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
9 bytes total). 9 bytes total), because the check used to select a smaller encoding compared magnitudes with NaN, which is
always `false` and caused the intended half-precision path to be skipped.
!!! info "Unused CBOR types" !!! info "Unused CBOR types"
@@ -140,6 +124,7 @@ see "binary" cells in the table above.
- expected conversions (0xD5..0xD7) - expected conversions (0xD5..0xD7)
- simple values (0xE0..0xF3, 0xF8) - simple values (0xE0..0xF3, 0xF8)
- undefined (0xF7) - undefined (0xF7)
- half-precision floats (0xF9)
- break (0xFF) - break (0xFF)
!!! info "Tagged items" !!! info "Tagged items"
@@ -241,20 +226,10 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a ([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
general-purpose CBOR library instead. general-purpose CBOR library instead.
A tag in front of a text string key is handled like a tag in front of a value: it is skipped with
`cbor_tag_handler_t::ignore` or `cbor_tag_handler_t::store` and rejected with the default `error`. A tag does not
turn a key of another type into a text string.
A map with duplicate keys is well-formed, but invalid under
[RFC 8949, Section 5.3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3.1). `from_cbor` accepts it and the
last value wins; the [SAX interface](../parsing/sax_interface.md) reports every occurrence.
!!! warning "Ill-formed UTF-8 in text strings" !!! warning "Ill-formed UTF-8 in text strings"
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings (major [RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings (major
type 3) to be valid UTF-8 (a string with ill-formed UTF-8 is "well-formed but invalid"), and type 3) to be valid UTF-8, but leaves it up to the decoder whether to enforce this, so checking is opt-in: with the
[Section 5.3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3.1) leaves it up to the decoder whether to
verify this, so checking is opt-in: with the
[`error_handler`](../../api/basic_json/from_cbor.md) parameter left at `keep` (the default), `from_cbor()` accepts a [`error_handler`](../../api/basic_json/from_cbor.md) parameter left at `keep` (the default), `from_cbor()` accepts a
text string (object keys included) whose bytes are not valid UTF-8 and hands them back unchanged. Passing text string (object keys included) whose bytes are not valid UTF-8 and hands them back unchanged. Passing
`error_handler_t::strict` makes `from_cbor()` check and throw `error_handler_t::strict` makes `from_cbor()` check and throw
@@ -267,14 +242,9 @@ string of the same major type, as required by [RFC 8949, Section 3.2.3](https://
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception
instead. Byte strings (major type 2) are unaffected, since they are not required to hold text. instead. Byte strings (major type 2) are unaffected, since they are not required to hold text.
The chunks of an indefinite-length text string are checked one by one, as required by
[RFC 8949, Section 3.2.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.2.3): a code point cannot be spread
over two chunks. With `error_handler_t::strict` such a string throws `parse_error.113`; `replace` and `ignore`
sanitize each chunk on its own.
!!! warning "Tagged items" !!! warning "Tagged items"
Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. Passing `cbor_tag_handler_t::store` to function `from_cbor` stores tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are read as if the tag were ignored. If several tags precede a byte string, only the tag directly in front of it is stored, and only when it has the form 0xD8..0xDB (a tag 0..23 written as 0xC0..0xD7 is skipped); for example, `D8 05 C1 41 AA` yields subtype `null`, whereas `C1 D8 05 41 AA` yields 5. Tags in front of a text string map key are skipped as well. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string. Tagged items (0xC0..0xDB) will throw a parse error by default. They can be ignored by passing `cbor_tag_handler_t::ignore` to function `from_cbor`, in which case the tag is skipped and the enclosed data item is parsed on its own. Passing `cbor_tag_handler_t::store` to function `from_cbor` stores tagged byte strings (for bytes 0xd8..0xdb) as binary values with the tag as subtype; other tagged values are read as if the tag were ignored. If several tags precede a byte string, only the innermost one is stored. Note that no tag is ever interpreted: for instance, a text string tagged with tag 0 (date/time) stays a string.
??? example "Example: deserialize a JSON value from CBOR" ??? example "Example: deserialize a JSON value from CBOR"
@@ -17,19 +17,19 @@ to efficiently encode JSON values to byte vectors and to decode such vectors.
### Completeness ### Completeness
| Format | Serialization | Deserialization | | Format | Serialization | Deserialization |
|-------------|-----------------------------------------------|-----------------------------------------------------------------------| |-------------|-----------------------------------------------|----------------------------------------------|
| BJData | complete | incomplete: no column-major ND-arrays, no Draft 4 SoA/extension types | | BJData | complete | complete |
| BON8 | incomplete: no unsigned integers above int64 | complete | | BON8 | incomplete: no unsigned integers above int64 | complete |
| BSON | incomplete: top-level value must be an object | incomplete, but all JSON types are supported | | BSON | incomplete: top-level value must be an object | incomplete, but all JSON types are supported |
| CBOR | complete | incomplete, but all JSON types are supported | | CBOR | complete | incomplete, but all JSON types are supported |
| MessagePack | complete | incomplete, but all JSON types are supported | | MessagePack | complete | complete |
| UBJSON | complete | complete | | UBJSON | complete | complete |
### Binary values ### Binary values
| Format | Binary values | Binary subtypes | | Format | Binary values | Binary subtypes |
|-------------|--------------------------|-----------------| |-------------|---------------|-----------------|
| BJData | supported (Draft 3 only) | not supported | | BJData | not supported | not supported |
| BON8 | not supported | not supported | | BON8 | not supported | not supported |
| BSON | supported | supported | | BSON | supported | supported |
| CBOR | supported | supported | | CBOR | supported | supported |
@@ -11,7 +11,7 @@ extra byte in addition to the strings themselves.
## Byte layout ## Byte layout
MessagePack encodes each value as a one-byte *format marker*, optionally followed by a length or the payload. The marker range is partitioned to keep common values small: small non-negative integers (0 to 127) and small negative integers (-32 to -1) are the marker byte itself; short strings, arrays, and maps (up to 31 bytes for strings and up to 15 elements for arrays and maps) hide their length in the marker's lower bits. Larger values use dedicated markers that announce the width (8, 16, 32, or 64 bits) of the length or number that follows, in big-endian byte order. Like CBOR, containers are length-prefixed, so no terminator is needed. MessagePack encodes each value as a one-byte *format marker*, optionally followed by a length or the payload. The marker range is partitioned to keep common values small: small non-negative integers (0 to 127) and small negative integers (-32 to -1) are the marker byte itself; short strings, arrays, and maps (up to 31 or 15 elements) hide their length in the marker's lower bits. Larger values use dedicated markers that announce the width (8, 16, 32, or 64 bits) of the length or number that follows, in big-endian byte order. Like CBOR, containers are length-prefixed, so no terminator is needed.
Consider the following JSON value (shown formatted for readability): Consider the following JSON value (shown formatted for readability):
@@ -109,9 +109,8 @@ specification:
!!! note !!! note
Prior to version 3.13.0, NaN and Infinity were instead serialized as a MessagePack float 64 (type 0xCB, 9 bytes Prior to version 3.13.0, NaN and Infinity were instead serialized as a MessagePack float 64 (type 0xCB, 9 bytes
total), because the check used to select the smaller float 32 encoding failed for NaN (all comparisons are `false`) total), because the check used to select the smaller float 32 encoding compared magnitudes with NaN, which is
and for Infinity and -Infinity (outside the float range checked against `std::numeric_limits<float>::lowest()` and always `false` and caused the float 32 path to be skipped.
`max()`), so the float 32 path was skipped.
??? example "Example: serialize a JSON value to MessagePack" ??? example "Example: serialize a JSON value to MessagePack"
@@ -136,7 +135,6 @@ The library maps MessagePack types to JSON value types as follows:
| fixarray | array | 0x90..0x9F | | fixarray | array | 0x90..0x9F |
| fixstr | string | 0xA0..0xBF | | fixstr | string | 0xA0..0xBF |
| nil | `null` | 0xC0 | | nil | `null` | 0xC0 |
| (never used) | – | 0xC1 |
| false | `false` | 0xC2 | | false | `false` | 0xC2 |
| true | `true` | 0xC3 | | true | `true` | 0xC3 |
| float 32 | number_float | 0xCA | | float 32 | number_float | 0xCA |
@@ -169,30 +167,6 @@ The library maps MessagePack types to JSON value types as follows:
| fixext 16 | binary | 0xD8 | | fixext 16 | binary | 0xD8 |
| negative fixint | number_integer | 0xE0-0xFF | | negative fixint | number_integer | 0xE0-0xFF |
The byte 0xC1 is reserved as "never used" by the specification. As a value it is rejected with
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) (`invalid byte: 0xC1`); as a map key it is
rejected with [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) and the generic "expected length
specification" message, because the key is read as a string.
Duplicate keys in a map are accepted; the last value wins.
### Extension types (ext, fixext)
MessagePack ext and fixext values are read as binary values, and the ext type byte is stored as the binary subtype.
Although the specification defines the type as a signed 8-bit integer (0 to 127 for applications, -128 to -1 reserved,
with -1 being the Timestamp extension), the library reads it as an unsigned byte: a negative ext type `n` becomes
subtype `256+n`, so ext type -1 is read as subtype 255 and -128 as subtype 128. The Timestamp extension is not
interpreted; it is returned as binary with subtype 255. Use
[`has_subtype()`](../../api/byte_container_with_subtype/has_subtype.md) to tell a `bin` value (no subtype) from an ext
value (subtype, even if it is 0).
When serializing, a binary value with a subtype is always written as fixext (1, 2, 4, 8, or 16 bytes) or ext, even
for subtype 0 (`d6 00 01 02 03 04`); a binary value without a subtype is written as bin. Subtypes 128 to 255 are
written as the reserved negative ext types (255 as -1), so `json::binary({1,2,3,4}, 255)` becomes
`d6 ff 01 02 03 04`, which other MessagePack libraries decode as a 32-bit timestamp. A subtype above 255 throws
[`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415). The payload length is limited to
4294967295 bytes like all other lengths.
!!! info !!! info
Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`. Any MessagePack output created by `to_msgpack` can be successfully parsed by `from_msgpack`.
@@ -203,8 +177,7 @@ written as the reserved negative ext types (255 as -1), so `json::binary({1,2,3,
JSON-compatible [profile](https://github.com/msgpack/msgpack/blob/master/spec.md#profile) sketched in the JSON-compatible [profile](https://github.com/msgpack/msgpack/blob/master/spec.md#profile) sketched in the
MessagePack specification, this library restricts map keys to `str` values. Maps with keys of any other type are MessagePack specification, this library restricts map keys to `str` values. Maps with keys of any other type are
rejected with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with rejected with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
`allow_exceptions` set to `false`, a discarded value). For most key types the message names the type that was `allow_exceptions` set to `false`, a discarded value) naming the type of the key that was found, for instance:
found, for instance:
``` ```
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0 [json.exception.parse_error.113] parse error at byte 2: syntax error while parsing MessagePack object key: only string keys are supported, but found nil; last byte: 0xC0
@@ -216,7 +189,7 @@ written as the reserved negative ext types (255 as -1), so `json::binary({1,2,3,
!!! warning "Ill-formed UTF-8 in string values" !!! warning "Ill-formed UTF-8 in string values"
The MessagePack specification explicitly allows a `str` value (`fixstr`, `str 8`, `str 16`, `str 32`) to contain The MessagePack specification explicitly allows a `str` value (`fixstr`, `str 8`, `str 16`, `str 32`) to contain
a byte sequence that is not valid UTF-8, and recommends that a deserializer provide a way to obtain the original bytes. a byte sequence that is not valid UTF-8, and expects a deserializer to hand the original bytes back unchanged.
This library follows that by default: with its This library follows that by default: with its
[`error_handler`](../../api/basic_json/from_msgpack.md) parameter left at `keep` (the default), [`error_handler`](../../api/basic_json/from_msgpack.md) parameter left at `keep` (the default),
`from_msgpack()` reads `str` bytes (object keys included) as-is, without validating them, so such a value `from_msgpack()` reads `str` bytes (object keys included) as-is, without validating them, so such a value
@@ -5,11 +5,11 @@ achieve the generality of JSON, combined with being much easier to process than
!!! abstract "References" !!! abstract "References"
- [UBJSON Website](https://ubjson.org) - [UBJSON Website](http://ubjson.org)
## Byte layout ## Byte layout
UBJSON is built from just a one-byte, human-readable ASCII *type marker* (for example, `i` for int8, `S` for string, `[` for array) followed by the payload of that type. Strings and the like are prefixed with their length, which is itself a UBJSON integer value including its marker. Numbers are stored in big-endian byte order. Containers can be written in two ways: as an *unoptimized* container that is opened with `[` or `{` and closed with `]` or `}`, or as an *optimized* container that declares a count (`#`) after the opening marker, optionally preceded by an element type (`$`); an element type is only allowed together with a count. An optimized container saves the closing marker and, with a type, the per-element markers. UBJSON is built from just a one-byte, human-readable ASCII *type marker* (for example, `i` for int8, `S` for string, `[` for array) followed by the payload of that type. Strings and the like are prefixed with their length, which is itself a UBJSON integer value including its marker. Numbers are stored in big-endian byte order. Containers can be written in two ways: as an *unoptimized* container that is opened with `[` or `{` and closed with `]` or `}`, or as an *optimized* container that declares an element type (`$`) and/or a count (`#`) after the opening marker, which saves per-element markers and (for a count) the closing marker.
Consider the following JSON value (shown formatted for readability): Consider the following JSON value (shown formatted for readability):
@@ -45,7 +45,7 @@ Consider the following JSON value (shown formatted for readability):
The library uses the following mapping from JSON values types to UBJSON types according to the UBJSON specification: The library uses the following mapping from JSON values types to UBJSON types according to the UBJSON specification:
| JSON value type | value/range | UBJSON type | marker | | JSON value type | value/range | UBJSON type | marker |
|-----------------|-------------------------------------------|--------------------------------------------------|-----------| |-----------------|-------------------------------------------|----------------|--------|
| null | `null` | null | `Z` | | null | `null` | null | `Z` |
| boolean | `true` | true | `T` | | boolean | `true` | true | `T` |
| boolean | `false` | false | `F` | | boolean | `false` | false | `F` |
@@ -63,10 +63,10 @@ The library uses the following mapping from JSON values types to UBJSON types ac
| number_unsigned | 32768..2147483647 | int32 | `l` | | number_unsigned | 32768..2147483647 | int32 | `l` |
| number_unsigned | 2147483648..9223372036854775807 | int64 | `L` | | number_unsigned | 2147483648..9223372036854775807 | int64 | `L` |
| number_unsigned | 9223372036854775808..18446744073709551615 | high-precision | `H` | | number_unsigned | 9223372036854775808..18446744073709551615 | high-precision | `H` |
| number_float | *any value* | float64 (float32 if `number_float_t` is `float`) | `D` (`d`) | | number_float | *any value* | float64 | `D` |
| string | *with shortest length indicator* | string | `S` | | string | *with shortest length indicator* | string | `S` |
| array | *see notes on optimized format* | array | `[` | | array | *see notes on optimized format* | array | `[` |
| object | *see notes on optimized format* | object | `{` | | object | *see notes on optimized format* | map | `{` |
!!! success "Complete mapping" !!! success "Complete mapping"
@@ -93,27 +93,20 @@ The library uses the following mapping from JSON values types to UBJSON types ac
The following markers are not used in the conversion: The following markers are not used in the conversion:
- `N`: no-op values are never written (they are skipped when reading). - `Z`: no-op values are not created.
- `C`: single-byte strings are serialized with `S` markers. - `C`: single-byte strings are serialized with `S` markers.
!!! info "NaN/infinity handling" !!! info "NaN/infinity handling"
NaN and infinity are written as IEEE 754 float64 values (`D`) and read back unchanged. This differs from If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the
[`dump()`](../../api/basic_json/dump.md), which serializes them to `null`, and from the UBJSON specification, which [`dump()`](../../api/basic_json/dump.md) function which serializes NaN or Infinity to `null`.
encodes infinity as `null` (it does not mention NaN); other UBJSON readers may therefore not accept these values.
!!! info "Optimized formats" !!! info "Optimized formats"
The optimized formats for containers are supported: Parameter `use_size` adds size information to the beginning of a The optimized formats for containers are supported: Parameter `use_size` adds size information to the beginning of a
container and removes the closing marker. Parameter `use_type` further checks whether all elements of a container container and removes the closing marker. Parameter `use_type` further checks whether all elements of a container
have the same type and adds the type marker to the beginning of the container. The `use_type` parameter must only be have the same type and adds the type marker to the beginning of the container. The `use_type` parameter must only be
used together with `use_size = true`: combining `use_type` with `use_size = false` throws used together with `use_size = true`.
[`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if the value contains a non-empty array,
object, or binary value.
Two elements have the same type if they are written with the same type marker. For example, `[1, 2]` is written with
the type `i`, but `[1, 200]` (`i`, `U`), `[true, false]` (`T`, `F`), and `[1, 2.5]` (`i`, `D`) are not; an empty
container is never given a `$` annotation of its own (an array of empty arrays is still typed `[`).
Note that `use_size = true` alone may result in larger representations - the benefit of this parameter is that the Note that `use_size = true` alone may result in larger representations - the benefit of this parameter is that the
receiving side is immediately informed on the number of elements of the container. receiving side is immediately informed on the number of elements of the container.
@@ -122,9 +115,8 @@ The library uses the following mapping from JSON values types to UBJSON types ac
already is the value. Its declared count is therefore the only thing that decides how much memory the receiving side already is the value. Its declared count is therefore the only thing that decides how much memory the receiving side
allocates, and a handful of bytes can describe billions of elements. `from_ubjson` rejects such an array with allocates, and a handful of bytes can describe billions of elements. `from_ubjson` rejects such an array with
[`out_of_range.408`](../../home/exceptions.md#jsonexceptionout_of_range408) when the count exceeds 1,048,576 [`out_of_range.408`](../../home/exceptions.md#jsonexceptionout_of_range408) when the count exceeds 1,048,576
(`1 << 20`), and `to_ubjson` writes longer arrays of these types in the counted but untyped form: the `$` type marker is (`1 << 20`), and `to_ubjson` writes longer arrays of these types without the annotation, so any value it produces
dropped, the `#` count is kept, and every element is written with its own marker (`[#l 1048577 Z Z ...`), so any value can be read back.
it produces can be read back. Objects and untyped counted arrays are not subject to this limit.
!!! info "Binary values" !!! info "Binary values"
@@ -149,7 +141,7 @@ The library uses the following mapping from JSON values types to UBJSON types ac
The library maps UBJSON types to JSON value types as follows: The library maps UBJSON types to JSON value types as follows:
| UBJSON type | JSON value type | marker | | UBJSON type | JSON value type | marker |
|----------------|--------------------------------------------------|--------| |-------------|-----------------------------------------|--------|
| no-op | *no value, next value is read* | `N` | | no-op | *no value, next value is read* | `N` |
| null | `null` | `Z` | | null | `null` | `Z` |
| false | `false` | `F` | | false | `false` | `F` |
@@ -163,39 +155,12 @@ The library maps UBJSON types to JSON value types as follows:
| int64 | number_integer | `L` | | int64 | number_integer | `L` |
| string | string | `S` | | string | string | `S` |
| char | string | `C` | | char | string | `C` |
| high-precision | number_unsigned, number_integer, or number_float | `H` |
| array | array (optimized values are supported) | `[` | | array | array (optimized values are supported) | `[` |
| object | object (optimized values are supported) | `{` | | object | object (optimized values are supported) | `{` |
!!! info "High-precision numbers"
The payload of `H` is a length-prefixed string that is parsed as a JSON number (surrounding whitespace is ignored).
Integer text in the range of `number_unsigned_t` (non-negative) or `number_integer_t` (negative) is stored as such;
any other valid number text (larger or smaller integers, fractions, and exponents such as `1e2`) is stored as
`number_float`, so precision may be lost. Text that is not a valid JSON number raises
[`parse_error.115`](../../home/exceptions.md#jsonexceptionparse_error115), a number outside the range of
`number_float_t` (`1e999`) raises [`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406).
!!! success "Complete mapping" !!! success "Complete mapping"
Every well-formed UBJSON value can be converted to a JSON value, with these exceptions: a high-precision number The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
outside the range of `number_float_t` ([`out_of_range.406`](../../home/exceptions.md#jsonexceptionout_of_range406)),
an optimized array of `Z`, `T`, or `F` with more than 1,048,576 elements, and a count that exceeds the capacity of the
container ([`out_of_range.408`](../../home/exceptions.md#jsonexceptionout_of_range408)). High-precision numbers that
do not fit an integer type are stored as floating-point numbers and may lose precision. Ill-formed input is rejected
with a parse error.
!!! info "Duplicate object keys"
If an object contains the same key more than once, the last value is kept (the UBJSON specification does not
address duplicate keys).
!!! info "Type marker of optimized containers"
The type marker after `$` must be one of `Z`, `N`, `T`, `F`, `i`, `U`, `I`, `l`, `L`, `d`, `D`, `H`, `C`, `S`, `[`, or
`{`. It is checked even if the count is 0; any other marker raises
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112). A counted object typed `N` (`{$N#i1 ...`)
is not supported.
!!! warning "Ill-formed UTF-8 in string values and object keys" !!! warning "Ill-formed UTF-8 in string values and object keys"
+8 -26
View File
@@ -132,11 +132,8 @@ is an integer or `null`.
### BJData ### BJData
[BJData](binary_formats/bjdata.md) Draft 3 supports binary values as optimized arrays of `B` (byte) elements, but no [BJData](binary_formats/bjdata.md) neither supports binary values nor subtypes and proposes to serialize binary values
subtypes. With `version = bjdata_version_t::draft3` and both `use_size` and `use_type` set, `to_bjdata` writes a binary as an array of uint8 values. The library implements this translation.
value as `[$B#...`, and `from_bjdata` reads it back as a binary value (without subtype). In all other cases, including
the default Draft 2 mode, the library follows the Draft 2 proposal and writes an array of integers (`U` in Draft 2, `B`
in Draft 3), which is read back as an array.
??? example "Example: serialize a binary value to BJData" ??? example "Example: serialize a binary value to BJData"
@@ -174,7 +171,7 @@ in Draft 3), which is read back as an array.
```c ```c
0x7B // '{' 0x7B // '{'
0x23 0x69 0x01 // '#' 'i' 1 number of object members 0x23 0x69 0x01 // '#' 'i' type of the array elements: unsigned integers
0x69 0x06 // i 6 (length of the key) 0x69 0x06 // i 6 (length of the key)
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary" 0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
0x5B // '[' array 0x5B // '[' array
@@ -183,7 +180,7 @@ in Draft 3), which is read back as an array.
0xCA 0xFE 0xBA 0xBE // content 0xCA 0xFE 0xBA 0xBE // content
``` ```
Note that subtype (42) is **not** serialized and that, in Draft 2 mode, BJData has **no binary type**, and deserializing `v` would Note that subtype (42) is **not** serialized and that BJData has **no binary type**, and deserializing `v` would
yield the following value: yield the following value:
```json ```json
@@ -216,7 +213,7 @@ array of integers.
0x87 // object with 1 member 0x87 // object with 1 member
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary" 0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
0x84 // array with 4 elements 0x84 // array with 4 elements
0xC3 0x22 0xC3 0x56 0xC3 0x12 0xC3 0x16 // content (each byte, here all above 39, as a 2-byte integer) 0xC3 0x22 0xC3 0x56 0xC3 0x12 0xC3 0x16 // content (each byte as a 2-byte integer)
``` ```
Note that the subtype is lost, and deserializing `v` would yield the following value: Note that the subtype is lost, and deserializing `v` would yield the following value:
@@ -230,10 +227,7 @@ array of integers.
### BSON ### BSON
[BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as an [BSON](binary_formats/bson.md) supports binary values and subtypes. If a subtype is given, it is used and added as an
unsigned 8-bit integer; a subtype above 255 throws unsigned 8-bit integer. If no subtype is given, the generic binary subtype 0x00 is used.
[`out_of_range.415`](../home/exceptions.md#jsonexceptionout_of_range415). If no subtype is given, the generic binary
subtype 0x00 is used. The payload of the "old binary" subtype 0x02 is preceded by a second length, which the library
writes and removes again when reading; it is not part of the payload.
??? example "Example: serialize a binary value to BSON" ??? example "Example: serialize a binary value to BSON"
@@ -307,8 +301,6 @@ representation using the length of the byte array.
[`json::from_cbor`](../api/basic_json/from_cbor.md) (see [`json::from_cbor`](../api/basic_json/from_cbor.md) (see
[`cbor_tag_handler_t`](../api/basic_json/cbor_tag_handler_t.md)). [`cbor_tag_handler_t`](../api/basic_json/cbor_tag_handler_t.md)).
With `ignore`, the subtype is dropped (`"subtype": null`); with `store`, the tag is kept (`"subtype": 42`):
```json ```json
{ {
"binary": { "binary": {
@@ -318,21 +310,11 @@ representation using the length of the byte array.
} }
``` ```
```json
{
"binary": {
"bytes": [202, 254, 186, 190],
"subtype": 42
}
}
```
### MessagePack ### MessagePack
[MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family [MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family
is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, fixext16, ext8, is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and
ext16, and ext32. The subtype is then written as the signed 8-bit ext type: subtypes 128 to 255 map to the negative ext32. The subtype is then added as a signed 8-bit integer.
(reserved) ext types, and on reading the ext type is exposed as an unsigned subtype from 0 to 255.
If no subtype is given, the bin family (bin8, bin16, bin32) is used. If no subtype is given, the bin family (bin8, bin16, bin32) is used.
+1 -18
View File
@@ -13,12 +13,6 @@ to the current JSON value by executing all operations from the patch, yielding t
If the document is large and the original value is no longer needed, If the document is large and the original value is no longer needed,
[`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead. [`patch_inplace`](../api/basic_json/patch_inplace.md) applies the same operations in place instead.
!!! note "Duplicate member names"
Duplicate member names inside an operation object are resolved by the parser before the patch is applied (the last
one is kept, see [`object_t`](../api/basic_json/object_t.md#behavior)), so
`#!json {"op": "add", "op": "remove", "path": "/a"}` is a `remove` operation.
??? example "Example: apply a JSON Patch" ??? example "Example: apply a JSON Patch"
The following code shows how a JSON patch is applied to a value. The following code shows how a JSON patch is applied to a value.
@@ -47,23 +41,12 @@ flowchart LR
!!! success "Invariant" !!! success "Invariant"
For two JSON values *source* and *target*, the following code yields true: For two JSON values *source* and *target*, the following code yields always true:
```cpp ```cpp
source.patch(diff(source, target)) == target; source.patch(diff(source, target)) == target;
``` ```
The equality holds up to [`operator==`](../api/basic_json/operator_eq.md), with these exceptions:
- `NaN` and [discarded](../api/basic_json/is_discarded.md) values are not equal to themselves, so the invariant
cannot hold for them. `diff` emits a `replace` operation for such a value, and a `NaN` in a patch is serialized
as `#!json null`.
- Numbers that compare equal produce no operation even if their types differ: `diff(1, 1.0)`, `diff(1u, 1)`, and
`diff(0.0, -0.0)` return `#!json []`, so the patched value keeps the number type of *source*.
- A [binary value](binary_values.md) in a patch is serialized as an object
(`#!json {"bytes":[...],"subtype":null}`), so a patch that was dumped and parsed again no longer holds a binary
value.
??? example "Example: create a JSON Patch from the difference of two values" ??? example "Example: create a JSON Patch from the difference of two values"
The following code shows how a JSON patch is created as a diff for two JSON values. The following code shows how a JSON patch is created as a diff for two JSON values.
-11
View File
@@ -37,10 +37,6 @@ Then every value inside the JSON document can be identified as follows:
Note `/` does not identify the root (i.e., the whole document), but an object entry with empty key `""`. See Note `/` does not identify the root (i.e., the whole document), but an object entry with empty key `""`. See
[RFC 6901](https://tools.ietf.org/html/rfc6901) for more information. [RFC 6901](https://tools.ietf.org/html/rfc6901) for more information.
[RFC 6901, section 4](https://tools.ietf.org/html/rfc6901#section-4) states that evaluation fails if an object has
non-unique member names. A `basic_json` object holds one value per key (see [`object_t`](../api/basic_json/object_t.md)
for how duplicate keys are handled while parsing), so evaluating a JSON Pointer never encounters duplicates.
## JSON Pointer creation ## JSON Pointer creation
JSON Pointers can be created from a string: JSON Pointers can be created from a string:
@@ -58,13 +54,6 @@ auto p = "/nested/one"_json_pointer;
The escaping rules of [RFC 6901](https://tools.ietf.org/html/rfc6901) are implemented. See the The escaping rules of [RFC 6901](https://tools.ietf.org/html/rfc6901) are implemented. See the
[constructor documentation](../api/json_pointer/json_pointer.md) for more information. [constructor documentation](../api/json_pointer/json_pointer.md) for more information.
!!! note "URI fragment representation"
[RFC 6901, section 6](https://tools.ietf.org/html/rfc6901#section-6) also defines a URI fragment identifier
representation (`#/a%20b`). It is not supported: `#!cpp json::json_pointer("#/a")` throws
[`parse_error.107`](../home/exceptions.md#jsonexceptionparse_error107). Strip the leading `#` and
percent-decode the remainder before creating the JSON pointer.
## Value access ## Value access
JSON Pointers can be used in the [`at`](../api/basic_json/at.md), [`operator[]`](../api/basic_json/operator%5B%5D.md), JSON Pointers can be used in the [`at`](../api/basic_json/at.md), [`operator[]`](../api/basic_json/operator%5B%5D.md),
+11 -104
View File
@@ -185,10 +185,6 @@ key as well. Other zero-copy parsers that index the source text, such as yyjson
member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees member. RFC 8259 only says that names within an object SHOULD be unique and that the behavior of a receiver that sees
duplicates is unpredictable, so neither choice is wrong. duplicates is unpredictable, so neither choice is wrong.
The same holds for [`get<T>()`](../api/basic_json_view/get.md) of a type whose `from_json()` reads the view, such as
the types of the `NLOHMANN_DEFINE_TYPE_*` macros: their members are looked up, so `#!cpp get<point>()` of
`#!cpp {"x":1,"x":2}` reads `#!cpp x == 1`, while `#!cpp materialize().get<point>()` reads `#!cpp 2`.
**What stays the same as `parse()`?** [`materialize()`](../api/basic_json_view/materialize.md) and **What stays the same as `parse()`?** [`materialize()`](../api/basic_json_view/materialize.md) and
[`get<std::map<...>>()`](../api/basic_json_view/get.md) replay every member in order, so they keep the *last* value [`get<std::map<...>>()`](../api/basic_json_view/get.md) replay every member in order, so they keep the *last* value
exactly like `#!cpp basic_json::parse()` (at the position of the first occurrence of the key, for an exactly like `#!cpp basic_json::parse()` (at the position of the first occurrence of the key, for an
@@ -214,15 +210,14 @@ number of distinct keys, or reject duplicates when iterating.
## Getting values out without copying ## Getting values out without copying
[`get<T>()`](../api/basic_json_view/get.md) converts most `T` directly from the flat index, without ever building a [`get<T>()`](../api/basic_json_view/get.md) converts many `T` directly from the flat index, without ever building a
`basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`, `basic_json` value for the conversion: `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`,
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json_view` itself, the standard containers `#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json`/`ordered_json` (via
and other types the library converts (`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::list`, `#!cpp std::array`, [`materialize()`](../api/basic_json_view/materialize.md)), `basic_json_view` itself, `#!cpp std::vector<U>`, and
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, ...), and the types of the `NLOHMANN_DEFINE_TYPE_*` and `#!cpp std::map`/`#!cpp std::unordered_map` with string-like keys. Every other type -- `#!cpp std::list`,
`NLOHMANN_JSON_SERIALIZE_ENUM` macros. Types whose `from_json()` is written for `basic_json` only go through `#!cpp std::pair`, `#!cpp std::array`, enumerations, user types with a `from_json()` -- goes through
[`materialize()`](../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real [`materialize()`](../api/basic_json_view/materialize.md)`.get<T>()` instead: the subtree is built into a real
`basic_json` value first, exactly as [`parse()`](../api/basic_json/parse.md) would, and converted from there. See `basic_json` value first, exactly as [`parse()`](../api/basic_json/parse.md) would, and converted from there.
[Writing generic code](#writing-generic-code) for how to write a `from_json()` that reads views.
Two conversions never copy at all: Two conversions never copy at all:
@@ -237,93 +232,6 @@ Two conversions never copy at all:
Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is. Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is.
## Writing generic code
A view's type names its `BasicJsonType` and its [`view_mode`](../api/view_mode.md):
[`json_view`](../api/json_view.md) is `#!cpp basic_json_view<json, view_mode::read_only>`, and
[`json_editable_view`](../api/json_editable_view.md) is `#!cpp basic_json_view<json, view_mode::editable>`. These are
different types. The only conversion between them turns a view of a read-only document into a view of an editable
document of the same `BasicJsonType`, so a function written for one shortcut accepts few other views:
```cpp
void print_names(json_view v); // accepts neither an ordered_json_view nor a json_editable_view
void print_names(json_editable_view v); // also accepts a json_view, but no ordered_json_view
```
This is the same pitfall as a `to_json`/`from_json` written for `json` instead of any `BasicJsonType`, which then does
not work for `ordered_json`. The shortcuts are for declaring documents and views; a function that takes a view names
the views it accepts with a template:
```cpp
// views of editable documents only
template<typename BasicJsonType>
void f(basic_json_view<BasicJsonType, view_mode::editable> v);
// views of read-only documents only
template<typename BasicJsonType>
void f(basic_json_view<BasicJsonType, view_mode::read_only> v);
// views of either document type
template<typename BasicJsonType, view_mode Mode>
void f(basic_json_view<BasicJsonType, Mode> v);
```
Views are passed by value: they are two pointers, cheap to copy, like a `#!cpp std::string_view`. A `const&` works as
well, but a non-`const` reference does not bind to the views that [`root()`](../api/basic_json_document/root.md),
[`operator[]`](../api/basic_json_view/operator%5B%5D.md), and [`at()`](../api/basic_json_view/at.md) return, so
`#!cpp f(doc.root())` would not compile.
There is no conversion from an editable view to a read-only one, by design: the mode decides how a view walks the
index. A read-only view reads a container in place, while a view of an editable document follows the links that edits
leave behind, so a read-only view of an edited document would read stale values. The other direction is safe: a
read-only document has no edits, so a view of it [converts](../api/basic_json_view/basic_json_view.md) to an
editable view, which reads it like an editable document before its first edit. A template deduces the mode without
conversions, so the template forms above still accept only the modes they name. Views of both modes compare with
[`operator==`](../api/basic_json_view/operator_eq.md).
[`is_json_view<T>`](../api/is_json_view.md) tells views from other types, for constraints that cannot be written as a
`basic_json_view<...>` parameter.
### A `from_json()` that reads views
[`get<T>()`](../api/basic_json_view/get.md) on a view calls `T`'s `from_json()` with the view itself if that
`from_json()` accepts views, and with [`materialize()`](../api/basic_json_view/materialize.md) otherwise. The
`from_json()` functions of the [`NLOHMANN_DEFINE_TYPE_*`](../api/macros/nlohmann_define_type_intrusive.md),
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../api/macros/nlohmann_define_derived_type.md), and
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../api/macros/nlohmann_json_serialize_enum.md) macros, and those of the library
for the standard types, accept views, so these types need nothing else. For decoding the statuses of
twitter.json into structs, reading the view is about 5 times faster than `#!cpp materialize().get<T>()`.
A hand-written `from_json()` reads views if it is constrained to `json` values and views:
```cpp
template < typename J, typename std::enable_if <
nlohmann::is_json_view<J>::value || std::is_same<J, nlohmann::json>::value, int >::type = 0 >
void from_json(const J& j, person& p)
{
p.name = j.at("name").template get<std::string>();
p.age = j.at("age").template get<int>();
}
```
Its body may only use what views and `basic_json` have in common: the type checks, `at()`, `operator[]`, `contains()`,
`value()`, `size()`, iteration and `items()`, and `get<T>()`/`get_to()`. An unconstrained
`#!cpp template<typename BasicJsonType> void from_json(const BasicJsonType& j, person& p)`, as in
[arbitrary type conversions](arbitrary_types.md), is never called with a view, as it might use `basic_json` functions
a view does not have; such a type is converted through `materialize()`. A `from_json()` for `json` only is
converted through `materialize()` as well.
Two things differ when a `from_json()` reads the view:
- **Duplicate keys.** Members looked up by name (`at()`, `value()`, ... and thus the macros) find the **first** member
with a repeated key, see [Duplicate keys](#duplicate-keys); `#!cpp materialize().get<T>()` would see the last one.
Containers built from an object, such as `#!cpp std::map`, keep the **last** value either way, as
[`parse()`](../api/basic_json/parse.md) does.
- **[`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md).** Exceptions thrown by the view carry no path to the
value.
`to_json()` is unaffected: it writes `basic_json` values, never views.
## Writing a view back ## Writing a view back
[`dump()`](../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a [`dump()`](../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
@@ -344,14 +252,14 @@ the stream's `width`/`fill` for indentation.
## Editing a document ## Editing a document
Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but Everything above is read-only: a `json_document`/`json_view` lets you look at a parsed text without copying it, but
not change it. [`basic_json_document<BasicJsonType, view_mode::editable>`](../api/basic_json_document/index.md) -- more conveniently not change it. [`basic_json_document<BasicJsonType, true>`](../api/basic_json_document/index.md) -- more conveniently
spelled [`json_editable_document`](../api/json_editable_document.md) or spelled [`json_editable_document`](../api/json_editable_document.md) or
[`ordered_json_editable_document`](../api/ordered_json_editable_document.md) -- also lets you [`ordered_json_editable_document`](../api/ordered_json_editable_document.md) -- also lets you
[`set`](../api/basic_json_document/set.md) a value, [`push_back`](../api/basic_json_document/push_back.md) onto or [`set`](../api/basic_json_document/set.md) a value, [`push_back`](../api/basic_json_document/push_back.md) onto or
[`insert`](../api/basic_json_document/insert.md) into an array, and [`erase`](../api/basic_json_document/erase.md) [`insert`](../api/basic_json_document/insert.md) into an array, and [`erase`](../api/basic_json_document/erase.md)
an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch. an object member or an array element, still without ever building a `basic_json` tree for parts you do not touch.
The [mode](../api/view_mode.md) defaults to `#!cpp view_mode::read_only`, so `json_document`/`ordered_json_document` are unaffected -- they carry `#!cpp Editable` defaults to `#!cpp false`, so `json_document`/`ordered_json_document` are unaffected -- they carry
none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a none of the bookkeeping edits need, and calling `set`/`push_back`/`insert`/`erase` on one is a compile error, not a
runtime one. runtime one.
@@ -446,10 +354,9 @@ validation entirely, for an image trusted as much as the process's own memory. S
!!! warning "Experimental" !!! warning "Experimental"
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
stable. It is little-endian only (on big-endian targets, `save()` and `load()` do not exist), and tied to the stable. It is little-endian only, and tied to the library build that wrote it -- use it to cache a document or to
library build that wrote it -- use it to cache a document or to hand one to another process running the *same* hand one to another process running the *same* build, not as a long-term storage format; keep the original JSON
build, not as a long-term storage format; keep the original JSON text if a saved document needs to be readable by text if a saved document needs to be readable by a future library version.
a future library version.
The idea of a document you can read without parsing comes from zero-copy formats such as The idea of a document you can read without parsing comes from zero-copy formats such as
[FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check [FlatBuffers](https://github.com/google/flatbuffers) and [YaFF](https://github.com/yandex/yaff); the check
+17
View File
@@ -287,6 +287,23 @@ external dependency and is not defined by default.
See [full documentation of `JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md). See [full documentation of `JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md).
## `JSON_VIEW_NO_SIMD`
When defined, the parser of [`basic_json_document`](../api/basic_json_document/index.md)
(`<nlohmann/json_view.hpp>`) uses only portable C++ to scan strings, instead of vector instructions (SSE2 on x86-64,
NEON on AArch64). The same input is accepted or rejected either way; only the speed differs. It is not defined by
default.
See [full documentation of `JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md).
## `JSON_VIEW_USE_SSSE3`
When defined on x86-64, the parser of [`basic_json_document`](../api/basic_json_document/index.md)
(`<nlohmann/json_view.hpp>`) validates non-ASCII text in strings with SSSE3 without asking the CPU first. It is not
defined by default.
See [full documentation of `JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md).
## `NLOHMANN_DEFINE_TYPE_*(...)`, `NLOHMANN_DEFINE_DERIVED_TYPE_*(...)` ## `NLOHMANN_DEFINE_TYPE_*(...)`, `NLOHMANN_DEFINE_DERIVED_TYPE_*(...)`
The library defines 12 macros to simplify the serialization/deserialization of types. See the page on The library defines 12 macros to simplify the serialization/deserialization of types. See the page on
+1 -13
View File
@@ -2,7 +2,7 @@
The library supports JSON Merge Patch ([RFC 7396](https://tools.ietf.org/html/rfc7396)) as a patch format. The library supports JSON Merge Patch ([RFC 7396](https://tools.ietf.org/html/rfc7396)) as a patch format.
The merge patch format is primarily intended for use with the HTTP PATCH method as a means of describing a set of The merge patch format is primarily intended for use with the HTTP PATCH method as a means of describing a set of
modifications to a target resource's content. Applying a merge patch changes the JSON value the patch is applied to. modifications to a target resource's content. This function applies a merge patch to the current JSON value.
Instead of using [JSON Pointer](json_pointer.md) to specify values to be manipulated, it describes the changes using a Instead of using [JSON Pointer](json_pointer.md) to specify values to be manipulated, it describes the changes using a
syntax that closely mimics the document being modified. Unlike [JSON Patch](json_patch.md), a JSON Merge Patch cannot syntax that closely mimics the document being modified. Unlike [JSON Patch](json_patch.md), a JSON Merge Patch cannot
@@ -19,18 +19,6 @@ another.
[`update`](../api/basic_json/update.md) with `merge_objects` set to `#!cpp true`; see [`update`](../api/basic_json/update.md) with `merge_objects` set to `#!cpp true`; see
[Merging objects](modifying_values.md#merging-objects). [Merging objects](modifying_values.md#merging-objects).
!!! note "Limitations of RFC 7396"
- A patch cannot set a member to `#!json null`, because `#!json null` removes the member:
`#!json {"a":1}` patched with `#!json {"a":null}` gives `#!json {}`.
- Arrays are replaced as a whole, never merged, and a `#!json null` inside an array is kept.
- `#!json null` members of the target that the patch does not mention are preserved.
- A `#!json null` that would remove a member of a not yet existing object still creates that object:
`#!json {}` patched with `#!json {"a":{"b":null}}` gives `#!json {"a":{}}`.
- A patch that is not an object replaces the whole target (a `#!json null` patch makes the target
`#!json null`); an object patch applied to a non-object target starts from an empty object.
- [`merge_diff`](../api/basic_json/merge_diff.md#notes) has a matching limitation for `#!json null` members.
??? example ??? example
The following code shows how a JSON Merge Patch is applied to a JSON document. The following code shows how a JSON Merge Patch is applied to a JSON document.
+6 -2
View File
@@ -28,6 +28,8 @@ The complete default namespace name is derived as follows:
- [`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) defined non-zero appends - [`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md) defined non-zero appends
`_dufio`. `_dufio`.
- [`JSON_NO_SIMD`](../api/macros/json_no_simd.md) defined appends `_ns`. - [`JSON_NO_SIMD`](../api/macros/json_no_simd.md) defined appends `_ns`.
- [`JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md) defined appends `_vns`.
- [`JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md) defined appends `_vssse3`.
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by - The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component) underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
below. below.
@@ -113,5 +115,7 @@ follows:
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), `_ekmo` ([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), `_ekmo`
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)), `_dsbs` ([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)), `_dsbs`
([`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md)), `_dufio` ([`JSON_DISABLE_STREAM_BULK_SCAN`](../api/macros/json_disable_stream_bulk_scan.md)), `_dufio`
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md)), and `_ns` ([`JSON_DISABLE_UNLOCKED_FILE_IO`](../api/macros/json_disable_unlocked_file_io.md)), `_ns`
([`JSON_NO_SIMD`](../api/macros/json_no_simd.md)) in version 3.13.0. ([`JSON_NO_SIMD`](../api/macros/json_no_simd.md)), `_vns`
([`JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md)), and `_vssse3`
([`JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md)) in version 3.13.0.
@@ -0,0 +1,122 @@
# Error Recovery
By default, parsing stops at the first error. With the [SAX interface](sax_interface.md), you can instead ask the
parser to *recover*: to repair the error and continue, so that you get as much as possible out of malformed input, for
instance a file that was cut off, JSON edited by hand, or the output of a language model.
## Recovering from errors
The SAX parser's [`parse_error`](../../api/json_sax/parse_error.md) function is called for every error. Its return value
decides what happens next:
- `#!cpp false` stops parsing. This is what the SAX parsers of the library do, so [`parse`](../../api/basic_json/parse.md)
and [`accept`](../../api/basic_json/accept.md) never recover.
- `#!cpp true` repairs the error and continues parsing.
When recovering, the SAX parser still receives well-formed events: every `start_object` or `start_array` is followed by
the matching `end_object` or `end_array`, and every `key` is followed by exactly one value. A SAX parser that creates a
JSON value, such as the one in the example below, therefore gets a complete value. Parsing always ends, and
[`sax_parse`](../../api/basic_json/sax_parse.md) returns `#!cpp false` for input that is not valid JSON, even if every
error was repaired. Each token is reported at most once, and the SAX parser can stop at any error by returning
`#!cpp false`.
!!! example
The example below derives a SAX parser from the library's parser for `json` values (`json_sax_dom_parser`),
and recovers from all errors.
```cpp
--8<-- "examples/sax_parse__error_recovery.cpp"
```
Output:
```
--8<-- "examples/sax_parse__error_recovery.output"
```
## How errors are repaired
Each error is repaired with the smallest local edit: a missing separator is inserted, a stray token is removed, what can
be read of a broken string or number is kept, and a value that cannot be read at all becomes `#!json null`.
| Mistake | Repair | Example | Result |
|---------------------------|--------------------------------------------------------------------------------|------------------------------------------|----------------------------|
| missing `,` or `:` | inserted | `#!json [1 2]`, `#!json {"a" 1}` | `[1,2]`, `{"a":1}` |
| missing value | `#!json null` for an object key or between commas in an array | `#!json {"a":}`, `#!json [1,,2]` | `{"a":null}`, `[1,null,2]` |
| trailing comma | removed | `#!json [1,2,]` | `[1,2]` |
| broken string | invalid escapes and bytes are replaced (see below); a line break ends the string | `#!json ["a\qb"]` | `["aqb"]` |
| broken number | the longest valid beginning is kept | `#!json [1., 2e+]` | `[1,2]` |
| unreadable value | `#!json null` | `#!json [1, NaN, tru]` | `[1,null,null]` |
| number too large | passed as infinity, together with its text | `#!json [1e999]` | infinity (see below) |
| stray `:` | removed | `#!json ["a":1]` | `["a",1]` |
| member without a key | skipped up to the next `,` or `}` | `#!json {1:2, "b":3}` | `{"b":3}` |
| wrong closing bracket | closes the innermost array or object | `#!json {"a":[1,2}, "b":3}` | `{"a":[1,2],"b":3}` |
| input ends too early | all open arrays and objects are closed | `#!json {"a":[1,2` | `{"a":[1,2]}` |
| text before the value | skipped | `#!json )]}'{"a":1}` | `{"a":1}` |
In a string, an unknown escape like `\q` stands for the escaped character (`q`), as in JavaScript. An invalid `\u`
escape, a lone surrogate, and ill-formed UTF-8 are each replaced by U+FFFD (REPLACEMENT CHARACTER), and control
characters are kept. A string without its closing quote ends at the next line break or at the end of the input.
The input after the top-level value is not repaired: as without recovery, it is reported as an error, and parsing stops.
## Binary formats
The binary formats ([BJData](../binary_formats/bjdata.md), [BON8](../binary_formats/bon8.md),
[BSON](../binary_formats/bson.md), [CBOR](../binary_formats/cbor.md), [MessagePack](../binary_formats/messagepack.md),
and [UBJSON](../binary_formats/ubjson.md)) have no delimiters to find the next value by. So what can be repaired depends
on whether the end of the item with the error is known, a distinction that
[RFC 8949, Section 5.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3) makes for CBOR, too.
If the item is complete, but cannot be passed on as it is, it is replaced, and parsing continues after it:
| Mistake | Formats | Repair |
|---------------------------------------------------------------------|-------------------------|-------------------------------------------------------------------------|
| tag, if `tag_handler` is `cbor_tag_handler_t::error` (the default) | CBOR | ignored |
| simple value other than `false`, `true`, and `null`, like undefined | CBOR | `#!json null` |
| number too large for a custom `number_float_t`, like `float` | all | infinity |
| character (`C`) that is not ASCII | BJData, UBJSON | U+FFFD |
| invalid high-precision number (`H`) | BJData, UBJSON | the longest valid beginning is kept, as for JSON text, or `#!json null` |
| high-precision number too large | BJData, UBJSON | passed as infinity, together with its text |
| object key that is not a string | BON8, CBOR, MessagePack | the member is skipped |
| element of a type the library does not read, like ObjectId or date | BSON | `#!json null` |
| string without its terminator | BSON | kept |
| document whose size does not match its content | BSON | kept |
CBOR tags and simple values are repaired as [RFC 8949, Section 6.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-6.1)
suggests for converting CBOR to JSON. By default, [`sax_parse`](../../api/basic_json/sax_parse.md) reports every tag as
an error; when recovering, tags are then ignored like with
[`cbor_tag_handler_t::ignore`](../../api/basic_json/cbor_tag_handler_t.md). Strings that are not valid UTF-8 are no
error: like [`from_cbor`](../../api/basic_json/from_cbor.md) and the other functions by default, `sax_parse` passes
them on as they are.
After any other error, the end of the item is unknown: the input ended, a byte is not a valid type marker, or a size
cannot be right. Parsing then stops, and the value read so far is completed: a key that waits for its value gets
`#!json null`, and all open arrays and objects are closed. This keeps everything before the error of an input that was
cut off. The exception is BSON, which stores the size of every document: an element whose end is unknown gets
`#!json null`, the rest of its document is skipped, and parsing continues after the document.
## Limitations
- A repair is a guess. For example, `#!json {"a" "b": 1}` could be meant as `#!json {"a": "b"}` or as
`#!json {"a": null, "b": 1}`; it is repaired to the former. Treat recovered values as a best effort, and check the
reported errors.
- A closing bracket always closes the innermost array or object. If a bracket is missing rather than wrong, the
repair differs from the intention: `#!json {"a": {"b": [1, 2}, "c": 3}` is repaired to
`#!json {"a": {"b": [1, 2], "c": 3}}`, although `#!json {"a": {"b": [1, 2]}, "c": 3}` may have been meant.
- Keys without quotes, and strings in single quotes, are not supported; such members are skipped.
- In the binary formats, a member that is skipped because its key is not a string is lost, and so are the elements of a
BSON document after one whose end is unknown.
- A number that is too large for `number_float_t` is passed as positive or negative infinity. The SAX parser's
`number_float` also gets the number's text, but a JSON value cannot store it, and
[`dump`](../../api/basic_json/dump.md) serializes infinity as `#!json null`.
- When parsing is not strict (see [`sax_parse`](../../api/basic_json/sax_parse.md)), a repair may read parts of the
input after the value, for instance of the next value in a stream of concatenated values.
## See also
- [SAX interface](sax_interface.md) - implement a custom SAX handler
- [`parse_error`](../../api/json_sax/parse_error.md) - the SAX event for parse errors
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
- [parsing and exceptions](parse_exceptions.md) - control error handling
+2 -1
View File
@@ -139,7 +139,7 @@ See [Zero-copy JSON views](../json_view.md) for the concept and the trade-offs.
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used
to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available
options. options. To get as much as possible out of malformed input, a SAX parser can [recover from errors](error_recovery.md).
## See also ## See also
@@ -153,5 +153,6 @@ options.
- [SAX interface](sax_interface.md) - implement a custom SAX handler - [SAX interface](sax_interface.md) - implement a custom SAX handler
- [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views - [Zero-copy JSON views](../json_view.md) - parse into a read-only document and read it through views
- [parsing and exceptions](parse_exceptions.md) - control error handling - [parsing and exceptions](parse_exceptions.md) - control error handling
- [error recovery](error_recovery.md) - get as much as possible out of malformed input
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources - [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
- [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view - [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view
@@ -68,7 +68,8 @@ bool parse_error(std::size_t position,
const json::exception& ex); const json::exception& ex);
``` ```
The return value indicates whether the parsing should continue, so the function should usually return `#!cpp false`. The return value decides whether to stop parsing (`#!cpp false`) or to repair the error and continue
(`#!cpp true`); see [error recovery](error_recovery.md) for the latter.
??? example "Example: report parse errors without exceptions" ??? example "Example: report parse errors without exceptions"
@@ -60,7 +60,8 @@ bool key(string_t& val);
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex); bool parse_error(std::size_t position, const std::string& last_token, const json::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
`#!cpp true` [recovers from the error](error_recovery.md).
To implement your own SAX handler, proceed as follows: To implement your own SAX handler, proceed as follows:
@@ -68,7 +69,7 @@ To implement your own SAX handler, proceed as follows:
2. Create an object of your SAX interface class, e.g. `my_sax`. 2. Create an object of your SAX interface class, e.g. `my_sax`.
3. Call `#!cpp 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. 3. Call `#!cpp 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 `#!cpp bool` indicating the result of the last executed SAX event. It does not return `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`. Note the `sax_parse` function only returns a `#!cpp bool` indicating whether the input was parsed without errors and no SAX event returned `#!cpp false`. It does not return `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`.
## See also ## See also
@@ -368,7 +368,8 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
`#!cpp std::from_chars` is not available or declines the token, and `std::strtold` reads up to the null character. A type whose `data()` `#!cpp std::from_chars` is not available or declines the token, and `std::strtold` reads up to the null character. A type whose `data()`
is not null-terminated does not fail to compile; with such a `NumberFloatType` it can silently misparse is not null-terminated does not fail to compile; with such a `NumberFloatType` it can silently misparse
floating-point numbers. floating-point numbers.
- `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md) and by the binary readers. The library's internal string concatenation additionally has - `append(const char*, size_type)`, used by [`dump`](../../api/basic_json/dump.md), and `append(const StringType&)`,
used by the CBOR reader for indefinite-length strings. The library's internal string concatenation additionally has
to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`, to append a `#!cpp char` and a `#!cpp const char*`; for each it selects between `append(arg)`, `#!cpp operator+=`,
`append(first, last)`, and `append(data, size)`. `append(first, last)`, and `append(data, size)`.
- The comparison operator `==` against another `StringType`, and `<` for use as a key of the chosen - The comparison operator `==` against another `StringType`, and `<` for use as a key of the chosen
@@ -389,7 +390,6 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
described in RFC 6901. Neither `find(const StringType&, size_type)`, nor `substr(pos, count)`, nor described in RFC 6901. Neither `find(const StringType&, size_type)`, nor `substr(pos, count)`, nor
`replace(pos, count, const StringType&)` is required. `replace(pos, count, const StringType&)` is required.
- `empty()`. - `empty()`.
- `resize(n)` (one argument) -- used by `to_string` to size the result before the reference tokens are copied into it.
- `begin()` and `end()` -- used by - `begin()` and `end()` -- used by
[`operator[](const json_pointer&)`](../../api/basic_json/operator%5B%5D.md) to decide whether a reference token [`operator[](const json_pointer&)`](../../api/basic_json/operator%5B%5D.md) to decide whether a reference token
denotes an array index. denotes an array index.
@@ -678,7 +678,8 @@ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
[`StringType`](#stringtype), whose non-`#!cpp const` `data()` is C++17 and later only. [`StringType`](#stringtype), whose non-`#!cpp const` `data()` is C++17 and later only.
- Default-constructible, copy-constructible, and move-constructible. - Default-constructible, copy-constructible, and move-constructible.
- Member functions `size()`, `empty()`, `data()`, `resize()`, `operator[]`, `back()`, `begin()`, `end()`, `cbegin()`, - Member functions `size()`, `empty()`, `data()`, `resize()`, `operator[]`, `back()`, `begin()`, `end()`, `cbegin()`,
and `cend()` with random-access iterators. `push_back()` is **not** required. and `cend()` with random-access iterators, and `insert(pos, first, last)`, which the CBOR reader uses to join the
chunks of an indefinite-length byte string. `push_back()` is **not** required.
- Comparison operators: `==` is used by - Comparison operators: `==` is used by
[`byte_container_with_subtype`](../../api/byte_container_with_subtype/index.md), the relational operators by [`byte_container_with_subtype`](../../api/byte_container_with_subtype/index.md), the relational operators by
[`basic_json`'s comparison operators](../../api/basic_json/operator_le.md). [`basic_json`'s comparison operators](../../api/basic_json/operator_le.md).
+2 -2
View File
@@ -21,10 +21,10 @@ ABI tags are not covered and may show raw internal fields instead. To generate a
``` ```
3. Generate a Natvis file with the tags of your configuration, for example for `JSON_DIAGNOSTICS`, 3. Generate a Natvis file with the tags of your configuration, for example for `JSON_DIAGNOSTICS`,
`JSON_DIAGNOSTIC_POSITIONS`, and `JSON_NO_SIMD`: `JSON_DIAGNOSTIC_POSITIONS`, and `JSON_VIEW_NO_SIMD`:
```shell ```shell
make natvis NATVIS_ARGS="--abi-tags _diag,_dp,_ns --output my_nlohmann_json.natvis" make natvis NATVIS_ARGS="--abi-tags _diag,_dp,_vns --output my_nlohmann_json.natvis"
``` ```
4. Use the generated file instead of `nlohmann_json.natvis`. 4. Use the generated file instead of `nlohmann_json.natvis`.
+19 -40
View File
@@ -330,7 +330,7 @@ where they cannot be resolved.
### json.exception.parse_error.110 ### json.exception.parse_error.110
When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has
been read, or (with `strict = true`) further bytes follow the value. been read.
!!! failure "Example messages" !!! failure "Example messages"
@@ -346,7 +346,7 @@ been read, or (with `strict = true`) further bytes follow the value.
### json.exception.parse_error.112 ### json.exception.parse_error.112
An unexpected byte was read in a [binary format](../features/binary_formats/index.md) or length information is invalid ([BSON](../features/binary_formats/bson.md)). In [BON8](../features/binary_formats/bon8.md), this exception is also thrown for a key that is not a string and for ill-formed UTF-8. An unexpected byte was read in a [binary format](../features/binary_formats/index.md) or length information is invalid ([BSON](../features/binary_formats/bson.md)).
!!! failure "Example messages" !!! failure "Example messages"
@@ -371,21 +371,15 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
``` ```
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5) [json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
``` ```
```
[json.exception.parse_error.112] parse error at byte 2: syntax error while parsing BON8 string: invalid UTF-8 byte: 0x80
```
### json.exception.parse_error.113 ### json.exception.parse_error.113
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
string was read where one was required (for instance as a map key), the string's length specification is invalid, or string was read where one was required (for instance as a map key), the string's length specification is invalid, or
the string's bytes are not valid UTF-8 and the `error_handler` parameter of the corresponding `from_*` function is the string's bytes are not valid UTF-8 and the `error_handler` parameter of the corresponding `from_*` function is
set to `strict`. By default (`error_handler_t::keep`), the bytes of a string are not checked for valid UTF-8 on read. set to `strict`. By default (`error_handler_t::keep`), the bytes of a string are not checked for valid UTF-8 on read;
This applies to the `from_*` functions that have an `error_handler` parameter; `from_bon8` does not have one. see the ill-formed UTF-8 notes on the individual [binary format](../features/binary_formats/index.md) pages for how
See the ill-formed UTF-8 notes on the individual [binary format](../features/binary_formats/index.md) pages for how
such a string is handled depending on `error_handler`. such a string is handled depending on `error_handler`.
[`from_bon8()`](../api/basic_json/from_bon8.md) never throws this exception: BON8 non-string keys and ill-formed UTF-8
are reported as [parse_error.112](#jsonexceptionparse_error112).
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
type (for instance integers or `null`) are therefore not supported; see the notes on type (for instance integers or `null`) are therefore not supported; see the notes on
@@ -424,16 +418,13 @@ The parsing of the corresponding BSON record type is not implemented (yet).
### json.exception.parse_error.115 ### json.exception.parse_error.115
A UBJSON or BJData high-precision number could not be parsed. A UBJSON high-precision number could not be parsed.
!!! failure "Example messages" !!! failure "Example message"
``` ```
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A [json.exception.parse_error.115] parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A
``` ```
```
[json.exception.parse_error.115] parse error at byte 5: syntax error while parsing BJData high-precision number: invalid number text: 1A
```
### json.exception.parse_error.116 ### json.exception.parse_error.116
@@ -821,9 +812,6 @@ The binary writers [`to_cbor()`](../api/basic_json/to_cbor.md), [`to_ubjson()`](
as well for a string value or object key that is not valid UTF-8 if their `error_handler` is `strict` (the default if as well for a string value or object key that is not valid UTF-8 if their `error_handler` is `strict` (the default if
[`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) is enabled). So does [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) is enabled). So does
[`to_msgpack()`](../api/basic_json/to_msgpack.md) if `error_handler_t::strict` is passed. [`to_msgpack()`](../api/basic_json/to_msgpack.md) if `error_handler_t::strict` is passed.
[`to_bon8()`](../api/basic_json/to_bon8.md) always throws this exception for such a string or key, regardless of
[`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md), because the end of a BON8 string is determined
from its UTF-8 encoding.
!!! failure "Example message" !!! failure "Example message"
@@ -895,12 +883,17 @@ from JSON text.
[`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an [`basic_json_document::save()`](../api/basic_json_document/save.md) cannot write an
[image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document. [image](../features/json_view.md#images) of a [discarded](../api/basic_json_document/is_discarded.md) document.
[`save()`](../api/basic_json_document/save.md) and [`load()`](../api/basic_json_document/load.md) also throw this
exception on a big-endian target, since the image format is little-endian only.
!!! failure "Example messages" !!! failure "Example messages"
``` ```
[json.exception.type_error.320] cannot save a discarded json_document [json.exception.type_error.320] cannot save a discarded json_document
``` ```
```
[json.exception.type_error.320] json_document images need a little-endian target
```
!!! note !!! note
@@ -1013,8 +1006,7 @@ double-precision number when `number_float_t` is `#!cpp float`.
An integer number cannot be represented by the binary format it is serialized to: An integer number cannot be represented by the binary format it is serialized to:
- [BON8](../features/binary_formats/bon8.md) only stores integers that fit into int64, so - [BON8](../features/binary_formats/bon8.md) only stores integers that fit into int64, so
[`to_bon8`](../api/basic_json/to_bon8.md) throws for unsigned integers greater than 9223372036854775807 and, with a [`to_bon8`](../api/basic_json/to_bon8.md) throws for unsigned integers greater than 9223372036854775807.
number type wider than 64 bits, for any integer outside the int64 range.
- [CBOR](../features/binary_formats/cbor.md), [MessagePack](../features/binary_formats/messagepack.md), and - [CBOR](../features/binary_formats/cbor.md), [MessagePack](../features/binary_formats/messagepack.md), and
[BSON](../features/binary_formats/bson.md) store integers in at most 64 bits. With the default number types, every [BSON](../features/binary_formats/bson.md) store integers in at most 64 bits. With the default number types, every
integer fits, but a [`number_integer_t`](../api/basic_json/number_integer_t.md) or integer fits, but a [`number_integer_t`](../api/basic_json/number_integer_t.md) or
@@ -1033,7 +1025,7 @@ exception, because they serialize integers beyond 64 bits as high-precision numb
- Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers. - Since version 3.12.0, integer numbers beyond int64 are serialized as uint64 BSON numbers.
Before version 3.13.0, CBOR, MessagePack, and BSON silently truncated integers wider than 64 bits, and BJData Before version 3.13.0, CBOR, MessagePack, and BSON silently truncated integers wider than 64 bits, and BJData
truncated unsigned integers wider than 64 bits that were elements of ND-arrays with `"_ArrayType_": "uint64"`. truncated unsigned integers wider than 64 bits.
!!! failure "Example messages" !!! failure "Example messages"
@@ -1053,8 +1045,8 @@ or the encoded length for [CBOR](../features/binary_formats/cbor.md).
The exception is also thrown for a [UBJSON](../features/binary_formats/ubjson.md) array of a type that is encoded by its The exception is also thrown for a [UBJSON](../features/binary_formats/ubjson.md) array of a type that is encoded by its
marker alone (`Z`, `T` or `F`) whose declared count exceeds 1,048,576 (`1 << 20`). Such an array has no payload, so its marker alone (`Z`, `T` or `F`) whose declared count exceeds 1,048,576 (`1 << 20`). Such an array has no payload, so its
count alone decides how much memory is allocated, and a handful of bytes would otherwise describe billions of values. count alone decides how much memory is allocated, and a handful of bytes would otherwise describe billions of values.
[`to_ubjson`](../api/basic_json/to_ubjson.md) writes longer arrays of these types without the `$` type annotation (the `#` [`to_ubjson`](../api/basic_json/to_ubjson.md) writes longer arrays of these types without the size and type annotation,
count and one marker per element are kept), so any value it produces can still be read back. so any value it produces can still be read back.
!!! failure "Example messages" !!! failure "Example messages"
@@ -1091,14 +1083,9 @@ a JSON pointer exceeds the range of `size_type` (e.g., on 32-bit platforms).
``` ```
enum value out of range enum value out of range
array index 18446744073709551615 exceeds size_type array index 18446744073709551616 exceeds size_type
``` ```
The second message is thrown, for instance, by `#!cpp json::parse("[1, 2]").at("/18446744073709551615"_json_pointer)`
on a 64-bit platform, where `size_type` is `#!cpp std::size_t` and this index is `SIZE_MAX`. An index that does not
fit into 64 bits (such as `18446744073709551616`) is not reported as 410 but as
[`out_of_range.404`](#jsonexceptionout_of_range404).
### json.exception.out_of_range.411 ### json.exception.out_of_range.411
A JSON Patch `add` operation cannot be applied because the target location's parent is neither an object nor an array. Per [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902), an `add` target must reference a member of an existing object or an element of an existing array; a primitive value (string, number, boolean, etc.) cannot receive a new member or element. A JSON Patch `add` operation cannot be applied because the target location's parent is neither an object nor an array. Per [RFC 6902](https://datatracker.ietf.org/doc/html/rfc6902), an `add` target must reference a member of an existing object or an element of an existing array; a primitive value (string, number, boolean, etc.) cannot receive a new member or element.
@@ -1113,8 +1100,6 @@ A JSON Patch `add` operation cannot be applied because the target location's par
This exception was added in version 3.13.0. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled. This exception was added in version 3.13.0. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
A `null` parent is rejected as well. Before version 3.13.0, an `add` into a `null` parent silently converted it to an object (e.g., `add` at `/foo/bar` turned `{"foo":null}` into `{"foo":{"bar":1}}`).
### json.exception.out_of_range.412 ### json.exception.out_of_range.412
BSON stores the length of documents, arrays, strings, and binary values in a signed 32-bit integer, and MessagePack BSON stores the length of documents, arrays, strings, and binary values in a signed 32-bit integer, and MessagePack
@@ -1168,18 +1153,14 @@ A JSON Patch `move` operation's `"from"` location is a proper prefix of its `"pa
### json.exception.out_of_range.415 ### json.exception.out_of_range.415
MessagePack's ext type and BSON's binary subtype are each stored in a single byte. This exception is thrown when serializing a MessagePack's ext type and BSON's binary subtype are each stored in a single byte. This exception is thrown when serializing a
[`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) whose subtype exceeds 255, the largest value the library can store in that byte. [`byte_container_with_subtype`](../api/byte_container_with_subtype/index.md) whose subtype exceeds 255.
!!! failure "Example messages" !!! failure "Example message"
``` ```
[json.exception.out_of_range.415] subtype 70000 is too large for the MessagePack ext type (max 255) [json.exception.out_of_range.415] subtype 70000 is too large for the MessagePack ext type (max 255)
``` ```
```
[json.exception.out_of_range.415] subtype 70000 is too large for the BSON binary subtype (max 255)
```
!!! note !!! note
This exception was added in version 3.13.0. Before that, subtypes above 255 were silently truncated modulo 256 instead of raising an error. This exception was added in version 3.13.0. Before that, subtypes above 255 were silently truncated modulo 256 instead of raising an error.
@@ -1248,9 +1229,7 @@ A JSON Patch operation 'test' failed. The unsuccessful operation is also printed
### json.exception.other_error.502 ### json.exception.other_error.502
[`to_ubjson`](../api/basic_json/to_ubjson.md) and [`to_bjdata`](../api/basic_json/to_bjdata.md) were called with [`to_ubjson`](../api/basic_json/to_ubjson.md) and [`to_bjdata`](../api/basic_json/to_bjdata.md) were called with
`use_type = true` but `use_size = false`, and the value contains a non-empty array or object (or, with BJData Draft 3, `use_type = true` but `use_size = false`. UBJSON requires a size marker (`#`) after a type marker (`$`).
any binary value). UBJSON requires a size marker (`#`) after a type marker (`$`); scalars and empty arrays or objects
do not throw.
!!! failure "Example message" !!! failure "Example message"
+3 -3
View File
@@ -136,6 +136,7 @@ nav:
- features/performance.md - features/performance.md
- Parsing: - Parsing:
- features/parsing/index.md - features/parsing/index.md
- features/parsing/error_recovery.md
- features/parsing/json_lines.md - features/parsing/json_lines.md
- features/parsing/large_and_partial_input.md - features/parsing/large_and_partial_input.md
- features/parsing/parse_exceptions.md - features/parsing/parse_exceptions.md
@@ -173,7 +174,6 @@ nav:
- 'begin': api/basic_json/begin.md - 'begin': api/basic_json/begin.md
- 'binary': api/basic_json/binary.md - 'binary': api/basic_json/binary.md
- 'binary_t': api/basic_json/binary_t.md - 'binary_t': api/basic_json/binary_t.md
- 'bjdata_version_t': api/basic_json/bjdata_version_t.md
- 'boolean_t': api/basic_json/boolean_t.md - 'boolean_t': api/basic_json/boolean_t.md
- 'cbegin': api/basic_json/cbegin.md - 'cbegin': api/basic_json/cbegin.md
- 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md - 'cbor_tag_handler_t': api/basic_json/cbor_tag_handler_t.md
@@ -362,7 +362,6 @@ nav:
- 'Overview': api/adl_serializer/index.md - 'Overview': api/adl_serializer/index.md
- 'from_json': api/adl_serializer/from_json.md - 'from_json': api/adl_serializer/from_json.md
- 'to_json': api/adl_serializer/to_json.md - 'to_json': api/adl_serializer/to_json.md
- 'is_json_view': api/is_json_view.md
- 'json': api/json.md - 'json': api/json.md
- 'json_document': api/json_document.md - 'json_document': api/json_document.md
- 'json_editable_document': api/json_editable_document.md - 'json_editable_document': api/json_editable_document.md
@@ -412,7 +411,6 @@ nav:
- 'ordered_json_editable_view': api/ordered_json_editable_view.md - 'ordered_json_editable_view': api/ordered_json_editable_view.md
- 'ordered_json_view': api/ordered_json_view.md - 'ordered_json_view': api/ordered_json_view.md
- 'ordered_map': api/ordered_map.md - 'ordered_map': api/ordered_map.md
- 'view_mode': api/view_mode.md
- macros: - macros:
- 'Overview': api/macros/index.md - 'Overview': api/macros/index.md
- 'JSON_ASSERT': api/macros/json_assert.md - 'JSON_ASSERT': api/macros/json_assert.md
@@ -447,6 +445,8 @@ nav:
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md - 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.md
- 'JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS': api/macros/json_use_objects_for_enum_keyed_maps.md - 'JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS': api/macros/json_use_objects_for_enum_keyed_maps.md
- 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md - 'JSON_USE_SIMDUTF': api/macros/json_use_simdutf.md
- 'JSON_VIEW_NO_SIMD': api/macros/json_view_no_simd.md
- 'JSON_VIEW_USE_SSSE3': api/macros/json_view_use_ssse3.md
- 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md - 'NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_derived_type.md
- 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md - 'NLOHMANN_DEFINE_TYPE_INTRUSIVE, NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_intrusive.md
- 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md - 'NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT, NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE': api/macros/nlohmann_define_type_non_intrusive.md
+18 -4
View File
@@ -128,14 +128,26 @@
#define NLOHMANN_JSON_ABI_TAG_NO_SIMD #define NLOHMANN_JSON_ABI_TAG_NO_SIMD
#endif #endif
#ifdef JSON_VIEW_NO_SIMD
#define NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD _vns
#else
#define NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD
#endif
#ifdef JSON_VIEW_USE_SSSE3
#define NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3 _vssse3
#else
#define NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3
#endif
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION #ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
#endif #endif
// Construct the namespace ABI tags component // Construct the namespace ABI tags component
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k #define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l, m) json_abi ## a ## b ## c ## d ## e ## f ## g ## h ## i ## j ## k ## l ## m
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h, i, j, k) \ #define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g, h, i, j, k, l, m) \
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k) NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g, h, i, j, k, l, m)
#define NLOHMANN_JSON_ABI_TAGS \ #define NLOHMANN_JSON_ABI_TAGS \
NLOHMANN_JSON_ABI_TAGS_CONCAT( \ NLOHMANN_JSON_ABI_TAGS_CONCAT( \
@@ -149,7 +161,9 @@
NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \ NLOHMANN_JSON_ABI_TAG_OBJECTS_FOR_ENUM_KEYED_MAPS, \
NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \ NLOHMANN_JSON_ABI_TAG_DISABLE_STREAM_BULK_SCAN, \
NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO, \ NLOHMANN_JSON_ABI_TAG_DISABLE_UNLOCKED_FILE_IO, \
NLOHMANN_JSON_ABI_TAG_NO_SIMD) NLOHMANN_JSON_ABI_TAG_NO_SIMD, \
NLOHMANN_JSON_ABI_TAG_VIEW_NO_SIMD, \
NLOHMANN_JSON_ABI_TAG_VIEW_USE_SSSE3)
// Construct the namespace version component // Construct the namespace version component
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+59 -287
View File
@@ -10,7 +10,6 @@
#include <algorithm> // min, transform #include <algorithm> // min, transform
#include <array> // array #include <array> // array
#include <cstddef> // nullptr_t
#include <forward_list> // forward_list #include <forward_list> // forward_list
#include <iterator> // inserter, front_inserter, end #include <iterator> // inserter, front_inserter, end
#include <map> // map #include <map> // map
@@ -28,7 +27,6 @@
#include <nlohmann/detail/meta/identity_tag.hpp> #include <nlohmann/detail/meta/identity_tag.hpp>
#include <nlohmann/detail/meta/std_fs.hpp> #include <nlohmann/detail/meta/std_fs.hpp>
#include <nlohmann/detail/meta/type_traits.hpp> #include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/output/error_handler.hpp>
#include <nlohmann/detail/string_concat.hpp> #include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/value_t.hpp> #include <nlohmann/detail/value_t.hpp>
@@ -45,70 +43,6 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail namespace detail
{ {
// The from_json() overloads below read basic_json values and the views of
// <nlohmann/json_view.hpp> (BasicJsonType is then a view, and
// json_type_of_t<BasicJsonType> its basic_json type). The helpers below are
// the accesses that differ between the two.
// the enable_if parameter of the overloads that read basic_json values (written
// with negation<> because astyle spaces out a template argument list with '!')
template<typename T>
using enable_if_not_view_t = enable_if_t<negation<is_json_view<T>>::value, int>;
// the boolean of j, a boolean
template<typename BasicJsonType, enable_if_not_view_t<BasicJsonType> = 0>
typename BasicJsonType::boolean_t boolean_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
bool boolean_value(const View& v)
{
return v.template get<bool>();
}
// the string of j, a string
template<typename BasicJsonType, enable_if_not_view_t<BasicJsonType> = 0>
const typename BasicJsonType::string_t& string_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::string_t string_value(const View& v)
{
const auto s = v.get_string();
return typename View::string_t(s.data(), s.size());
}
// the binary value of j, a binary value (never a view: a view of a JSON text
// holds no binary values)
template<typename BasicJsonType, enable_if_not_view_t<BasicJsonType> = 0>
const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::binary_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::basic_json_type::binary_t binary_value(const View& /*unused*/)
{
return {}; // LCOV_EXCL_LINE
}
// the elements of j, an array, for a range-based for loop
template<typename BasicJsonType, enable_if_not_view_t<BasicJsonType> = 0>
const typename BasicJsonType::array_t& array_elements(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
View array_elements(const View& v) noexcept
{
return v; // (a view is two pointers)
}
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n) inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{ {
@@ -137,7 +71,7 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
// read a number value of any of the three number types into val; // read a number value of any of the three number types into val;
// returns false (leaving val untouched) if j is not a number // returns false (leaving val untouched) if j is not a number
template<typename BasicJsonType, typename ArithmeticType, enable_if_not_view_t<BasicJsonType> = 0> template<typename BasicJsonType, typename ArithmeticType>
bool get_number_value(const BasicJsonType& j, ArithmeticType& val) bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
{ {
switch (static_cast<value_t>(j)) switch (static_cast<value_t>(j))
@@ -174,22 +108,10 @@ bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
} }
} }
// a view converts numbers itself (as basic_json does)
template<typename View, typename ArithmeticType, enable_if_t<is_json_view<View>::value, int> = 0>
bool get_number_value(const View& v, ArithmeticType& val)
{
if (!v.is_number())
{
return false;
}
val = v.template get<ArithmeticType>();
return true;
}
// overloads for basic_json template parameters // overloads for basic_json template parameters
template < typename BasicJsonType, typename ArithmeticType, template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < std::is_arithmetic<ArithmeticType>::value&& enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::boolean_t>::value, !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 > int > = 0 >
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{ {
@@ -200,31 +122,31 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
} }
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::boolean_t& b) inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{ {
throw_type_must_be("boolean", j); throw_type_must_be("boolean", j);
} }
b = boolean_value(j); b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
} }
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::string_t& s) inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); throw_type_must_be("string", j);
} }
s = string_value(j); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
template < template <
typename BasicJsonType, typename StringType, typename BasicJsonType, typename StringType,
enable_if_t < enable_if_t <
std::is_assignable<StringType&, const typename json_type_of_t<BasicJsonType>::string_t>::value std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
&& is_detected_exact<typename json_type_of_t<BasicJsonType>::string_t::value_type, value_type_t, StringType>::value && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
&& !std::is_same<typename json_type_of_t<BasicJsonType>::string_t, StringType>::value && !std::is_same<typename BasicJsonType::string_t, StringType>::value
&& !is_json_ref<StringType>::value, int > = 0 > && !is_json_ref<StringType>::value, int > = 0 >
inline void from_json(const BasicJsonType& j, StringType& s) inline void from_json(const BasicJsonType& j, StringType& s)
{ {
@@ -233,23 +155,23 @@ inline void from_json(const BasicJsonType& j, StringType& s)
throw_type_must_be("string", j); throw_type_must_be("string", j);
} }
s = string_value(j); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::number_float_t& val) inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
{ {
get_arithmetic_value(j, val); get_arithmetic_value(j, val);
} }
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<json_type_of_t<BasicJsonType>>& val) inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<BasicJsonType>& val)
{ {
get_arithmetic_value(j, val); get_arithmetic_value(j, val);
} }
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::number_integer_t& val) inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
{ {
get_arithmetic_value(j, val); get_arithmetic_value(j, val);
} }
@@ -261,8 +183,8 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
{ {
using underlying_type = typename std::underlying_type<EnumType>::type; using underlying_type = typename std::underlying_type<EnumType>::type;
// get_arithmetic_value() does not accept boolean_t; read the number that to_json() wrote instead // get_arithmetic_value() does not accept boolean_t; read the number that to_json() wrote instead
using value_type = typename std::conditional<std::is_same<underlying_type, typename json_type_of_t<BasicJsonType>::boolean_t>::value, using value_type = typename std::conditional<std::is_same<underlying_type, typename BasicJsonType::boolean_t>::value,
typename json_type_of_t<BasicJsonType>::number_unsigned_t, underlying_type>::type; typename BasicJsonType::number_unsigned_t, underlying_type>::type;
value_type val; value_type val;
get_arithmetic_value(j, val); get_arithmetic_value(j, val);
e = static_cast<EnumType>(bool_aware_static_cast<underlying_type>(val)); e = static_cast<EnumType>(bool_aware_static_cast<underlying_type>(val));
@@ -271,7 +193,7 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method // forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator, template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<json_type_of_t<BasicJsonType>, T>::value, int> = 0> enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l) inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -279,16 +201,16 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
throw_type_must_be("array", j); throw_type_must_be("array", j);
} }
l.clear(); l.clear();
auto pos = l.before_begin(); std::transform(j.rbegin(), j.rend(),
for (const auto& elem : array_elements(j)) std::front_inserter(l), [](const BasicJsonType & i)
{ {
pos = l.insert_after(pos, elem.template get<T>()); return i.template get<T>();
} });
} }
// valarray doesn't have an insert method // valarray doesn't have an insert method
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T,
enable_if_t<is_getable<json_type_of_t<BasicJsonType>, T>::value, int> = 0> enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
inline void from_json(const BasicJsonType& j, std::valarray<T>& l) inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -296,11 +218,11 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
throw_type_must_be("array", j); throw_type_must_be("array", j);
} }
l.resize(j.size()); l.resize(j.size());
std::size_t i = 0; std::transform(j.begin(), j.end(), std::begin(l),
for (const auto& elem : array_elements(j)) [](const BasicJsonType & elem)
{ {
l[i++] = elem.template get<T>(); return elem.template get<T>();
} });
} }
// element is not itself a C array: read it directly // element is not itself a C array: read it directly
@@ -328,8 +250,7 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines
from_json_c_array_element(j, arr); from_json_c_array_element(j, arr);
} }
// (for a view, array_t is read element by element below) template<typename BasicJsonType>
template<typename BasicJsonType, enable_if_not_view_t<BasicJsonType> = 0>
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{ {
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>(); arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
@@ -338,7 +259,7 @@ inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType:
// fill a std::array from the JSON array j: the size is checked once and the // fill a std::array from the JSON array j: the size is checked once and the
// elements are read in order, so a short array converts the elements it has and // elements are read in order, so a short array converts the elements it has and
// then throws the out_of_range error at() would throw for the first missing one // then throws the out_of_range error at() would throw for the first missing one
template<typename BasicJsonType, typename T, std::size_t N, enable_if_not_view_t<BasicJsonType> = 0> template<typename BasicJsonType, typename T, std::size_t N>
void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr) void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr)
{ {
const auto& inner_array = *j.template get_ptr<const typename BasicJsonType::array_t*>(); const auto& inner_array = *j.template get_ptr<const typename BasicJsonType::array_t*>();
@@ -353,21 +274,6 @@ void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr)
} }
} }
// (a view has no random access to the elements: they are read in order)
template<typename View, typename T, std::size_t N, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_std_array(const View& v, std::array<T, N>& arr)
{
std::size_t size = 0;
for (auto it = v.begin(); size < N && it != v.end(); ++it)
{
arr[size++] = (*it).template get<T>();
}
if (JSON_HEDLEY_UNLIKELY(size < N))
{
static_cast<void>(v.at(size));
}
}
template<typename BasicJsonType, typename T, std::size_t N> template<typename BasicJsonType, typename T, std::size_t N>
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr, auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
priority_tag<2> /*unused*/) priority_tag<2> /*unused*/)
@@ -416,8 +322,9 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
void()) void())
{ {
ConstructibleArrayType ret; ConstructibleArrayType ret;
reserve_if_possible(ret, j.size(), priority_tag<1> {}); const auto* inner_array = j.template get_ptr<const typename BasicJsonType::array_t*>();
for (const auto& elem : array_elements(j)) reserve_if_possible(ret, inner_array->size(), priority_tag<1> {});
for (const auto& elem : *inner_array)
{ {
// get<BasicJsonType>() returns *this, this won't call a from_json // get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType // method when value_type is BasicJsonType
@@ -428,11 +335,11 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
template < typename BasicJsonType, typename ConstructibleArrayType, template < typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t < enable_if_t <
is_constructible_array_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&& is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_object_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&& !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&& !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename json_type_of_t<BasicJsonType>::binary_t>::value&& !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_compatible_binary_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&& !is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_basic_json<ConstructibleArrayType>::value, !is_basic_json<ConstructibleArrayType>::value,
int > = 0 > int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
@@ -499,24 +406,24 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
} }
template<typename BasicJsonType> template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::binary_t& bin) inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{ {
throw_type_must_be("binary", j); throw_type_must_be("binary", j);
} }
bin = binary_value(j); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
} }
template < typename BasicJsonType, typename CompatibleArrayType, template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<json_type_of_t<BasicJsonType>, CompatibleArrayType>::value, enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
int > = 0 > int > = 0 >
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin) inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
{ {
if (j.is_binary()) if (j.is_binary())
{ {
bin = static_cast<CompatibleArrayType>(binary_value(j)); bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
} }
else if (j.is_array()) else if (j.is_array())
{ {
@@ -545,47 +452,8 @@ void from_json_object_emplace(ConstructibleObjectType& obj, Key&& key, Value&& v
obj.emplace(std::forward<Key>(key), std::forward<Value>(value)); obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
} }
// a member of a view's object: a later member with the same key replaces an
// earlier one, as parse() keeps the last value of a repeated key (containers
// without operator[] keep the first)
template<typename ConstructibleObjectType, typename Key, typename Value>
auto from_json_object_assign(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<1> /*unused*/)
-> decltype(obj[std::forward<Key>(key)] = std::forward<Value>(value), void())
{
obj[std::forward<Key>(key)] = std::forward<Value>(value);
}
template<typename ConstructibleObjectType, typename Key, typename Value>
void from_json_object_assign(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<0> /*unused*/)
{
obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
}
template<typename BasicJsonType, typename ConstructibleObjectType, enable_if_not_view_t<BasicJsonType> = 0>
void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& ret)
{
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
for (const auto& p : *inner_object)
{
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
}
template<typename View, typename ConstructibleObjectType, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_object_impl(const View& v, ConstructibleObjectType& ret)
{
using string_t = typename View::basic_json_type::object_t::key_type;
reserve_if_possible(ret, v.size(), priority_tag<1> {});
for (const auto item : v.items())
{
const auto key = item.key();
from_json_object_assign(ret, string_t(key.data(), key.size()), item.value().template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
}
template<typename BasicJsonType, typename ConstructibleObjectType, template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<json_type_of_t<BasicJsonType>, ConstructibleObjectType>::value, int> = 0> enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
@@ -594,7 +462,12 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
} }
ConstructibleObjectType ret; ConstructibleObjectType ret;
from_json_object_impl(j, ret); const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
for (const auto& p : *inner_object)
{
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
obj = std::move(ret); obj = std::move(ret);
} }
@@ -603,10 +476,10 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
template < typename BasicJsonType, typename ArithmeticType, template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < enable_if_t <
std::is_arithmetic<ArithmeticType>::value&& std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_unsigned_t>::value&& !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_integer_t>::value&& !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_float_t>::value&& !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::boolean_t>::value, !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
int > = 0 > int > = 0 >
inline void from_json(const BasicJsonType& j, ArithmeticType& val) inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{ {
@@ -620,7 +493,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{ {
throw_type_must_be("number", j); throw_type_must_be("number", j);
} }
val = static_cast<ArithmeticType>(boolean_value(j)); val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
} }
template<typename BasicJsonType, typename Type> template<typename BasicJsonType, typename Type>
@@ -630,7 +503,7 @@ detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType&& j, detail::iden
} }
template<typename BasicJsonType, typename Type, template<typename BasicJsonType, typename Type,
detail::enable_if_t<conjunction<is_basic_json<uncvref_t<BasicJsonType>>, is_compatible_reference_type<BasicJsonType, Type>>::value, int> = 0> detail::enable_if_t<detail::is_compatible_reference_type<BasicJsonType, Type>::value, int> = 0>
Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<1> /*unused*/) Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<1> /*unused*/)
{ {
return std::forward<BasicJsonType>(j).template get_ref<Type>(); return std::forward<BasicJsonType>(j).template get_ref<Type>();
@@ -674,7 +547,7 @@ inline void from_json_tuple_impl(const BasicJsonType& j, std::pair<A1, A2>& p, p
template<typename BasicJsonType, typename... Args> template<typename BasicJsonType, typename... Args>
std::tuple<Args...> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/) std::tuple<Args...> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
{ {
static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, conjunction<is_basic_json<BasicJsonType>, is_compatible_reference_type<const BasicJsonType&, Args>>>...>::value, static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, is_compatible_reference_type<const BasicJsonType&, Args>>...>::value,
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()"); "Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
return from_json_tuple_impl_base<1, Args...>(j, index_sequence_for<Args...> {}); return from_json_tuple_impl_base<1, Args...>(j, index_sequence_for<Args...> {});
} }
@@ -720,7 +593,7 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
// read a map with enum keys from an object, using the enum's own from_json for // read a map with enum keys from an object, using the enum's own from_json for
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written // the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS // with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename BasicJsonType, typename Map, enable_if_not_view_t<BasicJsonType> = 0> template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/) inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
{ {
if (!j.is_object()) if (!j.is_object())
@@ -735,24 +608,6 @@ inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::tru
return true; return true;
} }
template<typename View, typename Map, enable_if_t<is_json_view<View>::value, int> = 0>
inline bool from_json_enum_keyed_object(const View& v, Map& m, std::true_type /*key is enum*/)
{
using json_t = typename View::basic_json_type;
if (!v.is_object())
{
return false;
}
m.clear();
for (const auto item : v.items())
{
const auto key = item.key();
from_json_object_assign(m, json_t(typename json_t::string_t(key.data(), key.size())).template get<typename Map::key_type>(),
item.value().template get<typename Map::mapped_type>(), priority_tag<1> {});
}
return true;
}
template<typename BasicJsonType, typename Map> template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/) inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
{ {
@@ -761,7 +616,7 @@ inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/,
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible < typename = enable_if_t < !std::is_constructible <
typename json_type_of_t<BasicJsonType>::string_t, Key >::value >> typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m) void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{ {
// NOLINTNEXTLINE(modernize-type-traits) we use C++11 // NOLINTNEXTLINE(modernize-type-traits) we use C++11
@@ -774,7 +629,7 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible < typename = enable_if_t < !std::is_constructible <
typename json_type_of_t<BasicJsonType>::string_t, Key >::value >> typename BasicJsonType::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m) void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{ {
// NOLINTNEXTLINE(modernize-type-traits) we use C++11 // NOLINTNEXTLINE(modernize-type-traits) we use C++11
@@ -798,7 +653,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{ {
throw_type_must_be("string", j); throw_type_must_be("string", j);
} }
const auto& s = string_value(j); const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
// Checking for C++20 standard or later can be insufficient in case the // Checking for C++20 standard or later can be insufficient in case the
// library support for char8_t is either incomplete or was disabled // library support for char8_t is either incomplete or was disabled
// altogether. Use the __cpp_lib_char8_t feature test instead. // altogether. Use the __cpp_lib_char8_t feature test instead.
@@ -810,100 +665,17 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
} }
#endif #endif
namespace from_json_adl
{
// hides the library's from_json() overloads above (and the from_json object):
// a call from this namespace finds only the from_json() functions of the
// arguments' own namespaces and classes (ADL); the declaration is never viable
// and never defined (not "= delete", which GCC before 6 reports as a use)
struct hidden;
void from_json(const hidden&);
// a type no from_json() written for JSON values accepts: a from_json() that
// accepts it is not constrained to them (see has_view_from_json; it lives
// here, so that ADL finds no other from_json() for it)
struct probe
{};
struct fn
{
template<typename SourceType, typename T>
auto operator()(const SourceType& j, T&& val) const
noexcept(noexcept(from_json(j, std::forward<T>(val))))
-> decltype(from_json(j, std::forward<T>(val)))
{
return from_json(j, std::forward<T>(val));
}
};
} // namespace from_json_adl
// the sources whose from_json() is only looked up by ADL: the probe
template<typename T>
struct is_adl_only_from_json_source : std::is_same<T, from_json_adl::probe> {};
struct from_json_fn struct from_json_fn
{ {
template<typename BasicJsonType, typename T, template<typename BasicJsonType, typename T>
enable_if_t<negation<is_adl_only_from_json_source<BasicJsonType>>::value, int> = 0> auto operator()(const BasicJsonType& j, T&& val) const
auto operator()(const BasicJsonType& j, T && val) const
noexcept(noexcept(from_json(j, std::forward<T>(val)))) noexcept(noexcept(from_json(j, std::forward<T>(val))))
-> decltype(from_json(j, std::forward<T>(val))) -> decltype(from_json(j, std::forward<T>(val)))
{ {
return from_json(j, std::forward<T>(val)); return from_json(j, std::forward<T>(val));
} }
template<typename SourceType, typename T,
enable_if_t<is_adl_only_from_json_source<SourceType>::value, int> = 0>
auto operator()(const SourceType& j, T && val) const
noexcept(noexcept(from_json_adl::fn {}(j, std::forward<T>(val))))
-> decltype(from_json_adl::fn {}(j, std::forward<T>(val)))
{
return from_json_adl::fn {}(j, std::forward<T>(val));
}
}; };
// Whether JSONSerializer::from_json(view, T&) reads T from a view (of
// <nlohmann/json_view.hpp>) directly: it must accept the view but not the
// probe. An unconstrained from_json() template (written for basic_json, but
// accepting anything) is thus not called with a view, nor are the library's
// own conversions; the view converts such types through materialize().
template<typename Serializer, typename SourceType, typename T>
using serializer_from_json_t = decltype(Serializer::from_json(std::declval<const SourceType&>(), std::declval<T&>()));
template<typename Serializer, typename View, typename T>
struct has_view_from_json
: conjunction < is_detected<serializer_from_json_t, Serializer, View, T>,
negation<is_detected<serializer_from_json_t, Serializer, from_json_adl::probe, T>>> {};
// the base part of a type of the NLOHMANN_DEFINE_DERIVED_TYPE_* macros: from a
// view through its get_to(), which reads the base from the view where the
// base's from_json() accepts views, and from materialize() otherwise
template<typename BasicJsonType, typename T, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
void from_json_base(const BasicJsonType& j, T& base)
{
from_json_fn {}(j, base);
}
template<typename View, typename T, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_base(const View& v, T& base)
{
v.get_to(base);
}
// a value as an exception message shows it (invalid UTF-8 replaced; the
// strings of a view are always valid UTF-8)
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
typename BasicJsonType::string_t from_json_dump(const BasicJsonType& j)
{
return j.dump(-1, ' ', false, error_handler_t::replace);
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::string_t from_json_dump(const View& v)
{
return v.dump();
}
} // namespace detail } // namespace detail
#ifndef JSON_HAS_CPP_17 #ifndef JSON_HAS_CPP_17
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff. Show more