mirror of
https://github.com/nlohmann/json.git
synced 2026-09-29 19:20:30 +00:00
Compare commits
39
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a04aa3d095 | ||
|
|
cbdc502fbf | ||
|
|
300ab011b0 | ||
|
|
8d9088ab05 | ||
|
|
5a936a2ef3 | ||
|
|
5876a22712 | ||
|
|
cc36e26254 | ||
|
|
3c5c000408 | ||
|
|
1e212bc50c | ||
|
|
5d25f863c0 | ||
|
|
ada8f17230 | ||
|
|
266c2249fe | ||
|
|
2e5419eebc | ||
|
|
7e577fb2d4 | ||
|
|
baf4ccfda8 | ||
|
|
76897a9b9b | ||
|
|
2b1a720fc0 | ||
|
|
95c2fe08a6 | ||
|
|
0d59ae5d49 | ||
|
|
61b104606d | ||
|
|
57b5d56522 | ||
|
|
ebfbbf29cb | ||
|
|
a491cc7663 | ||
|
|
6536c1b869 | ||
|
|
038f448dec | ||
|
|
bf6b5b4719 | ||
|
|
1c7041908d | ||
|
|
20fd4c6a8b | ||
|
|
f57de1aa0a | ||
|
|
401af52511 | ||
|
|
ecce914701 | ||
|
|
1318023103 | ||
|
|
0192171c37 | ||
|
|
1ef0582ed7 | ||
|
|
2aedf52d54 | ||
|
|
bc36fd323b | ||
|
|
9856da2561 | ||
|
|
85f414ae7a | ||
|
|
358970ee7f |
+4
-3
@@ -51,12 +51,13 @@ labels:
|
||||
- "include/nlohmann/detail/view/.*"
|
||||
- "single_include/nlohmann/json_view\\.hpp"
|
||||
- "tests/src/unit-json_view.*"
|
||||
- "tests/src/fuzzer-parse_json_view\\.cpp"
|
||||
- "tests/src/fuzzer-(parse_json_view|json_view_image)\\.cpp"
|
||||
- "tests/benchmarks/json_view/.*"
|
||||
- "tools/amalgamate/config_json_view\\.json"
|
||||
- "docs/mkdocs/docs/features/json_view\\.md"
|
||||
- "docs/mkdocs/docs/api/basic_json_(document|view)/.*"
|
||||
- "docs/mkdocs/docs/api/(ordered_)?json_(document|view)\\.md"
|
||||
- "docs/mkdocs/docs/examples/(basic_json_(document|view)__|(ordered_)?json_(document|view)).*"
|
||||
- "docs/mkdocs/docs/api/(ordered_)?json_(editable_)?(document|view)\\.md"
|
||||
- "docs/mkdocs/docs/examples/(basic_json_(document|view)__|(ordered_)?json_(editable_)?(document|view)).*"
|
||||
- "tests/benchmarks/src/benchmarks_view\\.cpp"
|
||||
|
||||
- label: "aspect: json_view"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
name: "json_view benchmarks"
|
||||
|
||||
# On demand only: runs the comparison of json_view with yyjson, simdjson, and
|
||||
# Boost.JSON (tests/benchmarks/json_view/compare.py) on GitHub-hosted runners,
|
||||
# for numbers from x86-64 and AArch64 Linux. It runs when started by hand, or
|
||||
# when a pull request gets the label "benchmark" (on both architectures, with
|
||||
# GCC and the default settings). Shared runners are noisy: the results show
|
||||
# where json_view stands, but published numbers need a quiet machine (see
|
||||
# tests/benchmarks/json_view/README.md).
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runner:
|
||||
description: "Runner image"
|
||||
type: choice
|
||||
options:
|
||||
- ubuntu-24.04
|
||||
- ubuntu-24.04-arm
|
||||
default: ubuntu-24.04
|
||||
compiler:
|
||||
description: "Compiler"
|
||||
type: choice
|
||||
options:
|
||||
- g++
|
||||
- clang++
|
||||
default: g++
|
||||
native:
|
||||
description: "Compile for the runner's CPU (-march=native)"
|
||||
type: boolean
|
||||
default: false
|
||||
rounds:
|
||||
description: "Rounds of bench_view"
|
||||
type: number
|
||||
default: 30
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
compare:
|
||||
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'benchmark'
|
||||
strategy:
|
||||
matrix:
|
||||
runner: ${{ fromJSON(github.event_name == 'workflow_dispatch' && format('["{0}"]', inputs.runner) || '["ubuntu-24.04", "ubuntu-24.04-arm"]') }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download test data
|
||||
run: |
|
||||
cmake -S . -B build -DJSON_BuildTests=On
|
||||
cmake --build build --target download_test_data
|
||||
|
||||
- name: Run the comparison
|
||||
env:
|
||||
CXX: ${{ inputs.compiler || 'g++' }}
|
||||
CC: ${{ inputs.compiler == 'clang++' && 'clang' || 'gcc' }}
|
||||
ROUNDS: ${{ inputs.rounds || 30 }}
|
||||
NATIVE: ${{ inputs.native && '--native' || '' }}
|
||||
run: python3 tests/benchmarks/json_view/compare.py --data build/test_files --download --rounds "$ROUNDS" $NATIVE
|
||||
|
||||
- name: Summary
|
||||
run: cat tests/benchmarks/json_view/results/*.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: json_view-benchmarks-${{ matrix.runner }}-${{ inputs.compiler || 'g++' }}
|
||||
path: tests/benchmarks/json_view/results/
|
||||
+10
@@ -26,6 +26,7 @@ cc_library(
|
||||
"include/nlohmann/detail/conversions/from_json.hpp",
|
||||
"include/nlohmann/detail/conversions/to_chars.hpp",
|
||||
"include/nlohmann/detail/conversions/to_json.hpp",
|
||||
"include/nlohmann/detail/conversions/zmij.hpp",
|
||||
"include/nlohmann/detail/exceptions.hpp",
|
||||
"include/nlohmann/detail/hash.hpp",
|
||||
"include/nlohmann/detail/input/binary_reader.hpp",
|
||||
@@ -67,8 +68,12 @@ cc_library(
|
||||
"include/nlohmann/detail/string_utils.hpp",
|
||||
"include/nlohmann/detail/value_t.hpp",
|
||||
"include/nlohmann/detail/view/builder.hpp",
|
||||
"include/nlohmann/detail/view/compare.hpp",
|
||||
"include/nlohmann/detail/view/document_data.hpp",
|
||||
"include/nlohmann/detail/view/edit.hpp",
|
||||
"include/nlohmann/detail/view/edit_storage.hpp",
|
||||
"include/nlohmann/detail/view/errors.hpp",
|
||||
"include/nlohmann/detail/view/image.hpp",
|
||||
"include/nlohmann/detail/view/input.hpp",
|
||||
"include/nlohmann/detail/view/iterator.hpp",
|
||||
"include/nlohmann/detail/view/lookup.hpp",
|
||||
@@ -77,8 +82,13 @@ cc_library(
|
||||
"include/nlohmann/detail/view/materialize.hpp",
|
||||
"include/nlohmann/detail/view/node.hpp",
|
||||
"include/nlohmann/detail/view/number.hpp",
|
||||
"include/nlohmann/detail/view/object_index.hpp",
|
||||
"include/nlohmann/detail/view/pointer.hpp",
|
||||
"include/nlohmann/detail/view/scan.hpp",
|
||||
"include/nlohmann/detail/view/serializer.hpp",
|
||||
"include/nlohmann/detail/view/simd.hpp",
|
||||
"include/nlohmann/detail/view/string_ref.hpp",
|
||||
"include/nlohmann/detail/view/value.hpp",
|
||||
"include/nlohmann/json.hpp",
|
||||
"include/nlohmann/json_fwd.hpp",
|
||||
"include/nlohmann/json_view.hpp",
|
||||
|
||||
@@ -1401,10 +1401,12 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
|
||||
|
||||
- The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2008-2009 [Björn Hoehrmann](https://bjoern.hoehrmann.de/) <bjoern@hoehrmann.de>
|
||||
- The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||
- The class contains a port of the shortest double-to-decimal conversion of [Żmij](https://github.com/vitaut/zmij) by Victor Zverovich, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2025 [Victor Zverovich](https://github.com/vitaut)
|
||||
- The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
- The class contains parts of [Google Abseil](https://github.com/abseil/abseil-cpp) which is licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0).
|
||||
- The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
- The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
||||
- The view's parser (`<nlohmann/json_view.hpp>`) validates non-ASCII strings with the vector UTF-8 check of [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser, and contributors (its "lookup4" algorithm and tables, after J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte", 2021), which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here) and the Apache 2.0 License. Copyright © 2018-2025 The simdjson authors
|
||||
|
||||
<img align="right" src="https://git.fsfe.org/reuse/reuse-ci/raw/branch/master/reuse-horizontal.png" alt="REUSE Software">
|
||||
|
||||
|
||||
@@ -131,14 +131,20 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Me
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document', 'Class', 'api/basic_json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::basic_json_document', 'Constructor', 'api/basic_json_document/basic_json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::accept', 'Function', 'api/basic_json_document/accept/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::erase', 'Method', 'api/basic_json_document/erase/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::insert', 'Method', 'api/basic_json_document/insert/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::is_discarded', 'Method', 'api/basic_json_document/is_discarded/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::load', 'Function', 'api/basic_json_document/load/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::memory_usage', 'Method', 'api/basic_json_document/memory_usage/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::node_count', 'Method', 'api/basic_json_document/node_count/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::owns_source', 'Method', 'api/basic_json_document/owns_source/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::parse', 'Function', 'api/basic_json_document/parse/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::parse_copy', 'Function', 'api/basic_json_document/parse_copy/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::push_back', 'Method', 'api/basic_json_document/push_back/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::read', 'Method', 'api/basic_json_document/read/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::root', 'Method', 'api/basic_json_document/root/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::save', 'Method', 'api/basic_json_document/save/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::set', 'Method', 'api/basic_json_document/set/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::shrink_to_fit', 'Method', 'api/basic_json_document/shrink_to_fit/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_document::source', 'Method', 'api/basic_json_document/source/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view', 'Class', 'api/basic_json_view/index.html');
|
||||
@@ -150,10 +156,14 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::cbegin', 'Me
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::cend', 'Method', 'api/basic_json_view/cend/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::contains', 'Method', 'api/basic_json_view/contains/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::count', 'Method', 'api/basic_json_view/count/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::dump', 'Method', 'api/basic_json_view/dump/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::empty', 'Method', 'api/basic_json_view/empty/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::end', 'Method', 'api/basic_json_view/end/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::find', 'Method', 'api/basic_json_view/find/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::front', 'Method', 'api/basic_json_view/front/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::get', 'Method', 'api/basic_json_view/get/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::get_string', 'Method', 'api/basic_json_view/get_string/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::get_to', 'Method', 'api/basic_json_view/get_to/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_array', 'Method', 'api/basic_json_view/is_array/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_binary', 'Method', 'api/basic_json_view/is_binary/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_boolean', 'Method', 'api/basic_json_view/is_boolean/index.html');
|
||||
@@ -169,14 +179,22 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_string',
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::is_structured', 'Method', 'api/basic_json_view/is_structured/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::items', 'Method', 'api/basic_json_view/items/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::materialize', 'Method', 'api/basic_json_view/materialize/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::number_format', 'Enum', 'api/basic_json_view/number_format/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::number_token', 'Method', 'api/basic_json_view/number_token/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator bool', 'Method', 'api/basic_json_view/operator_bool/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator<<', 'Operator', 'api/basic_json_view/operator_ltlt/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator[]', 'Operator', 'api/basic_json_view/operator[]/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator==', 'Operator', 'api/basic_json_view/operator_eq/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator!=', 'Operator', 'api/basic_json_view/operator_ne/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::size', 'Method', 'api/basic_json_view/size/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::source_offset', 'Method', 'api/basic_json_view/source_offset/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type', 'Method', 'api/basic_json_view/type/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type_name', 'Method', 'api/basic_json_view/type_name/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::value', 'Method', 'api/basic_json_view/value/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_document', 'Class', 'api/json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_editable_document', 'Class', 'api/json_editable_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_editable_view', 'Class', 'api/json_editable_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_view', 'Class', 'api/json_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer', 'Class', 'api/json_pointer/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::back', 'Method', 'api/json_pointer/back/index.html');
|
||||
@@ -212,6 +230,8 @@ INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('operator>>', 'Operator', 'api/operator_gtgt/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json', 'Class', 'api/ordered_json/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_document', 'Class', 'api/ordered_json_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_editable_document', 'Class', 'api/ordered_json_editable_document/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_editable_view', 'Class', 'api/ordered_json_editable_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_json_view', 'Class', 'api/ordered_json_view/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('ordered_map', 'Class', 'api/ordered_map/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
|
||||
@@ -297,3 +317,5 @@ INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM'
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MAJOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_MINOR', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_VERSION_PATCH', 'Macro', 'api/macros/nlohmann_json_version_major/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_VIEW_NO_SIMD', 'Macro', 'api/macros/json_view_no_simd/index.html');
|
||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_VIEW_USE_SSSE3', 'Macro', 'api/macros/json_view_use_ssse3/index.html');
|
||||
|
||||
@@ -60,6 +60,9 @@ Linear.
|
||||
|
||||
## Notes
|
||||
|
||||
Floating-point numbers are written with the fewest digits that read back as the same value (for `#!cpp double`; see
|
||||
[number handling](../../features/types/number_handling.md#number-serialization)).
|
||||
|
||||
Binary values are serialized as an object containing two keys:
|
||||
|
||||
- "bytes": an array of bytes as integers
|
||||
@@ -86,6 +89,8 @@ Binary values are serialized as an object containing two keys:
|
||||
|
||||
- [to_string](to_string.md) returns a string representation of a JSON value
|
||||
- [operator<<](../operator_ltlt.md) serialize to stream
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) the corresponding function of `basic_json_view`, serializing
|
||||
directly from a flat index without building a `basic_json` value
|
||||
- [Serialization](../../features/serialization.md) - the serialization article
|
||||
|
||||
## Version history
|
||||
@@ -94,3 +99,5 @@ Binary values are serialized as an object containing two keys:
|
||||
- Indentation character `indent_char`, option `ensure_ascii` and exceptions added in version 3.0.0.
|
||||
- Error handlers added in version 3.4.0.
|
||||
- Serialization of binary values added in version 3.8.0.
|
||||
- Doubles are written with the shortest digits (Żmij instead of Grisu2) since version 3.13.0; about 0.1% of doubles are
|
||||
written differently, most of them with fewer digits.
|
||||
|
||||
@@ -163,6 +163,8 @@ overload (3).
|
||||
- [get_ref](get_ref.md) get a reference to the stored value
|
||||
- [operator ValueType](operator_ValueType.md) get a value via implicit conversion
|
||||
- [Converting values](../../features/conversions.md) - the type conversions article
|
||||
- [basic_json_view::get](../basic_json_view/get.md) - the same conversion on a zero-copy view (many types are
|
||||
converted without ever building a `basic_json` value)
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ Constant.
|
||||
## See also
|
||||
|
||||
- [get_ptr()](get_ptr.md) get a pointer value
|
||||
- [basic_json_view::get_string](../basic_json_view/get_string.md) - the closest counterpart on a zero-copy view: a
|
||||
string without a copy, but as a view rather than a reference to a value that must already exist
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ Depends on the `json_serializer<ValueType>::from_json()` implementation.
|
||||
- [get_ref](get_ref.md) get a reference to the stored value
|
||||
- [get_ptr](get_ptr.md) get a pointer to the stored value
|
||||
- [Converting values](../../features/conversions.md) - the type conversions article
|
||||
- [basic_json_view::get_to](../basic_json_view/get_to.md) - the same conversion on a zero-copy view
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -166,6 +166,8 @@ Linear.
|
||||
|
||||
- [operator!=](operator_ne.md) compare for inequality
|
||||
- [operator<=>](operator_spaceship.md) comparison: 3-way (C++20)
|
||||
- [basic_json_view::operator==](../basic_json_view/operator_eq.md) - the same comparison on a zero-copy view, without
|
||||
building a `basic_json` value for it
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -89,6 +89,12 @@ Linear.
|
||||
--8<-- "examples/operator__notequal__nullptr_t.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [operator==](operator_eq.md) compare for equality
|
||||
- [basic_json_view::operator!=](../basic_json_view/operator_ne.md) - the same comparison on a zero-copy view, without
|
||||
building a `basic_json` value for it
|
||||
|
||||
## Version history
|
||||
|
||||
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
||||
|
||||
@@ -181,6 +181,7 @@ changes to any JSON value.
|
||||
|
||||
- see [`at`](at.md) for access by reference with range checking
|
||||
- see [`operator[]`](operator%5B%5D.md) for unchecked access by reference
|
||||
- [basic_json_view::value](../basic_json_view/value.md) - the same access on a zero-copy view
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# <small>nlohmann::basic_json_document::</small>erase
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
std::size_t erase(view_type object, string_view_t key);
|
||||
|
||||
// (2)
|
||||
template<typename I>
|
||||
void erase(view_type array, I idx);
|
||||
|
||||
// (3)
|
||||
std::size_t erase(const json_pointer& ptr);
|
||||
```
|
||||
|
||||
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`).
|
||||
|
||||
1. Removes every member of `object` whose key is `key` (see [Notes](#notes) on duplicate keys) and returns how many
|
||||
were removed; `#!cpp 0` if `object` has no member with this key.
|
||||
2. Removes the element at index `idx` of `array`, which must already exist (`#!cpp idx < array.size()`).
|
||||
3. Removes the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md), and returns how many values
|
||||
were removed: the *parent* of the target must already exist, and the target itself is removed as in 1. (an object
|
||||
member; `#!cpp 0` or more) or 2. (an array element; always `#!cpp 1`). `ptr` must not be empty -- [`root()`](root.md)
|
||||
itself cannot be erased.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`I`
|
||||
: an integral type other than `#!cpp bool`, deduced (overloads taking a `#!cpp bool` or a non-integral type for
|
||||
`idx` do not participate in overload resolution).
|
||||
|
||||
## Parameters
|
||||
|
||||
`object` (in)
|
||||
: the object to remove a member of
|
||||
|
||||
`array` (in)
|
||||
: the array to remove an element of
|
||||
|
||||
`key` (in)
|
||||
: the key of the member(s) to remove
|
||||
|
||||
`idx` (in)
|
||||
: the index of the element to remove; a negative value throws (see [Exceptions](#exceptions))
|
||||
|
||||
`ptr` (in)
|
||||
: a JSON pointer to the value to remove, relative to `root()`
|
||||
|
||||
## Return value
|
||||
|
||||
1. the number of removed members (`#!cpp 0` if `object` had none with this `key`)
|
||||
2. (nothing)
|
||||
3. the number of removed values (`#!cpp 0` or more for an object member, always `#!cpp 1` for an array element)
|
||||
|
||||
## Exceptions
|
||||
|
||||
1. Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) if `object` is not an object -- the
|
||||
same message [`BasicJsonType::erase`](../basic_json/erase.md) throws for the same type.
|
||||
2. Throws `type_error.307` if `array` is not an array. Throws
|
||||
[`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `idx` is negative, or if
|
||||
`#!cpp idx >= array.size()`.
|
||||
3. Throws [`out_of_range.405`](../../home/exceptions.md#jsonexceptionout_of_range405) ("JSON pointer has no parent")
|
||||
if `ptr` is empty. Throws what [`at`](../basic_json_view/at.md) throws (overload 3) for resolving `ptr`'s parent.
|
||||
For the last reference token itself: if the parent is an array, throws what 2. throws for an index that is out of
|
||||
range, or, for a token that is not a valid array index,
|
||||
[`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) (a leading `#!cpp '0'`),
|
||||
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) (not a number),
|
||||
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) (too large for `size_type`), or
|
||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token); otherwise (an
|
||||
object, or a primitive value the pointer's parent resolves to) throws what 1. throws.
|
||||
|
||||
Every overload also throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view
|
||||
does not belong to this document") if `object`/`array` is a [discarded](../basic_json_view/is_discarded.md) view or a
|
||||
view of a *different* document (overloads 1-2 only; overload 3 always starts from this document's own
|
||||
[`root()`](root.md)).
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the number of members of `object`.
|
||||
2. Linear in the number of elements of `array` at or after `idx` (they move one slot over).
|
||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array (as [`at`](../basic_json_view/at.md)), plus the complexity of 1. or 2. for
|
||||
the last token.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "Duplicate keys"
|
||||
|
||||
Overload 1. removes *every* member with `key`, not just the first -- unlike [`set`](set.md), which assigns the
|
||||
first occurrence and drops the rest. This is why it returns a count rather than a single view: there may be
|
||||
more than one member removed, or none.
|
||||
|
||||
Like [`set`](set.md) and [`push_back`](push_back.md), `erase` never moves an element's *value*: a view still
|
||||
referring to a removed member or element keeps showing what it last held (see [Edits](index.md#edits)) -- it just no
|
||||
longer appears when `array`/`object` is read, dumped, or iterated. Removing an element of `array` (2.) does shift the
|
||||
*links* to the elements after it, the same way `insert`, `set`, or `push_back` on the same array would; any iterator
|
||||
already taken over `array`/`object` is invalidated by an erase, since it was walking the old layout.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below drops a deprecated field and a decommissioned entry from a configuration document -- using all
|
||||
three overloads -- and shows what stays intact that would not with a plain `json`/`ordered_json` value: the order
|
||||
of the fields around the ones removed, and the exact spelling of a number that was never touched.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__erase.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__erase.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [insert](insert.md) - insert an element into an array
|
||||
- [set](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [push_back](push_back.md) - append to an array
|
||||
- [root](root.md) - the view of the root value, the starting point of overload 3
|
||||
- [`BasicJsonType::erase`](../basic_json/erase.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -3,7 +3,7 @@
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
template<typename BasicJsonType>
|
||||
template<typename BasicJsonType, bool Editable = false>
|
||||
class basic_json_document;
|
||||
```
|
||||
|
||||
@@ -19,6 +19,11 @@ it (a copy, or an rvalue `#!cpp std::string` that was moved in); see [`owns_sour
|
||||
is move-only: copying a document would either duplicate a potentially large index and text, or leave two documents
|
||||
claiming to borrow the same buffer, so it is disabled.
|
||||
|
||||
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
|
||||
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`).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`BasicJsonType`
|
||||
@@ -26,23 +31,37 @@ claiming to borrow the same buffer, so it is disabled.
|
||||
[`ordered_json`](../ordered_json.md). Only 64-bit `number_integer_t`/`number_unsigned_t` types are supported; this
|
||||
is checked with a `static_assert`.
|
||||
|
||||
`Editable`
|
||||
: whether the document supports [`set`](set.md), [`push_back`](push_back.md), [`insert`](insert.md), and
|
||||
[`erase`](erase.md) (optional, `#!cpp false` by default). See [Edits](#edits) below.
|
||||
|
||||
## Specializations
|
||||
|
||||
- [**json_document**](../json_document.md) - documents of the default specialization [`json`](../json.md)
|
||||
- [**ordered_json_document**](../ordered_json_document.md) - documents of [`ordered_json`](../ordered_json.md)
|
||||
- [**json_document**](../json_document.md) - read-only documents of the default specialization [`json`](../json.md)
|
||||
- [**ordered_json_document**](../ordered_json_document.md) - read-only documents of
|
||||
[`ordered_json`](../ordered_json.md)
|
||||
- [**json_editable_document**](../json_editable_document.md) - editable documents of [`json`](../json.md)
|
||||
- [**ordered_json_editable_document**](../ordered_json_editable_document.md) - editable documents of
|
||||
[`ordered_json`](../ordered_json.md)
|
||||
|
||||
## Member types
|
||||
|
||||
- **view_type** - the type of view returned by [`root()`](root.md) (`#!cpp basic_json_view<BasicJsonType>`)
|
||||
- **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)
|
||||
|
||||
## Member functions
|
||||
|
||||
- [(constructor)](basic_json_document.md)
|
||||
|
||||
### Parsing
|
||||
|
||||
- [**parse**](parse.md) (_static_) - deserialize from a compatible input, borrowing or owning it as appropriate
|
||||
- [**parse_copy**](parse_copy.md) (_static_) - deserialize a copy of a compatible input
|
||||
- [**accept**](accept.md) (_static_) - check whether the input is valid JSON
|
||||
- [**read**](read.md) - (re-)parse into this document, reusing its memory
|
||||
|
||||
### Access
|
||||
|
||||
- [**root**](root.md) - the view of the root value
|
||||
- [**is_discarded**](is_discarded.md) - return whether the last parse failed
|
||||
- [**source**](source.md) - the parsed text
|
||||
@@ -51,6 +70,49 @@ claiming to borrow the same buffer, so it is disabled.
|
||||
- [**memory_usage**](memory_usage.md) - the number of bytes held by the document
|
||||
- [**shrink_to_fit**](shrink_to_fit.md) - release unused index capacity
|
||||
|
||||
### Images
|
||||
|
||||
- [**save**](save.md) - the document as an image that `load()` reads without parsing
|
||||
- [**load**](load.md) (_static_) - read an image written by `save()`
|
||||
|
||||
### Edits
|
||||
|
||||
- [**set**](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers
|
||||
to (`#!cpp 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 (`#!cpp Editable` documents only)
|
||||
- [**erase**](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
(`#!cpp Editable` documents only)
|
||||
|
||||
## Edits
|
||||
|
||||
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);
|
||||
[`json_editable_document`](../json_editable_document.md) and
|
||||
[`ordered_json_editable_document`](../ordered_json_editable_document.md) are the corresponding specializations. A few
|
||||
points apply to every edit:
|
||||
|
||||
- **The source text is never written**, and the parsed index never moves: every value keeps the node it was parsed
|
||||
into, so [views](../basic_json_view/index.md) taken before an edit stay valid, including
|
||||
[`root()`](root.md). New values (and the element sequences of an edited array/object) go to storage owned by the
|
||||
document, allocated on demand.
|
||||
- **A view keeps referring to the same value.** After [`set`](set.md) replaces the value a view refers to, that view
|
||||
sees the new value; a view of a value that a later edit replaces or drops keeps showing what it last held. An edit
|
||||
of an array or object, however, **invalidates the iterators taken over it** (its members may now live in a
|
||||
different sequence), and a string obtained with [`get_string()`](../basic_json_view/get_string.md) stays valid even
|
||||
as further edits happen (earlier buffers of edited text are kept alive, not overwritten).
|
||||
- **Values are accepted three ways:** a [`basic_json_view`](../basic_json_view/index.md) of *any* document
|
||||
(read-only or editable; it is copied, nothing is shared with the source document), a `BasicJsonType` value, or
|
||||
anything `BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
- [`dump()`](../basic_json_view/dump.md) writes an edited document with members in document order, new members at
|
||||
the end, and, with [`number_format::source`](../basic_json_view/number_format.md), keeps the spelling of every
|
||||
number that was not itself edited -- see [Editing a document](../../features/json_view.md#editing-a-document) for
|
||||
why this matters.
|
||||
- [`read()`](read.md) discards all edits, [`shrink_to_fit()`](shrink_to_fit.md) does not move the node index once
|
||||
there are edits, and [`memory_usage()`](memory_usage.md) includes the memory edits use.
|
||||
[`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is
|
||||
`#!cpp static_cast<std::size_t>(-1)`, the same value it reports for a decoded string.
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# <small>nlohmann::basic_json_document::</small>insert
|
||||
|
||||
```cpp
|
||||
template<typename I, typename V>
|
||||
view_type insert(view_type array, I idx, V&& value);
|
||||
```
|
||||
|
||||
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`).
|
||||
|
||||
Inserts `value` into `array` as a new element before position `idx`, which must not be past the end
|
||||
(`#!cpp idx <= array.size()`; `#!cpp idx == array.size()` appends, like [`push_back`](push_back.md)). Unlike
|
||||
[`push_back`](push_back.md), a [null](../basic_json_view/is_null.md) `array` does *not* first become an empty array:
|
||||
`array` must already be an array.
|
||||
|
||||
`value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any* document -- read-only or
|
||||
editable, and it does not have to be `array`'s own document -- which is copied so that nothing is shared with the
|
||||
source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers,
|
||||
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`I`
|
||||
: an integral type other than `#!cpp bool`, deduced (overloads taking a `#!cpp bool` or a non-integral type for
|
||||
`idx` do not participate in overload resolution).
|
||||
|
||||
`V`
|
||||
: the type of `value`, deduced; see above for what is accepted.
|
||||
|
||||
## Parameters
|
||||
|
||||
`array` (in)
|
||||
: the array to insert into
|
||||
|
||||
`idx` (in)
|
||||
: the position to insert `value` before; a negative value throws (see [Exceptions](#exceptions))
|
||||
|
||||
`value` (in)
|
||||
: the value to insert
|
||||
|
||||
## Return value
|
||||
|
||||
a view of the new element, now holding `value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Basic exception safety: `value` is fully encoded -- including the checks below -- into storage owned by the document
|
||||
before anything already reachable from [`root()`](root.md) is touched, so a failure while encoding `value` (an
|
||||
invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unchanged, other than memory allocated
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while `array` switches from its parsed
|
||||
layout to a growable block, or while that block grows, see [Notes](#notes) -- can still leave `array` already
|
||||
switched to that layout even though `value` itself was not inserted.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if `array` is not an array -- the same
|
||||
message [`BasicJsonType::insert`](../basic_json/insert.md) throws for the same type; a null `array` throws this too
|
||||
(see above). Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `idx` is negative,
|
||||
or if `#!cpp idx > array.size()`. Throws
|
||||
[`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view does not belong to this
|
||||
document") if `array` is a [discarded](../basic_json_view/is_discarded.md) view or a view of a *different* document.
|
||||
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `value` is a
|
||||
[discarded](../basic_json_view/is_discarded.md) view or a [discarded](../basic_json/is_discarded.md) `BasicJsonType`
|
||||
value, and [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or contains) a
|
||||
binary value -- `BasicJsonType` can hold one, but a `json_document` cannot. Throws
|
||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if `value` is (or contains) a string that is
|
||||
not valid UTF-8, with the same message [`BasicJsonType::dump()`](../basic_json/dump.md) gives for that string.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of elements of `array` at or after `idx` (they move one slot over), plus time linear in the
|
||||
size of `value` to encode it into the document's storage (constant for a scalar, linear in the number of nested
|
||||
values for an array or object): like [`push_back`](push_back.md), the elements of `array` move to a growable block
|
||||
of links the first time it is inserted into (or [`set`](set.md)/[`push_back`](push_back.md) on), and that block
|
||||
grows in amortized constant time; inserting before the end within that block still shifts every later element.
|
||||
|
||||
## Notes
|
||||
|
||||
Like [`set`](set.md) on a member or an element, `insert` never moves an existing *element's value* -- only where
|
||||
`array`'s *links* to its elements live -- so a view of an existing element of `array` stays valid across an
|
||||
`insert`, and keeps referring to the same element even though its index shifts. Any iterator already taken over
|
||||
`array` is invalidated, since it was walking the old layout. See [Edits](index.md#edits) for what stays valid across
|
||||
an edit in general.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below inserts a step into the middle of a deployment plan, without touching the steps that come
|
||||
after it, and shows that a view taken before the insert keeps referring to the same element even though its
|
||||
index shifts -- something a plain `json`/`ordered_json` array, or its `std::vector`-based storage, has no
|
||||
equivalent for.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__insert.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__insert.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [push_back](push_back.md) - append to an array
|
||||
- [erase](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
- [set](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [`BasicJsonType::insert`](../basic_json/insert.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,169 @@
|
||||
# <small>nlohmann::basic_json_document::</small>load
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
static basic_json_document load(const std::uint8_t* image, std::size_t size,
|
||||
const image_check check = image_check::full);
|
||||
|
||||
// (2)
|
||||
static basic_json_document load(const std::vector<std::uint8_t>& image,
|
||||
const image_check check = image_check::full);
|
||||
|
||||
// (3)
|
||||
static basic_json_document load(std::vector<std::uint8_t>&& image,
|
||||
const image_check check = image_check::full);
|
||||
```
|
||||
|
||||
1. Reads an image [`save()`](save.md) wrote, from a pointer and a byte count. The image is **borrowed**: `image`
|
||||
must stay alive and unchanged for as long as the returned document, and any view taken from it, is used.
|
||||
2. Reads an image from a `#!cpp std::vector`. Also **borrowed** -- equivalent to overload 1 called with
|
||||
`#!cpp image.data()` and `#!cpp image.size()`.
|
||||
3. Reads an image, keeping the vector instead of copying it: `image` is moved into the document (no copy), which
|
||||
then owns it for as long as it needs the text and the decoded strings. [`owns_source()`](owns_source.md) is
|
||||
`#!cpp true` afterward.
|
||||
|
||||
In every overload, the node index is copied into storage the document itself owns -- so that it is properly aligned,
|
||||
and, for an [editable](index.md#edits) document, can be edited -- while the text and the decoded strings stay in
|
||||
`image`. The hash indexes [large objects](../../features/json_view.md) use for lookup are rebuilt, exactly as after
|
||||
parsing.
|
||||
|
||||
## Parameters
|
||||
|
||||
`image` (in)
|
||||
: the image [`save()`](save.md) wrote (overloads 1 and 2), or one to take ownership of (overload 3)
|
||||
|
||||
`size` (in)
|
||||
: the number of bytes at `image` (overload 1)
|
||||
|
||||
`check` (in)
|
||||
: how thoroughly to validate `image` before trusting it; see [`image_check`](#image_check) below (optional,
|
||||
`#!cpp image_check::full` by default)
|
||||
|
||||
## Return value
|
||||
|
||||
The document read from the image.
|
||||
|
||||
## Exception safety
|
||||
|
||||
Overloads 1 and 2 give the strong guarantee: `image` is only read, never written, so a thrown exception leaves the
|
||||
caller's buffer untouched.
|
||||
|
||||
Overload 3 moves `image` into the document *before* validating it, so that a good image is kept without a copy. If
|
||||
loading then fails, the partially built document -- and the vector now inside it -- is discarded along with the
|
||||
exception, and `image` itself is left **empty**, not restored to what was passed in. Move a copy in instead, or
|
||||
validate with overload 2 first, if the original vector must survive a failed load.
|
||||
|
||||
## Exceptions
|
||||
|
||||
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`:
|
||||
|
||||
| message | when |
|
||||
|------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `too short` | `image` is `#!cpp nullptr`, or `size` is smaller than the 64-byte header |
|
||||
| `unknown format` | the header's magic bytes or version do not match, or a reserved header field is not zero |
|
||||
| `sizes out of range` | the node count, text size, or decoded-string size the header describes does not fit `size`, or the `#!cpp '\0'` after the text or after the decoded strings is missing |
|
||||
| `the check failed` | `check` is not `#!cpp image_check::none`, and the image fails it -- see [`image_check`](#image_check) |
|
||||
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: too short
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: unknown format
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: sizes out of range
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: the check failed
|
||||
```
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of nodes, which are always copied into the document. With `#!cpp check == image_check::full`,
|
||||
additionally linear in the combined length of the text and the decoded strings; `#!cpp image_check::bounds` and
|
||||
`#!cpp image_check::none` do not read them.
|
||||
|
||||
## `image_check`
|
||||
|
||||
```cpp
|
||||
using image_check = detail::view::image_check;
|
||||
|
||||
enum class image_check
|
||||
{
|
||||
full,
|
||||
bounds,
|
||||
none
|
||||
};
|
||||
```
|
||||
|
||||
How thoroughly `load()` validates `image` before trusting it.
|
||||
|
||||
| value | checks | guarantees |
|
||||
|----------|--------------------------------------------------------------------------------------------------------------|------------|
|
||||
| `full` | everything the parser itself guarantees: structure and bounds; that every string is valid UTF-8 (and, for a string still in the source text, that it contains no quote, backslash, or control character); and that every number token is well-formed and matches the value stored for it | reading and serializing a checked image is safe and always produces valid JSON, exactly as for a parsed document |
|
||||
| `bounds` | structure and bounds only -- that every offset and count in the node index stays inside the image | reading and serializing stay memory-safe, but a crafted image can hold strings that are not valid UTF-8 or that serialize to invalid JSON ([`dump()`](../basic_json_view/dump.md) writes them unchanged or throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316)), and numbers whose values differ from their text |
|
||||
| `none` | nothing | images from a trusted source only -- reading a damaged image is undefined behavior |
|
||||
|
||||
`full` is the default and the right choice for an image from anything you do not fully control -- a file, a cache
|
||||
shared with other processes, a peer on the network. `bounds` skips scanning the text and the decoded strings, so it
|
||||
fits a cache your own process just wrote and reads straight back, where damage would mean a bug or a hardware fault
|
||||
rather than adversarial input; it still cannot crash or read out of bounds. `none` skips validation entirely and
|
||||
should only be used for an image you trust as much as your own memory.
|
||||
|
||||
## Notes
|
||||
|
||||
**Lifetime.** Overloads 1 and 2 borrow `image`: it must stay alive and byte-for-byte unchanged for as long as the
|
||||
returned document, and any [view](../basic_json_view/index.md) taken from it, is used -- exactly like a document
|
||||
[`parse()`](parse.md) borrowed its input for. Overload 3 avoids this by keeping the vector itself; see
|
||||
[`owns_source`](owns_source.md).
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable, and may change in an incompatible way before it is declared
|
||||
stable; `load()` already rejects an image written by a different format version with `parse_error.116`
|
||||
("unknown format"). Use images to cache a document within one build of the library, or to hand one to another
|
||||
process running the *same* build on the *same* (little-endian) machine -- not as a long-term storage format.
|
||||
|
||||
**What `image_check::bounds` does not guarantee.** A bounds-checked image can never make `load()`,
|
||||
[`root()`](root.md), element access, or [`materialize()`](../basic_json_view/materialize.md) read outside the image,
|
||||
so those stay safe on a damaged one. It does *not* guarantee that the image describes valid JSON: a string
|
||||
that a `full` check would have rejected can make [`dump()`](../basic_json_view/dump.md) write invalid UTF-8 or invalid
|
||||
JSON, or throw `type_error.316`, and a number can read back with a value that does not match how it is spelled. Reserve `bounds` for images you already trust to be well-formed, and use it
|
||||
only to skip the extra scan.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Caching a document, ownership, and a rejected image"
|
||||
|
||||
The example below saves a parsed document as an image, checks that `load()` reproduces the original
|
||||
[`dump()`](../basic_json_view/dump.md) without parsing, and shows the difference between
|
||||
`load(std::move(image))` (owned) and `load(image)` (borrowed). It then damages one byte of the image and shows
|
||||
`image_check::full` rejecting it with `parse_error.116`, while `image_check::bounds` -- meant for a cache the
|
||||
process already trusts -- still reads it without going out of bounds.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__load.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__load.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [save](save.md) - write the document as an image
|
||||
- [owns_source](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [parse](parse.md) - deserialize from JSON text instead of an image
|
||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -132,6 +132,7 @@ integer type becomes a floating-point value.
|
||||
- [accept](accept.md) - check whether the input is valid JSON
|
||||
- [read](read.md) - (re-)parse into this document, reusing its memory
|
||||
- [owns_source](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [load](load.md) - read a document from an image instead of parsing JSON text
|
||||
- [`BasicJsonType::parse`](../basic_json/parse.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# <small>nlohmann::basic_json_document::</small>push_back
|
||||
|
||||
```cpp
|
||||
template<typename V>
|
||||
view_type push_back(view_type array, V&& value);
|
||||
```
|
||||
|
||||
Appends `value` as a new last element of `array`. A [null](../basic_json_view/is_null.md) `array` first becomes an
|
||||
empty array, the same way [`set`](set.md) turns a null `object` into an empty object.
|
||||
|
||||
`value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any* document -- read-only or
|
||||
editable, and it does not have to be `array`'s own document -- which is copied so that nothing is shared with the
|
||||
source document afterward; a `BasicJsonType` value; or anything `BasicJsonType` can be constructed from (numbers,
|
||||
strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
|
||||
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`).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`V`
|
||||
: the type of `value`, deduced; see above for what is accepted.
|
||||
|
||||
## Parameters
|
||||
|
||||
`array` (in)
|
||||
: the array (or null value) to append to
|
||||
|
||||
`value` (in)
|
||||
: the value to append
|
||||
|
||||
## Return value
|
||||
|
||||
a view of the new last element of `array`, now holding `value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Basic exception safety: `value` is fully encoded -- including the checks below -- into storage owned by the document
|
||||
before anything already reachable from [`root()`](root.md) is touched, so a failure while encoding `value` (an
|
||||
invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unchanged, other than memory allocated
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while `array` switches from its parsed
|
||||
layout to a growable block, or while that block grows, see [Notes](#notes) -- can still leave a partial effect, such
|
||||
as a null `array` argument already turned into an empty array even though `value` itself was not appended.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.308`](../../home/exceptions.md#jsonexceptiontype_error308) if `array` is neither an array nor
|
||||
null -- the same message [`BasicJsonType::push_back`](../basic_json/push_back.md) throws for the same type. Throws
|
||||
[`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view does not belong to this
|
||||
document") if `array` is a [discarded](../basic_json_view/is_discarded.md) view or a view of a *different* document.
|
||||
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `value` is a
|
||||
[discarded](../basic_json_view/is_discarded.md) view or a [discarded](../basic_json/is_discarded.md) `BasicJsonType`
|
||||
value, and [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or contains) a
|
||||
binary value -- `BasicJsonType` can hold one, but a `json_document` cannot. Throws
|
||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if `value` is (or contains) a string that is
|
||||
not valid UTF-8, with the same message [`BasicJsonType::dump()`](../basic_json/dump.md) gives for that string.
|
||||
|
||||
## Complexity
|
||||
|
||||
Amortized constant, plus time linear in the size of `value` to encode it into the document's storage (constant for
|
||||
a scalar, linear in the number of nested values for an array or object): the elements of `array` move to a growable
|
||||
block of links the first time it is appended to (or [`set`](set.md) on), and that block itself grows -- doubling its
|
||||
capacity, so the cost of growing it amortizes to constant per element -- only once it runs out of room. See
|
||||
[Notes](#notes).
|
||||
|
||||
## Notes
|
||||
|
||||
Like [`set`](set.md) on a member or an element, `push_back` never moves an existing element itself -- only where
|
||||
`array`'s *links* to its elements live -- so a view of an existing element of `array` stays valid across a
|
||||
`push_back`, but any iterator already taken over `array` is invalidated, since it was walking the old layout. See
|
||||
[Edits](index.md#edits) for what stays valid across an edit in general.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below appends records to an array one at a time, as they might arrive from a stream of events,
|
||||
without ever building a `BasicJsonType` value for the array or for the records already in it, and shows that a
|
||||
view taken from an earlier `push_back` still refers to the same element once later ones have run.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__push_back.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__push_back.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [set](set.md) - replace a value, or set an object member, an array element, or the value a JSON pointer refers to
|
||||
- [insert](insert.md) - insert an element into an array before a given position
|
||||
- [erase](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
- [root](root.md) - the view of the root value
|
||||
- [`BasicJsonType::push_back`](../basic_json/push_back.md) - the corresponding function of `basic_json`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -70,6 +70,7 @@ own on the next, since ownership is decided freshly each time.
|
||||
|
||||
- [parse](parse.md) - deserialize from a compatible input
|
||||
- [root](root.md) - the view of the root value
|
||||
- [load](load.md) - read a document from an image instead of parsing JSON text
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
# <small>nlohmann::basic_json_document::</small>save
|
||||
|
||||
```cpp
|
||||
std::vector<std::uint8_t> save() const;
|
||||
```
|
||||
|
||||
Writes the document as an *image*: a byte buffer that [`load`](load.md) reads back without parsing. The image holds
|
||||
the node index, the source text (plus, for an edited document, the number tokens edits wrote), and the decoded
|
||||
strings (plus the strings edits wrote) -- everything [`root()`](root.md) needs, with nothing left to parse.
|
||||
|
||||
An edited document is written in its *current* state, with its values in document order, the way the library's own
|
||||
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)
|
||||
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 `#!cpp Editable`, since the image reflects document order and
|
||||
values only, not which specialization produced them.
|
||||
|
||||
## Return value
|
||||
|
||||
The image, as a `#!cpp std::vector<std::uint8_t>`. Pass it, or a pointer to its data together with its size, to
|
||||
[`load`](load.md) to read the document back.
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: `save()` does not modify `#!cpp *this` (it is `#!cpp const`), so if it throws, the document is left
|
||||
exactly as it was, and the partially built image is discarded with the exception.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.320`](../../home/exceptions.md#jsonexceptiontype_error320) if the document is
|
||||
[discarded](is_discarded.md) -- a default-constructed document, or one a failed [`parse()`](parse.md)/
|
||||
[`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
|
||||
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.
|
||||
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
[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
|
||||
```
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the size of the document: the number of nodes, plus the length of the text and the decoded strings that end
|
||||
up in the image.
|
||||
|
||||
## Notes
|
||||
|
||||
**Format.** The image begins with a 64-byte header (the magic bytes `#!cpp "NJVI"`, a version number, the node count,
|
||||
and the sizes of the text and the decoded strings, all little-endian), followed by the nodes
|
||||
([16 bytes each](../../home/architecture.md#node-index-of-json-views)), the text and a `#!cpp '\0'`, and the decoded
|
||||
strings and a `#!cpp '\0'`. [`load`](load.md) checks the header, and the sizes it describes, before reading anything
|
||||
else -- see [`load`'s Exceptions](load.md#exceptions).
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
The image format is versioned but not yet stable: it may change in an incompatible way before it is declared
|
||||
stable. Use images to cache a document within one build of the library, or to hand one to another process running
|
||||
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.
|
||||
|
||||
**Little-endian only.** The image is written as raw little-endian bytes, with no byte-swapping. `save()` (and
|
||||
[`load`](load.md)) throw `type_error.320` on a big-endian target rather than silently produce bytes a big-endian
|
||||
reader could not interpret correctly.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Caching a parsed document as an image"
|
||||
|
||||
The example below saves a parsed configuration as an image -- the way a service might cache one to answer later
|
||||
requests without parsing the text again -- and confirms that loading it back gives exactly the same result as
|
||||
parsing did, and that saving is deterministic.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__save.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__save.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [load](load.md) - read an image written by `save()`
|
||||
- [owns_source](owns_source.md) - return whether the document holds its own copy of the text
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document to JSON text instead of an image
|
||||
- [Images](../../features/json_view.md#images) - why and when to use images
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,194 @@
|
||||
# <small>nlohmann::basic_json_document::</small>set
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
template<typename V>
|
||||
view_type set(view_type target, V&& value);
|
||||
|
||||
// (2)
|
||||
template<typename V>
|
||||
view_type set(view_type object, string_view_t key, V&& value);
|
||||
|
||||
// (3)
|
||||
template<typename I, typename V>
|
||||
view_type set(view_type array, I idx, V&& value);
|
||||
|
||||
// (4)
|
||||
template<typename V>
|
||||
view_type set(const json_pointer& ptr, V&& value);
|
||||
```
|
||||
|
||||
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`).
|
||||
|
||||
1. Replaces the value `target` refers to with `value`.
|
||||
2. Sets the member `key` of the object `object` to `value`: assigns it if `object` already has a member with this
|
||||
key -- the first one, should the key occur more than once, and the later duplicates are then dropped (see the
|
||||
[Notes](#notes) below) -- or appends a new member at the end otherwise. A [null](../basic_json_view/is_null.md)
|
||||
`object` first becomes an empty object.
|
||||
3. Assigns `value` to the element at index `idx` of the array `array`, which must already exist (`#!cpp idx <
|
||||
array.size()`).
|
||||
4. Sets the value the JSON pointer `ptr` refers to, relative to [`root()`](root.md), to `value`. The *parent* of the
|
||||
target must already exist: an object member is set as in 2. (added if it does not exist yet), an array element is
|
||||
assigned as in 3., and a last reference token of `#!cpp "-"`, or equal to the size of the array, appends `value`
|
||||
instead, exactly as [`push_back`](push_back.md) would. An empty `ptr` sets [`root()`](root.md) itself, as in 1.
|
||||
|
||||
In every overload, `value` is accepted three ways: a [`basic_json_view`](../basic_json_view/index.md) of *any*
|
||||
document -- read-only or editable, and it does not have to be `target`'s/`object`'s/`array`'s own document -- which
|
||||
is copied so that nothing is shared with the source document afterward; a `BasicJsonType` value; or anything
|
||||
`BasicJsonType` can be constructed from (numbers, strings, `#!cpp bool`, `#!cpp nullptr`, containers, ...).
|
||||
|
||||
## Template parameters
|
||||
|
||||
`V`
|
||||
: the type of `value`, deduced; see above for what is accepted.
|
||||
|
||||
`I`
|
||||
: an integral type other than `#!cpp bool`, deduced (overloads taking a `#!cpp bool` or a non-integral type for
|
||||
`idx` do not participate in overload resolution).
|
||||
|
||||
## Parameters
|
||||
|
||||
`target` (in)
|
||||
: the value to replace
|
||||
|
||||
`object` (in)
|
||||
: the object (or null value) whose member to set
|
||||
|
||||
`array` (in)
|
||||
: the array whose element to assign
|
||||
|
||||
`key` (in)
|
||||
: the key of the member to set
|
||||
|
||||
`idx` (in)
|
||||
: the index of the element to assign; a negative value throws (see [Exceptions](#exceptions))
|
||||
|
||||
`ptr` (in)
|
||||
: a JSON pointer to the value to set, relative to `root()`
|
||||
|
||||
`value` (in)
|
||||
: the new value
|
||||
|
||||
## Return value
|
||||
|
||||
1. a view of `target`, now holding `value`
|
||||
2. a view of the member `key` of `object`, now holding `value`
|
||||
3. a view of the element `idx` of `array`, now holding `value`
|
||||
4. a view of the value `ptr` refers to, now holding `value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Basic exception safety: `value` is fully encoded -- including the checks below -- into storage owned by the document
|
||||
before anything already reachable from [`root()`](root.md) is touched, so a failure while encoding `value` (an
|
||||
invalid argument, or `#!cpp std::bad_alloc`) leaves the document completely unchanged, other than memory allocated
|
||||
for the encoding that is not reclaimed. A failure of a later allocation -- while an edited array or object switches
|
||||
from its parsed layout to a growable block, see [Notes](#notes) -- can still leave a partial effect, such as a
|
||||
[null](../basic_json_view/is_null.md) `object`/`array` argument already turned into an empty object/array even
|
||||
though `value` itself was not linked in.
|
||||
|
||||
## Exceptions
|
||||
|
||||
1. Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `value` is a
|
||||
[discarded](../basic_json_view/is_discarded.md) view, or a [discarded](../basic_json/is_discarded.md)
|
||||
`BasicJsonType` value (e.g. `#!cpp BasicJsonType(value_t::discarded)`) -- an object or array `value`, of either
|
||||
kind, is fine and is encoded as a whole subtree.
|
||||
2. Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if `object` is neither an object
|
||||
nor null -- the same message [`operator[]`](../basic_json_view/operator%5B%5D.md) throws for a string argument on
|
||||
such a value. Throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if `key` is not
|
||||
valid UTF-8, with the same message [`BasicJsonType::dump()`](../basic_json/dump.md) gives for that string.
|
||||
Also throws what 1. throws for `value`.
|
||||
3. Throws `type_error.305` if `array` is not an array -- the same message `operator[]` throws for a numeric argument
|
||||
on such a value. Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `idx` is
|
||||
negative, or if `#!cpp idx >= array.size()`. Also throws what 1. throws for `value`.
|
||||
4. Throws what [`at`](../basic_json_view/at.md) throws (overload 3) for resolving `ptr`'s parent, except that a
|
||||
missing object member or an array index equal to the array's size at the very last reference token is not an
|
||||
error there (it becomes a new member or an appended element) instead of
|
||||
[`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403)/[`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402).
|
||||
For the last reference token itself: if the parent is an object (or a primitive value, where it throws
|
||||
`type_error.305`), throws what 2. throws; if the parent is an array, throws what 3. throws for an index that is
|
||||
out of range, or, for a token that is not a valid array index,
|
||||
[`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) (a leading `#!cpp '0'`),
|
||||
[`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) (not a number),
|
||||
[`out_of_range.410`](../../home/exceptions.md#jsonexceptionout_of_range410) (too large for `size_type`), or
|
||||
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) (an empty token). Also throws what 1.
|
||||
throws for `value`.
|
||||
|
||||
Every overload also throws [`type_error.319`](../../home/exceptions.md#jsonexceptiontype_error319) if `value` is (or
|
||||
contains) a binary value -- `BasicJsonType` can hold one, but a `json_document` cannot -- and
|
||||
[`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) ("view does not belong to this
|
||||
document") if `target`/`object`/`array` is a [discarded](../basic_json_view/is_discarded.md) view or a view of a
|
||||
*different* document (overloads 1-3 only; overload 4 always starts from this document's own [`root()`](root.md)).
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the size of `value` (encoding it into the document's storage): constant for a scalar, linear in the
|
||||
number of nested values for an array or object. If `target` is itself an array or object that spans more than one
|
||||
node in its parent's original, unedited layout, and `value` is a scalar, replacing it additionally costs time
|
||||
linear in the number of elements of that parent, the *first* time -- see [Notes](#notes).
|
||||
2. Linear in the number of members of `object`, to find an existing member with `key`, plus the complexity of 1. for
|
||||
`value`.
|
||||
3. Constant, plus the complexity of 1. for `value`.
|
||||
4. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array (as [`at`](../basic_json_view/at.md)), plus the complexity of 2. or 3. for
|
||||
the last token.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "Duplicate keys"
|
||||
|
||||
If `object` already has more than one member with `key` (2.), the *first* one is assigned `value` and every
|
||||
later member with the same key is removed -- so that a lookup, an iteration, and
|
||||
[`materialize()`](../basic_json_view/materialize.md) of `object` afterward all agree on a single value for
|
||||
`key`, the same way [`operator[]`](../basic_json_view/operator%5B%5D.md) already picks the first occurrence of a
|
||||
duplicate key for reading. See the [Notes on duplicate keys](../basic_json_view/operator%5B%5D.md#notes) of
|
||||
`operator[]`.
|
||||
|
||||
Setting a member (2.) or an element (3., through 4.) of an array or object whose elements have not been edited
|
||||
before switches it from its parsed layout to a growable block holding links to its elements; a later
|
||||
[`push_back`](push_back.md) or `set` on the same container reuses that block, growing it (amortized constant time)
|
||||
only once it runs out of room. This never moves an element itself -- only where the container's *links* to its
|
||||
elements live -- so a view of an element stays valid, but any iterator already taken over the container is
|
||||
invalidated, since it was walking the old layout. See [Edits](index.md#edits) for what stays valid across an edit in
|
||||
general.
|
||||
|
||||
The same switch happens, for the same reason, when overload 1. replaces a multi-node array/object value with a
|
||||
scalar: the *parent's* element sequence is what has to switch to links, not `target` itself, because the parent
|
||||
originally stepped over `target`'s whole subtree by its node count, which no longer applies once `target` is a
|
||||
one-node scalar.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: (1)/(2)/(3)/(4) replace a value, set a member, assign an element, set via a JSON pointer"
|
||||
|
||||
The example below edits a small configuration document -- replacing a value, adding an object member, assigning
|
||||
an array element, and reaching a field through a JSON pointer -- and shows what
|
||||
[`dump()`](../basic_json_view/dump.md) preserves that is lost once the same edits are made on a `BasicJsonType`
|
||||
value instead: the order object members were written in, and the exact spelling of a number that was never
|
||||
touched.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__set.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__set.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [push_back](push_back.md) - append to an array
|
||||
- [insert](insert.md) - insert an element into an array
|
||||
- [erase](erase.md) - remove an object member, an array element, or the value a JSON pointer refers to
|
||||
- [root](root.md) - the view of the root value, the starting point of overload 4
|
||||
- [`basic_json_view::dump`](../basic_json_view/dump.md) - serialize the document, keeping an untouched number's
|
||||
spelling with `#!cpp number_format::source`
|
||||
- [Edits](index.md#edits) - what an edit guarantees, for every overload
|
||||
- [Editing a document](../../features/json_view.md#editing-a-document) - why editable documents keep the source
|
||||
text's order and number spelling
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -9,11 +9,15 @@ basic_json_view at(const string_t& key) const;
|
||||
// (2)
|
||||
basic_json_view at(size_type idx) const;
|
||||
basic_json_view at(int idx) const;
|
||||
|
||||
// (3)
|
||||
basic_json_view at(const json_pointer& ptr) const;
|
||||
```
|
||||
|
||||
1. Returns the value of the object member with key `key` -- the first one, should the key occur more than once (see
|
||||
[Notes on duplicate keys](operator[].md#notes)).
|
||||
2. Returns the array element at index `idx`.
|
||||
3. Returns the value a JSON pointer `ptr` refers to, starting at this value.
|
||||
|
||||
## Parameters
|
||||
|
||||
@@ -23,10 +27,14 @@ basic_json_view at(int idx) const;
|
||||
`idx` (in)
|
||||
: index of the element to access
|
||||
|
||||
`ptr` (in)
|
||||
: JSON pointer to the element to access
|
||||
|
||||
## Return value
|
||||
|
||||
1. the value of the first member with key `key`
|
||||
2. the element at index `idx`
|
||||
3. the value `ptr` resolves to, starting at this value
|
||||
|
||||
## Exception safety
|
||||
|
||||
@@ -42,6 +50,20 @@ Strong exception safety: if an exception is thrown, there are no changes to the
|
||||
[`BasicJsonType::at`](../basic_json/at.md):
|
||||
- Throws [`type_error.304`](../../home/exceptions.md#jsonexceptiontype_error304) if the value is not an array.
|
||||
- Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if `#!cpp idx >= size()`.
|
||||
3. The function can throw the following exceptions, all with the same message as the corresponding call to
|
||||
[`BasicJsonType::at`](../basic_json/at.md):
|
||||
- Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in `ptr`
|
||||
begins with `#!cpp '0'`.
|
||||
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in `ptr` is
|
||||
not a number.
|
||||
- Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) if an array index in `ptr`
|
||||
is out of range.
|
||||
- Throws [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if a reference token is
|
||||
`#!cpp "-"` at an array -- `at` never inserts an element, so `#!cpp "-"` is always invalid.
|
||||
- Throws [`out_of_range.403`](../../home/exceptions.md#jsonexceptionout_of_range403) if a reference token names
|
||||
an object member that does not exist.
|
||||
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if `ptr` cannot be resolved
|
||||
because a reference token is used on a primitive value.
|
||||
|
||||
None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||
@@ -52,18 +74,24 @@ None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics
|
||||
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length --
|
||||
already known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level (as 1.) or the index into the array (as 2.).
|
||||
|
||||
## Notes
|
||||
|
||||
Unlike [`operator[]`](operator[].md), which returns a [discarded](is_discarded.md) view for a missing key or an
|
||||
out-of-range index, `at` always throws -- exactly as `BasicJsonType::at` does, and with the same messages, so
|
||||
existing error handling written against `BasicJsonType::at` keeps working unchanged when switched to a view.
|
||||
existing error handling written against `BasicJsonType::at` keeps working unchanged when switched to a view. This
|
||||
also holds for overload 3: unlike [`operator[]`](operator[].md) with a JSON pointer, which returns a discarded view
|
||||
for a missing key or an out-of-range index, `at` throws for those too (`out_of_range.403`/`out_of_range.401`).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
??? example "Example: (1)/(2) access specified element with bounds checking"
|
||||
|
||||
The example below reads required fields out of a service configuration with `at`, and shows that the exceptions
|
||||
it throws -- for a wrong type and for a missing key -- carry the same messages
|
||||
@@ -79,11 +107,27 @@ existing error handling written against `BasicJsonType::at` keeps working unchan
|
||||
--8<-- "examples/basic_json_view__at.output"
|
||||
```
|
||||
|
||||
??? example "Example: (3) access specified element via JSON pointer with bounds checking"
|
||||
|
||||
The example below shows that `at` with a JSON pointer throws exactly the exceptions, with exactly the messages,
|
||||
that [`BasicJsonType::at`](../basic_json/at.md) throws for the same pointer and the same document.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__at_json_pointer.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__at_json_pointer.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [operator[]](operator[].md) - access specified element (returns a discarded view instead of throwing)
|
||||
- [front](front.md), [back](back.md) - access the first or last element
|
||||
- [`BasicJsonType::at`](../basic_json/at.md) - the corresponding function of `basic_json`
|
||||
- [`json_pointer`](../json_pointer/index.md) - JSON pointer type used by overload 3
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
# <small>nlohmann::basic_json_view::</small>contains
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
bool contains(string_view_t key) const;
|
||||
bool contains(const char* key) const;
|
||||
bool contains(const string_t& key) const;
|
||||
|
||||
// (2)
|
||||
bool contains(const json_pointer& ptr) const;
|
||||
```
|
||||
|
||||
Checks whether the value is an object with a member with key `key`.
|
||||
1. Checks whether the value is an object with a member with key `key`.
|
||||
2. Checks whether a JSON pointer `ptr` can be resolved, starting at this value.
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: key value to check its existence
|
||||
|
||||
`ptr` (in)
|
||||
: JSON pointer to check its existence
|
||||
|
||||
## Return value
|
||||
|
||||
`#!cpp true` if the value is an object and has a member with key `key`, `#!cpp false` otherwise.
|
||||
1. `#!cpp true` if the value is an object and has a member with key `key`, `#!cpp false` otherwise
|
||||
2. `#!cpp true` if `ptr` can be resolved to a value starting at this view, `#!cpp false` otherwise
|
||||
|
||||
## Exception safety
|
||||
|
||||
@@ -23,22 +32,36 @@ No-throw guarantee: this function never throws exceptions.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
1. Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array -- as for [`operator[]`](operator[].md#complexity) and
|
||||
[`at`](at.md#complexity) with a JSON pointer.
|
||||
|
||||
## Notes
|
||||
|
||||
This method always returns `#!cpp false` when the value is not an object -- including a [discarded](is_discarded.md)
|
||||
Overload 1 always returns `#!cpp false` when the value is not an object -- including a [discarded](is_discarded.md)
|
||||
view.
|
||||
|
||||
!!! info "Postconditions"
|
||||
|
||||
If `#!cpp v.contains(key)` returns `#!cpp true`, then `#!cpp v[key]` is not [discarded](is_discarded.md).
|
||||
If `#!cpp v.contains(key)` returns `#!cpp true`, then `#!cpp v[key]` is not [discarded](is_discarded.md). If
|
||||
`#!cpp v.contains(ptr)` returns `#!cpp true`, then `#!cpp v[ptr]` is not discarded and `#!cpp v.at(ptr)` does not
|
||||
throw.
|
||||
|
||||
!!! info "Overload 2 never throws"
|
||||
|
||||
Unlike [`BasicJsonType::contains(const json_pointer&)`](../basic_json/contains.md), which can throw for certain
|
||||
malformed pointers (for instance an empty array-index reference token), overload 2 never throws: a missing key,
|
||||
an out-of-range or malformed array index, a `#!cpp "-"` index, or a reference token used on a primitive all
|
||||
simply make it return `#!cpp false`.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
??? example "Example: (1) check with key"
|
||||
|
||||
The example below counts how many of a batch of records carry an optional `retry_of` field, using `contains()`
|
||||
to check without ever materializing a single record of the batch.
|
||||
@@ -53,10 +76,26 @@ view.
|
||||
--8<-- "examples/basic_json_view__contains.output"
|
||||
```
|
||||
|
||||
??? example "Example: (2) check with JSON pointer"
|
||||
|
||||
The example below checks an optional, nested field with a JSON pointer, and shows two pointers that
|
||||
`#!cpp contains()` resolves to `#!cpp false` without throwing.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__contains_json_pointer.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__contains_json_pointer.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [find](find.md) - find a value in an object
|
||||
- [count](count.md) - returns the number of occurrences of a key
|
||||
- [at](at.md), [operator[]](operator[].md) - resolve a JSON pointer and throw, or return a discarded view
|
||||
- [`BasicJsonType::contains`](../basic_json/contains.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -26,6 +26,8 @@ No-throw guarantee: this function never throws exceptions.
|
||||
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time on
|
||||
average.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# <small>nlohmann::basic_json_view::</small>dump
|
||||
|
||||
```cpp
|
||||
string_t dump(const int indent = -1,
|
||||
const char indent_char = ' ',
|
||||
const bool ensure_ascii = false,
|
||||
const number_format numbers = number_format::shortest) const;
|
||||
```
|
||||
|
||||
Serializes this value (and its subtree) directly from the flat index, without ever building a `BasicJsonType` value
|
||||
first. With the default `#!cpp numbers == number_format::shortest`, the result is the same string
|
||||
[`BasicJsonType::dump`](../basic_json/dump.md) would produce for the value
|
||||
[`BasicJsonType::parse()`](../basic_json/parse.md) builds from the same source text, called with the same `indent`,
|
||||
`indent_char`, and `ensure_ascii` -- except that members of an object appear in document order rather than sorted by
|
||||
key, and *every* occurrence of a repeated key is written rather than only the last one (see
|
||||
[Notes on duplicate keys](operator[].md#notes)). For a `json_view` (whose `BasicJsonType` is not ordered), this means
|
||||
`dump()` can print an object's members in a different order than [`materialize()`](materialize.md)`.dump()` of the
|
||||
same subtree.
|
||||
|
||||
## Parameters
|
||||
|
||||
`indent` (in)
|
||||
: If `indent` is nonnegative, array elements and object members are pretty-printed with that indent level. An
|
||||
indent level of `0` only inserts newlines. `-1` (the default) selects the most compact representation.
|
||||
|
||||
`indent_char` (in)
|
||||
: The character used for indentation if `indent` is greater than `0`. The default is ` ` (space).
|
||||
|
||||
`ensure_ascii` (in)
|
||||
: If `ensure_ascii` is `#!cpp true`, all non-ASCII characters in the output are escaped with `\uXXXX` sequences, and
|
||||
the result consists of ASCII characters only.
|
||||
|
||||
`numbers` (in)
|
||||
: how to write numbers, see [`number_format`](number_format.md): `shortest` (the default) writes them the way
|
||||
[`BasicJsonType::dump`](../basic_json/dump.md) would; `source` copies every number exactly as it appears in the
|
||||
source text.
|
||||
|
||||
## Return value
|
||||
|
||||
string containing the serialization of this value, or `#!cpp "<discarded>"` if the view is
|
||||
[discarded](is_discarded.md).
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
May throw `#!cpp std::bad_alloc` if allocating the output string fails. Unlike
|
||||
[`BasicJsonType::dump`](../basic_json/dump.md), there is no `error_handler` parameter and no
|
||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316): the view only ever holds text the parser
|
||||
already validated as UTF-8, so there is nothing to replace or ignore.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the size of the output text.
|
||||
|
||||
## Notes
|
||||
|
||||
The walk over the subtree is iterative, so the nesting depth it can write is limited by available memory only, not by
|
||||
the call stack -- as for [`materialize()`](materialize.md).
|
||||
|
||||
Strings are escaped by the same rules as [`BasicJsonType::dump`](../basic_json/dump.md). With
|
||||
`#!cpp numbers == number_format::shortest`, floats are written with the library's shortest round-trip conversion,
|
||||
exactly as [`BasicJsonType::dump`](../basic_json/dump.md) would (e.g. `#!cpp 1.5`, `#!cpp 100.0`, `#!cpp 1e+100`), and
|
||||
integers are copied from the source text -- already canonical in JSON, so this matches their shortest form too --
|
||||
except that `#!cpp -0` is written as `#!cpp 0`, the way [`BasicJsonType::parse()`](../basic_json/parse.md) reads it.
|
||||
`#!cpp number_format::source` copies every number exactly as written in the source text instead, with no exception
|
||||
for `#!cpp -0` -- `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0.0`, `#!cpp -0`, or all digits of an integer literal with more
|
||||
digits than any number type holds (such a literal is itself classified as a float, see
|
||||
[What is different](../../features/json_view.md#what-is-different)) -- something `BasicJsonType` cannot do, since
|
||||
parsing already reduces every number to its parsed value.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below forwards a single record out of a larger batch, and re-serializes a configuration file, both
|
||||
without ever building a `BasicJsonType` value for the surrounding array or for the parts of it that were not
|
||||
needed. It also shows that [`materialize()`](materialize.md)`.dump()` of the configuration sorts its keys, where
|
||||
`dump()` on the view keeps the order they appear in the source text.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__dump.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__dump.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [`number_format`](number_format.md) - how `dump()` writes numbers
|
||||
- [operator<<](operator_ltlt.md) - serialize this value to a stream
|
||||
- [materialize](materialize.md) - build a `BasicJsonType` value, e.g. to use `BasicJsonType::dump`'s `error_handler`
|
||||
- [`BasicJsonType::dump`](../basic_json/dump.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -28,6 +28,8 @@ No-throw guarantee: this function never throws exceptions.
|
||||
Linear in the number of members: as for [`ordered_json`](../ordered_json.md), members are compared one after
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length -- already
|
||||
known from the index, without reading the key bytes -- before comparing its content.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time on
|
||||
average.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# <small>nlohmann::basic_json_view::</small>get
|
||||
|
||||
```cpp
|
||||
template<typename T>
|
||||
T get() const;
|
||||
```
|
||||
|
||||
Converts the value to `T`.
|
||||
|
||||
For the types below, the conversion works directly on the flat index -- no `BasicJsonType` value is built for it:
|
||||
|
||||
- `#!cpp bool`
|
||||
- arithmetic types other than `#!cpp bool` (from a number; from a boolean, as `#!cpp 0`/`#!cpp 1`, exactly as
|
||||
[`BasicJsonType::get<T>()`](../basic_json/get.md) converts a boolean)
|
||||
- `#!cpp std::nullptr_t`
|
||||
- `#!cpp std::basic_string<char, Traits, Alloc>` (including `string_t`) -- a copy of the string
|
||||
- [`string_view_t`](index.md#member-types) -- **no copy**: the returned view points into the document's
|
||||
[`source()`](../basic_json_document/source.md) text, or, for a string that contains escape sequences, into the
|
||||
document's own buffer of decoded strings (see [`get_string()`](get_string.md))
|
||||
- `BasicJsonType` -- equivalent to [`materialize()`](materialize.md)
|
||||
- `basic_json_view` -- returns `#!cpp *this`
|
||||
- `#!cpp std::vector<U, A>` -- element by element, each converted with `#!cpp get<U>()`; `#!cpp
|
||||
std::vector<basic_json_view>` keeps a view of every element instead of a value
|
||||
- `#!cpp std::map<K, V, C, A>` and `#!cpp std::unordered_map<K, V, H, E, A>`, if `K` is constructible from a `#!cpp
|
||||
(const char*, std::size_t)` pair -- member by member, each value converted with `#!cpp get<V>()`; with a repeated
|
||||
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
|
||||
|
||||
Every other `T` -- `#!cpp std::list`, `#!cpp std::pair`, `#!cpp std::array`, enumerations, user types with a
|
||||
`from_json()`, ... -- 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
|
||||
[`BasicJsonType::get<T>()`](../basic_json/get.md) would convert it.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`T`
|
||||
: the type to convert the value to
|
||||
|
||||
## Return value
|
||||
|
||||
the value, converted to `T`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
- For the directly-converted types listed above (other than `BasicJsonType` and `basic_json_view`, which never
|
||||
throw): throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if the value's type does not
|
||||
match `T` -- the same exception, with the same message, that [`BasicJsonType::get<T>()`](../basic_json/get.md)
|
||||
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
|
||||
an element to `U` throws.
|
||||
- For `#!cpp std::map`/`#!cpp std::unordered_map`: throws `type_error.302` if the value is not an object; otherwise,
|
||||
whatever converting a member to the mapped type throws.
|
||||
- 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.
|
||||
|
||||
None of the exceptions thrown directly by this function (the first three bullets above) carry a
|
||||
[`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
|
||||
`BasicJsonType` value, so it **does** carry a `JSON_DIAGNOSTICS` path if `BasicJsonType` was built with it enabled.
|
||||
|
||||
## Complexity
|
||||
|
||||
- `#!cpp bool`, arithmetic types, `#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `basic_json_view`:
|
||||
constant.
|
||||
- `#!cpp std::basic_string<char, Traits, Alloc>`: constant, plus one allocation and a copy of the string's bytes.
|
||||
- `BasicJsonType`: linear in the size of the subtree, see [`materialize()`](materialize.md).
|
||||
- `#!cpp std::vector<U, A>`: linear in the number of elements, times the complexity of converting one element to `U`.
|
||||
- `#!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
|
||||
std::unordered_map`), times the complexity of converting one member to the mapped type.
|
||||
- every other `T`: linear in the size of the subtree (building the `BasicJsonType` value), plus the complexity of
|
||||
[`BasicJsonType::get<T>()`](../basic_json/get.md) on it.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "Floating-point values"
|
||||
|
||||
A floating-point `T` is converted from the same digits the lexer would see during `#!cpp BasicJsonType::parse()`,
|
||||
using the same conversion, so the result is bit-for-bit identical to `#!cpp BasicJsonType::parse(text).get<T>()`
|
||||
for the same source text.
|
||||
|
||||
!!! info "Duplicate keys"
|
||||
|
||||
`#!cpp std::map`/`#!cpp std::unordered_map` conversions keep the *last* value of a repeated key, like
|
||||
[`materialize()`](materialize.md) and [`BasicJsonType::parse()`](../basic_json/parse.md) do. This is the opposite
|
||||
of [`operator[]`](operator[].md)/[`at`](at.md)/[`find`](find.md)/[`contains`](contains.md), which resolve to the
|
||||
*first* occurrence (see the [Notes on duplicate keys](operator[].md#notes)).
|
||||
|
||||
!!! info "No pointers, references, or implicit conversion"
|
||||
|
||||
Unlike `BasicJsonType`, `basic_json_view` has no stored value anywhere to hand out a pointer or a reference to, so
|
||||
it provides neither `#!cpp get_ptr()`, `#!cpp get_ref()`, nor `#!cpp operator ValueType()`.
|
||||
[`get_string()`](get_string.md) (equivalently, `#!cpp get<string_view_t>()`) is the zero-copy alternative for
|
||||
strings.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below reads typed fields straight into C++ variables, collects a view of every array element with
|
||||
`#!cpp get<std::vector<basic_json_view>>()` instead of a value, and converts a nested object into a user type
|
||||
through its `from_json()` -- which runs on a `BasicJsonType` value `materialize()` builds for just that one
|
||||
member.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__get.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__get.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [get_to](get_to.md) - convert and write into a passed value
|
||||
- [get_string](get_string.md) - the string, without a copy
|
||||
- [number_token](number_token.md) - a number's token text, without a copy
|
||||
- [materialize](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||
- [`BasicJsonType::get`](../basic_json/get.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,71 @@
|
||||
# <small>nlohmann::basic_json_view::</small>get_string
|
||||
|
||||
```cpp
|
||||
string_view_t get_string() const;
|
||||
```
|
||||
|
||||
Returns the string value as a [`string_view_t`](index.md#member-types), without copying it.
|
||||
|
||||
## Return value
|
||||
|
||||
The string, as a [`string_view_t`](index.md#member-types) that points either into the document's
|
||||
[`source()`](../basic_json_document/source.md) text (a string with no escape sequences), or into the document's own
|
||||
buffer of decoded strings (a string that contains escape sequences, such as `#!json "\n"` or `#!json "\u00e9"`, which
|
||||
had to be decoded once when the document was parsed).
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if the value is not a string; example:
|
||||
`"type must be string, but is array"`.
|
||||
|
||||
This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||
`JSON_DIAGNOSTICS` enabled.
|
||||
|
||||
## Complexity
|
||||
|
||||
Constant.
|
||||
|
||||
## Notes
|
||||
|
||||
`basic_json_view` has no `BasicJsonType` value stored anywhere, so unlike `BasicJsonType`, it has no `get_ref()` to
|
||||
hand out a reference to a stored `string_t`. `get_string()` (equivalently, [`get<string_view_t>()`](get.md)) is the
|
||||
zero-copy alternative: [`BasicJsonType::get_ref<const string_t&>()`](../basic_json/get_ref.md) is its closest
|
||||
counterpart, except that it returns a view instead of a reference to a value that must already exist.
|
||||
|
||||
The returned [`string_view_t`](index.md#member-types) is valid exactly as long as the view that produced it -- see the
|
||||
[validity rules](index.md) of `basic_json_view` -- and, for a string with no escapes, for as long as the document's
|
||||
source text.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below pulls one field out of a JSON text that stands in for a large API response, and shows that no
|
||||
`#!cpp std::string` was allocated for it: the returned view still points inside the original buffer. A field that
|
||||
contains an escape sequence cannot point into the original text -- it was decoded once into the document's own
|
||||
buffer instead -- but still avoids a per-field allocation.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__get_string.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__get_string.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [get](get.md) - convert the value to a given type (`#!cpp get<string_view_t>()` is equivalent to this function)
|
||||
- [number_token](number_token.md) - a number's token text, without a copy
|
||||
- [`BasicJsonType::get_ref`](../basic_json/get_ref.md) - the closest counterpart of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,65 @@
|
||||
# <small>nlohmann::basic_json_view::</small>get_to
|
||||
|
||||
```cpp
|
||||
template<typename T>
|
||||
T& get_to(T& v) const;
|
||||
```
|
||||
|
||||
Converts the value to `T` and assigns it to `v`. Equivalent to
|
||||
|
||||
```cpp
|
||||
v = get<T>();
|
||||
return v;
|
||||
```
|
||||
|
||||
## Template parameters
|
||||
|
||||
`T`
|
||||
: the type to convert the value to
|
||||
|
||||
## Parameters
|
||||
|
||||
`v` (out)
|
||||
: the variable to store the converted value in
|
||||
|
||||
## Return value
|
||||
|
||||
`v`, allowing calls to chain
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, `v` is not modified.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Whatever [`get<T>()`](get.md) throws for the same value and `T`.
|
||||
|
||||
## Complexity
|
||||
|
||||
Whatever [`get<T>()`](get.md) has for the same `T`.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below reads several fields of a service configuration directly into existing variables, then uses
|
||||
the returned reference to fold the `#!cpp host`/`#!cpp port` pair into a single string in the same expression.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__get_to.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__get_to.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [get](get.md) - convert the value to a given type
|
||||
- [`BasicJsonType::get_to`](../basic_json/get_to.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -3,7 +3,7 @@
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
template<typename BasicJsonType>
|
||||
template<typename BasicJsonType, bool Editable = false>
|
||||
class basic_json_view;
|
||||
```
|
||||
|
||||
@@ -20,8 +20,19 @@ Moving the document itself does not invalidate its views: the index is heap-allo
|
||||
`basic_json_document` object.
|
||||
|
||||
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, element
|
||||
access, lookup, iteration, and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a subtree on
|
||||
demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or comparison.
|
||||
access, lookup, iteration, conversion, and comparison -- [`get<T>()`](get.md), [`get_string()`](get_string.md),
|
||||
[`number_token()`](number_token.md), and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a
|
||||
subtree on demand. [`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`](contains.md), and
|
||||
[`value`](value.md) also accept a [`json_pointer`](../json_pointer/index.md). [`operator==`](operator_eq.md) and
|
||||
[`operator!=`](operator_ne.md) compare two views, or a view and a `BasicJsonType` value, without ever building a
|
||||
`BasicJsonType` value for a view; no ordering comparison (`#!cpp operator<`) is provided.
|
||||
|
||||
`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 Editable == true`) sees every edit made through
|
||||
[`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
|
||||
still refers to it -- including ones taken before the change -- reads the new value. See
|
||||
[Edits](../basic_json_document/index.md#edits).
|
||||
|
||||
## Template parameters
|
||||
|
||||
@@ -29,10 +40,17 @@ demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or
|
||||
: a specialization of [`basic_json`](../basic_json/index.md), matching the
|
||||
[`basic_json_document`](../basic_json_document/index.md) the view was taken from.
|
||||
|
||||
`Editable`
|
||||
: whether the view is of an editable document, matching the [`basic_json_document`](../basic_json_document/index.md)
|
||||
it was taken from (optional, `#!cpp false` by default). See [Edits](../basic_json_document/index.md#edits).
|
||||
|
||||
## Specializations
|
||||
|
||||
- [**json_view**](../json_view.md) - views of a [`json_document`](../json_document.md)
|
||||
- [**ordered_json_view**](../ordered_json_view.md) - views of an [`ordered_json_document`](../ordered_json_document.md)
|
||||
- [**json_editable_view**](../json_editable_view.md) - views of a [`json_editable_document`](../json_editable_document.md)
|
||||
- [**ordered_json_editable_view**](../ordered_json_editable_view.md) - views of an
|
||||
[`ordered_json_editable_document`](../ordered_json_editable_document.md)
|
||||
|
||||
## Member types
|
||||
|
||||
@@ -44,6 +62,7 @@ demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or
|
||||
- **iterator**, **const_iterator** - a forward iterator over the elements of an array or the member values of an
|
||||
object, in document order; both names refer to the same type, since a view is always read-only
|
||||
- **item** - a (key, value) pair produced by [`items()`](items.md)
|
||||
- [**number_format**](number_format.md) - how [`dump()`](dump.md) writes numbers
|
||||
|
||||
## Member functions
|
||||
|
||||
@@ -72,6 +91,7 @@ demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or
|
||||
|
||||
- [**at**](at.md) - access specified element with bounds checking
|
||||
- [**operator[]**](operator[].md) - access specified element
|
||||
- [**value**](value.md) - access specified element with default value
|
||||
- [**front**](front.md) - access the first element
|
||||
- [**back**](back.md) - access the last element
|
||||
|
||||
@@ -96,8 +116,22 @@ demand. It does not (yet) provide `get<T>()`, JSON Pointer support, `dump()`, or
|
||||
|
||||
### Conversion
|
||||
|
||||
- [**get**](get.md) - get a value
|
||||
- [**get_to**](get_to.md) - get a value and write it to a destination
|
||||
- [**get_string**](get_string.md) - get a string value without a copy
|
||||
- [**number_token**](number_token.md) - get a number's token text without a copy
|
||||
- [**materialize**](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||
|
||||
### Comparison
|
||||
|
||||
- [**operator==**](operator_eq.md) - comparison: equal
|
||||
- [**operator!=**](operator_ne.md) - comparison: not equal
|
||||
|
||||
### Serialization
|
||||
|
||||
- [**dump**](dump.md) - serialize to a JSON-formatted string
|
||||
- [**operator<<**](operator_ltlt.md) - serialize to stream
|
||||
|
||||
### Source access
|
||||
|
||||
- [**source_offset**](source_offset.md) - byte offset of this value in the document's source text
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# <small>nlohmann::basic_json_view::</small>number_format
|
||||
|
||||
```cpp
|
||||
enum class number_format {
|
||||
shortest,
|
||||
source
|
||||
};
|
||||
```
|
||||
|
||||
This enumeration is used in [`dump`](dump.md) to choose how numbers are written. Two values are differentiated:
|
||||
|
||||
shortest
|
||||
: integers are copied from the source text -- already canonical in JSON -- except that `#!cpp -0` becomes
|
||||
`#!cpp 0`, the way [`BasicJsonType::parse()`](../basic_json/parse.md) reads it; floats are written with the
|
||||
library's shortest round-trip conversion, exactly as [`BasicJsonType::dump()`](../basic_json/dump.md) would (e.g.
|
||||
`#!cpp 1.5`, `#!cpp 100.0`, `#!cpp 1e+100`)
|
||||
|
||||
source
|
||||
: every number is copied exactly as it appears in the source text -- `#!cpp 1.50`, `#!cpp 1E2`, `#!cpp -0`, all
|
||||
digits of an integer literal with more digits than any number type holds -- something `BasicJsonType` cannot do,
|
||||
since parsing already reduces every number to its parsed value
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below writes back a price list received from a supplier: with `number_format::shortest` (the
|
||||
default), a trailing zero and scientific notation are normalized away and a long account number that overflows
|
||||
every number type is rounded, the same way `#!cpp materialize().dump()` (or `basic_json::dump()`) would;
|
||||
`number_format::source` keeps every number exactly as it was written in the source text instead.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__number_format.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__number_format.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [dump](dump.md) - serialize to a JSON-formatted string
|
||||
- [number_token](number_token.md) - get a single number's token text without dumping the whole value
|
||||
- [`BasicJsonType::error_handler_t`](../basic_json/error_handler_t.md) - the analogous enumeration for
|
||||
`BasicJsonType::dump`'s decoding-error behavior
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,74 @@
|
||||
# <small>nlohmann::basic_json_view::</small>number_token
|
||||
|
||||
```cpp
|
||||
string_view_t number_token() const;
|
||||
```
|
||||
|
||||
Returns the number exactly as it appears in the source text, without parsing or rounding it.
|
||||
|
||||
## Return value
|
||||
|
||||
The number's token text, as a [`string_view_t`](index.md#member-types) into the document's
|
||||
[`source()`](../basic_json_document/source.md) text -- for example `#!cpp "1.50"`, `#!cpp "1E2"`, `#!cpp "-0"`, or an
|
||||
integer literal with more digits than any number type holds (such as a 30-digit integer, which [`get<T>()`](get.md)
|
||||
and [`materialize()`](materialize.md) can only represent approximately, as a `number_float_t`).
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if the value is not a number; example:
|
||||
`"type must be number, but is string"`.
|
||||
|
||||
This exception does not carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||
`JSON_DIAGNOSTICS` enabled.
|
||||
|
||||
## Complexity
|
||||
|
||||
Constant.
|
||||
|
||||
## Notes
|
||||
|
||||
`BasicJsonType` has no counterpart to this function: once a number is parsed into `number_integer_t`,
|
||||
`number_unsigned_t`, or `number_float_t`, its original textual form (leading zeros aside, which are already rejected
|
||||
by the grammar; trailing zeros in the fraction; the case and sign of the exponent; ...) is gone. `number_token()` is
|
||||
useful precisely where that form must survive -- a price or an identifier that must be reproduced exactly, or a
|
||||
number too large for any of `BasicJsonType`'s number types to hold without loss.
|
||||
|
||||
The returned [`string_view_t`](index.md#member-types) always points into the document's
|
||||
[`source()`](../basic_json_document/source.md) text -- numbers are never decoded into the document's separate string
|
||||
buffer -- and is valid exactly as long as that text is, see the [validity rules](index.md) of `basic_json_view`.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below keeps a price and an order ID exactly as they were written in an incoming order, where
|
||||
converting them with [`get<T>()`](get.md) would lose information: the price picks up floating-point rounding, and
|
||||
the order ID -- more digits than a 64-bit integer holds -- can only be approximated as a `#!cpp double` once
|
||||
`#!cpp materialize()`d.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__number_token.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__number_token.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [get](get.md) - convert the value to a given type
|
||||
- [get_string](get_string.md) - the string, without a copy
|
||||
- [`BasicJsonType::number_integer_t`](../basic_json/number_integer_t.md),
|
||||
[`number_unsigned_t`](../basic_json/number_unsigned_t.md), [`number_float_t`](../basic_json/number_float_t.md) - the
|
||||
number types `#!cpp get<T>()` and `materialize()` convert into
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -9,12 +9,18 @@ basic_json_view operator[](const string_t& key) const;
|
||||
// (2)
|
||||
basic_json_view operator[](size_type idx) const;
|
||||
basic_json_view operator[](int idx) const;
|
||||
|
||||
// (3)
|
||||
basic_json_view operator[](const json_pointer& ptr) const;
|
||||
```
|
||||
|
||||
1. Returns the value of the object member with key `key` -- the first one, should the key occur more than once (see
|
||||
the [Notes](#notes) below) -- or a [discarded](is_discarded.md) view if there is no such member.
|
||||
2. Returns the array element at index `idx`, or a [discarded](is_discarded.md) view if `idx` is out of range. (The
|
||||
`#!cpp int` overload only exists so that an integer literal is not ambiguous between this overload and 1.)
|
||||
3. Returns the value a JSON pointer `ptr` refers to, starting at this value, or a [discarded](is_discarded.md) view
|
||||
wherever resolving it further is not possible without inserting into or extending the document (see
|
||||
[Return value](#return-value) and [Exceptions](#exceptions) below).
|
||||
|
||||
## Parameters
|
||||
|
||||
@@ -24,11 +30,18 @@ basic_json_view operator[](int idx) const;
|
||||
`idx` (in)
|
||||
: index of the element to access
|
||||
|
||||
`ptr` (in)
|
||||
: JSON pointer to the element to access
|
||||
|
||||
## Return value
|
||||
|
||||
1. the value of the first member with key `key`, or a discarded view if `#!cpp is_object()` is `#!cpp false` or no
|
||||
member has this key
|
||||
2. the element at index `idx`, or a discarded view if `#!cpp is_array()` is `#!cpp false` or `#!cpp idx >= size()`
|
||||
3. the value `ptr` resolves to, starting at this value, or a discarded view for exactly the reference tokens where the
|
||||
**const** overload of [`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) invokes undefined behavior for
|
||||
the same pointer and the same document: an object member that does not exist, or an array index that is out of
|
||||
range
|
||||
|
||||
## Exception safety
|
||||
|
||||
@@ -42,9 +55,19 @@ Strong exception safety: if an exception is thrown, there are no changes to the
|
||||
2. Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the value is not an array --
|
||||
the same exception, with the same message, that the **const** overload of
|
||||
[`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) throws for a numeric argument on a non-array value.
|
||||
3. Throws the same exceptions, with the same messages, that the **const** overload of
|
||||
[`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) throws for the same pointer and the same document:
|
||||
- [`out_of_range.402`](../../home/exceptions.md#jsonexceptionout_of_range402) if a reference token is `#!cpp "-"`
|
||||
at an array.
|
||||
- [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a reference token cannot be
|
||||
resolved because it is used on a primitive value.
|
||||
- [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in `ptr` begins
|
||||
with `#!cpp '0'`.
|
||||
- [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in `ptr` is not a
|
||||
number.
|
||||
|
||||
Neither exception carries a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no `BasicJsonType`
|
||||
value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||
None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||
`JSON_DIAGNOSTICS` enabled.
|
||||
|
||||
## Complexity
|
||||
@@ -53,8 +76,12 @@ value to point at, so the exception is created without one, even if `BasicJsonTy
|
||||
another, in document order, stopping at the first match. Each comparison first checks the key's length --
|
||||
already known from the index, without reading the key bytes -- before comparing its content, so a key of a
|
||||
different length than `key` is rejected without touching the source text.
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in `idx`: elements are skipped one at a time from the first one, since they are not a fixed size in the
|
||||
index (unlike `BasicJsonType`'s array, which is random-access).
|
||||
3. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level (as 1.) or the index into the array (as 2.).
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -74,9 +101,18 @@ document.
|
||||
early. [`begin()`](begin.md)/[`end()`](end.md) and [`items()`](items.md) iterate over *all* members, including
|
||||
duplicates, in document order. See the example below and [`size()`](size.md#notes).
|
||||
|
||||
!!! info "JSON pointer resolution"
|
||||
|
||||
Overload 3 walks `ptr` one reference token at a time, starting at this value, the same way [`at`](at.md) and
|
||||
[`contains`](contains.md) do. It only ever returns a discarded view where the **const** overload of
|
||||
`BasicJsonType::operator[]` would be undefined behavior for the same pointer -- a missing object member or an
|
||||
out-of-range array index -- and still throws for every other way `ptr` can fail to resolve. See
|
||||
[`at`](at.md#exceptions) for the checked version, which throws in every case instead, and
|
||||
[`contains`](contains.md) for a version that never throws.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
??? example "Example: (1)/(2) access specified element"
|
||||
|
||||
The example below reads a couple of fields out of a batch of user records without ever materializing a full
|
||||
`BasicJsonType` value for the batch. `operator[]` is used both to look up an optional object member and to index
|
||||
@@ -93,12 +129,29 @@ document.
|
||||
--8<-- "examples/basic_json_view__operator[].output"
|
||||
```
|
||||
|
||||
??? example "Example: (3) access specified element via JSON pointer"
|
||||
|
||||
The example below reaches straight into one deeply nested field of a large document with a single JSON pointer,
|
||||
without ever building a tree for the rest of it, and shows the discarded-view and throwing outcomes of a pointer
|
||||
that cannot be fully resolved.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__operator[]_json_pointer.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__operator[]_json_pointer.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [at](at.md) - access specified element with bounds checking (throws instead of returning a discarded view)
|
||||
- [front](front.md), [back](back.md) - access the first or last element
|
||||
- [find](find.md), [contains](contains.md) - look up a member without throwing
|
||||
- [`BasicJsonType::operator[]`](../basic_json/operator%5B%5D.md) - the corresponding function of `basic_json`
|
||||
- [`json_pointer`](../json_pointer/index.md) - JSON pointer type used by overload 3
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
# <small>nlohmann::basic_json_view::</small>operator==
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
bool operator==(const basic_json_view& lhs, const basic_json_view& rhs);
|
||||
|
||||
// (2)
|
||||
bool operator==(const basic_json_view& lhs, const BasicJsonType& rhs);
|
||||
bool operator==(const BasicJsonType& lhs, const basic_json_view& rhs);
|
||||
```
|
||||
|
||||
1. Compares two views for equality: whether the values [`BasicJsonType::parse()`](../basic_json/parse.md) would
|
||||
produce for `lhs` and `rhs` are equal, according to `BasicJsonType`'s [`operator==`](../basic_json/operator_eq.md).
|
||||
2. Compares a view and a `BasicJsonType` value for equality, in either order: whether the value `parse()` would
|
||||
produce for the view and the other operand are equal, according to `BasicJsonType`'s
|
||||
[`operator==`](../basic_json/operator_eq.md).
|
||||
|
||||
Neither overload builds a `BasicJsonType` value for a view to do the comparison (see [Notes](#notes) below). Numbers
|
||||
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
|
||||
resolved exactly as `parse()` resolves them -- the last value, at the position of the first occurrence of the key.
|
||||
|
||||
## Parameters
|
||||
|
||||
`lhs` (in)
|
||||
: first value to consider
|
||||
|
||||
`rhs` (in)
|
||||
: second value to consider
|
||||
|
||||
## Return value
|
||||
|
||||
whether the values `lhs` and `rhs` are equal
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to either operand, or to the document(s) a
|
||||
view refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
May throw `#!cpp std::bad_alloc`. Unlike the other comparison and most other `basic_json_view` functions,
|
||||
`operator==` is not `#!cpp noexcept`: resolving an object's members needs a temporary array to sort them by key (see
|
||||
[Complexity](#complexity) below), and that allocation can fail.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear in the size of the compared values: every number, string, array element, and object member is visited at most
|
||||
once, and the walk is iterative, so the nesting depth it can compare is limited by available memory only, not by the
|
||||
call stack (as for [`materialize()`](materialize.md)). Resolving an object's members takes an additional O(n log n)
|
||||
in the number of members at that level, since they are sorted by key to detect and resolve duplicates before being
|
||||
compared. Two arrays of different [`size()`](size.md) are rejected without visiting either one's elements.
|
||||
|
||||
## Notes
|
||||
|
||||
Only a single number, boolean, or `#!cpp null` value is ever materialized into a `BasicJsonType`, to reuse its
|
||||
`operator==` -- for numbers, so that values written differently in the source text but equal in value (e.g. an
|
||||
integer and a floating-point literal) still compare equal, following the same rules `BasicJsonType` does for special
|
||||
values such as `#!cpp NaN`. Constructing one of these scalars never allocates. Strings are compared directly, without
|
||||
allocating, either from the source text on both sides or, for overload 2, against `BasicJsonType`'s own string.
|
||||
Arrays and objects are never materialized at all; only their elements or members are visited, one pair at a time.
|
||||
|
||||
!!! info "How objects are compared"
|
||||
|
||||
For a [`json_view`](../json_view.md) (`BasicJsonType::object_t` is `#!cpp std::map`), members are compared by
|
||||
key, regardless of the order they appear in the source text. For an
|
||||
[`ordered_json_view`](../ordered_json_view.md) (`object_t` is `ordered_map`), they are compared in the order
|
||||
they occur, so the very same two objects with their members reordered can compare equal as `json_view`s but not
|
||||
as `ordered_json_view`s. This is exactly how [`json`](../json.md) and [`ordered_json`](../ordered_json.md)
|
||||
compare, see ["Comparing different `basic_json` specializations"](../basic_json/operator_eq.md#notes).
|
||||
|
||||
!!! info "Discarded views"
|
||||
|
||||
A [discarded](is_discarded.md) view compares the same way a discarded `BasicJsonType` value does, which is
|
||||
governed by
|
||||
[`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../macros/json_use_legacy_discarded_value_comparison.md): by
|
||||
default, a discarded view is never equal to anything, not even another discarded view.
|
||||
|
||||
No ordering comparison (`#!cpp operator<`) is provided for `basic_json_view`; [`materialize()`](materialize.md) is
|
||||
the way to get a `BasicJsonType` value that supports it.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below checks whether a newly received configuration differs from the previous one, and whether a
|
||||
received document matches what a test expects -- directly on views, without ever materializing a `BasicJsonType`
|
||||
value for either side.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__operator_eq.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__operator_eq.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [operator!=](operator_ne.md) - compare for inequality
|
||||
- [materialize](materialize.md) - build a `BasicJsonType` value, e.g. to keep comparing after the document is gone
|
||||
- [`BasicJsonType::operator==`](../basic_json/operator_eq.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,74 @@
|
||||
# <small>nlohmann::basic_json_view::</small>operator<<
|
||||
|
||||
```cpp
|
||||
std::ostream& operator<<(std::ostream& o, const basic_json_view& v);
|
||||
```
|
||||
|
||||
Not available when [`JSON_NO_IO`](../macros/json_no_io.md) is defined.
|
||||
|
||||
Serializes the given view `v` to the output stream `o`, using [`dump`](dump.md) -- exactly as
|
||||
`#!cpp operator<<(std::ostream&, const basic_json&)` does for a `basic_json` value.
|
||||
|
||||
- The indentation of the output can be controlled with the member variable `width` of the output stream `o`. For
|
||||
instance, using the manipulator `std::setw(4)` on `o` sets the indentation level to `4`, and the serialization
|
||||
result is the same as calling `#!cpp v.dump(4)`. A `width` of `0` or less (the default) selects the most compact
|
||||
representation, as `#!cpp v.dump(-1)` does.
|
||||
- The indentation character can be controlled with the member variable `fill` of the output stream `o`. For instance,
|
||||
the manipulator `std::setfill('\t')` sets indentation to use a tab character rather than the default space
|
||||
character.
|
||||
- As for `basic_json`, `o`'s `width` is reset to `0` after this call, whether or not it was greater than `0` before.
|
||||
|
||||
Numbers are always written as `#!cpp v.dump()` writes them by default, i.e. as with
|
||||
[`number_format::shortest`](number_format.md); there is no way to select `#!cpp number_format::source` through the
|
||||
stream.
|
||||
|
||||
## Parameters
|
||||
|
||||
`o` (in, out)
|
||||
: stream to write to
|
||||
|
||||
`v` (in)
|
||||
: view to serialize
|
||||
|
||||
## Return value
|
||||
|
||||
the stream `o`
|
||||
|
||||
## Exceptions
|
||||
|
||||
May throw `#!cpp std::bad_alloc`, propagated from [`dump`](dump.md#exceptions). Unlike
|
||||
`#!cpp operator<<(std::ostream&, const basic_json&)`, there is no UTF-8 decoding step that could throw
|
||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316), and no `error_handler` to choose between --
|
||||
see the [Exceptions](dump.md#exceptions) of `dump`.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear, as [`dump`](dump.md#complexity).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below writes one record out of a larger batch straight to a log stream -- compact for a one-line
|
||||
entry, and pretty-printed with `std::setw`/`std::setfill` for a readable dump -- without ever building a
|
||||
`BasicJsonType` value for the record, or for the rest of the batch.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__operator_ltlt.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__operator_ltlt.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [dump](dump.md) - serialize to a JSON-formatted string
|
||||
- [`operator<<(std::ostream&)`](../operator_ltlt.md) - the corresponding operator for `basic_json`
|
||||
- [`JSON_NO_IO`](../macros/json_no_io.md) - switch off functions relying on certain C++ I/O headers
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,82 @@
|
||||
# <small>nlohmann::basic_json_view::</small>operator!=
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
bool operator!=(const basic_json_view& lhs, const basic_json_view& rhs);
|
||||
|
||||
// (2)
|
||||
bool operator!=(const basic_json_view& lhs, const BasicJsonType& rhs);
|
||||
bool operator!=(const BasicJsonType& lhs, const basic_json_view& rhs);
|
||||
```
|
||||
|
||||
1. Compares two views for inequality. Returns `#!cpp !(lhs == rhs)`, see [operator==](operator_eq.md).
|
||||
2. Compares a view and a `BasicJsonType` value for inequality, in either order. Returns `#!cpp !(lhs == rhs)` (or,
|
||||
for the reversed order, `#!cpp !(rhs == lhs)`), see [operator==](operator_eq.md).
|
||||
|
||||
Since `operator!=` is defined as the negation of [`operator==`](operator_eq.md), it follows the same rules for
|
||||
special cases: for instance, since a [discarded](is_discarded.md) view is never equal to anything by default (see
|
||||
[operator=='s Notes](operator_eq.md#notes)), it is never *unequal* to anything either -- `#!cpp discarded != discarded`
|
||||
is also `#!cpp false`, exactly as for a discarded `BasicJsonType` value.
|
||||
|
||||
## Parameters
|
||||
|
||||
`lhs` (in)
|
||||
: first value to consider
|
||||
|
||||
`rhs` (in)
|
||||
: second value to consider
|
||||
|
||||
## Return value
|
||||
|
||||
whether the values `lhs` and `rhs` are not equal
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to either operand, or to the document(s) a
|
||||
view refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
May throw `#!cpp std::bad_alloc`, propagated from [`operator==`](operator_eq.md#exceptions). Unlike most other
|
||||
`basic_json_view` functions, `operator!=` is not `#!cpp noexcept`.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear, as [`operator==`](operator_eq.md#complexity).
|
||||
|
||||
## Notes
|
||||
|
||||
See the [Notes](operator_eq.md#notes) of `operator==` -- in particular for how an object's members are compared
|
||||
(order matters for [`ordered_json_view`](../ordered_json_view.md) but not for [`json_view`](../json_view.md)) and
|
||||
for how discarded views compare.
|
||||
|
||||
No ordering comparison (`#!cpp operator<`) is provided for `basic_json_view`; [`materialize()`](materialize.md) is
|
||||
the way to get a `BasicJsonType` value that supports it.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below asserts, as a test would, that a received document differs from an unwanted value, and shows
|
||||
that -- as for [`json`](../json.md)/[`ordered_json`](../ordered_json.md) -- reordering an object's members is
|
||||
detected as a difference for an `ordered_json_view` but not for a `json_view`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__operator_ne.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__operator_ne.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [operator==](operator_eq.md) - compare for equality
|
||||
- [materialize](materialize.md) - build a `BasicJsonType` value, e.g. to keep comparing after the document is gone
|
||||
- [`BasicJsonType::operator!=`](../basic_json/operator_ne.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,133 @@
|
||||
# <small>nlohmann::basic_json_view::</small>value
|
||||
|
||||
```cpp
|
||||
// (1)
|
||||
template<typename T>
|
||||
T value(string_view_t key, const T& default_value) const;
|
||||
string_t value(string_view_t key, const char* default_value) const;
|
||||
|
||||
// (2)
|
||||
template<typename T>
|
||||
T value(const json_pointer& ptr, const T& default_value) const;
|
||||
string_t value(const json_pointer& ptr, const char* default_value) const;
|
||||
```
|
||||
|
||||
1. Returns the value of the object member with key `key` -- the first one, should the key occur more than once (see
|
||||
[Notes on duplicate keys](operator[].md#notes)) -- converted to `T`, or `default_value` if there is no such member.
|
||||
2. Returns the value a JSON pointer `ptr` refers to, starting at this value, converted to `T`, or `default_value` if
|
||||
`ptr` cannot be resolved.
|
||||
|
||||
Both overloads have a dedicated `#!cpp const char*` overload, so `#!cpp v.value(key, "default")` (and the JSON pointer
|
||||
equivalent) deduce `string_t`, not `const char*`, for their return type and for the comparison used to pick between
|
||||
`key` and `default_value`.
|
||||
|
||||
## Template parameters
|
||||
|
||||
`T`
|
||||
: the type to convert the found value to; also the type of `default_value`
|
||||
|
||||
## Parameters
|
||||
|
||||
`key` (in)
|
||||
: object key of the element to access
|
||||
|
||||
`ptr` (in)
|
||||
: JSON pointer to the element to access
|
||||
|
||||
`default_value` (in)
|
||||
: the value to return if `key`/`ptr` resolves to no value
|
||||
|
||||
## Return value
|
||||
|
||||
1. the first member with key `key`, converted to `T`, or `default_value`
|
||||
2. the value `ptr` resolves to, converted to `T`, or `default_value`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong exception safety: if an exception is thrown, there are no changes to the view or the document it refers to.
|
||||
|
||||
## Exceptions
|
||||
|
||||
1. Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the value is not an object --
|
||||
the same exception, with the same message, that [`BasicJsonType::value`](../basic_json/value.md) throws for the
|
||||
same call. If a member with key `key` is found, throws whatever converting it to `T` throws (typically
|
||||
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302), with the same message
|
||||
[`BasicJsonType::value`](../basic_json/value.md) throws for the same mismatch); a missing member never throws.
|
||||
2. Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if this value -- not the value `ptr`
|
||||
resolves to -- is neither an object nor an array. Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106)
|
||||
or [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if `ptr` contains a malformed array
|
||||
index. If `ptr` resolves to a value, throws whatever converting it to `T` throws. Every other way `ptr` can fail to
|
||||
resolve -- a missing key, an out-of-range or "`-`" array index, an unresolvable token on a primitive -- yields
|
||||
`default_value` instead of throwing, exactly as [`BasicJsonType::value`](../basic_json/value.md) catches
|
||||
`out_of_range` and returns `default_value`.
|
||||
|
||||
None of these exceptions carry a [`JSON_DIAGNOSTICS`](../macros/json_diagnostics.md) path: the view has no
|
||||
`BasicJsonType` value to point at, so the exception is created without one, even if `BasicJsonType` was built with
|
||||
`JSON_DIAGNOSTICS` enabled.
|
||||
|
||||
## Complexity
|
||||
|
||||
1. Linear in the number of members: as for [`operator[]`](operator[].md#complexity), members are compared one after
|
||||
another, in document order, stopping at the first match. Plus the complexity of converting the found member to
|
||||
`T` (see [`get`](get.md)).
|
||||
Objects with 128 or more members get a hash index while parsing, so that a lookup in them takes constant time
|
||||
on average.
|
||||
2. Linear in the number of reference tokens of `ptr` and, for each token, in the number of members of the object at
|
||||
that level or the index into the array -- as for the [`operator[]`](operator[].md#complexity) and
|
||||
[`at`](at.md#complexity) overloads that take a JSON pointer. Plus the complexity of converting the resolved value
|
||||
to `T`.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! info "Differences to `at` and `operator[]`"
|
||||
|
||||
Unlike [`at`](at.md), this function does not throw if `key`/`ptr` resolves to no value. Unlike
|
||||
[`operator[]`](operator[].md), it never returns a [discarded](is_discarded.md) view -- it always returns a `T` --
|
||||
and it is available on any view, since it never needs to insert a missing element the way the non-const
|
||||
`BasicJsonType::operator[]` would.
|
||||
|
||||
!!! info "Which values can be asked"
|
||||
|
||||
As for [`BasicJsonType::value`](../basic_json/value.md), the key overload (1) requires an object, and the JSON
|
||||
pointer overload (2) an object or an array.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: (1) access specified object element with default value"
|
||||
|
||||
The example below reads a couple of optional configuration fields with a default, so that a missing key never
|
||||
needs a `#!cpp try`/`#!cpp catch` of its own.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__value.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__value.output"
|
||||
```
|
||||
|
||||
??? example "Example: (2) access specified element via JSON pointer with default value"
|
||||
|
||||
The example below reads an optional, nested configuration value with a default, given as a JSON pointer.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_view__value_json_pointer.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_view__value_json_pointer.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [at](at.md) - access specified element with bounds checking (throws instead of returning a default value)
|
||||
- [operator[]](operator[].md) - access specified element (returns a discarded view instead of a default value)
|
||||
- [`BasicJsonType::value`](../basic_json/value.md) - the corresponding function of `basic_json`
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.13.0.
|
||||
@@ -0,0 +1,43 @@
|
||||
# <small>nlohmann::</small>json_editable_document
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
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
|
||||
[`json`](json.md) specialization: in addition to everything [`json_document`](json_document.md) offers,
|
||||
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) change values after
|
||||
parsing, without ever rewriting the source text -- see [Edits](basic_json_document/index.md#edits) and
|
||||
[Editing a document](../features/json_view.md#editing-a-document).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below patches two fields of a small configuration document -- changing one and adding another --
|
||||
and dumps it back out with the member order and the spelling of the untouched number preserved, something a
|
||||
plain [`json`](json.md) value cannot do.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [json_editable_view](json_editable_view.md) - a view of a value of a `json_editable_document`
|
||||
- [json_document](json_document.md) - the read-only document this type adds edits to
|
||||
- [ordered_json_editable_document](ordered_json_editable_document.md) - the corresponding editable document for
|
||||
`ordered_json`
|
||||
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -0,0 +1,41 @@
|
||||
# <small>nlohmann::</small>json_editable_view
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
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
|
||||
[`json_editable_document`](json_editable_document.md). It offers the same read-only interface as
|
||||
[`json_view`](json_view.md); what is different is what it can be a view *of* -- a value that
|
||||
[`set`](basic_json_document/set.md) and [`push_back`](basic_json_document/push_back.md) can change, with every view
|
||||
still referring to it seeing the change, see [Edits](basic_json_document/index.md#edits).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below is the same as [`json_editable_document`'s](json_editable_document.md): every view read back
|
||||
out of the document -- `#!cpp doc.root()` and the views nested under it -- sees the edits made through `set`.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [json_editable_document](json_editable_document.md) - the document type this view refers into
|
||||
- [json_view](json_view.md) - the corresponding read-only view
|
||||
- [ordered_json_editable_view](ordered_json_editable_view.md) - the corresponding view for
|
||||
`ordered_json_editable_document`
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -33,6 +33,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_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_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
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 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.
|
||||
|
||||
!!! warning "Define consistently"
|
||||
|
||||
The macro selects between two definitions of the same inline functions. It must therefore be defined identically for
|
||||
**every** translation unit that includes `<nlohmann/json_view.hpp>`; prefer a compile definition on the target.
|
||||
|
||||
## 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,49 @@
|
||||
# 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, 16 bytes at a time, using the "lookup4"
|
||||
algorithm of [simdjson](https://github.com/simdjson/simdjson). Without it, non-ASCII text is validated one UTF-8
|
||||
sequence at a time on x86-64; on AArch64, the vector check uses NEON and is always on.
|
||||
|
||||
SSSE3 is not part of the x86-64 baseline, so the code must be compiled for it: 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. The same input is accepted or rejected either way; only the speed of non-ASCII text differs.
|
||||
|
||||
!!! warning "Define consistently"
|
||||
|
||||
The macro selects between two definitions of the same inline functions. It must therefore be defined identically,
|
||||
with the same compiler options, for **every** translation unit that includes `<nlohmann/json_view.hpp>`; mixing
|
||||
translation units that define it with ones that do not is an ODR violation. Prefer a compile definition on the
|
||||
target.
|
||||
|
||||
## 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.
|
||||
@@ -84,6 +84,8 @@ Linear.
|
||||
## See also
|
||||
|
||||
- [dump](basic_json/dump.md) - serialize to a JSON-formatted string
|
||||
- [`basic_json_view::operator<<`](basic_json_view/operator_ltlt.md) - the corresponding operator for
|
||||
`basic_json_view`
|
||||
- [Serialization](../features/serialization.md) - the serialization article
|
||||
|
||||
## Version history
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# <small>nlohmann::</small>ordered_json_editable_document
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
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
|
||||
[`ordered_json`](ordered_json.md) specialization: [`set`](basic_json_document/set.md) and
|
||||
[`push_back`](basic_json_document/push_back.md) change values after parsing, as for
|
||||
[`json_editable_document`](json_editable_document.md), and
|
||||
[`materialize()`](basic_json_view/materialize.md) preserves the document order of object members -- including
|
||||
members [`set`](basic_json_document/set.md) added -- instead of sorting them like
|
||||
[`json_editable_document`](json_editable_document.md) does.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below edits a document with `set`, then shows that `materialize()` keeps the member order of the
|
||||
source text (with the new member at the end) for `ordered_json_editable_document`, where it would sort the
|
||||
members alphabetically for [`json_editable_document`](json_editable_document.md).
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/ordered_json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [ordered_json_editable_view](ordered_json_editable_view.md) - a view of a value of an
|
||||
`ordered_json_editable_document`
|
||||
- [ordered_json_document](ordered_json_document.md) - the read-only document this type adds edits to
|
||||
- [json_editable_document](json_editable_document.md) - the corresponding editable document for the default `json`
|
||||
specialization
|
||||
- [Object Order](../features/object_order.md)
|
||||
- [Edits](basic_json_document/index.md#edits) - what an edit guarantees
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -0,0 +1,40 @@
|
||||
# <small>nlohmann::</small>ordered_json_editable_view
|
||||
|
||||
<small>Defined in header `<nlohmann/json_view.hpp>`</small>
|
||||
|
||||
```cpp
|
||||
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
|
||||
[`ordered_json_editable_document`](ordered_json_editable_document.md), the corresponding view for
|
||||
[`ordered_json_view`](ordered_json_view.md) the way [`json_editable_view`](json_editable_view.md) is for
|
||||
[`json_view`](json_view.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
The example below is the same as [`ordered_json_editable_document`'s](ordered_json_editable_document.md): the
|
||||
views `set` returns see the document's member order preserved on `materialize()`, unlike for a
|
||||
[`json_editable_document`](json_editable_document.md).
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/ordered_json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/ordered_json_editable_document.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [ordered_json_editable_document](ordered_json_editable_document.md) - the document type this view refers into
|
||||
- [ordered_json_view](ordered_json_view.md) - the corresponding read-only view
|
||||
- [json_editable_view](json_editable_view.md) - the corresponding view for `json_editable_document`
|
||||
|
||||
## Version history
|
||||
|
||||
Since version 3.13.0.
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a deprecated field is dropped from a configuration file, and a
|
||||
// decommissioned replica is removed from the list -- "price" keeps its
|
||||
// trailing zero, and the fields around the removed ones keep their order
|
||||
const std::string text = R"({
|
||||
"name": "cache",
|
||||
"legacy_host": "db0",
|
||||
"host": "db1",
|
||||
"price": 19.90,
|
||||
"replicas": ["db2", "db3", "db4"]
|
||||
})";
|
||||
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
|
||||
doc.erase(doc.root(), "legacy_host"); // (1) an object member
|
||||
doc.erase(doc.root()["replicas"], 1); // (2) an array element ("db3")
|
||||
const std::size_t removed = doc.erase(json::json_pointer("/replicas/0")); // (3) via a JSON pointer
|
||||
|
||||
std::cout << removed << '\n';
|
||||
std::cout << doc.root().dump(2, ' ', false, json_editable_view::number_format::source) << "\n\n";
|
||||
|
||||
// the same edits on a plain json value: object_t is a std::map, so
|
||||
// parsing already sorted the keys, and dump() rewrites every number to
|
||||
// its shortest form, even "price", which was never touched
|
||||
json plain = json::parse(text);
|
||||
plain.erase("legacy_host");
|
||||
plain["replicas"].erase(1);
|
||||
plain["replicas"].erase(0);
|
||||
std::cout << plain.dump(2) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
1
|
||||
{
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"price": 19.90,
|
||||
"replicas": [
|
||||
"db4"
|
||||
]
|
||||
}
|
||||
|
||||
{
|
||||
"host": "db1",
|
||||
"name": "cache",
|
||||
"price": 19.9,
|
||||
"replicas": [
|
||||
"db4"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a deployment plan -- "budget" is written with a trailing zero that has
|
||||
// no effect on its value
|
||||
const std::string text = R"({
|
||||
"release": "2026.09",
|
||||
"steps": ["build", "test", "deploy"],
|
||||
"budget": 19.90
|
||||
})";
|
||||
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
|
||||
const std::size_t deploy_index = 2;
|
||||
const auto deploy = doc.root()["steps"][deploy_index]; // held across the insert
|
||||
|
||||
doc.insert(doc.root()["steps"], deploy_index, "smoke-test"); // insert before "deploy"
|
||||
|
||||
// the held view still refers to "deploy", even though its index moved
|
||||
// from 2 to 3, and nothing else in the document was touched
|
||||
std::cout << deploy.dump() << '\n';
|
||||
std::cout << doc.root().dump(2, ' ', false, json_editable_view::number_format::source) << "\n\n";
|
||||
|
||||
// the same edit on a plain json value: an index held from before the
|
||||
// insert now refers to whatever moved into that slot, and dump()
|
||||
// rewrites "budget" to its shortest form even though it was never
|
||||
// touched
|
||||
json plain = json::parse(text);
|
||||
plain["steps"].insert(plain["steps"].begin() + static_cast<std::ptrdiff_t>(deploy_index), "smoke-test");
|
||||
std::cout << plain["steps"][deploy_index].dump() << '\n';
|
||||
std::cout << plain.dump(2) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
"deploy"
|
||||
{
|
||||
"release": "2026.09",
|
||||
"steps": [
|
||||
"build",
|
||||
"test",
|
||||
"smoke-test",
|
||||
"deploy"
|
||||
],
|
||||
"budget": 19.90
|
||||
}
|
||||
|
||||
"smoke-test"
|
||||
{
|
||||
"budget": 19.9,
|
||||
"release": "2026.09",
|
||||
"steps": [
|
||||
"build",
|
||||
"test",
|
||||
"smoke-test",
|
||||
"deploy"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_document = nlohmann::json_document;
|
||||
using image_check = json_document::image_check;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// the image of a parsed document -- as if read back from a cache file or
|
||||
// received from another process running the same build of the library
|
||||
const std::string text = R"({"name": "cache", "note": "caf\u00e9", "replicas": ["db2", "db3"]})";
|
||||
const json_document parsed = json_document::parse(text);
|
||||
const std::vector<std::uint8_t> image = parsed.save();
|
||||
|
||||
// (1)/(2) load() needs no parsing, yet dumps exactly what parsing did
|
||||
const json_document borrowed = json_document::load(image);
|
||||
std::cout << (borrowed.root().dump() == parsed.root().dump()) << '\n';
|
||||
std::cout << borrowed.owns_source() << '\n'; // borrowed: still points into `image`
|
||||
|
||||
// (3) load(std::move(image)) keeps the vector instead of copying it
|
||||
std::vector<std::uint8_t> to_move = image;
|
||||
const json_document owned = json_document::load(std::move(to_move));
|
||||
std::cout << owned.owns_source() << '\n';
|
||||
|
||||
// a damaged image -- the last byte of the decoded string "note" holds
|
||||
// (an escape sequence, so it was unescaped into the document's own
|
||||
// buffer), flipped, as storage or transport corruption might do
|
||||
std::vector<std::uint8_t> damaged = image;
|
||||
damaged[damaged.size() - 2] = 0xFF;
|
||||
|
||||
// image_check::full inspects strings and numbers, so it catches the damage
|
||||
try
|
||||
{
|
||||
static_cast<void>(json_document::load(damaged, image_check::full));
|
||||
}
|
||||
catch (const json::parse_error& e)
|
||||
{
|
||||
std::cout << e.id << '\n';
|
||||
}
|
||||
|
||||
// image_check::bounds only checks structure and bounds, so a cache the
|
||||
// process already trusts loads without the extra scan -- reading a value
|
||||
// the damage did not touch is still safe
|
||||
const json_document trusted = json_document::load(damaged, image_check::bounds);
|
||||
std::cout << trusted.root()["name"].get<std::string>() << '\n';
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
true
|
||||
false
|
||||
true
|
||||
116
|
||||
cache
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// "events" starts out null -- the first push_back() turns it into an
|
||||
// array, exactly like set() turns a null object member into an object
|
||||
json_editable_document doc = json_editable_document::parse(R"({"source": "sensor-1", "events": null})");
|
||||
|
||||
const auto first = doc.push_back(doc.root()["events"], json{{"type", "start"}, {"t", 0}});
|
||||
for (int t = 1; t <= 3; ++t)
|
||||
{
|
||||
doc.push_back(doc.root()["events"], json{{"type", "tick"}, {"t", t}});
|
||||
}
|
||||
|
||||
// push_back() never moves an existing element: a view taken from an
|
||||
// earlier call still refers to the same element after later ones
|
||||
std::cout << first.dump() << '\n';
|
||||
std::cout << doc.root()["events"].size() << '\n';
|
||||
std::cout << doc.root().dump(2) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{"t":0,"type":"start"}
|
||||
4
|
||||
{
|
||||
"source": "sensor-1",
|
||||
"events": [
|
||||
{
|
||||
"t": 0,
|
||||
"type": "start"
|
||||
},
|
||||
{
|
||||
"t": 1,
|
||||
"type": "tick"
|
||||
},
|
||||
{
|
||||
"t": 2,
|
||||
"type": "tick"
|
||||
},
|
||||
{
|
||||
"t": 3,
|
||||
"type": "tick"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
// a configuration a service parses once and then caches as an image, so
|
||||
// that later requests can load() it instead of parsing the text again
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "replicas": ["db2", "db3"]})";
|
||||
const json_document config = json_document::parse(text);
|
||||
|
||||
// save() turns the parsed document into a byte buffer: a 64-byte header,
|
||||
// the node index, the source text, and the decoded strings
|
||||
const std::vector<std::uint8_t> image = config.save();
|
||||
std::cout << image.size() << '\n';
|
||||
|
||||
// the same document always saves to the same bytes
|
||||
std::cout << (image == json_document::parse(text).save()) << '\n';
|
||||
|
||||
// loading the image back needs no parsing, yet dumps exactly what
|
||||
// parsing the text produced
|
||||
const json_document reloaded = json_document::load(image);
|
||||
std::cout << (reloaded.root().dump() == config.root().dump()) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
316
|
||||
true
|
||||
true
|
||||
@@ -0,0 +1,41 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk -- "price" is
|
||||
// written with a trailing zero that has no effect on its value
|
||||
const std::string text = R"({
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"port": 6379,
|
||||
"price": 19.90,
|
||||
"replicas": ["db2", "db3"],
|
||||
"timeout": 30
|
||||
})";
|
||||
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
|
||||
doc.set(doc.root()["port"], 6380); // (1) replace a value
|
||||
doc.set(doc.root(), "region", "us-east"); // (2) add a member
|
||||
doc.set(doc.root()["replicas"], 0, "db4"); // (3) assign an element
|
||||
doc.set(json::json_pointer("/timeout"), 45); // (4) via a JSON pointer
|
||||
|
||||
// members stay in document order (the new one at the end), and a number
|
||||
// that was not itself edited keeps its exact spelling
|
||||
std::cout << doc.root().dump(2, ' ', false, json_editable_view::number_format::source) << "\n\n";
|
||||
|
||||
// the same edits on a plain json value: object_t is a std::map, so
|
||||
// parsing already sorted the keys, and dump() rewrites every number to
|
||||
// its shortest form, even "price", which was never touched
|
||||
json plain = json::parse(text);
|
||||
plain["port"] = 6380;
|
||||
plain["region"] = "us-east";
|
||||
plain["replicas"][0] = "db4";
|
||||
plain[json::json_pointer("/timeout")] = 45;
|
||||
std::cout << plain.dump(2) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"port": 6380,
|
||||
"price": 19.90,
|
||||
"replicas": [
|
||||
"db4",
|
||||
"db3"
|
||||
],
|
||||
"timeout": 45,
|
||||
"region": "us-east"
|
||||
}
|
||||
|
||||
{
|
||||
"host": "db1",
|
||||
"name": "cache",
|
||||
"port": 6380,
|
||||
"price": 19.9,
|
||||
"region": "us-east",
|
||||
"replicas": [
|
||||
"db4",
|
||||
"db3"
|
||||
],
|
||||
"timeout": 45
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_pointer = nlohmann::json::json_pointer;
|
||||
|
||||
int main()
|
||||
{
|
||||
json_document doc = json_document::parse(R"({"region": "eu", "servers": ["eu-1", "eu-2"]})");
|
||||
const auto root = doc.root();
|
||||
|
||||
std::cout << root.at(json_pointer("/servers/1")).materialize().dump() << '\n';
|
||||
|
||||
// at() throws for every resolution failure -- with the very same
|
||||
// message json::at(ptr) would throw for the same pointer and the same
|
||||
// document
|
||||
try
|
||||
{
|
||||
static_cast<void>(root.at(json_pointer("/servers/5")));
|
||||
}
|
||||
catch (const nlohmann::json::out_of_range& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
static_cast<void>(root.at(json_pointer("/missing")));
|
||||
}
|
||||
catch (const nlohmann::json::out_of_range& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
"eu-2"
|
||||
[json.exception.out_of_range.401] array index 5 is out of range
|
||||
[json.exception.out_of_range.403] key 'missing' not found
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_pointer = nlohmann::json::json_pointer;
|
||||
|
||||
int main()
|
||||
{
|
||||
// "retry_of" is only present on some records, nested a level down
|
||||
// inside "meta"
|
||||
json_document batch = json_document::parse(R"(
|
||||
[
|
||||
{"id": 1, "meta": {}},
|
||||
{"id": 2, "meta": {"retry_of": 1}}
|
||||
]
|
||||
)");
|
||||
|
||||
const auto records = batch.root();
|
||||
const json_pointer retry_of("/meta/retry_of");
|
||||
for (std::size_t i = 0; i < records.size(); ++i)
|
||||
{
|
||||
const auto record = records[i];
|
||||
if (record.contains(retry_of))
|
||||
{
|
||||
std::cout << "record " << i << " is a retry of " << record[retry_of].get<int>() << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "record " << i << " is original\n";
|
||||
}
|
||||
}
|
||||
|
||||
// contains() with a JSON pointer never throws -- not even for a
|
||||
// pointer that indexes into a primitive ("/0/id/x") or uses a
|
||||
// malformed array index ("/01"), either of which would need a
|
||||
// try/catch with json::contains(ptr)
|
||||
std::cout << std::boolalpha << records.contains(json_pointer("/0/id/x")) << '\n';
|
||||
std::cout << std::boolalpha << records.contains(json_pointer("/01")) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
record 0 is original
|
||||
record 1 is a retry of 1
|
||||
false
|
||||
false
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a large batch of sensor readings -- forward just the one that changed,
|
||||
// without ever building a basic_json value for the batch or for the
|
||||
// readings that are not needed
|
||||
const json_document batch = json_document::parse(R"(
|
||||
[{"id": 1, "temp": 21.5}, {"id": 2, "temp": 87.3}, {"id": 3, "temp": 21.7}]
|
||||
)");
|
||||
const json_view readings = batch.root();
|
||||
std::cout << readings[1].dump() << '\n';
|
||||
|
||||
// a configuration file -- dump() on the view keeps the member order of
|
||||
// the source text; a json value's object_t is std::map, so
|
||||
// materialize().dump() of the very same view sorts the keys instead
|
||||
const json_document config = json_document::parse(
|
||||
R"({"name": "cache", "host": "db1", "port": 6379, "timeout": 30})");
|
||||
std::cout << config.root().dump(2) << "\n\n";
|
||||
std::cout << config.root().materialize().dump(2) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{"id":2,"temp":87.3}
|
||||
{
|
||||
"name": "cache",
|
||||
"host": "db1",
|
||||
"port": 6379,
|
||||
"timeout": 30
|
||||
}
|
||||
|
||||
{
|
||||
"host": "db1",
|
||||
"name": "cache",
|
||||
"port": 6379,
|
||||
"timeout": 30
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
// address has no direct conversion in get<T>(), so get<address>() falls back
|
||||
// to materialize().get<address>() -- a real nlohmann::json value is built
|
||||
// for just this one member, and its own from_json() runs on that
|
||||
struct address
|
||||
{
|
||||
std::string city;
|
||||
int zip = 0;
|
||||
};
|
||||
|
||||
void from_json(const nlohmann::json& j, address& a)
|
||||
{
|
||||
j.at("city").get_to(a.city);
|
||||
j.at("zip").get_to(a.zip);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
json_document doc = json_document::parse(R"(
|
||||
{
|
||||
"name": "Alice",
|
||||
"active": true,
|
||||
"orders": [1, 2, 3],
|
||||
"address": {"city": "Berlin", "zip": 10115}
|
||||
}
|
||||
)");
|
||||
const json_view customer = doc.root();
|
||||
|
||||
// read typed fields straight into C++ variables -- none of these build
|
||||
// a nlohmann::json value
|
||||
const std::string name = customer["name"].get<std::string>();
|
||||
const bool active = customer["active"].get<bool>();
|
||||
std::cout << name << (active ? " (active)" : " (inactive)") << '\n';
|
||||
|
||||
// std::vector<json_view> keeps views of the array elements instead of
|
||||
// copies of their values
|
||||
bool first = true;
|
||||
for (const json_view order : customer["orders"].get<std::vector<json_view>>())
|
||||
{
|
||||
std::cout << (first ? "" : " ") << order.get<int>();
|
||||
first = false;
|
||||
}
|
||||
std::cout << '\n';
|
||||
|
||||
// everything else goes through materialize()
|
||||
const address a = customer["address"].get<address>();
|
||||
std::cout << a.city << ' ' << a.zip << '\n';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Alice (active)
|
||||
1 2 3
|
||||
Berlin 10115
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
#include <string>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a "large response" stand-in: only the "id" field is ever read out of it
|
||||
const std::string text =
|
||||
R"({"id": "8f14e45f-ceea-467e-bb92-963f5e3c7a08", "note": "created via API\n", "payload": "..."})";
|
||||
const json_document doc = json_document::parse(text);
|
||||
const json_view response = doc.root();
|
||||
|
||||
const json_view::string_view_t id = response["id"].get_string();
|
||||
std::cout << id << '\n';
|
||||
|
||||
// no std::string was allocated for "id": its bytes still live inside
|
||||
// the original buffer, so id's data lies inside [text.data(),
|
||||
// text.data() + text.size())
|
||||
const bool id_in_source = id.data() >= text.data() && id.data() + id.size() <= text.data() + text.size();
|
||||
std::cout << std::boolalpha << id_in_source << '\n';
|
||||
|
||||
// "note" contains an escape sequence ('\n'), so it was decoded once
|
||||
// into the document's own buffer -- get_string() still avoids a copy
|
||||
// into a new std::string, but the bytes no longer live inside "text"
|
||||
const json_view::string_view_t note = response["note"].get_string();
|
||||
const bool note_in_source = note.data() >= text.data() && note.data() + note.size() <= text.data() + text.size();
|
||||
std::cout << std::boolalpha << note_in_source << '\n';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
8f14e45f-ceea-467e-bb92-963f5e3c7a08
|
||||
true
|
||||
false
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
#include <string>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
json_document doc = json_document::parse(R"({"host": "db.example.com", "port": 5432, "ssl": true})");
|
||||
const json_view config = doc.root();
|
||||
|
||||
// get_to() writes directly into existing variables -- handy for filling
|
||||
// in the members of a struct one field at a time, without an
|
||||
// intermediate value from get<T>() for each one
|
||||
std::string host;
|
||||
int port = 0;
|
||||
bool ssl = false;
|
||||
config["host"].get_to(host);
|
||||
config["port"].get_to(port);
|
||||
config["ssl"].get_to(ssl);
|
||||
std::cout << host << ':' << port << (ssl ? " (tls)" : "") << '\n';
|
||||
|
||||
// the return value is a reference to the argument, so a call can be
|
||||
// used directly in a larger expression
|
||||
std::string other_host;
|
||||
std::cout << config["host"].get_to(other_host).size() << '\n';
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
db.example.com:5432 (tls)
|
||||
14
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a price list received from a supplier feed -- prices and account
|
||||
// numbers must be forwarded exactly, e.g. into an invoice
|
||||
const json_document doc = json_document::parse(R"(
|
||||
[{"sku": "A1", "price": 19.90, "account_id": 12345678901234567890123456},
|
||||
{"sku": "A2", "price": 1E2, "account_id": 98765432109876543210987654}]
|
||||
)");
|
||||
const json_view list = doc.root();
|
||||
|
||||
// number_format::shortest (the default) writes numbers the way
|
||||
// basic_json::dump() would: "19.90" becomes "19.9", "1E2" becomes
|
||||
// "100.0", and each account number -- far beyond any 64-bit integer --
|
||||
// is rounded to the nearest double, exactly as materialize().dump()
|
||||
// (or a plain nlohmann::json) would round it
|
||||
std::cout << list.dump() << '\n';
|
||||
|
||||
// number_format::source copies every number exactly as it was written
|
||||
// in the source text instead -- something basic_json cannot do at all,
|
||||
// since parsing already reduces every number to its parsed value
|
||||
std::cout << list.dump(-1, ' ', false, json_view::number_format::source) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[{"sku":"A1","price":19.9,"account_id":1.2345678901234568e+25},{"sku":"A2","price":100.0,"account_id":9.876543210987655e+25}]
|
||||
[{"sku":"A1","price":19.90,"account_id":12345678901234567890123456},{"sku":"A2","price":1E2,"account_id":98765432109876543210987654}]
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a price and an order id from an incoming order -- both need to be
|
||||
// reproduced exactly, e.g. for an invoice or an audit log
|
||||
json_document doc = json_document::parse(R"(
|
||||
{"price": 19.90, "order_id": 1234567890123456789012345, "quantity": 3}
|
||||
)");
|
||||
const json_view order = doc.root();
|
||||
|
||||
// number_token() returns the number exactly as written in the source
|
||||
std::cout << order["price"].number_token() << '\n';
|
||||
std::cout << order["order_id"].number_token() << '\n';
|
||||
|
||||
// get<double>() converts it instead -- the exact source text is gone:
|
||||
// "19.90" becomes the double closest to 19.9, printed without the
|
||||
// trailing zero, and the 25-digit order id -- far beyond any 64-bit
|
||||
// integer -- can only be approximated as a double
|
||||
std::cout << order["price"].get<double>() << '\n';
|
||||
std::cout << order.materialize()["order_id"].dump() << '\n';
|
||||
|
||||
// an ordinary quantity has nothing to lose either way
|
||||
std::cout << order["quantity"].number_token() << " == " << order["quantity"].get<int>() << '\n';
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
19.90
|
||||
1234567890123456789012345
|
||||
19.9
|
||||
1.2345678901234568e+24
|
||||
3 == 3
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_pointer = nlohmann::json::json_pointer;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a larger document; operator[] with a JSON pointer reaches straight to
|
||||
// one deeply nested field, without ever building a tree for the rest
|
||||
json_document doc = json_document::parse(R"(
|
||||
{
|
||||
"region": {
|
||||
"servers": [
|
||||
{"name": "eu-1", "metrics": {"cpu": 0.42}},
|
||||
{"name": "eu-2", "metrics": {"cpu": 0.71}}
|
||||
]
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
const auto root = doc.root();
|
||||
std::cout << root[json_pointer("/region/servers/1/metrics/cpu")].materialize().dump() << '\n';
|
||||
|
||||
// a missing key or an out-of-range index along the path gives a
|
||||
// discarded view, exactly where const json::operator[] would be
|
||||
// undefined behavior for the same pointer
|
||||
if (const auto missing = root[json_pointer("/region/servers/5/metrics/cpu")])
|
||||
{
|
||||
std::cout << missing.materialize().dump() << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "no such server\n";
|
||||
}
|
||||
|
||||
// indexing into a primitive still throws, as basic_json::operator[]
|
||||
// does for the same pointer
|
||||
try
|
||||
{
|
||||
static_cast<void>(root[json_pointer("/region/servers/0/name/x")]);
|
||||
}
|
||||
catch (const nlohmann::json::out_of_range& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
0.71
|
||||
no such server
|
||||
[json.exception.out_of_range.404] unresolved reference token 'x'
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
// two snapshots of a polled configuration endpoint -- compare them
|
||||
// directly as views, without ever building a nlohmann::json value for
|
||||
// either one
|
||||
const json_document previous = json_document::parse(
|
||||
R"({"name": "cache", "port": 6379, "timeout": 30})");
|
||||
const json_document current = json_document::parse(
|
||||
R"({"port": 6379.0, "timeout": 30, "name": "cache"})");
|
||||
|
||||
// same members, reordered, and 6379 written as a float -- operator==
|
||||
// treats them the same way BasicJsonType::operator== would
|
||||
std::cout << std::boolalpha << (previous.root() == current.root()) << '\n';
|
||||
|
||||
// an actually changed value is detected the same way
|
||||
const json_document changed = json_document::parse(
|
||||
R"({"name": "cache", "port": 6380, "timeout": 30})");
|
||||
std::cout << (previous.root() == changed.root()) << '\n';
|
||||
|
||||
// comparing a view directly against an expected json value -- handy in a
|
||||
// test, without materializing the received document at all
|
||||
const json expected = {{"name", "cache"}, {"port", 6379}, {"timeout", 30}};
|
||||
std::cout << (previous.root() == expected) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
true
|
||||
false
|
||||
true
|
||||
@@ -0,0 +1,26 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// one order out of a large incoming batch -- write it straight to a log
|
||||
// stream without ever building a basic_json value for it, or for the
|
||||
// rest of the batch
|
||||
const json_document doc = json_document::parse(R"(
|
||||
[{"id": 1, "item": "cable"}, {"id": 2, "item": "adapter"}]
|
||||
)");
|
||||
const json_view orders = doc.root();
|
||||
|
||||
// compact, for a one-line log entry
|
||||
std::cout << orders[1] << '\n';
|
||||
|
||||
// std::setw sets the indentation level, exactly as for basic_json
|
||||
std::cout << std::setw(2) << orders[1] << "\n\n";
|
||||
|
||||
// std::setfill changes the indentation character
|
||||
std::cout << std::setw(1) << std::setfill('\t') << orders[1] << '\n';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{"id":2,"item":"adapter"}
|
||||
{
|
||||
"id": 2,
|
||||
"item": "adapter"
|
||||
}
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"item": "adapter"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using ordered_json_document = nlohmann::ordered_json_document;
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
// assert, as a test would, that a received document differs from an
|
||||
// unwanted shape -- without ever materializing it into a json value just
|
||||
// to compare
|
||||
const json_document received = json_document::parse(
|
||||
R"({"status": "ok", "code": 200})");
|
||||
const json unwanted = {{"status", "error"}, {"code", 500}};
|
||||
std::cout << std::boolalpha << (received.root() != unwanted) << '\n';
|
||||
|
||||
// json (std::map) compares object members regardless of order ...
|
||||
const json_document a = json_document::parse(R"({"a": 1, "b": 2})");
|
||||
const json_document b = json_document::parse(R"({"b": 2, "a": 1})");
|
||||
std::cout << (a.root() != b.root()) << '\n';
|
||||
|
||||
// ... but ordered_json (ordered_map) compares them in the order they
|
||||
// appear, so the very same reordering is detected as a difference
|
||||
const ordered_json_document oa = ordered_json_document::parse(R"({"a": 1, "b": 2})");
|
||||
const ordered_json_document ob = ordered_json_document::parse(R"({"b": 2, "a": 1})");
|
||||
std::cout << (oa.root() != ob.root()) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
true
|
||||
false
|
||||
true
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
#include <string>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
json_document doc = json_document::parse(R"({"server": {"host": "localhost"}})");
|
||||
const json_view server = doc.root()["server"];
|
||||
|
||||
// "port" is missing -- value() returns the default instead of
|
||||
// throwing, so optional configuration fields never need their own
|
||||
// try/catch
|
||||
std::cout << server.value("host", std::string("0.0.0.0")) << '\n';
|
||||
std::cout << server.value("port", 8080) << '\n';
|
||||
|
||||
// a present but wrong-typed default still throws -- value() only
|
||||
// replaces "not found", not "wrong type", exactly as basic_json::value
|
||||
try
|
||||
{
|
||||
static_cast<void>(server.value("host", 0));
|
||||
}
|
||||
catch (const nlohmann::json::type_error& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
localhost
|
||||
8080
|
||||
[json.exception.type_error.302] type must be number, but is string
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json_document = nlohmann::json_document;
|
||||
using json_view = nlohmann::json_view;
|
||||
using json_pointer = nlohmann::json::json_pointer;
|
||||
|
||||
int main()
|
||||
{
|
||||
json_document doc = json_document::parse(R"({"server": {"host": "localhost", "limits": {"connections": 100}}})");
|
||||
const json_view config = doc.root();
|
||||
|
||||
// a nested, optional setting read with a default -- no exception, even
|
||||
// though "timeout" is missing several levels down
|
||||
std::cout << config.value(json_pointer("/server/limits/connections"), 10) << '\n';
|
||||
std::cout << config.value(json_pointer("/server/limits/timeout"), 30) << '\n';
|
||||
|
||||
// an out-of-range array index also falls back to the default
|
||||
json_document list_doc = json_document::parse(R"({"servers": ["a", "b"]})");
|
||||
std::cout << list_doc.root().value(json_pointer("/servers/5"), std::string("none")) << '\n';
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
100
|
||||
30
|
||||
none
|
||||
@@ -0,0 +1,30 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
using json_editable_document = nlohmann::json_editable_document;
|
||||
using json_editable_view = nlohmann::json_editable_view;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a configuration file, as it might be read from disk
|
||||
const std::string text = R"({"name": "cache", "host": "db1", "port": 6379, "price": 19.90})";
|
||||
std::cout << text << "\n\n";
|
||||
|
||||
// patch two fields -- "price" is never touched
|
||||
json_editable_document doc = json_editable_document::parse(text);
|
||||
doc.set(doc.root(), "host", "db2");
|
||||
doc.set(doc.root(), "retries", 3);
|
||||
|
||||
// member order (the new member at the end) and the untouched number's
|
||||
// exact spelling survive
|
||||
std::cout << doc.root().dump(-1, ' ', false, json_editable_view::number_format::source) << '\n';
|
||||
|
||||
// the same patch on a plain json value: keys are sorted (object_t is a
|
||||
// std::map), and "price" is rewritten even though the patch never
|
||||
// touched it
|
||||
json plain = json::parse(text);
|
||||
plain["host"] = "db2";
|
||||
plain["retries"] = 3;
|
||||
std::cout << plain.dump() << '\n';
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"name": "cache", "host": "db1", "port": 6379, "price": 19.90}
|
||||
|
||||
{"name":"cache","host":"db2","port":6379,"price":19.90,"retries":3}
|
||||
{"host":"db2","name":"cache","port":6379,"price":19.9,"retries":3}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json_view.hpp>
|
||||
|
||||
using ordered_json_editable_document = nlohmann::ordered_json_editable_document;
|
||||
|
||||
int main()
|
||||
{
|
||||
// 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})");
|
||||
doc.set(doc.root(), "b", 4); // set() always appends a new member at the end
|
||||
|
||||
// materialize() preserves the document order (with "b" at the end),
|
||||
// instead of sorting the keys the way json_editable_document does
|
||||
std::cout << doc.root().materialize().dump() << '\n';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"z":1,"a":2,"m":3,"b":4}
|
||||
@@ -13,6 +13,8 @@ C++ types, and finally serialize it again.
|
||||
[SAX interface](parsing/sax_interface.md), and [error handling](parsing/parse_exceptions.md).
|
||||
- [Zero-copy JSON views](json_view.md) — read a JSON text through a flat index instead of building a `json` tree;
|
||||
strings and numbers stay in the input and are only decoded when needed.
|
||||
[Editable documents](json_view.md#editing-a-document) can also be modified, and [images](json_view.md#images) load a
|
||||
parsed document again without parsing it.
|
||||
- [Comments](comments.md) and [trailing commas](trailing_commas.md) — opt-in relaxations of the JSON grammar.
|
||||
|
||||
## Accessing and modifying values
|
||||
|
||||
@@ -139,17 +139,177 @@ whenever any of the other conditions above was not met.
|
||||
element access and lookup functions never carry the JSON Pointer path `JSON_DIAGNOSTICS` would otherwise add: the
|
||||
view has no `basic_json` value to point at, so the exception is created without one, regardless of how
|
||||
`BasicJsonType` was built.
|
||||
- **`get<T>()`, JSON Pointer, `dump()`, and comparison are not (yet) provided** by `basic_json_view`. For now,
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can do those things with.
|
||||
- **Ordering comparisons are not provided** by `basic_json_view` -- there is no `#!cpp operator<`.
|
||||
[`operator==`](../api/basic_json_view/operator_eq.md) and [`operator!=`](../api/basic_json_view/operator_ne.md) are
|
||||
provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) or [`parse()`](../api/basic_json/parse.md) would produce
|
||||
equal values for them, without ever building a tree to do it. For ordering, too,
|
||||
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
||||
|
||||
## Getting values out without copying
|
||||
|
||||
[`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`,
|
||||
`#!cpp std::string`/other `#!cpp std::basic_string`s (copied once), `basic_json`/`ordered_json` (via
|
||||
[`materialize()`](../api/basic_json_view/materialize.md)), `basic_json_view` itself, `#!cpp std::vector<U>`, and
|
||||
`#!cpp std::map`/`#!cpp std::unordered_map` with string-like keys. Every other type -- `#!cpp std::list`,
|
||||
`#!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
|
||||
`basic_json` value first, exactly as [`parse()`](../api/basic_json/parse.md) would, and converted from there.
|
||||
|
||||
Two conversions never copy at all:
|
||||
|
||||
- [`get_string()`](../api/basic_json_view/get_string.md) (equivalently, `#!cpp get<string_view_t>()`) returns a
|
||||
string as a `string_view_t` pointing into the document's [`source()`](../api/basic_json_document/source.md) text --
|
||||
or, for a string that contains escape sequences, into the document's own buffer of decoded strings -- instead of
|
||||
allocating a new `#!cpp std::string`.
|
||||
- [`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written in the
|
||||
source, e.g. `#!cpp "1.50"`, `#!cpp "1E2"`, or an integer with more digits than any number type holds, instead of
|
||||
rounding it into a `#!cpp double`/`#!cpp int64_t` the way `#!cpp get<T>()` (and
|
||||
[`basic_json::parse()`](../api/basic_json/parse.md)) would.
|
||||
|
||||
Both results are only valid as long as the view -- and, for a string with no escapes, the borrowed source text -- is.
|
||||
|
||||
## Writing a view back
|
||||
|
||||
[`dump()`](../api/basic_json_view/dump.md) serializes a view directly from the flat index, without ever building a
|
||||
`basic_json` value. An object's members are written in document order, not sorted by key, and *every* occurrence of a
|
||||
repeated key is written, not only the last one -- the same two ways [iteration](#what-is-different) already differs
|
||||
from a [`materialize()`](../api/basic_json_view/materialize.md)d value, see above. `#!cpp materialize().dump()` gives
|
||||
a different result in both respects for a `json_view`.
|
||||
|
||||
By default, numbers are written the way [`basic_json::dump()`](../api/basic_json/dump.md) would.
|
||||
[`number_format::source`](../api/basic_json_view/number_format.md) instead copies every number exactly as it was
|
||||
written in the source text -- a price like `#!cpp 19.90`, a long order or account ID with more digits than any number
|
||||
type holds, or a high-precision coordinate -- something `basic_json` cannot do at all, since parsing already reduces
|
||||
a number to its parsed `#!cpp double`/`#!cpp int64_t` value.
|
||||
|
||||
[`operator<<`](../api/basic_json_view/operator_ltlt.md) writes a view to a stream the way `basic_json`'s does, using
|
||||
the stream's `width`/`fill` for indentation.
|
||||
|
||||
## Editing a document
|
||||
|
||||
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, true>`](../api/basic_json_document/index.md) -- more conveniently
|
||||
spelled [`json_editable_document`](../api/json_editable_document.md) or
|
||||
[`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
|
||||
[`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.
|
||||
|
||||
`#!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
|
||||
runtime one.
|
||||
|
||||
### Why: editing without reformatting
|
||||
|
||||
The `#!cpp 19.90` price from [above](#writing-a-view-back) is exactly the kind of value that makes editing a `json`
|
||||
or `ordered_json` value in place lossy. Say you parse a configuration file, patch one field, and write it back:
|
||||
|
||||
- **`json`** re-sorts every key on the way in (`object_t` is a `#!cpp std::map`) and rewrites every number to its
|
||||
shortest round-trip form on the way out -- a one-field patch turns into a diff that reorders the whole file and
|
||||
rewrites `#!cpp 19.90` to `#!cpp 19.9`.
|
||||
- **`ordered_json`** keeps the key order, but still rewrites every number the same way: parsing has already reduced
|
||||
it to a `#!cpp double`/`#!cpp int64_t`, and there is no way back to how it was spelled in the source text.
|
||||
|
||||
An editable document keeps both. [`dump()`](../api/basic_json_view/dump.md) of an edited document writes members in
|
||||
document order -- a member [`set`](../api/basic_json_document/set.md) added goes at the end, exactly where it was
|
||||
inserted, and an [`erase`](../api/basic_json_document/erase.md)d member simply leaves a gap: everything around it
|
||||
keeps its place -- and [`number_format::source`](../api/basic_json_view/number_format.md) keeps the exact spelling
|
||||
of every number an edit did not itself touch; a number an edit *did* touch is written the way
|
||||
[`BasicJsonType::dump()`](../api/basic_json/dump.md) would write it, since there is no source spelling for a brand
|
||||
new value.
|
||||
|
||||
??? example "Example: patch a configuration, keeping member order and number spellings"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/json_editable_document.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/json_editable_document.output"
|
||||
```
|
||||
|
||||
### What stays valid, and what an edit costs
|
||||
|
||||
The source text itself is **never written**, and the parsed index never moves -- a value keeps the node it was
|
||||
parsed into for as long as it is not itself replaced. So every [view](../api/basic_json_view/index.md) taken before
|
||||
an edit, including a previously obtained [`root()`](../api/basic_json_document/root.md), stays valid and, if it
|
||||
still refers to the edited value, sees the edit; a view of a value a later edit drops or replaces just keeps showing
|
||||
what it last held. New values go to storage the document allocates and owns on demand. The one thing an edit does
|
||||
invalidate is the **iterators** taken over an edited array or object: the first time one of its elements is set,
|
||||
appended to, inserted into, or erased, its elements move from the parsed, fixed layout to a growable block of links
|
||||
so that [`push_back`](../api/basic_json_document/push_back.md) can later grow it in amortized constant time --
|
||||
existing elements are not touched, but an iterator that was walking the old layout no longer matches. A string
|
||||
obtained with
|
||||
[`get_string()`](../api/basic_json_view/get_string.md) is unaffected either way and stays valid across further
|
||||
edits. See [`basic_json_document`'s Edits](../api/basic_json_document/index.md#edits) for the details, and
|
||||
[`set`'s Exception safety](../api/basic_json_document/set.md#exception-safety) for what an edit guarantees if it
|
||||
throws (the *basic* guarantee, not the strong one `dump()` and the read-only functions provide). How edits are kept in
|
||||
the index is described in the [architecture overview](../home/architecture.md#node-index-of-json-views).
|
||||
|
||||
## Images
|
||||
|
||||
[`save()`](../api/basic_json_document/save.md) writes a document as an *image*: a byte buffer that
|
||||
[`load()`](../api/basic_json_document/load.md) reads back into a document without parsing -- no lexing, no building
|
||||
the node index, nothing but copying the nodes and pointing the text and the decoded strings at the image. Where
|
||||
[`parse_copy()`](../api/basic_json_document/parse_copy.md) still has to scan the whole input,
|
||||
[`load()`](../api/basic_json_document/load.md) turns that scan into a copy of the node index alone.
|
||||
|
||||
**Why.** A document that is parsed once and then read many times -- a configuration loaded at startup, a template
|
||||
rendered on every request, a large reference dataset a worker process needs in memory -- pays for parsing once but
|
||||
can amortize [`save()`](../api/basic_json_document/save.md)'s cost across every later load. That makes images useful
|
||||
for a cache: save a document the first time it is parsed (to a file, a shared-memory segment, an in-process cache),
|
||||
and [`load()`](../api/basic_json_document/load.md) it on every later use instead of parsing the source text again.
|
||||
They are just as useful for handing a parsed document to another process (or a forked worker) running the same build
|
||||
of the library, since [`load()`](../api/basic_json_document/load.md) turns the transfer into a copy of the node index
|
||||
plus pointers into the received bytes, not a re-parse.
|
||||
|
||||
**Choosing a check.** [`load()`](../api/basic_json_document/load.md) takes an
|
||||
[`image_check`](../api/basic_json_document/load.md#image_check) that trades validation against speed:
|
||||
`image_check::full` (the default) checks everything the parser itself guarantees, so a checked image is exactly as
|
||||
safe to read and serialize as a freshly parsed document -- the right choice whenever the image did not come straight
|
||||
from this process's own [`save()`](../api/basic_json_document/save.md), such as a file or a network peer.
|
||||
`image_check::bounds` only checks structure and bounds -- cheaper, since it skips scanning the text and the decoded
|
||||
strings -- and fits a cache the process trusts, one it wrote and reads back itself. `image_check::none` skips
|
||||
validation entirely, for an image trusted as much as the process's own memory. See
|
||||
[`load()`'s Notes](../api/basic_json_document/load.md#notes) for exactly what each level does and does not guarantee.
|
||||
|
||||
??? example "Example: cache a parsed configuration as an image"
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/basic_json_document__save.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/basic_json_document__save.output"
|
||||
```
|
||||
|
||||
!!! warning "Experimental"
|
||||
|
||||
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, and tied to the library build that wrote it -- use it to cache a document or to
|
||||
hand one to another process running the *same* build, not as a long-term storage format; keep the original JSON
|
||||
text if a saved document needs to be readable by a future library version.
|
||||
|
||||
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
|
||||
[`load()`](../api/basic_json_document/load.md) runs follows the idea of FlatBuffers' Verifier. No code is taken from
|
||||
either.
|
||||
|
||||
## Choosing between `json`, `ordered_json`, the SAX interface, and `json_view`
|
||||
|
||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) |
|
||||
|---|---|---|---|
|
||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document |
|
||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only |
|
||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use |
|
||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives |
|
||||
| | [`json`](../api/json.md) / [`ordered_json`](../api/ordered_json.md) | [SAX interface](parsing/sax_interface.md) | [`json_document`](../api/json_document.md) / [`json_view`](../api/json_view.md) | [`json_editable_document`](../api/json_editable_document.md) / [`json_editable_view`](../api/json_editable_view.md) |
|
||||
|---|---|---|---|---|
|
||||
| **Ownership** | owns every value | owns nothing; you decide what to keep, in your handler | borrows or owns the *text*; the index is always owned by the document | same as `json_document`; edits go to storage the document owns |
|
||||
| **Mutability** | freely mutable | not applicable (a one-shot event stream) | read-only | [`set`](../api/basic_json_document/set.md)/[`push_back`](../api/basic_json_document/push_back.md)/[`insert`](../api/basic_json_document/insert.md)/[`erase`](../api/basic_json_document/erase.md) edit in place; the source text is never rewritten |
|
||||
| **What you get** | a full tree you can read, write, and keep as long as you like | a sequence of callbacks; whatever your handler builds from them | a flat index plus, on demand, [`materialize()`](../api/basic_json_view/materialize.md)d `json`/`ordered_json` values for the parts you actually use | the same, plus [`dump()`](../api/basic_json_view/dump.md) of an edited document that keeps the member order and, with [`number_format::source`](../api/basic_json_view/number_format.md), the spelling of every untouched number |
|
||||
| **Typical use** | general-purpose JSON handling: config, request/response bodies you build or modify, anything you hold onto | validating or projecting a text into your own data structure without ever holding the whole thing as JSON | large or high-volume input where you only need part of it, or need it repeatedly, and can keep the source text (or a copy) alive for as long as the document lives | a document you read, patch a few fields of, and write back -- a configuration file, for instance -- where the rest of it should come back exactly as it was |
|
||||
| **Caching/reload** | not applicable -- re-parse, or roll your own serialization | not applicable | [`save()`](../api/basic_json_document/save.md)/[`load()`](../api/basic_json_document/load.md): cache the parsed index as an image and reload it without parsing | same, saving the document's current -- possibly edited -- state |
|
||||
|
||||
## Version history
|
||||
|
||||
|
||||
@@ -118,9 +118,10 @@ That is, `-0` is stored as a signed integer, but the serialization does not repr
|
||||
### Number serialization
|
||||
|
||||
- Integer numbers are serialized as is; that is, no scientific notation is used.
|
||||
- Floating-point numbers are serialized as specified by the `#!c %g` printf modifier with
|
||||
[`std::numeric_limits<double>::max_digits10`](https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10)
|
||||
significant digits. The rationale is to use the shortest representation while still allowing round-tripping.
|
||||
- Floating-point numbers are serialized with the fewest digits that read back as the same value (the closest such
|
||||
digits if there are several), in the layout of the `#!c %g` printf modifier: `#!c 1.5`, `#!c 100.0`, `#!c 1e+100`.
|
||||
Doubles are converted with the algorithm of [Żmij](https://github.com/vitaut/zmij), floats with Grisu2, which
|
||||
can write more digits than necessary.
|
||||
|
||||
!!! hint "Notes regarding precision of floating-point numbers"
|
||||
|
||||
|
||||
@@ -540,9 +540,10 @@ therefore silently changes parse results rather than raising an error. See
|
||||
specifiers, for which the library likewise provides only `#!cpp double` and `#!cpp long double` overloads
|
||||
(`#!cpp float` is promoted to `#!cpp double`).
|
||||
|
||||
If `#!cpp std::numeric_limits<NumberFloatType>` describes an IEEE 754 binary32 or binary64 number, `dump` uses the
|
||||
Grisu2 algorithm, which produces the shortest representation that round-trips. Otherwise the `snprintf` fallback with
|
||||
`max_digits10` digits is used.
|
||||
If `#!cpp std::numeric_limits<NumberFloatType>` describes an IEEE 754 binary64 number, `dump` uses the algorithm of
|
||||
Żmij, which produces the shortest representation that round-trips. For IEEE 754 binary32 numbers, it uses Grisu2,
|
||||
which produces a short representation that round-trips. Otherwise the `snprintf` fallback with `max_digits10` digits is
|
||||
used.
|
||||
|
||||
### Required for the binary formats
|
||||
|
||||
@@ -554,7 +555,7 @@ binary32 or binary64 field and have no encoding for `#!cpp long double`.
|
||||
|
||||
| Type | Support |
|
||||
|--------------------------|-----------------------------------------------------------------------------------------------------------------------|
|
||||
| `#!cpp double` (default) | full; short round-trip output through Grisu2 |
|
||||
| `#!cpp double` (default) | full; shortest round-trip output through Żmij |
|
||||
| `#!cpp float` | full; short round-trip output through Grisu2 |
|
||||
| `#!cpp long double` | `dump` and `parse` only; the binary format writers do not compile, as they only handle IEEE 754 binary32 and binary64 |
|
||||
| any other type | not usable |
|
||||
|
||||
@@ -194,9 +194,9 @@ packet-beta
|
||||
|
||||
| Bytes | Field | Type | Contents |
|
||||
|-------|---------|------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||
| 0 | `kind` | `uint8_t` | the type, numbered as [`value_t`](../api/basic_json/value_t.md): 0 null, 1 object, 2 array, 3 string, 4 boolean, 5 signed integer, 6 unsigned integer, 7 float |
|
||||
| 1 | `flags` | `uint8_t` | bits 0-1: where a string's bytes are (0: the source text, 1: the buffer of decoded strings, for strings with escapes); bit 2: the value of a boolean |
|
||||
| 2-3 | `extra` | `uint16_t` | numbers: the number of integer digits (low byte) and fraction digits (high byte), 255 for more; otherwise 0 |
|
||||
| 0 | `kind` | `uint8_t` | the type, numbered as [`value_t`](../api/basic_json/value_t.md): 0 null, 1 object, 2 array, 3 string, 4 boolean, 5 signed integer, 6 unsigned integer, 7 float; 10 for a link (see below) |
|
||||
| 1 | `flags` | `uint8_t` | bits 0-1: where a string's bytes (or a number's token) are (0: the source text, 1: the buffer of decoded strings, for strings with escapes, 2: the edit buffer); bit 2: the value of a boolean; bits 3 and 4: moved and new (see below) |
|
||||
| 2-3 | `extra` | `uint16_t` | numbers: the number of integer digits (low byte) and fraction digits (high byte), 255 for more; objects: the number of their hash index (1-based), or 0; otherwise 0 |
|
||||
| 4-7 | `off` | `uint32_t` | where the value starts: the first byte after a string's opening quote (or its position in the buffer of decoded strings), the first byte of a number or literal, the bracket of an array or object |
|
||||
| 8-11 | `len` | `uint32_t` | strings: the length after decoding; floats and literals: the length of the token; arrays and objects: the number of elements |
|
||||
| 12-15 | `next` | `uint32_t` | arrays and objects: the number of nodes of the subtree, including the node itself |
|
||||
@@ -211,6 +211,11 @@ packet-beta
|
||||
subtree is `next` nodes further for an array or object, and the next node otherwise (`document_data::after`). Views
|
||||
step from element to element this way and skip whole subtrees in constant time.
|
||||
- **Offsets** are 32 bits wide, so a document is limited to 4 GiB (`out_of_range.416`).
|
||||
- **Large objects** (128 members or more) get a hash index after parsing
|
||||
([`detail/view/object_index.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/object_index.hpp)):
|
||||
an open-addressing table whose slots hold the distance from the object's node to a key's node, so that a lookup does
|
||||
not compare every key. The object's `extra` holds the number of its table. Only 65,535 tables fit into `extra`;
|
||||
objects beyond them are searched linearly.
|
||||
|
||||
For example, `#!json {"a": [1, 2.5]}` becomes five nodes. Each node's elements follow it, and `next` leads from an
|
||||
array or object past its subtree:
|
||||
@@ -239,6 +244,29 @@ flowchart LR
|
||||
All `flags` are 0. The integer's bytes 8-15 hold its value, 1; its `extra` says it has one digit. The float's `extra`
|
||||
says it has one integer and one fraction digit, and its `len` is that of the token `2.5`.
|
||||
|
||||
Editable documents ([`json_editable_document`](../api/json_editable_document.md),
|
||||
[`detail/view/edit.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/edit.hpp) and
|
||||
[`detail/view/edit_storage.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/edit_storage.hpp))
|
||||
never write the source text and never move or resize the parsed index, so views stay valid while the document is
|
||||
edited:
|
||||
|
||||
- A new scalar is written over its node. Its text (a string, or the token of a number as `dump()` writes it) goes to
|
||||
the edit buffer, which `flags` bits 0-1 then name.
|
||||
- An array or object whose elements change gets the flag *moved* (bit 3): its elements then live in a separate
|
||||
sequence (a header node, then the entries), whose number is in `off`. The entries are links (`kind` 10), whose bytes
|
||||
8-15 hold the address of the value's node, so values never move.
|
||||
- A node written by an edit gets the flag *new* (bit 4): it has no position in the source text.
|
||||
- Views of read-only documents compile without any of this: how views walk the index is a template parameter
|
||||
(`navigation<Editable>`).
|
||||
|
||||
Images ([`save`](../api/basic_json_document/save.md) and [`load`](../api/basic_json_document/load.md),
|
||||
[`detail/view/image.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/view/image.hpp)) store
|
||||
the nodes as they are: a 64-byte header (the magic bytes `NJVI`, a format version, the sizes, and reserved bytes that
|
||||
must be zero), the nodes, the text, and the decoded strings. An edited document is first written in document order, as
|
||||
the parser would have written it (without links), and the numbers of the hash indexes are cleared, since `load`
|
||||
rebuilds the indexes. So a change of the node layout is a change of the image format: it must raise `image_version`,
|
||||
and `load` then rejects images of other versions (`parse_error.116`) instead of misreading them.
|
||||
|
||||
## Input adapters
|
||||
|
||||
Input is read via **input adapters** that abstract a source. Every input adapter provides this interface:
|
||||
|
||||
@@ -388,6 +388,23 @@ A UBJSON high-precision number could not be parsed.
|
||||
[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.116
|
||||
|
||||
[`basic_json_document::load()`](../api/basic_json_document/load.md) rejected an
|
||||
[image](../features/json_view.md#images): either the bytes are not one [`save()`](../api/basic_json_document/save.md)
|
||||
could have written (too short, an unknown magic number or format version, or sizes that do not fit the buffer), or
|
||||
they are, but fail the requested [`image_check`](../api/basic_json_document/load.md#image_check).
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.parse_error.116] parse error: invalid json_document image: the check failed
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
||||
|
||||
## Iterator errors
|
||||
|
||||
This exception is thrown if iterators passed to a library function do not match
|
||||
@@ -782,6 +799,44 @@ The dynamic type of the object cannot be represented in the requested serializat
|
||||
|
||||
Encapsulate the JSON value in an object. That is, instead of serializing `#!json true`, serialize `#!json {"value": true}`
|
||||
|
||||
### json.exception.type_error.319
|
||||
|
||||
[`basic_json_document::set`](../api/basic_json_document/set.md) and
|
||||
[`basic_json_document::push_back`](../api/basic_json_document/push_back.md) can store any `basic_json` value except
|
||||
a binary one: a `json_document` has no representation for [binary values](../features/binary_values.md), which only
|
||||
ever arise from parsing a binary format or from an explicit [`json::binary`](../api/basic_json/binary.md) value, not
|
||||
from JSON text.
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
[json.exception.type_error.319] cannot store a binary value in a json_document
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0, together with editable [`json_document`s](../features/json_view.md).
|
||||
|
||||
### json.exception.type_error.320
|
||||
|
||||
[`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.
|
||||
[`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"
|
||||
|
||||
```
|
||||
[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
|
||||
|
||||
This exception was added in version 3.13.0, together with [images](../features/json_view.md#images).
|
||||
|
||||
## Out of range
|
||||
|
||||
This exception is thrown in case a library function is called on an input parameter that exceeds the expected range, for instance, in the case of array indices or nonexisting object keys.
|
||||
@@ -1006,13 +1061,24 @@ MessagePack's ext type and BSON's binary subtype are each stored in a single byt
|
||||
|
||||
[`basic_json_document::parse()`](../api/basic_json_document/parse.md) and the other parsing functions of
|
||||
[`basic_json_document`](../api/basic_json_document/index.md) index a value's position in the source text in 32 bits,
|
||||
so they do not support an input of 4 GiB or more.
|
||||
so they do not support an input of 4 GiB or more. The same 32-bit limit applies to an **editable** document's own
|
||||
storage: [`set`](../api/basic_json_document/set.md) and [`push_back`](../api/basic_json_document/push_back.md) throw
|
||||
this exception once the strings and number tokens written by edits reach 4 GiB in total, or once more than
|
||||
4294967295 arrays/objects have had an element set or appended to them. The same limit applies to an
|
||||
[image](../features/json_view.md#images): [`save()`](../api/basic_json_document/save.md) throws it if the node
|
||||
count, the text, or the decoded strings it would write would individually reach 4 GiB.
|
||||
|
||||
!!! failure "Example message"
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
[json.exception.out_of_range.416] input of 4 GiB or more is not supported by json_document
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.416] edits 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
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
|
||||
@@ -18,8 +18,12 @@ The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed und
|
||||
|
||||
The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2009 [Florian Loitsch](https://florian.loitsch.com/)
|
||||
|
||||
The class contains a port of the shortest double-to-decimal conversion of [Żmij](https://github.com/vitaut/zmij) by Victor Zverovich, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above). Copyright © 2025 [Victor Zverovich](https://github.com/vitaut)
|
||||
|
||||
The class contains a copy of [Hedley](https://nemequ.github.io/hedley/) from Evan Nemerson which is licensed as [CC0-1.0](https://creativecommons.org/publicdomain/zero/1.0/).
|
||||
|
||||
The class contains an adapted version of the Eisel-Lemire algorithm and its table of powers of five from [fast_float](https://github.com/fastfloat/fast_float) by Daniel Lemire and contributors, which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here), the Apache 2.0 License, and the Boost Software License. Copyright © 2021 The fast_float authors
|
||||
|
||||
The view's parser (`<nlohmann/json_view.hpp>`) contains techniques and code adapted from [yyjson](https://github.com/ibireme/yyjson) by YaoYuan, which is licensed under the [MIT License](https://opensource.org/licenses/MIT) (see above): table-driven decoding of `\u` escapes and fixed-offset unrolled checks.
|
||||
|
||||
The view's parser (`<nlohmann/json_view.hpp>`) validates non-ASCII strings with the vector UTF-8 check of [simdjson](https://github.com/simdjson/simdjson) by Daniel Lemire, Geoff Langdale, John Keiser, and contributors (its "lookup4" algorithm and tables, after J. Keiser and D. Lemire, "Validating UTF-8 In Less Than One Instruction Per Byte", 2021), which is available under the [MIT License](https://opensource.org/licenses/MIT) (used here) and the Apache 2.0 License. Copyright © 2018-2025 The simdjson authors
|
||||
|
||||
@@ -233,14 +233,20 @@ nav:
|
||||
- 'Overview': api/basic_json_document/index.md
|
||||
- '(Constructor)': api/basic_json_document/basic_json_document.md
|
||||
- 'accept': api/basic_json_document/accept.md
|
||||
- 'erase': api/basic_json_document/erase.md
|
||||
- 'insert': api/basic_json_document/insert.md
|
||||
- 'is_discarded': api/basic_json_document/is_discarded.md
|
||||
- 'load': api/basic_json_document/load.md
|
||||
- 'memory_usage': api/basic_json_document/memory_usage.md
|
||||
- 'node_count': api/basic_json_document/node_count.md
|
||||
- 'owns_source': api/basic_json_document/owns_source.md
|
||||
- 'parse': api/basic_json_document/parse.md
|
||||
- 'parse_copy': api/basic_json_document/parse_copy.md
|
||||
- 'push_back': api/basic_json_document/push_back.md
|
||||
- 'read': api/basic_json_document/read.md
|
||||
- 'root': api/basic_json_document/root.md
|
||||
- 'save': api/basic_json_document/save.md
|
||||
- 'set': api/basic_json_document/set.md
|
||||
- 'shrink_to_fit': api/basic_json_document/shrink_to_fit.md
|
||||
- 'source': api/basic_json_document/source.md
|
||||
- basic_json_view:
|
||||
@@ -253,10 +259,14 @@ nav:
|
||||
- 'cend': api/basic_json_view/cend.md
|
||||
- 'contains': api/basic_json_view/contains.md
|
||||
- 'count': api/basic_json_view/count.md
|
||||
- 'dump': api/basic_json_view/dump.md
|
||||
- 'empty': api/basic_json_view/empty.md
|
||||
- 'end': api/basic_json_view/end.md
|
||||
- 'find': api/basic_json_view/find.md
|
||||
- 'front': api/basic_json_view/front.md
|
||||
- 'get': api/basic_json_view/get.md
|
||||
- 'get_string': api/basic_json_view/get_string.md
|
||||
- 'get_to': api/basic_json_view/get_to.md
|
||||
- 'is_array': api/basic_json_view/is_array.md
|
||||
- 'is_binary': api/basic_json_view/is_binary.md
|
||||
- 'is_boolean': api/basic_json_view/is_boolean.md
|
||||
@@ -272,12 +282,18 @@ nav:
|
||||
- 'is_structured': api/basic_json_view/is_structured.md
|
||||
- 'items': api/basic_json_view/items.md
|
||||
- 'materialize': api/basic_json_view/materialize.md
|
||||
- 'number_format': api/basic_json_view/number_format.md
|
||||
- 'number_token': api/basic_json_view/number_token.md
|
||||
- 'operator bool': api/basic_json_view/operator_bool.md
|
||||
- 'operator<<': api/basic_json_view/operator_ltlt.md
|
||||
- 'operator[]': api/basic_json_view/operator[].md
|
||||
- 'operator==': api/basic_json_view/operator_eq.md
|
||||
- 'operator!=': api/basic_json_view/operator_ne.md
|
||||
- 'size': api/basic_json_view/size.md
|
||||
- 'source_offset': api/basic_json_view/source_offset.md
|
||||
- 'type': api/basic_json_view/type.md
|
||||
- 'type_name': api/basic_json_view/type_name.md
|
||||
- 'value': api/basic_json_view/value.md
|
||||
- byte_container_with_subtype:
|
||||
- 'Overview': api/byte_container_with_subtype/index.md
|
||||
- '(constructor)': api/byte_container_with_subtype/byte_container_with_subtype.md
|
||||
@@ -291,6 +307,8 @@ nav:
|
||||
- 'to_json': api/adl_serializer/to_json.md
|
||||
- 'json': api/json.md
|
||||
- 'json_document': api/json_document.md
|
||||
- 'json_editable_document': api/json_editable_document.md
|
||||
- 'json_editable_view': api/json_editable_view.md
|
||||
- json_pointer:
|
||||
- 'Overview': api/json_pointer/index.md
|
||||
- '(Constructor)': api/json_pointer/json_pointer.md
|
||||
@@ -331,6 +349,8 @@ nav:
|
||||
- 'operator""_json_pointer': api/operator_literal_json_pointer.md
|
||||
- 'ordered_json': api/ordered_json.md
|
||||
- 'ordered_json_document': api/ordered_json_document.md
|
||||
- 'ordered_json_editable_document': api/ordered_json_editable_document.md
|
||||
- 'ordered_json_editable_view': api/ordered_json_editable_view.md
|
||||
- 'ordered_json_view': api/ordered_json_view.md
|
||||
- 'ordered_map': api/ordered_map.md
|
||||
- macros:
|
||||
@@ -358,6 +378,8 @@ nav:
|
||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||
- 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON': api/macros/json_use_legacy_discarded_value_comparison.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_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
|
||||
|
||||
@@ -11,11 +11,17 @@
|
||||
|
||||
#include <array> // array
|
||||
#include <cmath> // signbit, isfinite
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // intN_t, uintN_t
|
||||
#include <cstring> // memcpy, memmove
|
||||
#include <limits> // numeric_limits
|
||||
#include <type_traits> // conditional
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <cstdlib> // _byteswap_uint64
|
||||
#endif
|
||||
|
||||
#include <nlohmann/detail/conversions/zmij.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
@@ -918,6 +924,87 @@ void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
|
||||
grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the shortest digits of a positive finite float (other than double): Grisu2
|
||||
*/
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1)
|
||||
void shortest_digits(char* buf, int& len, int& decimal_exponent, FloatType value)
|
||||
{
|
||||
grisu2(buf, len, decimal_exponent, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief the shortest digits of a positive finite double: the conversion of
|
||||
Zmij (see zmij.hpp), which always finds the shortest digits that read back as
|
||||
the same value (Grisu2 does not for about one double in a thousand), and the
|
||||
closest of them if there are several
|
||||
|
||||
v = buf * 10^decimal_exponent, as for grisu2()
|
||||
*/
|
||||
JSON_HEDLEY_NON_NULL(1)
|
||||
inline void shortest_digits(char* buf, int& len, int& decimal_exponent, double value)
|
||||
{
|
||||
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
|
||||
"internal error: the conversion of Zmij needs IEEE 754 binary64 doubles");
|
||||
JSON_ASSERT(std::isfinite(value));
|
||||
JSON_ASSERT(value > 0);
|
||||
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
zmij::decimal d = zmij::to_decimal(bits);
|
||||
// without trailing zeros (up to 16): 8, 4, 2, 1 at a time
|
||||
while (d.significand % 100000000 == 0)
|
||||
{
|
||||
d.significand /= 100000000;
|
||||
d.exponent += 8;
|
||||
}
|
||||
if (d.significand % 10000 == 0)
|
||||
{
|
||||
d.significand /= 10000;
|
||||
d.exponent += 4;
|
||||
}
|
||||
if (d.significand % 100 == 0)
|
||||
{
|
||||
d.significand /= 100;
|
||||
d.exponent += 2;
|
||||
}
|
||||
if (d.significand % 10 == 0)
|
||||
{
|
||||
d.significand /= 10;
|
||||
d.exponent += 1;
|
||||
}
|
||||
// at most 17 digits, written from the back two at a time
|
||||
static constexpr const char* pairs =
|
||||
"00010203040506070809101112131415161718192021222324252627282930313233343536373839"
|
||||
"40414243444546474849505152535455565758596061626364656667686970717273747576777879"
|
||||
"8081828384858687888990919293949596979899";
|
||||
std::array<char, 20> digits{};
|
||||
std::size_t n = digits.size();
|
||||
while (d.significand >= 100)
|
||||
{
|
||||
const auto i = static_cast<std::size_t>(d.significand % 100) * 2;
|
||||
d.significand /= 100;
|
||||
n -= 2;
|
||||
digits[n] = pairs[i];
|
||||
digits[n + 1] = pairs[i + 1];
|
||||
}
|
||||
if (d.significand >= 10)
|
||||
{
|
||||
const auto i = static_cast<std::size_t>(d.significand) * 2;
|
||||
n -= 2;
|
||||
digits[n] = pairs[i];
|
||||
digits[n + 1] = pairs[i + 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
digits[--n] = static_cast<char>('0' + d.significand);
|
||||
}
|
||||
len = static_cast<int>(digits.size() - n);
|
||||
std::memcpy(buf, digits.data() + n, static_cast<std::size_t>(len));
|
||||
decimal_exponent = d.exponent;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief appends a decimal representation of e to buf
|
||||
@return a pointer to the element following the exponent.
|
||||
@@ -1047,6 +1134,177 @@ inline char* format_buffer(char* buf, int len, int decimal_exponent,
|
||||
return append_exponent(buf, n - 1);
|
||||
}
|
||||
|
||||
/// eight decimal digits (a value below 10^8) as bytes 0..9, the first digit
|
||||
/// in the most significant byte: three steps that divide all lanes at once
|
||||
/// by a multiplication (the conversion of Xiang JunBo, as in Zmij)
|
||||
inline std::uint64_t eight_digit_bytes(std::uint64_t abcdefgh) noexcept
|
||||
{
|
||||
const std::uint64_t abcd_efgh = abcdefgh + (((std::uint64_t{1} << 32u) - 10000u) * ((abcdefgh * (((std::uint64_t{1} << 40u) / 10000u) + 1u)) >> 40u));
|
||||
const std::uint64_t ab_cd_ef_gh = abcd_efgh + (((std::uint64_t{1} << 16u) - 100u) * (((abcd_efgh * (((std::uint64_t{1} << 19u) / 100u) + 1u)) >> 19u) & 0x7F0000007Fu));
|
||||
return ab_cd_ef_gh + (((std::uint64_t{1} << 8u) - 10u) * (((ab_cd_ef_gh * (((std::uint64_t{1} << 10u) / 10u) + 1u)) >> 10u) & 0x000F000F000F000Fu));
|
||||
}
|
||||
|
||||
/// store the bytes of v, the most significant one first (one byte swap and
|
||||
/// one store where the byte order is known: compilers do not reliably merge
|
||||
/// the byte stores once this is inlined)
|
||||
inline void store_msb_first(char* p, std::uint64_t v) noexcept
|
||||
{
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
v = __builtin_bswap64(v);
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#elif defined(_MSC_VER) // (little-endian on all its targets)
|
||||
v = _byteswap_uint64(v);
|
||||
std::memcpy(p, &v, sizeof(v));
|
||||
#else
|
||||
for (unsigned i = 0; i < 8; ++i)
|
||||
{
|
||||
p[i] = static_cast<char>(v >> (56u - (8u * i)));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief digits * 10^exp for a double, in the layout of format_buffer()
|
||||
|
||||
The layout is that of format_buffer() with min_exp -4 and max_exp 15 (the
|
||||
digits10 of double). The digits are converted eight at a time and placed
|
||||
with fixed-size moves instead of per-digit loops and moves of the buffer.
|
||||
|
||||
@param[in] digits the digits (not 0, at most 17 digits; trailing zeros allowed)
|
||||
@param[in] exp the decimal exponent of the last digit
|
||||
@return a pointer past the text; up to 41 bytes at @a first are written
|
||||
(some beyond the returned end)
|
||||
*/
|
||||
JSON_HEDLEY_NON_NULL(1)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
inline char* write_decimal(char* first, std::uint64_t digits, int exp) noexcept
|
||||
{
|
||||
JSON_ASSERT(digits != 0 && digits < 100000000000000000u);
|
||||
const std::uint64_t upper = digits / 100000000u;
|
||||
const std::uint64_t b0 = upper / 100000000u; // (one digit: it is its own byte)
|
||||
const std::uint64_t b1 = eight_digit_bytes(upper % 100000000u);
|
||||
const std::uint64_t b2 = eight_digit_bytes(digits % 100000000u);
|
||||
// leading and trailing zero digits: zero bytes, counted without division
|
||||
int leading = 16;
|
||||
int zeros = 16;
|
||||
if (b0 != 0)
|
||||
{
|
||||
leading = count_leading_zeros(b0) / 8;
|
||||
}
|
||||
else if (b1 != 0)
|
||||
{
|
||||
leading = 8 + (count_leading_zeros(b1) / 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
leading += count_leading_zeros(b2) / 8;
|
||||
}
|
||||
if (b2 != 0)
|
||||
{
|
||||
zeros = count_trailing_zeros(b2) / 8;
|
||||
}
|
||||
else if (b1 != 0)
|
||||
{
|
||||
zeros = 8 + (count_trailing_zeros(b1) / 8);
|
||||
}
|
||||
// (else: 16, b0 is the one digit that is not 0)
|
||||
// the digits as text at text + leading, then '0's, so that fixed-size
|
||||
// moves need not check how many digits there are
|
||||
std::array<char, 64> text; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init): written before read
|
||||
store_msb_first(text.data(), b0 + 0x3030303030303030u);
|
||||
store_msb_first(text.data() + 8, b1 + 0x3030303030303030u);
|
||||
store_msb_first(text.data() + 16, b2 + 0x3030303030303030u);
|
||||
std::memset(text.data() + 24, '0', 40);
|
||||
const int k = 24 - leading - zeros; // significant digits
|
||||
const int n = k + exp + zeros; // position of the decimal point after the first digit
|
||||
const char* const s0 = text.data() + leading;
|
||||
|
||||
if (-4 < n && n <= 15)
|
||||
{
|
||||
// "0.[000]digits" (n <= 0) is the digits after 1 - n leading '0's
|
||||
// with the point after the first; "digits[000].0" (n >= k) and
|
||||
// "dig.its" put the point after n characters
|
||||
const int pad = n <= 0 ? 1 - n : 0;
|
||||
const char* const s = s0 - pad;
|
||||
const int len = k + pad;
|
||||
const int point = n + pad;
|
||||
std::memcpy(first, s, 16);
|
||||
std::memcpy(first + point + 1, s + point, 24);
|
||||
first[point] = '.';
|
||||
return first + (point >= len ? point + 2 : len + 1);
|
||||
}
|
||||
|
||||
// d.igitse+XX, with at least two exponent digits (as append_exponent())
|
||||
std::memcpy(first, s0, 16);
|
||||
std::memcpy(first + 2, s0 + 1, 16);
|
||||
first[1] = '.';
|
||||
char* const end = first + (k == 1 ? 1 : k + 1);
|
||||
const int e = n - 1;
|
||||
const auto ea = static_cast<unsigned>(e < 0 ? -e : e);
|
||||
const bool three = ea >= 100;
|
||||
end[0] = 'e';
|
||||
end[1] = e < 0 ? '-' : '+';
|
||||
end[2] = static_cast<char>('0' + (three ? ea / 100 : (ea / 10) % 10));
|
||||
end[3] = static_cast<char>('0' + (three ? (ea / 10) % 10 : ea % 10));
|
||||
end[4] = static_cast<char>('0' + (ea % 10));
|
||||
return end + (three ? 5 : 4);
|
||||
}
|
||||
|
||||
/// a positive finite float (other than double): Grisu2 and format_buffer()
|
||||
template<typename FloatType>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* write_positive(char* first, const char* last, FloatType value)
|
||||
{
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Compute v = buffer * 10^decimal_exponent.
|
||||
// The decimal digits are stored in the buffer, which needs to be interpreted
|
||||
// as an unsigned decimal integer.
|
||||
// len is the length of the buffer, i.e., the number of decimal digits.
|
||||
int len = 0;
|
||||
int decimal_exponent = 0;
|
||||
shortest_digits(first, len, decimal_exponent, value);
|
||||
|
||||
JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Format the buffer like printf("%.*g", prec, value)
|
||||
constexpr int kMinExp = -4;
|
||||
// Use digits10 here to increase compatibility with version 2.
|
||||
constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
|
||||
|
||||
JSON_ASSERT(last - first >= kMaxExp + 2);
|
||||
JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
|
||||
|
||||
return format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
|
||||
}
|
||||
|
||||
/// a positive finite double: the shortest digits (Zmij), laid out by
|
||||
/// write_decimal() (through a local buffer if [first, last) is shorter than
|
||||
/// the 41 bytes it may write)
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
inline char* write_positive(char* first, const char* last, double value)
|
||||
{
|
||||
static_assert(std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53,
|
||||
"internal error: the conversion of Zmij needs IEEE 754 binary64 doubles");
|
||||
std::uint64_t bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
const zmij::decimal d = zmij::to_decimal(bits);
|
||||
if (JSON_HEDLEY_LIKELY(last - first >= 41))
|
||||
{
|
||||
return write_decimal(first, d.significand, d.exponent);
|
||||
}
|
||||
std::array<char, 64> buf; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init): written before read
|
||||
const auto len = static_cast<std::size_t>(write_decimal(buf.data(), d.significand, d.exponent) - buf.data());
|
||||
JSON_ASSERT(static_cast<std::size_t>(last - first) >= len);
|
||||
std::memcpy(first, buf.data(), len);
|
||||
return first + len;
|
||||
}
|
||||
|
||||
} // namespace dtoa_impl
|
||||
|
||||
/*!
|
||||
@@ -1064,7 +1322,6 @@ JSON_HEDLEY_NON_NULL(1, 2)
|
||||
JSON_HEDLEY_RETURNS_NON_NULL
|
||||
char* to_chars(char* first, const char* last, FloatType value)
|
||||
{
|
||||
static_cast<void>(last); // maybe unused - fix warning
|
||||
JSON_ASSERT(std::isfinite(value));
|
||||
|
||||
// Use signbit(value) instead of (value < 0) since signbit works for -0.
|
||||
@@ -1090,28 +1347,7 @@ char* to_chars(char* first, const char* last, FloatType value)
|
||||
JSON_HEDLEY_DIAGNOSTIC_POP
|
||||
#endif
|
||||
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Compute v = buffer * 10^decimal_exponent.
|
||||
// The decimal digits are stored in the buffer, which needs to be interpreted
|
||||
// as an unsigned decimal integer.
|
||||
// len is the length of the buffer, i.e., the number of decimal digits.
|
||||
int len = 0;
|
||||
int decimal_exponent = 0;
|
||||
dtoa_impl::grisu2(first, len, decimal_exponent, value);
|
||||
|
||||
JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
|
||||
|
||||
// Format the buffer like printf("%.*g", prec, value)
|
||||
constexpr int kMinExp = -4;
|
||||
// Use digits10 here to increase compatibility with version 2.
|
||||
constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
|
||||
|
||||
JSON_ASSERT(last - first >= kMaxExp + 2);
|
||||
JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
|
||||
JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
|
||||
|
||||
return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
|
||||
return dtoa_impl::write_positive(first, last, value);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2025 Victor Zverovich <https://github.com/vitaut/zmij>
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint32_t, uint64_t
|
||||
|
||||
#include <nlohmann/detail/abi_macros.hpp>
|
||||
#include <nlohmann/detail/bit_ops.hpp>
|
||||
#include <nlohmann/detail/input/pow5_table.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief the shortest decimal representation of a double
|
||||
|
||||
A C++11 port of the conversion of Zmij by Victor Zverovich
|
||||
(https://github.com/vitaut/zmij, MIT license): the shortest decimal in the
|
||||
rounding interval of a double, the closest one if there are several. Zmij
|
||||
credits Xiang JunBo (producing the shorter candidate without a division) and
|
||||
Dougall Johnson (the compressed powers of ten). The powers of ten are taken
|
||||
from the table for number parsing (pow5_table.hpp) where it holds them, and
|
||||
computed from the compressed tables of Zmij beyond it.
|
||||
*/
|
||||
namespace zmij
|
||||
{
|
||||
|
||||
/// significand * 10^exponent
|
||||
struct decimal
|
||||
{
|
||||
std::uint64_t significand;
|
||||
int exponent;
|
||||
};
|
||||
|
||||
/// the compressed powers of ten of Zmij
|
||||
inline const std::array<std::uint64_t, 28>& pow10_minor() noexcept
|
||||
{
|
||||
static const std::array<std::uint64_t, 28> table =
|
||||
{
|
||||
{
|
||||
0x8000000000000000u, 0xa000000000000000u, 0xc800000000000000u, 0xfa00000000000000u, 0x9c40000000000000u,
|
||||
0xc350000000000000u, 0xf424000000000000u, 0x9896800000000000u, 0xbebc200000000000u, 0xee6b280000000000u,
|
||||
0x9502f90000000000u, 0xba43b74000000000u, 0xe8d4a51000000000u, 0x9184e72a00000000u, 0xb5e620f480000000u,
|
||||
0xe35fa931a0000000u, 0x8e1bc9bf04000000u, 0xb1a2bc2ec5000000u, 0xde0b6b3a76400000u, 0x8ac7230489e80000u,
|
||||
0xad78ebc5ac620000u, 0xd8d726b7177a8000u, 0x878678326eac9000u, 0xa968163f0a57b400u, 0xd3c21bcecceda100u,
|
||||
0x84595161401484a0u, 0xa56fa5b99019a5c8u, 0xcecb8f27f4200f3au
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
/// (high, low) pairs
|
||||
inline const std::array<std::uint64_t, 50>& pow10_major() noexcept
|
||||
{
|
||||
static const std::array<std::uint64_t, 50> table =
|
||||
{
|
||||
{
|
||||
0xaddcb9e83c6b1793u, 0xdf4abe242a1bbf3eu, 0xaf8e5410288e1b6fu, 0x07ecf0ae5ee44ddau, 0xb1442798f49ffb4au, 0x99cd11cfdf41779du,
|
||||
0xb2fe3f0b8599ef07u, 0x861fa7e6dcb4aa15u, 0xb4bca50b065abe63u, 0x0fed077a756b53aau, 0xb67f6455292cbf08u, 0x1a3bc84c17b1d543u,
|
||||
0xb84687c269ef3bfbu, 0x3d5d514f40eea742u, 0xba121a4650e4ddebu, 0x92f34d62616ce413u, 0xbbe226efb628afeau, 0x890489f70a55368cu,
|
||||
0xbdb6b8e905cb600fu, 0x5400e987bbc1c921u, 0xbf8fdb78849a5f96u, 0xde98520472bdd034u, 0xc16d9a0095928a27u, 0x75b7053c0f178294u,
|
||||
0xc350000000000000u, 0x0000000000000000u, 0xc5371912364ce305u, 0x6c28000000000000u, 0xc722f0ef9d80aad6u, 0x424d3ad2b7b97ef6u,
|
||||
0xc913936dd571c84cu, 0x03bc3a19cd1e38eau, 0xcb090c8001ab551cu, 0x5cadf5bfd3072cc6u, 0xcd036837130890a1u, 0x36dba887c37a8c10u,
|
||||
0xcf02b2c21207ef2eu, 0x94f967e45e03f4bcu, 0xd106f86e69d785c7u, 0xe13336d701beba52u, 0xd31045a8341ca07cu, 0x1ede48111209a051u,
|
||||
0xd51ea6fa85785631u, 0x552a74227f3ea566u, 0xd732290fbacaf133u, 0xa97c177947ad4096u, 0xd94ad8b1c7380874u, 0x18375281ae7822bdu,
|
||||
0xdb68c2ca82ed2a05u, 0xa67398db9f6820e1u
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
/// one bit per power: whether the computed value is one unit too large
|
||||
inline const std::array<std::uint32_t, 21>& pow10_fixups() noexcept
|
||||
{
|
||||
static const std::array<std::uint32_t, 21> table =
|
||||
{
|
||||
{
|
||||
0x8d8fc810u, 0x06100293u, 0x19000000u, 0x00100000u, 0x00000908u, 0x00000000u, 0x04e00300u, 0x3807e0b2u, 0x3d83d793u, 0x0006f5ccu,
|
||||
0x00000000u, 0xffff0000u, 0x8076337du, 0x4ff45ba0u, 0x09405033u, 0x034376d9u, 0x09000000u, 0x4e100501u, 0x076d14dcu, 0xf964f45eu,
|
||||
0x0000003du
|
||||
}
|
||||
};
|
||||
return table;
|
||||
}
|
||||
|
||||
/// the 128-bit significand of 10^k, rounded down, for k in [-307, 341]
|
||||
/// (compute_pow10 of Zmij)
|
||||
inline uint128_parts compute_pow10(int k) noexcept
|
||||
{
|
||||
const auto i = static_cast<unsigned>(k + 307);
|
||||
const std::uint64_t m = pow10_minor()[(i + 24) % 28];
|
||||
const std::size_t j = 2 * static_cast<std::size_t>((i + 24) / 28);
|
||||
const std::uint64_t h_hi = pow10_major()[j];
|
||||
const std::uint64_t h_lo = pow10_major()[j + 1];
|
||||
const std::uint64_t h1 = full_multiplication(h_lo, m).high;
|
||||
const std::uint64_t c0 = h_lo * m;
|
||||
const std::uint64_t c1 = h1 + (h_hi * m);
|
||||
const std::uint64_t c2 = (c1 < h1 ? 1u : 0u) + full_multiplication(h_hi, m).high;
|
||||
uint128_parts r{};
|
||||
if ((c2 >> 63u) != 0)
|
||||
{
|
||||
r.high = c2;
|
||||
r.low = c1;
|
||||
}
|
||||
else
|
||||
{
|
||||
r.high = (c2 << 1u) | (c1 >> 63u);
|
||||
r.low = (c1 << 1u) | (c0 >> 63u);
|
||||
}
|
||||
r.low -= (pow10_fixups()[i >> 5u] >> (i & 31u)) & 1u;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// The 128-bit significand of 10^k, rounded down, for k in [-342, 341].
|
||||
/// Up to 10^308, the table for number parsing holds the same significands
|
||||
/// (those of 5^k), except for k in [-27, -1], where it holds them one unit
|
||||
/// larger (as the Eisel-Lemire algorithm needs them).
|
||||
inline uint128_parts pow10(int k) noexcept
|
||||
{
|
||||
if (k > pow5_128_largest_power)
|
||||
{
|
||||
return compute_pow10(k); // (only for the smallest doubles)
|
||||
}
|
||||
const auto i = 2 * static_cast<std::size_t>(k - pow5_128_smallest_power);
|
||||
uint128_parts r{pow5_128()[i + 1], pow5_128()[i]};
|
||||
const std::uint64_t adjust = static_cast<unsigned>(k + 27) < 27u ? 1u : 0u;
|
||||
r.high -= r.low < adjust ? 1u : 0u;
|
||||
r.low -= adjust;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// (x_hi * 2^64 + x_lo) * y >> 64, as 128 bits
|
||||
inline uint128_parts umul192_hi128(std::uint64_t x_hi, std::uint64_t x_lo, std::uint64_t y) noexcept
|
||||
{
|
||||
const uint128_parts p = full_multiplication(x_hi, y);
|
||||
uint128_parts r{};
|
||||
r.low = p.low + full_multiplication(x_lo, y).high;
|
||||
r.high = p.high + (r.low < p.low ? 1u : 0u);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// (x * y + c) >> 64
|
||||
inline std::uint64_t umul128_add_hi64(std::uint64_t x, std::uint64_t y, std::uint64_t c) noexcept
|
||||
{
|
||||
const uint128_parts p = full_multiplication(x, y);
|
||||
return p.high + (p.low + c < p.low ? 1u : 0u);
|
||||
}
|
||||
|
||||
/// The shortest decimal in the rounding interval of a positive finite double
|
||||
/// given by its bits, the closest one if there are several (to_decimal of
|
||||
/// Zmij). The significand can end in zeros.
|
||||
inline decimal to_decimal(std::uint64_t bits) noexcept
|
||||
{
|
||||
constexpr int extra_shift = 9;
|
||||
const auto raw_exp = static_cast<int>((bits >> 52u) & 0x7FFu);
|
||||
std::uint64_t bin_sig = bits & ((std::uint64_t{1} << 52u) - 1);
|
||||
// a power of two has a narrower interval below (except the smallest normal)
|
||||
const bool regular = bin_sig != 0 || raw_exp <= 1;
|
||||
const int bin_exp = (raw_exp == 0 ? 1 : raw_exp) - 1075;
|
||||
if (raw_exp != 0)
|
||||
{
|
||||
bin_sig |= std::uint64_t{1} << 52u;
|
||||
}
|
||||
// floor(log10(2^bin_exp)), or floor(log10(3/4 * 2^bin_exp)) for the irregular case
|
||||
const int dec_exp = ((bin_exp * 315653) - (regular ? 0 : 131072)) >> 20;
|
||||
// scaled by 10^(-dec_exp - 1): the integral part is the shorter candidate
|
||||
const int shift = bin_exp + ((-(dec_exp + 1) * 217707) >> 16) + 1 + extra_shift;
|
||||
const uint128_parts p10 = pow10(-dec_exp - 1);
|
||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||
const std::uint64_t fractional = (p.high << static_cast<unsigned>(64 - extra_shift)) | (p.low >> static_cast<unsigned>(extra_shift));
|
||||
std::uint64_t digit = 0;
|
||||
bool round_up = false;
|
||||
bool round_down = false;
|
||||
if (JSON_HEDLEY_LIKELY(regular))
|
||||
{
|
||||
const std::uint64_t half_ulp = (p10.high >> static_cast<unsigned>(extra_shift + 1 - shift)) + (1 - (bin_sig & 1u));
|
||||
round_up = fractional + half_ulp < fractional;
|
||||
round_down = half_ulp > fractional;
|
||||
// the last digit of the longer candidate, rounded to nearest
|
||||
digit = umul128_add_hi64(fractional, 10, (std::uint64_t{1} << 63u) + 6);
|
||||
if (fractional == (std::uint64_t{1} << 62u))
|
||||
{
|
||||
digit = 2; // 2.5 rounds to 2
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::uint64_t half_ulp = p10.high >> static_cast<unsigned>(extra_shift + 1 - shift);
|
||||
round_up = half_ulp > ~std::uint64_t{0} - fractional;
|
||||
round_down = (half_ulp >> 1u) > fractional;
|
||||
digit = umul128_add_hi64(fractional, 10, (std::uint64_t{1} << 63u) - 1);
|
||||
const std::uint64_t lowest = umul128_add_hi64(fractional - (half_ulp >> 1u), 10, ~std::uint64_t{0});
|
||||
digit = digit < lowest ? lowest : digit;
|
||||
}
|
||||
integral += round_up ? 1u : 0u;
|
||||
if (!round_up && !round_down)
|
||||
{
|
||||
// the shorter candidate is outside the rounding interval: one digit more
|
||||
return decimal{(integral * 10) + digit, dec_exp};
|
||||
}
|
||||
return decimal{integral, dec_exp + 1};
|
||||
}
|
||||
|
||||
} // namespace zmij
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -30,6 +30,11 @@
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct json_pointer_access;
|
||||
} // namespace detail
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
@@ -42,6 +47,8 @@ class json_pointer
|
||||
template<typename>
|
||||
friend class json_pointer;
|
||||
|
||||
friend struct detail::json_pointer_access;
|
||||
|
||||
template<typename T>
|
||||
struct string_t_helper
|
||||
{
|
||||
@@ -1164,4 +1171,18 @@ inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/// the reference tokens of a json_pointer, for code that resolves pointers
|
||||
/// without a basic_json value (such as the zero-copy view)
|
||||
struct json_pointer_access
|
||||
{
|
||||
template<typename RefStringType>
|
||||
static const std::vector<typename json_pointer<RefStringType>::string_t>& reference_tokens(const json_pointer<RefStringType>& ptr) noexcept
|
||||
{
|
||||
return ptr.reference_tokens;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user