Compare commits

..
Author SHA1 Message Date
Niels Lohmann 26700c6dc3 Merge branch 'develop' into fix/json_pointer_create_object_5357
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-08 16:12:17 +02:00
Niels Lohmann b9a581e6d2 Merge branch 'develop' into fix/json_pointer_create_object_5357
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-07 20:12:05 +02:00
Niels Lohmann 0bd2d36f2a Merge remote-tracking branch 'origin/develop' into HEAD
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-04 17:19:42 +02:00
Niels Lohmann a2ca07a4b3 Merge branch 'develop' into fix/json_pointer_create_object_5357
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 20:58:31 +02:00
Niels Lohmann cda8d2b848 Clarify that the token 0 is a valid array index in operator[] docs
The note described valid array indices as "a nonempty sequence of digits
without a leading 0", which read literally excludes the token "0" itself,
contradicting both the code and the /foo/0/0/0 example in the next
sentence. Spell out the RFC 6901 grammar instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-27 18:20:28 +02:00
Niels Lohmann db1d0983e6 create objects for tokens that cannot be array indices (#5357)
When operator[](json_pointer) traverses a level that does not exist yet,
the null value is turned into an array or an object depending on the
reference token. The check only tested whether all characters are digits,
so tokens that can never be a valid array index selected an array and
then failed:

- "01" (and any other token with a leading '0') threw parse_error.106
- the empty token threw out_of_range.404

Both tokens are valid object keys, and both work when the level already
exists as an object, so creating the level changed the outcome. Test the
token against the RFC 6901, Sect. 4 grammar for array indices instead, so
that such tokens create an object. This only affects pointers that threw
before.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-08-04 08:51:54 +02:00
146 changed files with 3153 additions and 11887 deletions

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
# generated files are collapsed in diffs and excluded from language statistics
single_include/** linguist-generated
nlohmann_json.natvis linguist-generated
+1 -1
View File
@@ -1,5 +1,5 @@
# JSON for Modern C++ was originally written by Niels Lohmann. # JSON for Modern C++ was originally written by Niels Lohmann.
# Since 2013, over 400 contributors have helped to improve the library. # Since 2013, over 250 contributors have helped to improve the library.
# This CODEOWNERS file is only to make sure that @nlohmann is requested # This CODEOWNERS file is only to make sure that @nlohmann is requested
# for a code review in case of a pull request. # for a code review in case of a pull request.
+14 -18
View File
@@ -98,7 +98,7 @@ $ ctest --test-dir build -j 10
The test suite should report: The test suite should report:
``` ```
100% tests passed, 0 tests failed out of <number of tests> 100% tests passed, 0 tests failed out of 98
``` ```
#### Add tests #### Add tests
@@ -111,8 +111,6 @@ context which existing file needs to be extended, and only very few cases requir
When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue. When fixing a bug, edit `unit-regression3.cpp` and add a section referencing the fixed issue.
`unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the `unit-regression2.cpp` holds the older tests; the two files exist because a single one grew large enough for the
MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing the larger one. MinGW linker to fail relocating it, so please keep adding to the smaller file rather than growing the larger one.
Tests that call `sax_parse` go into `unit-sax_parse.cpp` rather than into a large test file: every call instantiates
the parser and the binary reader that recover from errors, which grows a test file considerably.
#### Exceptions #### Exceptions
@@ -151,17 +149,14 @@ make build -C docs/mkdocs # strict build: fails on broken links, anchor
make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js) make check_mermaid -C docs/mkdocs # checks the Mermaid diagrams (requires Node.js)
``` ```
The search index of the docset is generated from [`mkdocs.yml`](https://github.com/nlohmann/json/blob/develop/docs/mkdocs/mkdocs.yml) A new API page also needs an entry in [`docs/docset/docSet.sql`](https://github.com/nlohmann/json/blob/develop/docs/docset/docSet.sql),
and each page's title (H1) and declaration by the search index of the docset; `make build` reports missing entries.
[`docs/docset/generate_docset.py`](https://github.com/nlohmann/json/blob/develop/docs/docset/generate_docset.py);
`make build` reports API pages that cannot be classified.
### Amalgamate the source code ### Amalgamate the source code
The single-header files The single-header files
[`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp), [`single_include/nlohmann/json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) and
[`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp), and [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp)
[`single_include/nlohmann/json_literals.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_literals.hpp)
are **generated** from the source files in the are **generated** from the source files in the
[`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). **Do not** edit the [`include/nlohmann` directory](https://github.com/nlohmann/json/tree/develop/include/nlohmann). **Do not** edit the
files directly; instead, modify the include/nlohmann sources and regenerate the files by executing: files directly; instead, modify the include/nlohmann sources and regenerate the files by executing:
@@ -184,7 +179,7 @@ The amalgamation check in CI fails if any of these generated files is out of dat
## Recommended documentation ## Recommended documentation
- The library’s [README file](https://github.com/nlohmann/json/blob/develop/README.md) is an excellent starting point to - The library’s [README file](https://github.com/nlohmann/json/blob/master/README.md) is an excellent starting point to
understand its functionality. understand its functionality.
- The [documentation page](https://json.nlohmann.me) is the reference documentation of the library. - The [documentation page](https://json.nlohmann.me) is the reference documentation of the library.
- [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) is the reference for the JavaScript Object Notation (JSON) - [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) is the reference for the JavaScript Object Notation (JSON)
@@ -223,9 +218,9 @@ feature macros then allows users to test their code against the library in the n
### Break C++11 language conformance ### Break C++11 language conformance
This library is designed to work with C++11 and later. This means that any This library is designed to work with C++11 and later. This means that any
[supported C++11 compiler](https://github.com/nlohmann/json/blob/develop/README.md#supported-compilers) should compile [supported C++11 compiler](https://github.com/nlohmann/json/blob/master/README.md#supported-compilers) should compile
the library without problems. Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual the library without problems. Some compilers like GCC 4.7 (and earlier), Clang 3.3 (and earlier), or Microsoft Visual
C++ 2013 and earlier are known not to work due to missing or incomplete C++11 support. Studio 13.0 and earlier are known not to work due to missing or incomplete C++11 support.
Please do not add features that do not work with the mentioned supported compilers. Please guard features from C++14 and Please do not add features that do not work with the mentioned supported compilers. Please guard features from C++14 and
later against the respective [`JSON_HAS_CPP_14`](https://json.nlohmann.me/api/macros/json_has_cpp_11/) macros. later against the respective [`JSON_HAS_CPP_14`](https://json.nlohmann.me/api/macros/json_has_cpp_11/) macros.
@@ -241,11 +236,12 @@ extension.
The following areas really need contribution and are always welcomed: The following areas really need contribution and are always welcomed:
- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the - Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the
bleeding-edge versions of Clang. bleeding-edge versions Clang.
- Improving the efficiency of the **JSON parser**. The current parser is a hand-written iterative parser that keeps an - Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser
explicit stack instead of recursing, with hand-coded string handling. Speeding up the lexer or the value construction with hand-coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That
would be appreciated. Parser generators like Bison or ANTLR do not play nice with single-header files -- I really said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep
would like to keep the parser inside the `json.hpp` header. the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for
parsing.
- Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency - Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency
is not everything, speed and memory consumption are very important characteristics for C++ developers, so having is not everything, speed and memory consumption are very important characteristics for C++ developers, so having
proper comparisons would be interesting. proper comparisons would be interesting.
+2 -2
View File
@@ -52,7 +52,7 @@ body:
If possible, provide a small and self-contained example that triggers If possible, provide a small and self-contained example that triggers
the bug. Please understand that we cannot analyze and debug large code the bug. Please understand that we cannot analyze and debug large code
bases. Please do not paste screenshots here. bases. Please do not paste screenshots here.
render: cpp render: Shell
- type: textarea - type: textarea
id: output id: output
attributes: attributes:
@@ -79,7 +79,7 @@ body:
description: > description: >
Which version of the library did you use? If it is a released version, Which version of the library did you use? If it is a released version,
please enter the version number (e.g., 3.12.0). Otherwise, please enter please enter the version number (e.g., 3.12.0). Otherwise, please enter
the commit hash. If you got the library from another source than the the commit hash. If you got the library from another source as the
GitHub repository (e.g., via a package manager), please also state GitHub repository (e.g., via a package manager), please also state
this. this.
validations: validations:
-11
View File
@@ -21,17 +21,6 @@ labels:
files: files:
- "cmake/.*" - "cmake/.*"
# other build systems
- label: "CMake"
files:
- "(meson\\.build|meson_options\\.txt|BUILD\\.bazel|MODULE\\.bazel|Package\\.swift)"
- label: "documentation"
files:
- ".github/ISSUE_TEMPLATE/.*"
- ".github/config\\.yml"
- ".github/(CONTRIBUTING|SECURITY|CODE_OF_CONDUCT|PULL_REQUEST_TEMPLATE)\\.md"
- label: "CI" - label: "CI"
files: files:
- ".github/workflows/.*" - ".github/workflows/.*"
+13 -7
View File
@@ -83,7 +83,8 @@ jobs:
# byte for byte), so there is nothing to gain from checking out a # byte for byte), so there is nothing to gain from checking out a
# separate copy, and doing so would make this step fail on a PR # separate copy, and doing so would make this step fail on a PR
# that adds support for a new dispatch table until that PR itself # that adds support for a new dispatch table until that PR itself
# merges to develop. # merges to develop, the same way generate_natvis.py's --version
# requirement briefly did.
TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX) TMPDIR=$(mktemp -d ./macro_builder_check.XXXXXX)
c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder" c++ -std=c++11 tools/macro_builder/main.cpp -o "$TMPDIR/macro_builder"
"$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp" "$TMPDIR/macro_builder" > "$TMPDIR/paste.hpp"
@@ -116,6 +117,7 @@ jobs:
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s . python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s .
python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s . python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s .
cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp
# the header list of the Bazel "json" target must match the files in include/ # the header list of the Bazel "json" target must match the files in include/
cmake -P cmake/scripts/gen_bazel_build_file.cmake cmake -P cmake/scripts/gen_bazel_build_file.cmake
@@ -136,13 +138,17 @@ jobs:
${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \ ${{ github.workspace }}/venv/bin/astyle --project=tools/astyle/.astylerc --suffix=none --quiet \
$(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort) $(find $SOURCE_DIRS -type f \( -name '*.hpp' -o -name '*.cpp' -o -name '*.cu' \) -not -path 'tests/thirdparty/*' -not -path 'tests/abi/include/nlohmann/*' | sort)
# json_literals.hpp is copied verbatim from the (now formatted) include/
# directory, like `make amalgamate` does
cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp
- name: Regenerate nlohmann_json.natvis - name: Regenerate nlohmann_json.natvis
run: | run: |
cd $MAIN_DIR cd $MAIN_DIR
# Pass --version explicitly so this step also works with the tool
# copy from develop before this repository's own generate_natvis.py
# learns to derive the version itself: the older script requires
# --version, and the newer one accepts it as an explicit override.
ABI_MACROS=include/nlohmann/detail/abi_macros.hpp
VERSION_MAJOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MAJOR' $ABI_MACROS | grep -o '[0-9]\+')
VERSION_MINOR=$(grep -m1 'define NLOHMANN_JSON_VERSION_MINOR' $ABI_MACROS | grep -o '[0-9]\+')
VERSION_PATCH=$(grep -m1 'define NLOHMANN_JSON_VERSION_PATCH' $ABI_MACROS | grep -o '[0-9]\+')
# The template is what this check verifies the committed natvis # The template is what this check verifies the committed natvis
# file against, so take it from this PR rather than from the # file against, so take it from this PR rather than from the
# develop checkout: generate_natvis.py loads the template from its # develop checkout: generate_natvis.py loads the template from its
@@ -150,7 +156,7 @@ jobs:
# template could never pass. Like the macro_builder step above, # template could never pass. Like the macro_builder step above,
# this only renders PR content in a job without credentials. # this only renders PR content in a job without credentials.
cp tools/generate_natvis/nlohmann_json.natvis.j2 $NATVIS_TOOL_DIR/ cp tools/generate_natvis/nlohmann_json.natvis.j2 $NATVIS_TOOL_DIR/
python3 $NATVIS_TOOL_DIR/generate_natvis.py $MAIN_DIR python3 $NATVIS_TOOL_DIR/generate_natvis.py --version "$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" $MAIN_DIR
- name: Build patch and check for differences - name: Build patch and check for differences
id: diff id: diff
@@ -159,7 +165,7 @@ jobs:
mkdir -p ${{ github.workspace }}/patch mkdir -p ${{ github.workspace }}/patch
git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch git diff --patch --no-color > ${{ github.workspace }}/patch/amalgamation.patch
if [ -s ${{ github.workspace }}/patch/amalgamation.patch ]; then if [ -s ${{ github.workspace }}/patch/amalgamation.patch ]; then
echo "The source code has not been amalgamated/formatted correctly, or BUILD.bazel, nlohmann_json.natvis, or the macro_builder tables in macro_scope.hpp are out of date ('make amalgamate BUILD.bazel natvis' fixes all but the tables, see tools/macro_builder/README.md). Diff:" echo "The source code has not been amalgamated/formatted correctly or BUILD.bazel is out of date. Diff:"
cat ${{ github.workspace }}/patch/amalgamation.patch cat ${{ github.workspace }}/patch/amalgamation.patch
echo "has_diff=true" >> "$GITHUB_OUTPUT" echo "has_diff=true" >> "$GITHUB_OUTPUT"
else else
@@ -96,7 +96,7 @@ jobs:
issue_number: issue_number, issue_number: issue_number,
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: '## 🔴 Amalgamation check failed! 🔴\nThe source code has not been amalgamated and/or formatted correctly, or `BUILD.bazel`, `nlohmann_json.natvis`, or the macro_builder tables in `macro_scope.hpp` are out of date. `make amalgamate BUILD.bazel natvis` fixes all but the tables (see `tools/macro_builder/README.md`).' body: '## 🔴 Amalgamation check failed! 🔴\nThe source code has not been amalgamated and/or formatted correctly, or `BUILD.bazel` is out of date.'
+ (hasPatch ? '\n\n📎 A ready-to-apply patch is attached to the [failed workflow run](' + runUrl + ') as the `amalgamation-patch` artifact.' + (hasPatch ? '\n\n📎 A ready-to-apply patch is attached to the [failed workflow run](' + runUrl + ') as the `amalgamation-patch` artifact.'
+ ' Download it, then apply it locally from the repository root with:' + ' Download it, then apply it locally from the repository root with:'
+ '\n\n```shell\ngit apply amalgamation.patch\n```\n\n' + '\n\n```shell\ngit apply amalgamation.patch\n```\n\n'
@@ -15,7 +15,6 @@ on:
- .github/SECURITY.md - .github/SECURITY.md
- cmake/clang_flags.cmake - cmake/clang_flags.cmake
- cmake/gcc_flags.cmake - cmake/gcc_flags.cmake
- include/nlohmann/detail/abi_macros.hpp
- tests/fmt_formatter/project/main.cpp - tests/fmt_formatter/project/main.cpp
- tools/astyle/.astylerc - tools/astyle/.astylerc
workflow_dispatch: workflow_dispatch:
+2 -3
View File
@@ -109,7 +109,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
target: [ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind] target: [ci_test_amalgamation, ci_test_single_header, ci_cppcheck, ci_cpplint, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_reuse_compliance, ci_test_valgrind]
steps: steps:
- name: Harden Runner - name: Harden Runner
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
@@ -117,7 +117,6 @@ jobs:
egress-policy: audit egress-policy: audit
- name: Install Valgrind - name: Install Valgrind
if: matrix.target == 'ci_test_valgrind'
run: sudo apt-get update ; sudo apt-get install -y valgrind run: sudo apt-get update ; sudo apt-get install -y valgrind
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: with:
@@ -280,7 +279,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Get latest CMake and ninja - name: Get latest CMake and ninja
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2 uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
- name: Disable filesystem support (std::filesystem is unusable) for clang 7 / 8 / 9 / 10 - name: Set env FORCE_STDCPPFS_FLAG for clang 7 / 8 / 9 / 10
run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV" run: echo "JSON_FORCED_GLOBAL_COMPILE_OPTIONS=-DJSON_HAS_FILESYSTEM=0;-DJSON_HAS_EXPERIMENTAL_FILESYSTEM=0" >> "$GITHUB_ENV"
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }} if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
- name: Run CMake - name: Run CMake
+2 -11
View File
@@ -166,13 +166,6 @@ jobs:
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash # link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
# before doctest prints its first line - 39 of 102 tests on clang 18. # before doctest prints its first line - 39 of 102 tests on clang 18.
# Keep the objects small by splitting the test files instead. # Keep the objects small by splitting the test files instead.
# Objects with more than 65535 sections fail to link with "relocation
# truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'": the MinGW
# linker stores the section an associative COMDAT section belongs to in
# 16 bits (x_associated in binutils' include/coff/internal.h), so it
# discards the jump tables of inline functions together with the wrong
# function. Each basic_json specialization a test file instantiates adds
# many sections, so test a second one in a file of its own.
- name: Run CMake - name: Run CMake
run: cmake -S . -B build ^ run: cmake -S . -B build ^
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^ -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
@@ -187,7 +180,7 @@ jobs:
- name: Test - name: Test
run: cd build ; ctest -j 10 -C Debug --output-on-failure run: cd build ; ctest -j 10 -C Debug --output-on-failure
clang-cl: clang-cl-12:
runs-on: windows-2022 runs-on: windows-2022
strategy: strategy:
matrix: matrix:
@@ -217,10 +210,8 @@ jobs:
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2 uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
with: with:
cmakeVersion: 4.3.4 cmakeVersion: 4.3.4
# CMAKE_CXX_FLAGS replaces CMake's MSVC defaults (which include /EHsc), and
# ci_module_cpp20 forwards it to the module build, so list /EHsc explicitly
- name: Run CMake (Debug) - name: Run CMake (Debug)
run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_CI=ON -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /EHsc /W4 /WX" run: cmake -S . -B build -G "Visual Studio 17 2022" -DJSON_CI=ON -DCMAKE_CXX_FLAGS="/permissive- /std:c++latest /utf-8 /W4 /WX"
- name: Build - name: Build
run: cmake --build build --config Debug --target ci_module_cpp20 run: cmake --build build --config Debug --target ci_module_cpp20
+1 -14
View File
@@ -27,6 +27,7 @@
/docs/docset/JSON_for_Modern_C++.tgz /docs/docset/JSON_for_Modern_C++.tgz
/docs/docset/docSet.dsidx /docs/docset/docSet.dsidx
/docs/mkdocs/.cache/ /docs/mkdocs/.cache/
/docs/mkdocs/docs/__pycache__/
/docs/mkdocs/hooks/__pycache__/ /docs/mkdocs/hooks/__pycache__/
/docs/mkdocs/scripts/mermaid/node_modules/ /docs/mkdocs/scripts/mermaid/node_modules/
/docs/mkdocs/site/ /docs/mkdocs/site/
@@ -46,17 +47,3 @@ nlohmann_json.spdx
# Bazel-related # Bazel-related
MODULE.bazel.lock MODULE.bazel.lock
# backups left by `make check-amalgamation` or the ci_test_amalgamation target if they are interrupted
/BUILD.bazel~
/nlohmann_json.natvis~
/single_include/nlohmann/*.hpp~
/venv_astyle/
# release outputs of `make release`
/include.zip
/include.zip.asc
/json.tar.xz
/json.tar.xz.asc
/release_files/
/single_include/nlohmann/*.asc
-1
View File
@@ -81,7 +81,6 @@ cc_library(
hdrs = [ hdrs = [
"single_include/nlohmann/json.hpp", "single_include/nlohmann/json.hpp",
"single_include/nlohmann/json_fwd.hpp", "single_include/nlohmann/json_fwd.hpp",
"single_include/nlohmann/json_literals.hpp",
], ],
includes = ["single_include"], includes = ["single_include"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
+1 -1
View File
@@ -8,7 +8,7 @@ authors:
website: https://nlohmann.me website: https://nlohmann.me
title: "JSON for Modern C++" title: "JSON for Modern C++"
version: 3.12.0 version: 3.12.0
date-released: 2025-04-11 date-released: 2025-04-07
license: MIT license: MIT
repository-code: "https://github.com/nlohmann/json" repository-code: "https://github.com/nlohmann/json"
url: https://json.nlohmann.me url: https://json.nlohmann.me
+1 -1
View File
@@ -30,7 +30,7 @@ endif ()
option(NLOHMANN_JSON_BUILD_MODULES "Build C++ modules support" OFF) option(NLOHMANN_JSON_BUILD_MODULES "Build C++ modules support" OFF)
if(NLOHMANN_JSON_BUILD_MODULES) if(NLOHMANN_JSON_BUILD_MODULES)
if(NOT CMAKE_VERSION VERSION_LESS 3.28) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
message(STATUS "Building nlohmann.json C++ module") message(STATUS "Building nlohmann.json C++ module")
add_subdirectory(src/modules) add_subdirectory(src/modules)
else() else()
+4 -7
View File
@@ -14,7 +14,7 @@ This file describes the source for supporting files; that is, files that are not
The [GitHub Actions](https://docs.github.com/en/actions) workflows that build, test, and analyze the library. Each file in this folder defines one workflow: The [GitHub Actions](https://docs.github.com/en/actions) workflows that build, test, and analyze the library. Each file in this folder defines one workflow:
- `ubuntu.yml`, `macos.yml`, `windows.yml` — build and run the test suite on Linux, macOS, and Windows. - `ubuntu.yml`, `macos.yml`, `windows.yml` — build and run the test suite on Linux, macOS, and Windows.
- `check_amalgamation.yml` — verify on pull requests and pushes to `develop` that the generated files (the single-header amalgamation in `single_include`, `BUILD.bazel`, and `nlohmann_json.natvis`) are up to date. - `check_amalgamation.yml` — verify that the single-header amalgamation in `single_include` is up to date on pull requests.
- `comment_check_amalgamation.yml` — comment on a pull request when the amalgamation check failed. - `comment_check_amalgamation.yml` — comment on a pull request when the amalgamation check failed.
- `cifuzz.yml` — run short fuzzing sessions via [OSS-Fuzz CIFuzz](https://google.github.io/oss-fuzz/getting-started/continuous-integration/) on pull requests. - `cifuzz.yml` — run short fuzzing sessions via [OSS-Fuzz CIFuzz](https://google.github.io/oss-fuzz/getting-started/continuous-integration/) on pull requests.
- `codeql-analysis.yml` — run [CodeQL](https://codeql.github.com) code scanning. - `codeql-analysis.yml` — run [CodeQL](https://codeql.github.com) code scanning.
@@ -24,9 +24,7 @@ The [GitHub Actions](https://docs.github.com/en/actions) workflows that build, t
- `dependency-review.yml` — scan dependency changes in pull requests for known vulnerabilities. - `dependency-review.yml` — scan dependency changes in pull requests for known vulnerabilities.
- `labeler.yml` — the "Pull Request Labeler" workflow (see `.github/labeler.yml`). - `labeler.yml` — the "Pull Request Labeler" workflow (see `.github/labeler.yml`).
- `stale.yml` — comment on and close stale issues and pull requests. - `stale.yml` — comment on and close stale issues and pull requests.
- `publish_documentation.yml` — build and publish the documentation on pushes to the `develop` branch that touch the documentation sources (or the files it embeds). - `publish_documentation.yml` — build and publish the documentation on every merge to the `develop` branch.
- `check_docs_links.yml` — check the links of the documentation weekly, as external links can break without any change in this repository.
- `cancel_closed_pr_runs.yml` — cancel unfinished workflow runs of a pull request once it is closed or merged.
Further documentation: Further documentation:
@@ -268,9 +266,8 @@ meson setup builddir
ninja -C builddir ninja -C builddir
``` ```
`meson_options.txt` defines the options, which mirror the names of the CMake options that change the library's target (for example, `meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example,
`-DDiagnostics=true`); their defaults may differ (for example, `MultipleHeaders` defaults to `false` in Meson, while `-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
`JSON_MultipleHeaders` is `ON` in CMake). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files ([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files
stay in sync with the CMake options. stay in sync with the CMake options.
-1
View File
@@ -1,6 +1,5 @@
module( module(
name = "nlohmann_json", name = "nlohmann_json",
version = "3.12.0",
compatibility_level = 1, compatibility_level = 1,
) )
+26 -37
View File
@@ -1,4 +1,4 @@
.PHONY: all amalgamate check-amalgamation check_build_options pretty install_astyle clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check run_benchmarks pvs_studio serve_header reuse spdx .PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check check_build_options
########################################################################## ##########################################################################
# configuration # configuration
@@ -41,18 +41,12 @@ all:
@echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources" @echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources"
@echo "ChangeLog.md - generate ChangeLog file" @echo "ChangeLog.md - generate ChangeLog file"
@echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date" @echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date"
@echo "check_build_options - check that the Meson build and the pkg-config files offer the options of the CMake target"
@echo "clean - remove built files" @echo "clean - remove built files"
@echo "(fuzzers) - see tests/fuzzing.md, or run 'make -C tests fuzzers' to build the AFL fuzzers" @echo "fuzzing - see tests/fuzzing.md for how to build and run the fuzzers"
@echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output" @echo "macro_builder_check - check that macro_scope.hpp matches tools/macro_builder's output"
@echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version" @echo "natvis - regenerate nlohmann_json.natvis from the current ABI tags and version"
@echo "pretty - beautify code with Artistic Style" @echo "pretty - beautify code with Artistic Style"
@echo "pvs_studio - run the PVS-Studio static analyzer and open the report"
@echo "release - create the release files (include.zip, json.tar.xz, single headers, signatures, hashes)"
@echo "reuse - annotate the source files with REUSE license headers and lint them"
@echo "run_benchmarks - build and run benchmarks" @echo "run_benchmarks - build and run benchmarks"
@echo "serve_header - serve the single header, re-amalgamated on demand (see tools/serve_header)"
@echo "spdx - generate the SPDX software bill of materials nlohmann_json.spdx"
@echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp" @echo "update_hedley - download Hedley and regenerate hedley.hpp / hedley_undef.hpp"
@echo "update_hedley_undef - rebuild hedley_undef.hpp from the JSON_HEDLEY_* #define names in hedley.hpp" @echo "update_hedley_undef - rebuild hedley_undef.hpp from the JSON_HEDLEY_* #define names in hedley.hpp"
@@ -114,18 +108,13 @@ $(AMALGAMATED_FWD_FILE): $(SRCS)
$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp
cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE) cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE)
NATVIS_VENV=tools/generate_natvis/venv
# regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp # regenerate nlohmann_json.natvis from the ABI tags and version in include/nlohmann/detail/abi_macros.hpp
# (the dependencies are installed into a virtual environment first)
natvis: natvis:
@test -d $(NATVIS_VENV) || python3 -mvenv $(NATVIS_VENV) python3 tools/generate_natvis/generate_natvis.py .
@$(NATVIS_VENV)/bin/python3 -c 'import jinja2' 2>/dev/null || $(NATVIS_VENV)/bin/pip3 install --quiet -r tools/generate_natvis/requirements.txt
$(NATVIS_VENV)/bin/python3 tools/generate_natvis/generate_natvis.py .
# regenerate the two tools/macro_builder blocks of $(MACRO_SCOPE_HPP) (see its README.md) and diff against the # regenerate the two tools/macro_builder blocks of $(MACRO_SCOPE_HPP) (see its README.md) and diff against the
# checked-in header; phony, because it never writes $(MACRO_SCOPE_HPP) itself # checked-in header; phony, because it never writes $(MACRO_SCOPE_HPP) itself
macro_builder_check: install_astyle macro_builder_check:
@set -e; \ @set -e; \
TMPDIR=$$(mktemp -d ./macro_builder_check.XXXXXX); \ TMPDIR=$$(mktemp -d ./macro_builder_check.XXXXXX); \
trap 'rm -rf "$$TMPDIR"' EXIT; \ trap 'rm -rf "$$TMPDIR"' EXIT; \
@@ -142,27 +131,27 @@ macro_builder_check: install_astyle
check_build_options: check_build_options:
python3 tools/check_build_options/check_build_options.py . python3 tools/check_build_options/check_build_options.py .
# check if the single headers have been amalgamated from the nlohmann sources and if BUILD.bazel and # check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
# nlohmann_json.natvis are up to date; all checks are run, and the checked-in files are always restored
check-amalgamation: check-amalgamation:
@status=0; \ @mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
banner() { printf '===================================================================\n %s\n===================================================================\n' "$$1"; }; \ @mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~
for FILE in $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel nlohmann_json.natvis; do \ @mv $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~
mv "$$FILE" "$$FILE~" || exit 1; \ @$(MAKE) amalgamate
done; \ @diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false)
$(MAKE) amalgamate || status=1; \ @diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false)
$(MAKE) BUILD.bazel || status=1; \ @diff $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) ; false)
$(MAKE) natvis || status=1; \ @mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE)
for FILE in $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE); do \ @mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE)
diff "$$FILE" "$$FILE~" || { banner "$$FILE is out of date! Amalgamation required! Please read the contribution guidelines in file .github/CONTRIBUTING.md."; status=1; }; \ @mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE)
done; \ @mv BUILD.bazel BUILD.bazel~
diff BUILD.bazel BUILD.bazel~ || { banner "BUILD.bazel is out of date! Please run 'make BUILD.bazel'."; status=1; }; \ @$(MAKE) BUILD.bazel
diff nlohmann_json.natvis nlohmann_json.natvis~ || { banner "nlohmann_json.natvis is out of date! Please run 'make natvis'."; status=1; }; \ @diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false)
for FILE in $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel nlohmann_json.natvis; do \ @mv BUILD.bazel~ BUILD.bazel
mv "$$FILE~" "$$FILE"; \ @mv nlohmann_json.natvis nlohmann_json.natvis~
done; \ @$(MAKE) natvis
$(MAKE) macro_builder_check || status=1; \ @diff nlohmann_json.natvis nlohmann_json.natvis~ || (echo "===================================================================\n nlohmann_json.natvis is out of date! Please run 'make natvis'.\n===================================================================" ; mv nlohmann_json.natvis~ nlohmann_json.natvis ; false)
exit $$status @mv nlohmann_json.natvis~ nlohmann_json.natvis
@$(MAKE) macro_builder_check
# generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild # generate the Bazel BUILD file; phony, because a removed header would not trigger a rebuild
BUILD.bazel: BUILD.bazel:
@@ -223,8 +212,8 @@ release: include.zip json.tar.xz
# clean up # clean up
clean: clean:
rm -fr *.dSYM tests/*.dSYM tests/parse_*_fuzzer rm -fr fuzz fuzz-testing *.dSYM tests/*.dSYM
rm -fr cmake-build-benchmarks cmake-build-pvs-studio release_files rm -fr cmake-build-benchmarks fuzz-testing cmake-build-pvs-studio release_files
$(MAKE) clean -Cdocs $(MAKE) clean -Cdocs
+448 -426
View File
@@ -48,7 +48,6 @@
- [Pkg-config](#pkg-config) - [Pkg-config](#pkg-config)
- [License](#license) - [License](#license)
- [Contact](#contact) - [Contact](#contact)
- [Security](#security)
- [Thanks](#thanks) - [Thanks](#thanks)
- [Used third-party tools](#used-third-party-tools) - [Used third-party tools](#used-third-party-tools)
- [Notes](#notes) - [Notes](#notes)
@@ -113,9 +112,9 @@ There is also a [**docset**](https://github.com/Kapeli/Dash-User-Contributions/t
## Quick reference ## Quick reference
- **Constructors**: [basic_json](https://json.nlohmann.me/api/basic_json/basic_json), [array](https://json.nlohmann.me/api/basic_json/array), [binary](https://json.nlohmann.me/api/basic_json/binary), [object](https://json.nlohmann.me/api/basic_json/object) - **Constructors** [basic_json](https://json.nlohmann.me/api/basic_json/basic_json), [array](https://json.nlohmann.me/api/basic_json/array), [binary](https://json.nlohmann.me/api/basic_json/binary), [object](https://json.nlohmann.me/api/basic_json/object)
- **Object inspection**: [type](https://json.nlohmann.me/api/basic_json/type), [operator value_t](https://json.nlohmann.me/api/basic_json/operator_value_t), [type_name](https://json.nlohmann.me/api/basic_json/type_name), [is_primitive](https://json.nlohmann.me/api/basic_json/is_primitive), [is_structured](https://json.nlohmann.me/api/basic_json/is_structured), [is_null](https://json.nlohmann.me/api/basic_json/is_null), [is_boolean](https://json.nlohmann.me/api/basic_json/is_boolean), [is_number](https://json.nlohmann.me/api/basic_json/is_number), [is_number_integer](https://json.nlohmann.me/api/basic_json/is_number_integer), [is_number_unsigned](https://json.nlohmann.me/api/basic_json/is_number_unsigned), [is_number_float](https://json.nlohmann.me/api/basic_json/is_number_float), [is_object](https://json.nlohmann.me/api/basic_json/is_object), [is_array](https://json.nlohmann.me/api/basic_json/is_array), [is_string](https://json.nlohmann.me/api/basic_json/is_string), [is_binary](https://json.nlohmann.me/api/basic_json/is_binary), [is_discarded](https://json.nlohmann.me/api/basic_json/is_discarded) - **Object inspection**: [type](https://json.nlohmann.me/api/basic_json/type), [operator value_t](https://json.nlohmann.me/api/basic_json/operator_value_t), [type_name](https://json.nlohmann.me/api/basic_json/type_name), [is_primitive](https://json.nlohmann.me/api/basic_json/is_primitive), [is_structured](https://json.nlohmann.me/api/basic_json/is_structured), [is_null](https://json.nlohmann.me/api/basic_json/is_null), [is_boolean](https://json.nlohmann.me/api/basic_json/is_boolean), [is_number](https://json.nlohmann.me/api/basic_json/is_number), [is_number_integer](https://json.nlohmann.me/api/basic_json/is_number_integer), [is_number_unsigned](https://json.nlohmann.me/api/basic_json/is_number_unsigned), [is_number_float](https://json.nlohmann.me/api/basic_json/is_number_float), [is_object](https://json.nlohmann.me/api/basic_json/is_object), [is_array](https://json.nlohmann.me/api/basic_json/is_array), [is_string](https://json.nlohmann.me/api/basic_json/is_string), [is_binary](https://json.nlohmann.me/api/basic_json/is_binary), [is_discarded](https://json.nlohmann.me/api/basic_json/is_discarded)
- **Value access**: [get](https://json.nlohmann.me/api/basic_json/get), [get_to](https://json.nlohmann.me/api/basic_json/get_to), [get_ptr](https://json.nlohmann.me/api/basic_json/get_ptr), [get_ref](https://json.nlohmann.me/api/basic_json/get_ref), [operator ValueType](https://json.nlohmann.me/api/basic_json/operator_ValueType), [get_binary](https://json.nlohmann.me/api/basic_json/get_binary) - **Value access**; [get](https://json.nlohmann.me/api/basic_json/get), [get_to](https://json.nlohmann.me/api/basic_json/get_to), [get_ptr](https://json.nlohmann.me/api/basic_json/get_ptr), [get_ref](https://json.nlohmann.me/api/basic_json/get_ref), [operator ValueType](https://json.nlohmann.me/api/basic_json/operator_ValueType), [get_binary](https://json.nlohmann.me/api/basic_json/get_binary)
- **Element access**: [at](https://json.nlohmann.me/api/basic_json/at), [operator[]](https://json.nlohmann.me/api/basic_json/operator[]), [value](https://json.nlohmann.me/api/basic_json/value), [front](https://json.nlohmann.me/api/basic_json/front), [back](https://json.nlohmann.me/api/basic_json/back) - **Element access**: [at](https://json.nlohmann.me/api/basic_json/at), [operator[]](https://json.nlohmann.me/api/basic_json/operator[]), [value](https://json.nlohmann.me/api/basic_json/value), [front](https://json.nlohmann.me/api/basic_json/front), [back](https://json.nlohmann.me/api/basic_json/back)
- **Lookup**: [find](https://json.nlohmann.me/api/basic_json/find), [count](https://json.nlohmann.me/api/basic_json/count), [contains](https://json.nlohmann.me/api/basic_json/contains) - **Lookup**: [find](https://json.nlohmann.me/api/basic_json/find), [count](https://json.nlohmann.me/api/basic_json/count), [contains](https://json.nlohmann.me/api/basic_json/contains)
- **Iterators**: [begin](https://json.nlohmann.me/api/basic_json/begin), [cbegin](https://json.nlohmann.me/api/basic_json/cbegin), [end](https://json.nlohmann.me/api/basic_json/end), [cend](https://json.nlohmann.me/api/basic_json/cend), [rbegin](https://json.nlohmann.me/api/basic_json/rbegin), [rend](https://json.nlohmann.me/api/basic_json/rend), [crbegin](https://json.nlohmann.me/api/basic_json/crbegin), [crend](https://json.nlohmann.me/api/basic_json/crend), [items](https://json.nlohmann.me/api/basic_json/items) - **Iterators**: [begin](https://json.nlohmann.me/api/basic_json/begin), [cbegin](https://json.nlohmann.me/api/basic_json/cbegin), [end](https://json.nlohmann.me/api/basic_json/end), [cend](https://json.nlohmann.me/api/basic_json/cend), [rbegin](https://json.nlohmann.me/api/basic_json/rbegin), [rend](https://json.nlohmann.me/api/basic_json/rend), [crbegin](https://json.nlohmann.me/api/basic_json/crbegin), [crend](https://json.nlohmann.me/api/basic_json/crend), [items](https://json.nlohmann.me/api/basic_json/items)
@@ -495,7 +494,7 @@ bool key(string_t& val);
bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex); bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
``` ```
The return value of each function determines whether parsing should proceed. For `parse_error`, returning `true` [recovers from the error](https://json.nlohmann.me/features/parsing/error_recovery/): the parser repairs the input and continues. The return value of each function determines whether parsing should proceed.
To implement your own SAX handler, proceed as follows: To implement your own SAX handler, proceed as follows:
@@ -503,7 +502,7 @@ To implement your own SAX handler, proceed as follows:
2. Create an object of your SAX interface class, e.g. `my_sax`. 2. Create an object of your SAX interface class, e.g. `my_sax`.
3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface. 3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
Note the `sax_parse` function only returns a `bool` indicating whether the input was parsed without errors and no SAX event returned `false`. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp). Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error -- it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
### STL-like access ### STL-like access
@@ -1185,7 +1184,7 @@ binary[1]; // 0xFE
binary.set_subtype(0x10); binary.set_subtype(0x10);
// serialize to MessagePack // serialize to MessagePack
auto msgpack = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE auto cbor = json::to_msgpack(j); // 0xD5 (fixext2), 0x10, 0xCA, 0xFE
``` ```
## Customers ## Customers
@@ -1221,7 +1220,17 @@ I would be happy to learn about other compilers/versions.
Please note: Please note:
- GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler. - GCC 4.8 has a bug [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824): multiline raw strings cannot be the arguments to macros. Don't use multiline raw strings directly in macros with this compiler.
- For GCC running on MinGW or Android, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler or C++ standard library itself: use a recent toolchain (for Android, a current [NDK](https://developer.android.com/ndk) with the LLVM C++ library `APP_STL := c++_shared`, and for MinGW, a distribution with a complete C++11 standard library). See [this discussion](https://github.com/nlohmann/json/issues/136) for MinGW and [this discussion](https://github.com/nlohmann/json/issues/219) for the old Android NDK using `APP_STL := gnustl_static`. - Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default.
```makefile
APP_STL := c++_shared
NDK_TOOLCHAIN_VERSION := clang3.6
APP_CPPFLAGS += -frtti -fexceptions
```
The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10.
- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod` or `strtof`) may occur. Note this is not an issue with the code, but rather with the compiler itself. On Android, see above to build with a newer environment. For MinGW, please refer to [this site](https://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219).
- Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case. - Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case.
@@ -1428,416 +1437,436 @@ I deeply appreciate the help of the following people.
12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual Studio. 12. [kepkin](https://github.com/kepkin) patiently pushed forward the support for Microsoft Visual Studio.
13. [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types. 13. [gregmarr](https://github.com/gregmarr) simplified the implementation of reverse iterators and helped with numerous hints and improvements. In particular, he pushed forward the implementation of user-defined types.
14. [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling. 14. [Caio Luppi](https://github.com/caiovlp) fixed a bug in the Unicode handling.
15. [dariomt](https://github.com/dariomt) fixed some typos in the examples and fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values. 15. [dariomt](https://github.com/dariomt) fixed some typos in the examples.
16. [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation. 16. [Daniel Frey](https://github.com/d-frey) cleaned up some pointers and implemented exception-safe memory allocation.
17. [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue. 17. [Colin Hirsch](https://github.com/ColinH) took care of a small namespace issue.
18. [Huu Nguyen](https://github.com/whoshuu) corrected a variable name in the documentation. 18. [Huu Nguyen](https://github.com/whoshuu) corrected a variable name in the documentation.
19. [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference. 19. [Silverweed](https://github.com/silverweed) overloaded `parse()` to accept an rvalue reference.
20. [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK. 20. [dariomt](https://github.com/dariomt) fixed a subtlety in MSVC type support and implemented the `get_ref()` function to get a reference to stored values.
21. [whackashoe](https://github.com/whackashoe) replaced a function that was marked as unsafe by Visual Studio. 21. [ZahlGraf](https://github.com/ZahlGraf) added a workaround that allows compilation using Android NDK.
22. [406345](https://github.com/406345) fixed two small warnings. 22. [whackashoe](https://github.com/whackashoe) replaced a function that was marked as unsafe by Visual Studio.
23. [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function. 23. [406345](https://github.com/406345) fixed two small warnings.
24. [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines. 24. [Glen Fernandes](https://github.com/glenfe) noted a potential portability problem in the `has_mapped_type` function.
25. [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers. 25. [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines.
26. [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file. 26. [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers.
27. [msm-](https://github.com/msm-) added support for American Fuzzy Lop. 27. [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file.
28. [Annihil](https://github.com/Annihil) fixed an example in the README file. 28. [msm-](https://github.com/msm-) added support for American Fuzzy Lop.
29. [Themercee](https://github.com/Themercee) noted a wrong URL in the README file. 29. [Annihil](https://github.com/Annihil) fixed an example in the README file.
30. [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`. 30. [Themercee](https://github.com/Themercee) noted a wrong URL in the README file.
31. [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212). 31. [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`.
32. [zewt](https://github.com/zewt) added useful notes to the README file about Android. 32. [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212).
33. [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake. 33. [zewt](https://github.com/zewt) added useful notes to the README file about Android.
34. [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files. 34. [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake.
35. [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal). 35. [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files.
36. [Mário Feroldi](https://github.com/thelostt) fixed a small typo. 36. [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal).
37. [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release. 37. [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
38. [Damien](https://github.com/dtoma) fixed one of the last conversion warnings. 38. [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
39. [Thomas Braun](https://github.com/t-b) fixed a warning in a test case and adjusted MSVC calls in the CI. 39. [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
40. [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks. 40. [Thomas Braun](https://github.com/t-b) fixed a warning in a test case and adjusted MSVC calls in the CI.
41. [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation. 41. [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks.
42. [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`. 42. [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
43. [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion. 43. [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
44. [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix. 44. [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
45. [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file. 45. [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix.
46. [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function. 46. [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
47. [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing. 47. [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
48. [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan. 48. [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
49. [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning and supported the implementation of user-defined types. 49. [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan.
50. [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check. 50. [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning.
51. [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one. 51. [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
52. [Daniel599](https://github.com/Daniel599) helped to get Travis to execute the tests with Clang's sanitizers and allowed to use more algorithms with the `items()` function. 52. [Bosswestfalen](https://github.com/Bosswestfalen) merged two iterator classes into a smaller one.
53. [Jonathan Lee](https://github.com/vjon) fixed an example in the README file. 53. [Daniel599](https://github.com/Daniel599) helped to get Travis to execute the tests with Clang's sanitizers.
54. [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types. 54. [Jonathan Lee](https://github.com/vjon) fixed an example in the README file.
55. [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio. 55. [gnzlbg](https://github.com/gnzlbg) supported the implementation of user-defined types.
56. [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example. 56. [Alexej Harm](https://github.com/qis) helped to get the user-defined types working with Visual Studio.
57. [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite. 57. [Jared Grubb](https://github.com/jaredgrubb) supported the implementation of user-defined types.
58. [ukhegg](https://github.com/ukhegg) proposed an improvement for the examples section. 58. [EnricoBilla](https://github.com/EnricoBilla) noted a typo in an example.
59. [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README. 59. [Martin Hořeňovský](https://github.com/horenmar) found a way for a 2x speedup for the compilation time of the test suite.
60. [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s. 60. [ukhegg](https://github.com/ukhegg) proposed an improvement for the examples section.
61. [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation. 61. [rswanson-ihi](https://github.com/rswanson-ihi) noted a typo in the README.
62. [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings. 62. [Mihai Stan](https://github.com/stanmihai4) fixed a bug in the comparison with `nullptr`s.
63. [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible. 63. [Tushar Maheshwari](https://github.com/tusharpm) added [cotire](https://github.com/sakra/cotire) support to speed up the compilation.
64. [ftillier](https://github.com/ftillier) fixed a compiler warning. 64. [TedLyngmo](https://github.com/TedLyngmo) noted a typo in the README, removed unnecessary bit arithmetic, and fixed some `-Weffc++` warnings.
65. [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped. 65. [Krzysztof Woś](https://github.com/krzysztofwos) made exceptions more visible.
66. [Fytch](https://github.com/Fytch) found a bug in the documentation. 66. [ftillier](https://github.com/ftillier) fixed a compiler warning.
67. [Jay Sistar](https://github.com/Type1J) implemented a Meson build description. 67. [tinloaf](https://github.com/tinloaf) made sure all pushed warnings are properly popped.
68. [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation. 68. [Fytch](https://github.com/Fytch) found a bug in the documentation.
69. [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager. 69. [Jay Sistar](https://github.com/Type1J) implemented a Meson build description.
70. [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`. 70. [Henry Lee](https://github.com/HenryRLee) fixed a warning in ICC and improved the iterator implementation.
71. [Mike Tzou](https://github.com/Chocobo1) fixed some typos. 71. [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager.
72. [amrcode](https://github.com/amrcode) noted misleading documentation about comparison of floats. 72. [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`.
73. [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`. 73. [Mike Tzou](https://github.com/Chocobo1) fixed some typos.
74. [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library. 74. [amrcode](https://github.com/amrcode) noted misleading documentation about comparison of floats.
75. [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists. 75. [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`.
76. [Greg Hurrell](https://github.com/wincent) fixed a typo. 76. [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library.
77. [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo. 77. [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists.
78. [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler. 78. [Greg Hurrell](https://github.com/wincent) fixed a typo.
79. [Markus Werle](https://github.com/daixtrose) fixed a typo. 79. [Dmitry Kukovinets](https://github.com/DmitryKuk) fixed a typo.
80. [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check. 80. [kbthomp1](https://github.com/kbthomp1) fixed an issue related to the Intel OSX compiler.
81. [Alex](https://github.com/leha-bot) noted an error in a code sample. 81. [Markus Werle](https://github.com/daixtrose) fixed a typo.
82. [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped to fix them. 82. [WebProdPP](https://github.com/WebProdPP) fixed a subtle error in a precondition check.
83. [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams. 83. [Alex](https://github.com/leha-bot) noted an error in a code sample.
84. [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error, fixed the compilation with MSVC 2015 in debug mode and fixed a warning. 84. [Tom de Geus](https://github.com/tdegeus) reported some warnings with ICC and helped to fix them.
85. [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings and avoided unnecessary string copies in `find()` and `count()`. 85. [Perry Kundert](https://github.com/pjkundert) simplified reading from input streams.
86. [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file. 86. [Sonu Lohani](https://github.com/sonulohani) fixed a small compilation error.
87. [pvleuven](https://github.com/pvleuven) helped to fix a warning in ICC. 87. [Jamie Seward](https://github.com/jseward) fixed all MSVC warnings.
88. [Pavel](https://github.com/crea7or) helped to fix some warnings in MSVC. 88. [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file.
89. [Mitja](https://github.com/Itja) fixed some typos. 89. [pvleuven](https://github.com/pvleuven) helped to fix a warning in ICC.
90. [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links. 90. [Pavel](https://github.com/crea7or) helped to fix some warnings in MSVC.
91. [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view and removed the dependency from `std::stringstream`. 91. [Jamie Seward](https://github.com/jseward) avoided unnecessary string copies in `find()` and `count()`.
92. [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings. 92. [Mitja](https://github.com/Itja) fixed some typos.
93. [Eren Okka](https://github.com/erengy) fixed some MSVC warnings. 93. [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links.
94. [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed. 94. [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view.
95. [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README. 95. [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
96. [zerodefect](https://github.com/zerodefect) fixed a compiler warning. 96. [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
97. [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior. 97. [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed.
98. [mark-99](https://github.com/mark-99) helped fix an ICC error. 98. [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README.
99. [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file. 99. [zerodefect](https://github.com/zerodefect) fixed a compiler warning.
100. [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings. 100. [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior.
101. [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager. 101. [mark-99](https://github.com/mark-99) helped fix an ICC error.
102. [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise. 102. [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file.
103. [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback. 103. [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings.
104. [agrianius](https://github.com/agrianius) allowed dumping JSON to an alternative string type and added code to use alternative string implementations. 104. [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager.
105. [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake. 105. [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise.
106. [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io). 106. [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback.
107. [Carlos O'Ryan](https://github.com/coryan) fixed a typo. 107. [agrianius](https://github.com/agrianius) allowed dumping JSON to an alternative string type.
108. [James Upjohn](https://github.com/jupjohn) fixed a version number in the compilers section. 108. [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake.
109. [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration. 109. [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io).
110. [Jan Schöppach](https://github.com/dns13) fixed a typo. 110. [Carlos O'Ryan](https://github.com/coryan) fixed a typo.
111. [martin-mfg](https://github.com/martin-mfg) fixed a typo. 111. [James Upjohn](https://github.com/jupjohn) fixed a version number in the compilers section.
112. [Julius Rakow](https://github.com/juliusrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com). 112. [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines and provided documentation for the CMake integration.
113. [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases. 113. [Jan Schöppach](https://github.com/dns13) fixed a typo.
114. [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library and fixed an issue with a double macro definition. 114. [martin-mfg](https://github.com/martin-mfg) fixed a typo.
115. [thyu](https://github.com/thyu) fixed a compiler warning. 115. [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`.
116. [David Guthrie](https://github.com/LEgregius) fixed a subtle compilation error with Clang 3.4.2. 116. [agrianius](https://github.com/agrianius) added code to use alternative string implementations.
117. [Dennis Fischer](https://github.com/dennisfischer) allowed to call `find_package` without installing the library. 117. [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function.
118. [Ben Berman](https://github.com/rivertam) made some error messages more understandable. 118. [Julius Rakow](https://github.com/juliusrakow) fixed the Meson include directory and fixed the links to [cppreference.com](https://cppreference.com).
119. [zakalibit](https://github.com/zakalibit) fixed a compilation problem with the Intel C++ compiler. 119. [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode.
120. [mandreyel](https://github.com/mandreyel) fixed a compilation problem. 120. [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases.
121. [Kostiantyn Ponomarenko](https://github.com/koponomarenko) added version and license information to the Meson build file. 121. [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library.
122. [Henry Schreiner](https://github.com/henryiii) added support for GCC 4.8. 122. [thyu](https://github.com/thyu) fixed a compiler warning.
123. [knilch](https://github.com/knilch0r) made sure the test suite does not stall when run in the wrong directory. 123. [David Guthrie](https://github.com/LEgregius) fixed a subtle compilation error with Clang 3.4.2.
124. [Antonio Borondo](https://github.com/antonioborondo) fixed an MSVC 2017 warning. 124. [Dennis Fischer](https://github.com/dennisfischer) allowed to call `find_package` without installing the library.
125. [Dan Gendreau](https://github.com/dgendreau) implemented the `NLOHMANN_JSON_SERIALIZE_ENUM` macro to quickly define an enum/JSON mapping. 125. [Hyeon Kim](https://github.com/simnalamburt) fixed an issue with a double macro definition.
126. [efp](https://github.com/efp) added line and column information to parse errors. 126. [Ben Berman](https://github.com/rivertam) made some error messages more understandable.
127. [julian-becker](https://github.com/julian-becker) added BSON support. 127. [zakalibit](https://github.com/zakalibit) fixed a compilation problem with the Intel C++ compiler.
128. [Pratik Chowdhury](https://github.com/pratikpc) added support for structured bindings. 128. [mandreyel](https://github.com/mandreyel) fixed a compilation problem.
129. [David Avedissian](https://github.com/davedissian) added support for Clang 5.0.1 (PS4 version). 129. [Kostiantyn Ponomarenko](https://github.com/koponomarenko) added version and license information to the Meson build file.
130. [Jonathan Dumaresq](https://github.com/dumarjo) implemented an input adapter to read from `FILE*`. 130. [Henry Schreiner](https://github.com/henryiii) added support for GCC 4.8.
131. [kjpus](https://github.com/kjpus) fixed a link in the documentation. 131. [knilch](https://github.com/knilch0r) made sure the test suite does not stall when run in the wrong directory.
132. [Manvendra Singh](https://github.com/manu-chroma) fixed a typo in the documentation. 132. [Antonio Borondo](https://github.com/antonioborondo) fixed an MSVC 2017 warning.
133. [ziggurat29](https://github.com/ziggurat29) fixed an MSVC warning. 133. [Dan Gendreau](https://github.com/dgendreau) implemented the `NLOHMANN_JSON_SERIALIZE_ENUM` macro to quickly define an enum/JSON mapping.
134. [Sylvain Corlay](https://github.com/SylvainCorlay) added code to avoid an issue with MSVC. 134. [efp](https://github.com/efp) added line and column information to parse errors.
135. [mefyl](https://github.com/mefyl) fixed a bug when JSON was parsed from an input stream. 135. [julian-becker](https://github.com/julian-becker) added BSON support.
136. [Millian Poquet](https://github.com/mpoquet) allowed to install the library via Meson. 136. [Pratik Chowdhury](https://github.com/pratikpc) added support for structured bindings.
137. [Michael Behrns-Miller](https://github.com/moodboom) found an issue with a missing namespace. 137. [David Avedissian](https://github.com/davedissian) added support for Clang 5.0.1 (PS4 version).
138. [Nasztanovics Ferenc](https://github.com/naszta) fixed a compilation issue with libc 2.12. 138. [Jonathan Dumaresq](https://github.com/dumarjo) implemented an input adapter to read from `FILE*`.
139. [Andreas Schwab](https://github.com/andreas-schwab) fixed the endian conversion. 139. [kjpus](https://github.com/kjpus) fixed a link in the documentation.
140. [Mark-Dunning](https://github.com/Mark-Dunning) fixed a warning in MSVC. 140. [Manvendra Singh](https://github.com/manu-chroma) fixed a typo in the documentation.
141. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) added `operator/` for JSON Pointers and fixed a compilation issue with MSVC. 141. [ziggurat29](https://github.com/ziggurat29) fixed an MSVC warning.
142. [John-Mark](https://github.com/johnmarkwayve) noted a missing header. 142. [Sylvain Corlay](https://github.com/SylvainCorlay) added code to avoid an issue with MSVC.
143. [Vitaly Zaitsev](https://github.com/xvitaly) fixed compilation with GCC 9.0. 143. [mefyl](https://github.com/mefyl) fixed a bug when JSON was parsed from an input stream.
144. [Laurent Stacul](https://github.com/stac47) fixed compilation with GCC 9.0. 144. [Millian Poquet](https://github.com/mpoquet) allowed to install the library via Meson.
145. [Ivor Wanders](https://github.com/iwanders) helped to reduce the CMake requirement to version 3.1 and fixed the examples to catch exception by `const&`. 145. [Michael Behrns-Miller](https://github.com/moodboom) found an issue with a missing namespace.
146. [njlr](https://github.com/njlr) updated the Buckaroo instructions. 146. [Nasztanovics Ferenc](https://github.com/naszta) fixed a compilation issue with libc 2.12.
147. [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS. 147. [Andreas Schwab](https://github.com/andreas-schwab) fixed the endian conversion.
148. [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function. 148. [Mark-Dunning](https://github.com/Mark-Dunning) fixed a warning in MSVC.
149. [past-due](https://github.com/past-due) suppressed an unfixable warning. 149. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) added `operator/` for JSON Pointers.
150. [Elvis Oric](https://github.com/elvisoric) improved Meson support. 150. [John-Mark](https://github.com/johnmarkwayve) noted a missing header.
151. [Matěj Plch](https://github.com/Afforix) fixed an example in the README. 151. [Vitaly Zaitsev](https://github.com/xvitaly) fixed compilation with GCC 9.0.
152. [Mark Beckwith](https://github.com/wythe) fixed two typos. 152. [Laurent Stacul](https://github.com/stac47) fixed compilation with GCC 9.0.
153. [scinart](https://github.com/scinart) fixed a bug in the serializer. 153. [Ivor Wanders](https://github.com/iwanders) helped to reduce the CMake requirement to version 3.1.
154. [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers. 154. [njlr](https://github.com/njlr) updated the Buckaroo instructions.
155. [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda. 155. [Lion](https://github.com/lieff) fixed a compilation issue with GCC 7 on CentOS.
156. [Michele Caini](https://github.com/skypjack) fixed links in the README. 156. [Isaac Nickaein](https://github.com/nickaein) improved the integer serialization performance and implemented the `contains()` function.
157. [Hani](https://github.com/hnkb) documented how to install the library with NuGet. 157. [past-due](https://github.com/past-due) suppressed an unfixable warning.
158. [yann-morin-1998](https://github.com/yann-morin-1998) helped to reduce the CMake requirement to version 3.1. 158. [Elvis Oric](https://github.com/elvisoric) improved Meson support.
159. [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro and fixed the MSYS2 package documentation. 159. [Matěj Plch](https://github.com/Afforix) fixed an example in the README.
160. [remyabel](https://github.com/remyabel2) added GNUInstallDirs to the CMake files. 160. [Mark Beckwith](https://github.com/wythe) fixed a typo.
161. [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test. 161. [scinart](https://github.com/scinart) fixed a bug in the serializer.
162. [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method. 162. [Patrick Boettcher](https://github.com/pboettch) implemented `push_back()` and `pop_back()` for JSON Pointers.
163. [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning. 163. [Bruno Oliveira](https://github.com/nicoddemus) added support for Conda.
164. [Viktor Kirilov](https://github.com/onqtam) switched the unit tests from [Catch](https://github.com/philsquared/Catch) to [doctest](https://github.com/doctest/doctest) 164. [Michele Caini](https://github.com/skypjack) fixed links in the README.
165. [Juncheng E](https://github.com/ejcjason) fixed a typo. 165. [Hani](https://github.com/hnkb) documented how to install the library with NuGet.
166. [tete17](https://github.com/tete17) fixed a bug in the `contains` function. 166. [Mark Beckwith](https://github.com/wythe) fixed a typo.
167. [Xav83](https://github.com/Xav83) fixed some cppcheck warnings. 167. [yann-morin-1998](https://github.com/yann-morin-1998) helped to reduce the CMake requirement to version 3.1.
168. [0xflotus](https://github.com/0xflotus) fixed some typos. 168. [Konstantin Podsvirov](https://github.com/podsvirov) maintains a package for the MSYS2 software distro.
169. [Christian Deneke](https://github.com/chris0x44) added a const version of `json_pointer::back`. 169. [remyabel](https://github.com/remyabel2) added GNUInstallDirs to the CMake files.
170. [Julien Hamaide](https://github.com/crazyjul) made the `items()` function work with custom string types. 170. [Taylor Howard](https://github.com/taylorhoward92) fixed a unit test.
171. [Evan Nemerson](https://github.com/nemequ) updated fixed a bug in Hedley and updated this library accordingly. 171. [Gabe Ron](https://github.com/Macr0Nerd) implemented the `to_string` method.
172. [Florian Pigorsch](https://github.com/flopp) fixed a lot of typos. 172. [Watal M. Iwasaki](https://github.com/heavywatal) fixed a Clang warning.
173. [Camille Bégué](https://github.com/cbegue) fixed an issue in the conversion from `std::pair` and `std::tuple` to `json`. 173. [Viktor Kirilov](https://github.com/onqtam) switched the unit tests from [Catch](https://github.com/philsquared/Catch) to [doctest](https://github.com/onqtam/doctest)
174. [Anthony VH](https://github.com/AnthonyVH) fixed a compile error in an enum deserialization. 174. [Juncheng E](https://github.com/ejcjason) fixed a typo.
175. [Yuriy Vountesmery](https://github.com/ua-code-dragon) noted a subtle bug in a preprocessor check. 175. [tete17](https://github.com/tete17) fixed a bug in the `contains` function.
176. [Chen](https://github.com/dota17) fixed numerous issues in the library. 176. [Xav83](https://github.com/Xav83) fixed some cppcheck warnings.
177. [Antony Kellermann](https://github.com/aokellermann) added a CI step for GCC 10.1. 177. [0xflotus](https://github.com/0xflotus) fixed some typos.
178. [Alex](https://github.com/gistrec) fixed an MSVC warning. 178. [Christian Deneke](https://github.com/chris0x44) added a const version of `json_pointer::back`.
179. [Rainer](https://github.com/rvjr) proposed an improvement in the floating-point serialization in CBOR. 179. [Julien Hamaide](https://github.com/crazyjul) made the `items()` function work with custom string types.
180. [Francois Chabot](https://github.com/FrancoisChabot) made performance improvements in the input adapters. 180. [Evan Nemerson](https://github.com/nemequ) updated fixed a bug in Hedley and updated this library accordingly.
181. [Arthur Sonzogni](https://github.com/ArthurSonzogni) documented how the library can be included via `FetchContent`. 181. [Florian Pigorsch](https://github.com/flopp) fixed a lot of typos.
182. [Rimas Misevičius](https://github.com/rmisev) fixed an error message. 182. [Camille Bégué](https://github.com/cbegue) fixed an issue in the conversion from `std::pair` and `std::tuple` to `json`.
183. [Alexander Myasnikov](https://github.com/alexandermyasnikov) fixed some examples and a link in the README. 183. [Anthony VH](https://github.com/AnthonyVH) fixed a compile error in an enum deserialization.
184. [Hubert Chathi](https://github.com/uhoreg) made CMake's version config file architecture-independent. 184. [Yuriy Vountesmery](https://github.com/ua-code-dragon) noted a subtle bug in a preprocessor check.
185. [OmnipotentEntity](https://github.com/OmnipotentEntity) implemented the binary values for CBOR, MessagePack, BSON, and UBJSON. 185. [Chen](https://github.com/dota17) fixed numerous issues in the library.
186. [ArtemSarmini](https://github.com/ArtemSarmini) fixed a compilation issue with GCC 10 and fixed a leak. 186. [Antony Kellermann](https://github.com/aokellermann) added a CI step for GCC 10.1.
187. [Evgenii Sopov](https://github.com/sea5kg) integrated the library to the wsjcpp package manager. 187. [Alex](https://github.com/gistrec) fixed an MSVC warning.
188. [Sergey Linev](https://github.com/linev) fixed a compiler warning. 188. [Rainer](https://github.com/rvjr) proposed an improvement in the floating-point serialization in CBOR.
189. [Miguel Magalhães](https://github.com/magamig) fixed the year in the copyright. 189. [Francois Chabot](https://github.com/FrancoisChabot) made performance improvements in the input adapters.
190. [Alexander “weej” Jones](https://github.com/alex-weej) fixed an example in the README. 190. [Arthur Sonzogni](https://github.com/ArthurSonzogni) documented how the library can be included via `FetchContent`.
191. [Antoine Cœur](https://github.com/Coeur) fixed some typos in the documentation. 191. [Rimas Misevičius](https://github.com/rmisev) fixed an error message.
192. [jothepro](https://github.com/jothepro) updated links to the Hunter package. 192. [Alexander Myasnikov](https://github.com/alexandermyasnikov) fixed some examples and a link in the README.
193. [Dave Lee](https://github.com/kastiglione) fixed a link in the README. 193. [Hubert Chathi](https://github.com/uhoreg) made CMake's version config file architecture-independent.
194. [Joël Lamotte](https://github.com/Klaim) added instruction for using Build2's package manager. 194. [OmnipotentEntity](https://github.com/OmnipotentEntity) implemented the binary values for CBOR, MessagePack, BSON, and UBJSON.
195. [Paul Jurczak](https://github.com/pauljurczak) fixed an example in the README. 195. [ArtemSarmini](https://github.com/ArtemSarmini) fixed a compilation issue with GCC 10 and fixed a leak.
196. [Carlos Gomes Martinho](https://github.com/gocarlos) updated the Conan package source. 196. [Evgenii Sopov](https://github.com/sea5kg) integrated the library to the wsjcpp package manager.
197. [Tridacnid](https://github.com/Tridacnid) improved the CMake tests. 197. [Sergey Linev](https://github.com/linev) fixed a compiler warning.
198. [Michael](https://github.com/MBalszun) fixed MSVC warnings. 198. [Miguel Magalhães](https://github.com/magamig) fixed the year in the copyright.
199. [Quentin Barbarat](https://github.com/quentin-dev) fixed an example in the documentation. 199. [Gareth Sylvester-Bradley](https://github.com/garethsb-sony) fixed a compilation issue with MSVC.
200. [XyFreak](https://github.com/XyFreak) fixed a compiler warning. 200. [Alexander “weej” Jones](https://github.com/alex-weej) fixed an example in the README.
201. [TotalCaesar659](https://github.com/TotalCaesar659) fixed links in the README and updated the links in the documentation to use HTTPS. 201. [Antoine Cœur](https://github.com/Coeur) fixed some typos in the documentation.
202. [Tanuj Garg](https://github.com/tanuj208) improved the fuzzer coverage for UBSAN input. 202. [jothepro](https://github.com/jothepro) updated links to the Hunter package.
203. [AODQ](https://github.com/AODQ) fixed a compiler warning. 203. [Dave Lee](https://github.com/kastiglione) fixed a link in the README.
204. [jwittbrodt](https://github.com/jwittbrodt) made `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` inline. 204. [Joël Lamotte](https://github.com/Klaim) added instruction for using Build2's package manager.
205. [pfeatherstone](https://github.com/pfeatherstone) improved the upper bound of arguments of the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros. 205. [Paul Jurczak](https://github.com/pauljurczak) fixed an example in the README.
206. [Jan Procházka](https://github.com/jprochazk) fixed a bug in the CBOR parser for binary and string values. 206. [Sonu Lohani](https://github.com/sonulohani) fixed a warning.
207. [T0b1-iOS](https://github.com/T0b1-iOS) fixed a bug in the new hash implementation. 207. [Carlos Gomes Martinho](https://github.com/gocarlos) updated the Conan package source.
208. [Matthew Bauer](https://github.com/matthewbauer) adjusted the CBOR writer to create tags for binary subtypes. 208. [Konstantin Podsvirov](https://github.com/podsvirov) fixed the MSYS2 package documentation.
209. [gatopeich](https://github.com/gatopeich) implemented an ordered map container for `nlohmann::ordered_json`. 209. [Tridacnid](https://github.com/Tridacnid) improved the CMake tests.
210. [Érico Nogueira Rolim](https://github.com/ericonr) added support for pkg-config. 210. [Michael](https://github.com/MBalszun) fixed MSVC warnings.
211. [KonanM](https://github.com/KonanM) proposed an implementation for the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros. 211. [Quentin Barbarat](https://github.com/quentin-dev) fixed an example in the documentation.
212. [Guillaume Racicot](https://github.com/gracicot) implemented `string_view` support and allowed C++20 support. 212. [XyFreak](https://github.com/XyFreak) fixed a compiler warning.
213. [Alex Reinking](https://github.com/alexreinking) improved CMake support for `FetchContent`. 213. [TotalCaesar659](https://github.com/TotalCaesar659) fixed links in the README.
214. [Hannes Domani](https://github.com/ssbssa) provided a GDB pretty printer. 214. [Tanuj Garg](https://github.com/tanuj208) improved the fuzzer coverage for UBSAN input.
215. Lars Wirzenius reviewed the README file. 215. [AODQ](https://github.com/AODQ) fixed a compiler warning.
216. [Jun Jie](https://github.com/ongjunjie) fixed a compiler path in the CMake scripts. 216. [jwittbrodt](https://github.com/jwittbrodt) made `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE` inline.
217. [Ronak Buch](https://github.com/rbuch) fixed typos in the documentation. 217. [pfeatherstone](https://github.com/pfeatherstone) improved the upper bound of arguments of the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros.
218. [Alexander Karzhenkov](https://github.com/karzhenkov) fixed a move constructor and the Travis builds. 218. [Jan Procházka](https://github.com/jprochazk) fixed a bug in the CBOR parser for binary and string values.
219. [Leonardo Lima](https://github.com/leozz37) added CPM.Cmake support. 219. [T0b1-iOS](https://github.com/T0b1-iOS) fixed a bug in the new hash implementation.
220. [Joseph Blackman](https://github.com/jbzdarkid) fixed a warning. 220. [Matthew Bauer](https://github.com/matthewbauer) adjusted the CBOR writer to create tags for binary subtypes.
221. [Yaroslav](https://github.com/YarikTH) updated doctest and implemented unit tests. 221. [gatopeich](https://github.com/gatopeich) implemented an ordered map container for `nlohmann::ordered_json`.
222. [Martin Stump](https://github.com/globberwops) fixed a bug in the CMake files. 222. [Érico Nogueira Rolim](https://github.com/ericonr) added support for pkg-config.
223. [Jaakko Moisio](https://github.com/jasujm) fixed a bug in the input adapters. 223. [KonanM](https://github.com/KonanM) proposed an implementation for the `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE`/`NLOHMANN_DEFINE_TYPE_INTRUSIVE` macros.
224. [bl-ue](https://github.com/bl-ue) fixed some Markdown issues in the README file. 224. [Guillaume Racicot](https://github.com/gracicot) implemented `string_view` support and allowed C++20 support.
225. [William A. Wieselquist](https://github.com/wawiesel) fixed an example from the README. 225. [Alex Reinking](https://github.com/alexreinking) improved CMake support for `FetchContent`.
226. [abbaswasim](https://github.com/abbaswasim) fixed an example from the README. 226. [Hannes Domani](https://github.com/ssbssa) provided a GDB pretty printer.
227. [Remy Jette](https://github.com/remyjette) fixed a warning. 227. Lars Wirzenius reviewed the README file.
228. [Fraser](https://github.com/frasermarlow) fixed the documentation. 228. [Jun Jie](https://github.com/ongjunjie) fixed a compiler path in the CMake scripts.
229. [Ben Beasley](https://github.com/musicinmybrain) updated doctest. 229. [Ronak Buch](https://github.com/rbuch) fixed typos in the documentation.
230. [Doron Behar](https://github.com/doronbehar) fixed pkg-config.pc. 230. [Alexander Karzhenkov](https://github.com/karzhenkov) fixed a move constructor and the Travis builds.
231. [raduteo](https://github.com/raduteo) fixed a warning. 231. [Leonardo Lima](https://github.com/leozz37) added CPM.Cmake support.
232. [David Pfahler](https://github.com/theShmoo) added the possibility to compile the library without I/O support. 232. [Joseph Blackman](https://github.com/jbzdarkid) fixed a warning.
233. [Morten Fyhn Amundsen](https://github.com/mortenfyhn) fixed a typo. 233. [Yaroslav](https://github.com/YarikTH) updated doctest and implemented unit tests.
234. [jpl-mac](https://github.com/jpl-mac) allowed treating the library as a system header in CMake. 234. [Martin Stump](https://github.com/globberwops) fixed a bug in the CMake files.
235. [Jason Dsouza](https://github.com/jasmcaus) fixed the indentation of the CMake file. 235. [Jaakko Moisio](https://github.com/jasujm) fixed a bug in the input adapters.
236. [offa](https://github.com/offa) added a link to Conan Center to the documentation. 236. [bl-ue](https://github.com/bl-ue) fixed some Markdown issues in the README file.
237. [Rafail Giavrimis](https://github.com/grafail) fixed the Google Benchmark default branch. 237. [William A. Wieselquist](https://github.com/wawiesel) fixed an example from the README.
238. [Louis Dionne](https://github.com/ldionne) fixed a conversion operator. 238. [abbaswasim](https://github.com/abbaswasim) fixed an example from the README.
239. [justanotheranonymoususer](https://github.com/justanotheranonymoususer) made the examples in the README more consistent. 239. [Remy Jette](https://github.com/remyjette) fixed a warning.
240. [Finkman](https://github.com/Finkman) suppressed some `-Wfloat-equal` warnings and fixed the debug pretty-printer. 240. [Fraser](https://github.com/frasermarlow) fixed the documentation.
241. [Ferry Huberts](https://github.com/fhuberts) fixed `-Wswitch-enum` warnings and fixed a typo. 241. [Ben Beasley](https://github.com/musicinmybrain) updated doctest.
242. [Arseniy Terekhin](https://github.com/senyai) made the GDB pretty-printer robust against unset variable names. 242. [Doron Behar](https://github.com/doronbehar) fixed pkg-config.pc.
243. [Amir Masoud Abdol](https://github.com/amirmasoudabdol) updated the Homebrew command as nlohmann/json is now in homebrew-core. 243. [raduteo](https://github.com/raduteo) fixed a warning.
244. [Hallot](https://github.com/Hallot) fixed some `-Wextra-semi-stmt warnings`. 244. [David Pfahler](https://github.com/theShmoo) added the possibility to compile the library without I/O support.
245. [Giovanni Cerretani](https://github.com/gcerretani) fixed `-Wunused` warnings on `JSON_DIAGNOSTICS` and fixed a check for `std::filesystem`. 245. [Morten Fyhn Amundsen](https://github.com/mortenfyhn) fixed a typo.
246. [Bogdan Popescu](https://github.com/Kapeli) hosts the [docset](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for offline documentation viewers. 246. [jpl-mac](https://github.com/jpl-mac) allowed treating the library as a system header in CMake.
247. [Carl Smedstad](https://github.com/carlsmedstad) fixed an assertion error when using `JSON_DIAGNOSTICS`. 247. [Jason Dsouza](https://github.com/jasmcaus) fixed the indentation of the CMake file.
248. [miikka75](https://github.com/miikka75) provided an important fix to compile C++17 code with Clang 9. 248. [offa](https://github.com/offa) added a link to Conan Center to the documentation.
249. [Maarten Becker](https://github.com/kernie) fixed a warning for shadowed variables. 249. [TotalCaesar659](https://github.com/TotalCaesar659) updated the links in the documentation to use HTTPS.
250. [Cristi Vîjdea](https://github.com/axnsan12) fixed typos in the `operator[]` documentation. 250. [Rafail Giavrimis](https://github.com/grafail) fixed the Google Benchmark default branch.
251. [Alex Beregszaszi](https://github.com/axic) fixed spelling mistakes in comments. 251. [Louis Dionne](https://github.com/ldionne) fixed a conversion operator.
252. [Dirk Stolle](https://github.com/striezel) fixed typos in the documentation. 252. [justanotheranonymoususer](https://github.com/justanotheranonymoususer) made the examples in the README more consistent.
253. [Daniel Albuschat](https://github.com/daniel-kun) corrected the parameter name in the `parse` documentation. 253. [Finkman](https://github.com/Finkman) suppressed some `-Wfloat-equal` warnings.
254. [Prince Mendiratta](https://github.com/Prince-Mendiratta) fixed a link to the FAQ. 254. [Ferry Huberts](https://github.com/fhuberts) fixed `-Wswitch-enum` warnings.
255. [Florian Albrechtskirchinger](https://github.com/falbrechtskirchinger) implemented `std::string_view` support for object keys and made dozens of other improvements. 255. [Arseniy Terekhin](https://github.com/senyai) made the GDB pretty-printer robust against unset variable names.
256. [Qianqian Fang](https://github.com/fangq) implemented the Binary JData (BJData) format. 256. [Amir Masoud Abdol](https://github.com/amirmasoudabdol) updated the Homebrew command as nlohmann/json is now in homebrew-core.
257. [pketelsen](https://github.com/pketelsen) added macros `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` and `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`. 257. [Hallot](https://github.com/Hallot) fixed some `-Wextra-semi-stmt warnings`.
258. [DarkZeros](https://github.com/DarkZeros) adjusted to code to not clash with Arduino defines. 258. [Giovanni Cerretani](https://github.com/gcerretani) fixed `-Wunused` warnings on `JSON_DIAGNOSTICS`.
259. [flagarde](https://github.com/flagarde) fixed the output of `meta()` for MSVC. 259. [Bogdan Popescu](https://github.com/Kapeli) hosts the [docset](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B) for offline documentation viewers.
260. [Dimitris Apostolou](https://github.com/rex4539) fixed a typo. 260. [Carl Smedstad](https://github.com/carlsmedstad) fixed an assertion error when using `JSON_DIAGNOSTICS`.
261. [Michael Nosthoff](https://github.com/heinemml) fixed a typo. 261. [miikka75](https://github.com/miikka75) provided an important fix to compile C++17 code with Clang 9.
262. [JungHoon Lee](https://github.com/jhnlee) fixed a typo. 262. [Maarten Becker](https://github.com/kernie) fixed a warning for shadowed variables.
263. [Faruk D.](https://github.com/fdiblen) fixed the CITATION.CFF file. 263. [Cristi Vîjdea](https://github.com/axnsan12) fixed typos in the `operator[]` documentation.
264. [Andrea Cocito](https://github.com/puffetto) added a clarification on macro usage to the documentation. 264. [Alex Beregszaszi](https://github.com/axic) fixed spelling mistakes in comments.
265. [Krzysiek Karbowiak](https://github.com/kkarbowiak) refactored the tests to use `CHECK_THROWS_WITH_AS`. 265. [Dirk Stolle](https://github.com/striezel) fixed typos in documentation.
266. [Chaoqi Zhang](https://github.com/prncoprs) fixed a typo. 266. [Daniel Albuschat](https://github.com/daniel-kun) corrected the parameter name in the `parse` documentation.
267. [ivanovmp](https://github.com/ivanovmp) fixed a whitespace error. 267. [Prince Mendiratta](https://github.com/Prince-Mendiratta) fixed a link to the FAQ.
268. [KsaNL](https://github.com/KsaNL) fixed a build error when including `<windows.h>`. 268. [Florian Albrechtskirchinger](https://github.com/falbrechtskirchinger) implemented `std::string_view` support for object keys and made dozens of other improvements.
269. [Andrea Pappacoda](https://github.com/Tachi107) moved `.pc` and `.cmake` files to `share` directory. 269. [Qianqian Fang](https://github.com/fangq) implemented the Binary JData (BJData) format.
270. [Wolf Vollprecht](https://github.com/wolfv) added the `patch_inplace` function. 270. [pketelsen](https://github.com/pketelsen) added macros `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` and `NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT`.
271. [Jake Zimmerman](https://github.com/jez) highlighted common usage patterns in the README file. 271. [DarkZeros](https://github.com/DarkZeros) adjusted to code to not clash with Arduino defines.
272. [NN](https://github.com/NN---) added the Visual Studio output directory to `.gitignore`. 272. [flagarde](https://github.com/flagarde) fixed the output of `meta()` for MSVC.
273. [Romain Reignier](https://github.com/romainreignier) improved the performance of the vector output adapter. 273. [Giovanni Cerretani](https://github.com/gcerretani) fixed a check for `std::filesystem`.
274. [Mike](https://github.com/Mike-Leo-Smith) fixed the `std::iterator_traits`. 274. [Dimitris Apostolou](https://github.com/rex4539) fixed a typo.
275. [Richard Hozák](https://github.com/richardhozak) added macro `JSON_NO_ENUM` to disable default enum conversions. 275. [Ferry Huberts](https://github.com/fhuberts) fixed a typo.
276. [vakokako](https://github.com/vakokako) fixed tests when compiling with C++20. 276. [Michael Nosthoff](https://github.com/heinemml) fixed a typo.
277. [Alexander “weej” Jones](https://github.com/alexweej) fixed an example in the README. 277. [JungHoon Lee](https://github.com/jhnlee) fixed a typo.
278. [Eli Schwartz](https://github.com/eli-schwartz) added more files to the `include.zip` archive. 278. [Faruk D.](https://github.com/fdiblen) fixed the CITATION.CFF file.
279. [Kevin Lu](https://github.com/kevinlul) fixed a compilation issue when typedefs with certain names were present. 279. [Andrea Cocito](https://github.com/puffetto) added a clarification on macro usage to the documentation.
280. [Trevor Hickey](https://github.com/luxe) improved the description of an example. 280. [Krzysiek Karbowiak](https://github.com/kkarbowiak) refactored the tests to use `CHECK_THROWS_WITH_AS`.
281. [Jef LeCompte](https://github.com/jef) updated the year in the README file. 281. [Chaoqi Zhang](https://github.com/prncoprs) fixed a typo.
282. [Alexandre Hamez](https://github.com/ahamez) fixed a warning. 282. [ivanovmp](https://github.com/ivanovmp) fixed a whitespace error.
283. [Maninderpal Badhan](https://github.com/mbadhan) fixed a typo. 283. [KsaNL](https://github.com/KsaNL) fixed a build error when including `<windows.h>`.
284. [kevin--](https://github.com/kevin--) added a note to an example in the README file. 284. [Andrea Pappacoda](https://github.com/Tachi107) moved `.pc` and `.cmake` files to `share` directory.
285. [I](https://github.com/wx257osn2) fixed a typo. 285. [Wolf Vollprecht](https://github.com/wolfv) added the `patch_inplace` function.
286. [Gregorio Litenstein](https://github.com/Lord-Kamina) fixed the Clang detection. 286. [Jake Zimmerman](https://github.com/jez) highlighted common usage patterns in the README file.
287. [Andreas Smas](https://github.com/andoma) added a Doozer badge. 287. [NN](https://github.com/NN---) added the Visual Studio output directory to `.gitignore`.
288. [WanCW](https://github.com/wancw) fixed the string conversion with Clang. 288. [Romain Reignier](https://github.com/romainreignier) improved the performance of the vector output adapter.
289. [zhaohuaxishi](https://github.com/zhaohuaxishi) fixed a Doxygen error. 289. [Mike](https://github.com/Mike-Leo-Smith) fixed the `std::iterator_traits`.
290. [emvivre](https://github.com/emvivre) removed an invalid parameter from CMake. 290. [Richard Hozák](https://github.com/richardhozak) added macro `JSON_NO_ENUM` to disable default enum conversions.
291. [Tobias Hermann](https://github.com/Dobiasd) fixed a link in the README file. 291. [vakokako](https://github.com/vakokako) fixed tests when compiling with C++20.
292. [Michael](https://github.com/traits) fixed a warning. 292. [Alexander “weej” Jones](https://github.com/alexweej) fixed an example in the README.
293. [Ryan Mulder](https://github.com/ryanjmulder) added `ensure_ascii` to the `dump` function. 293. [Eli Schwartz](https://github.com/eli-schwartz) added more files to the `include.zip` archive.
294. [Muri Nicanor](https://github.com/murinicanor) fixed the `sed` discovery in the Makefile. 294. [Kevin Lu](https://github.com/kevinlul) fixed a compilation issue when typedefs with certain names were present.
295. [David Avedissian](https://github.com/dgavedissian) implemented SFINAE-friendly `iterator_traits`. 295. [Trevor Hickey](https://github.com/luxe) improved the description of an example.
296. [AQNOUCH Mohammed](https://github.com/aqnouch) fixed a typo in the README. 296. [Jef LeCompte](https://github.com/jef) updated the year in the README file.
297. [Gareth Sylvester-Bradley](https://github.com/garethsb) added `operator/=` and `operator/` to construct JSON pointers. 297. [Alexandre Hamez](https://github.com/ahamez) fixed a warning.
298. [Michael Macnair](https://github.com/mykter) added support for afl-fuzz testing. 298. [Maninderpal Badhan](https://github.com/mbadhan) fixed a typo.
299. [Berkus Decker](https://github.com/berkus) fixed a typo in the README. 299. [kevin--](https://github.com/kevin--) added a note to an example in the README file.
300. [Illia Polishchuk](https://github.com/ilqvya) improved the CMake testing. 300. [I](https://github.com/wx257osn2) fixed a typo.
301. [Ikko Ashimine](https://github.com/eltociear) fixed two typos. 301. [Gregorio Litenstein](https://github.com/Lord-Kamina) fixed the Clang detection.
302. [Raphael Grimm](https://github.com/barcode) added the possibility to define a custom base class. 302. [Andreas Smas](https://github.com/andoma) added a Doozer badge.
303. [tocic](https://github.com/tocic) fixed typos in the documentation. 303. [WanCW](https://github.com/wancw) fixed the string conversion with Clang.
304. [Vertexwahn](https://github.com/Vertexwahn) added Bazel build support. 304. [zhaohuaxishi](https://github.com/zhaohuaxishi) fixed a Doxygen error.
305. [DavidKorczynski](https://github.com/DavidKorczynski) added a CIFuzz CI GitHub action. 305. [emvivre](https://github.com/emvivre) removed an invalid parameter from CMake.
306. [Florian Segginger](https://github.com/floriansegginger) bumped the years in the README. 306. [Tobias Hermann](https://github.com/Dobiasd) fixed a link in the README file.
307. [haadfida](https://github.com/haadfida) cleaned up the badges of used services. 307. [Michael](https://github.com/traits) fixed a warning.
308. [Arsen Arsenović](https://github.com/ArsenArsen) fixed a build error. 308. [Ryan Mulder](https://github.com/ryanjmulder) added `ensure_ascii` to the `dump` function.
309. [theevilone45](https://github.com/theevilone45) fixed a typo in a CMake file. 309. [Muri Nicanor](https://github.com/murinicanor) fixed the `sed` discovery in the Makefile.
310. [Sergei Trofimovich](https://github.com/trofi) fixed the custom allocator support. 310. [David Avedissian](https://github.com/dgavedissian) implemented SFINAE-friendly `iterator_traits`.
311. [Joyce](https://github.com/joycebrum) fixed some security issues in the GitHub workflows. 311. [AQNOUCH Mohammed](https://github.com/aqnouch) fixed a typo in the README.
312. [Nicolas Jakob](https://github.com/njakob) add vcpkg version badge. 312. [Gareth Sylvester-Bradley](https://github.com/garethsb) added `operator/=` and `operator/` to construct JSON pointers.
313. [Tomerkm](https://github.com/Tomerkm) added tests. 313. [Michael Macnair](https://github.com/mykter) added support for afl-fuzz testing.
314. [No.](https://github.com/tusooa) fixed the use of `get<>` calls. 314. [Berkus Decker](https://github.com/berkus) fixed a typo in the README.
315. [taro](https://github.com/tarolling) fixed a typo in the `CODEOWNERS` file. 315. [Illia Polishchuk](https://github.com/ilqvya) improved the CMake testing.
316. [Felix Yan](https://github.com/felixonmars) fixed a typo in the README. 316. [Ikko Ashimine](https://github.com/eltociear) fixed a typo.
317. [HO-COOH](https://github.com/HO-COOH) fixed a parenthesis in the documentation. 317. [Raphael Grimm](https://github.com/barcode) added the possibility to define a custom base class.
318. [miny1233](https://github.com/miny1233) fixed a parenthesis in the documentation. 318. [tocic](https://github.com/tocic) fixed typos in the documentation.
319. [tomalakgeretkal](https://github.com/tomalakgeretkal) fixed a compilation error. 319. [Vertexwahn](https://github.com/Vertexwahn) added Bazel build support.
320. [alferov](https://github.com/ALF-ONE) fixed a compilation error and fixed a version in the documentation. 320. [Dirk Stolle](https://github.com/striezel) fixed typos in the documentation.
321. [Craig Scott](https://github.com/craigscott-crascit) fixed a deprecation warning in CMake. 321. [DavidKorczynski](https://github.com/DavidKorczynski) added a CIFuzz CI GitHub action.
322. [Vyacheslav Zhdanovskiy](https://github.com/ZeronSix) added macros for serialization-only types. 322. [Finkman](https://github.com/Finkman) fixed the debug pretty-printer.
323. [Mathieu Westphal](https://github.com/mwestphal) fixed typos. 323. [Florian Segginger](https://github.com/floriansegginger) bumped the years in the README.
324. [scribam](https://github.com/scribam) fixed the MinGW workflow. 324. [haadfida](https://github.com/haadfida) cleaned up the badges of used services.
325. [Aleksei Sapitskii](https://github.com/aleksproger) added support for Apple's Swift Package Manager. 325. [Arsen Arsenović](https://github.com/ArsenArsen) fixed a build error.
326. [Benjamin Buch](https://github.com/bebuch) fixed the installation path in CMake. 326. [theevilone45](https://github.com/theevilone45) fixed a typo in a CMake file.
327. [Colby Haskell](https://github.com/colbychaskell) clarified the parse error message in case a file cannot be opened. 327. [Sergei Trofimovich](https://github.com/trofi) fixed the custom allocator support.
328. [Juan Carlos Arevalo Baeza](https://github.com/TheJCAB) fixed the enum conversion. 328. [Joyce](https://github.com/joycebrum) fixed some security issues in the GitHub workflows.
329. [ss](https://github.com/serge-s) fixed the amalgamation call. 329. [Nicolas Jakob](https://github.com/njakob) add vcpkg version badge.
330. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation. 330. [Tomerkm](https://github.com/Tomerkm) added tests.
331. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example. 331. [No.](https://github.com/tusooa) fixed the use of `get<>` calls.
332. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test. 332. [taro](https://github.com/tarolling) fixed a typo in the `CODEOWNERS` file.
333. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation. 333. [Ikko Eltociear Ashimine](https://github.com/eltociear) fixed a typo.
334. [laterlaugh](https://github.com/laterlaugh) fixed some typos. 334. [Felix Yan](https://github.com/felixonmars) fixed a typo in the README.
335. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer. 335. [HO-COOH](https://github.com/HO-COOH) fixed a parenthesis in the documentation.
336. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer. 336. [Ivor Wanders](https://github.com/iwanders) fixed the examples to catch exception by `const&`.
337. [Nikhil Idiculla](https://github.com/tsnl) fixed some typos. 337. [miny1233](https://github.com/miny1233) fixed a parenthesis in the documentation.
338. [Griffin Myers](https://github.com/gmyers18) updated the Natvis file. 338. [tomalakgeretkal](https://github.com/tomalakgeretkal) fixed a compilation error.
339. [thetimr](https://github.com/thetimr) fixed a typo in the documentation. 339. [alferov](https://github.com/ALF-ONE) fixed a compilation error.
340. [Balazs Erseki](https://github.com/zerocukor287) fixed a URL in the contribution guidelines. 340. [Craig Scott](https://github.com/craigscott-crascit) fixed a deprecation warning in CMake.
341. [Niccolò Iardella](https://github.com/rotolof) added `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros. 341. [Vyacheslav Zhdanovskiy](https://github.com/ZeronSix) added macros for serialization-only types.
342. [Borislav Stanimirov](https://github.com/iboB) allowed overriding the CMake target name. 342. [Mathieu Westphal](https://github.com/mwestphal) fixed typos.
343. [Captain Crutches](https://github.com/captaincrutches) made `iterator_proxy_value` a `std::forward_iterator`. 343. [scribam](https://github.com/scribam) fixed the MinGW workflow.
344. [Fredrik Sandhei](https://github.com/fsandhei) added type conversion support for `std::optional`. 344. [Aleksei Sapitskii](https://github.com/aleksproger) added support for Apple's Swift Package Manager.
345. [jh96](https://github.com/jordan-hoang) added exceptions when `nullptr` is passed to `parse`. 345. [Benjamin Buch](https://github.com/bebuch) fixed the installation path in CMake.
346. [Stuart Gorman](https://github.com/StuartGorman) fixed number parsing when `EINTR` set in `errno`. 346. [Colby Haskell](https://github.com/colbychaskell) clarified the parse error message in case a file cannot be opened.
347. [Dylan Baker](https://github.com/dcbaker) generated a pkg-config file that follows the pkg-config conventions. 347. [Juan Carlos Arevalo Baeza](https://github.com/TheJCAB) fixed the enum conversion.
348. [Tianyi Chen](https://github.com/TianyiChen) optimized the binary `get_number` implementation. 348. [alferov](https://github.com/ALF-ONE) fixed a version in the documentation.
349. [peng-wang-cn](https://github.com/peng-wang-cn) added type conversion support for multidimensional arrays. 349. [ss](https://github.com/serge-s) fixed the amalgamation call.
350. [Einars Netlis-Galejs](https://github.com/EinarsNG) added `ONLY_SERIALIZE` for `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros. 350. [AniketDhemare](https://github.com/AniketDhemare) fixed a version in the documentation.
351. [Marcel](https://github.com/mering) removed `alwayslink=True` Bazel flag. 351. [Philip Müller](https://github.com/philip-paul-mueller) fixed an example.
352. [Harinath Nampally](https://github.com/hnampally) added diagnostic positions to exceptions. 352. [Leila Shcheglova](https://github.com/LeilaShcheglova) fixed a warning in a test.
353. [Nissim Armand Ben Danan](https://github.com/NissimBendanan) fixed `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` with an empty JSON instance. 353. [Alex Prabhat Bara](https://github.com/alexprabhat99) fixed a function name in the documentation.
354. [Michael Valladolid](https://github.com/codenut) added support for BSON uint64 serialization/deserialization. 354. [laterlaugh](https://github.com/laterlaugh) fixed some typos.
355. [Nikhil](https://github.com/nikhilreddydev) updated the documentation. 355. [Yuanhao Jia](https://github.com/MrJia1997) fixed the GDB pretty printer.
356. [Nebojša Cvetković](https://github.com/nebkat) added support for BJDATA optimized binary array type. 356. [Fallen_Breath](https://github.com/Fallen-Breath) fixed an example for JSON Pointer.
357. [Sushrut Shringarputale](https://github.com/sushshring) added support for diagnostic positions. 357. [Nikhil Idiculla](https://github.com/tsnl) fixed some typos.
358. [kimci86](https://github.com/kimci86) templated to `NLOHMANN_DEFINE_TYPE` macros to also support `ordered_json`. 358. [Griffin Myers](https://github.com/gmyers18) updated the Natvis file.
359. [Richard Topchii](https://github.com/richardtop) added support for VisionOS in the Swift Package Manager. 359. [thetimr](https://github.com/thetimr) fixed a typo in the documentation.
360. [Robert Chisholm](https://github.com/Robadob) fixed a typo. 360. [Balazs Erseki](https://github.com/zerocukor287) fixed a URL in the contribution guidelines.
361. [zjyhjqs](https://github.com/zjyhjqs) added CPack support. 361. [Niccolò Iardella](https://github.com/rotolof) added `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros.
362. [bitFiedler](https://github.com/bitFiedler) made GDB pretty printer work with Python 3.8. 362. [Borislav Stanimirov](https://github.com/iboB) allowed overriding the CMake target name.
363. [Gianfranco Costamagna](https://github.com/LocutusOfBorg) fixed a compiler warning. 363. [Captain Crutches](https://github.com/captaincrutches) made `iterator_proxy_value` a `std::forward_iterator`.
364. [risa2000](https://github.com/risa2000) made `std::filesystem::path` conversion to/from UTF-8 encoded string explicit. 364. [Fredrik Sandhei](https://github.com/fsandhei) added type conversion support for `std::optional`.
365. [AM](https://github.com/maqnouch) fixed typos in the README. 365. [jh96](https://github.com/jordan-hoang) added exceptions when `nullptr` is passed to `parse`.
366. [dmenendez-gruposantander](https://github.com/dmenendez-gruposantander) fixed typos in the comments of the examples. 366. [Stuart Gorman](https://github.com/StuartGorman) fixed number parsing when `EINTR` set in `errno`.
367. [Mihai Stan](https://github.com/mstan-xx) fixed comparisons against the literal `0`. 367. [Dylan Baker](https://github.com/dcbaker) generated a pkg-config file that follows the pkg-config conventions.
368. [Matt Gumbel](https://github.com/intelmatt) fixed some `-Weffc++` warnings. 368. [Tianyi Chen](https://github.com/TianyiChen) optimized the binary `get_number` implementation.
369. [vimpunk](https://github.com/vimpunk) moved a lambda out of an unevaluated context to support older compilers. 369. [peng-wang-cn](https://github.com/peng-wang-cn) added type conversion support for multidimensional arrays.
370. [Chris Harris](https://github.com/cjh1) fixed the compilation with GCC 4.8. 370. [Einars Netlis-Galejs](https://github.com/EinarsNG) added `ONLY_SERIALIZE` for `NLOHMANN_DEFINE_DERIVED_TYPE_*` macros.
371. [Palmer Dabbelt](https://github.com/palmer-dabbelt) generated and installed a pkg-config file. 371. [Marcel](https://github.com/mering) removed `alwayslink=True` Bazel flag.
372. [Gus Pozuelo](https://github.com/ap-viavi) made `ordered_map` compatible with GCC 5.5, Clang 3.6, and Xcode 9. 372. [Harinath Nampally](https://github.com/hnampally) added diagnostic positions to exceptions.
373. [AK](https://github.com/Lioncky) fixed an MSVC build error caused by the `min`/`max` macros from `windows.h`. 373. [Nissim Armand Ben Danan](https://github.com/NissimBendanan) fixed `NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT` with an empty JSON instance.
374. [Sergiu Deitsch](https://github.com/sergiud) provided a fallback for missing `char8_t` support. 374. [Michael Valladolid](https://github.com/codenut) added support for BSON uint64 serialization/deserialization.
375. [Xiaochuan Ye](https://github.com/XueSongTap) fixed `from_msgpack` for `std::byte` input by specializing `std::char_traits`. 375. [Nikhil](https://github.com/nikhilreddydev) updated the documentation.
376. [Ville Vesilehto](https://github.com/thevilledev) fixed an overflow in the BJData size calculation and rejected overflowing negative integers in CBOR. 376. [Nebojša Cvetković](https://github.com/nebkat) added support for BJDATA optimized binary array type.
377. [NmPassTHFan](https://github.com/nmpassthf) replaced the deprecated `std::is_trivial` for C++26. 377. [Sushrut Shringarputale](https://github.com/sushshring) added support for diagnostic positions.
378. [Chris Ever](https://github.com/chirsz-ever) added the `ignore_trailing_commas` parser option. 378. [kimci86](https://github.com/kimci86) templated to `NLOHMANN_DEFINE_TYPE` macros to also support `ordered_json`.
379. [Kuan-Fu Wu](https://github.com/kfwu1999) fixed the example code for `json_pointer` initialization. 379. [Richard Topchii](https://github.com/richardtop) added support for VisionOS in the Swift Package Manager.
380. [David Kilzer](https://github.com/ddkilzer) added a missing header to the input adapters. 380. [Robert Chisholm](https://github.com/Robadob) fixed a typo.
381. [Miko](https://github.com/mikomikotaishi) added proper C++20 module support, simplified the module API, and fixed missing exports. 381. [zjyhjqs](https://github.com/zjyhjqs) added CPack support.
382. [hitgirl](https://github.com/hitgil) fixed the CMake configuration when cross-compiling. 382. [bitFiedler](https://github.com/bitFiedler) made GDB pretty printer work with Python 3.8.
383. [Devon Thomas](https://github.com/ThomaDevOSU) mentioned the Artistic Style formatting in the contribution guidelines. 383. [Gianfranco Costamagna](https://github.com/LocutusOfBorg) fixed a compiler warning.
384. [Erik Hu](https://github.com/Erikhu1) made Coveralls upload errors non-fatal in the CI. 384. [risa2000](https://github.com/risa2000) made `std::filesystem::path` conversion to/from UTF-8 encoded string explicit.
385. [co63oc](https://github.com/co63oc) fixed typos. 385. [AM](https://github.com/maqnouch) fixed typos in the README.
386. [DmitriBogdanov](https://github.com/DmitriBogdanov) fixed broken package manager links in the documentation. 386. [dmenendez-gruposantander](https://github.com/dmenendez-gruposantander) fixed typos in the comments of the examples.
387. [Bander](https://github.com/banderzhm) improved the MSVC compatibility of the C++ modules. 387. [Mihai Stan](https://github.com/mstan-xx) fixed comparisons against the literal `0`.
388. [Andy Choi](https://github.com/ccpong) removed an unnecessary `template` keyword before `get` in the README and the documentation. 388. [Matt Gumbel](https://github.com/intelmatt) fixed some `-Weffc++` warnings.
389. [SamareshSingh](https://github.com/ssam18) fixed single-element brace initialization to copy/move instead of wrapping in an array, fixed the `WITH_DEFAULT` macros for `ordered_map`, and handled moved events in `serve_header.py`. 389. [vimpunk](https://github.com/vimpunk) moved a lambda out of an unevaluated context to support older compilers.
390. [Aditya](https://github.com/Lumowhisp) improved the documentation of the documentation generation. 390. [Chris Harris](https://github.com/cjh1) fixed the compilation with GCC 4.8.
391. [cheese1](https://github.com/cheese1) clarified the README. 391. [Palmer Dabbelt](https://github.com/palmer-dabbelt) generated and installed a pkg-config file.
392. [KhloodElhossiny](https://github.com/khloodelhossiny) enabled `std::string_view` keys in `operator[]`. 392. [Gus Pozuelo](https://github.com/ap-viavi) made `ordered_map` compatible with GCC 5.5, Clang 3.6, and Xcode 9.
393. [Charles Cabergs](https://github.com/cacharle) fixed a `-Wtautological-constant-out-of-range-compare` warning. 393. [AK](https://github.com/Lioncky) fixed an MSVC build error caused by the `min`/`max` macros from `windows.h`.
394. [EALePain](https://github.com/EALePain) made the `std::tuple` conversion work with reference types such as `std::tie`. 394. [Sergiu Deitsch](https://github.com/sergiud) provided a fallback for missing `char8_t` support.
395. [koala_oishi](https://github.com/chibi-dogs) fixed grammatical wording in the README. 395. [Xiaochuan Ye](https://github.com/XueSongTap) fixed `from_msgpack` for `std::byte` input by specializing `std::char_traits`.
396. [riccardoori11](https://github.com/riccardoori11) fixed a typo in the documentation. 396. [Ville Vesilehto](https://github.com/thevilledev) fixed an overflow in the BJData size calculation and rejected overflowing negative integers in CBOR.
397. [Swastik Bose](https://github.com/VasuBhakt) fixed the parent pointers after `update()` with `JSON_DIAGNOSTICS` and fixed the Doxygen autolinking of requirements. 397. [NmPassTHFan](https://github.com/nmpassthf) replaced the deprecated `std::is_trivial` for C++26.
398. [trdesilva](https://github.com/trdesilva) added `front`, `pop_front`, and `push_front` to `json_pointer`. 398. [Chris Ever](https://github.com/chirsz-ever) added the `ignore_trailing_commas` parser option.
399. [Akhilesh Arora](https://github.com/akhilesharora) fixed an incomplete-type error with `ordered_json`. 399. [Kuan-Fu Wu](https://github.com/kfwu1999) fixed the example code for `json_pointer` initialization.
400. [Hariom Phulre](https://github.com/hariomphulre) fixed the C++20 modules compilation with GCC. 400. [David Kilzer](https://github.com/ddkilzer) added a missing header to the input adapters.
401. [Kirill Lokotkov](https://github.com/RUSLoker) fixed printing `long double` values. 401. [Miko](https://github.com/mikomikotaishi) added proper C++20 module support, simplified the module API, and fixed missing exports.
402. [George Sedov](https://github.com/radistmorse) added the `NLOHMANN_DEFINE_TYPE_*_WITH_NAMES` macros. 402. [hitgirl](https://github.com/hitgil) fixed the CMake configuration when cross-compiling.
403. [Caillin Nugent](https://github.com/nugentcaillin) added the `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macro. 403. [Devon Thomas](https://github.com/ThomaDevOSU) mentioned the Artistic Style formatting in the contribution guidelines.
404. [Cosmin D.](https://github.com/drcosmin) fixed `std::filesystem::path` conversions and added an MSVC workaround for `std::unique_ptr`. 404. [Erik Hu](https://github.com/Erikhu1) made Coveralls upload errors non-fatal in the CI.
405. [Paul Dreik](https://github.com/pauldreik) fixed a test relying on implementation-specific behavior. 405. [co63oc](https://github.com/co63oc) fixed typos.
406. [Daniel Falk](https://github.com/daniel-falk) added missing copyright notices to the SBOM. 406. [DmitriBogdanov](https://github.com/DmitriBogdanov) fixed broken package manager links in the documentation.
407. [Federico Sfriso](https://github.com/federicosfriso05-dotcom) added support for constructing JSON values from C++20 range views. 407. [Bander](https://github.com/banderzhm) improved the MSVC compatibility of the C++ modules.
408. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation. 408. [Andy Choi](https://github.com/ccpong) removed an unnecessary `template` keyword before `get` in the README and the documentation.
409. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions. 409. [SamareshSingh](https://github.com/ssam18) fixed single-element brace initialization to copy/move instead of wrapping in an array, fixed the `WITH_DEFAULT` macros for `ordered_map`, and handled moved events in `serve_header.py`.
410. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads. 410. [Aditya](https://github.com/Lumowhisp) improved the documentation of the documentation generation.
411. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON. 411. [cheese1](https://github.com/cheese1) clarified the README.
412. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes. 412. [KhloodElhossiny](https://github.com/khloodelhossiny) enabled `std::string_view` keys in `operator[]`.
413. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability. 413. [Charles Cabergs](https://github.com/cacharle) fixed a `-Wtautological-constant-out-of-range-compare` warning.
414. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`. 414. [EALePain](https://github.com/EALePain) made the `std::tuple` conversion work with reference types such as `std::tie`.
415. [Petr Bělohlávek](https://github.com/petrbel) added Clang 21 and 22 to the CI. 415. [koala_oishi](https://github.com/chibi-dogs) fixed grammatical wording in the README.
416. [Dmitry Rantovov](https://github.com/darkdi) fixed the placement of a CBOR documentation block. 416. [riccardoori11](https://github.com/riccardoori11) fixed a typo in the documentation.
417. [ljcjclljc](https://github.com/ljcjclljc) fixed the comparison of large unsigned integers with signed integers. 417. [Swastik Bose](https://github.com/VasuBhakt) fixed the parent pointers after `update()` with `JSON_DIAGNOSTICS` and fixed the Doxygen autolinking of requirements.
418. [Sahil Kamate](https://github.com/sahilkamate03) fixed the handling of CBOR tags 0-5 and 21-23. 418. [trdesilva](https://github.com/trdesilva) added `front`, `pop_front`, and `push_front` to `json_pointer`.
419. [Krishnanand G](https://github.com/Krishnanand-G) made the UBJSON writer reject `use_type` without `use_size`. 419. [Akhilesh Arora](https://github.com/akhilesharora) fixed an incomplete-type error with `ordered_json`.
420. [whn](https://github.com/Whning0513) documented the lenient BSON input handling and corrected the complexity of `to_bson`. 420. [Hariom Phulre](https://github.com/hariomphulre) fixed the C++20 modules compilation with GCC.
421. [elix3r](https://github.com/22elix3r) fixed `update()` with `merge_objects` when merging a primitive into an object. 421. [Kirill Lokotkov](https://github.com/RUSLoker) fixed printing `long double` values.
422. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks. 422. [George Sedov](https://github.com/radistmorse) added the `NLOHMANN_DEFINE_TYPE_*_WITH_NAMES` macros.
423. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output. 423. [Caillin Nugent](https://github.com/nugentcaillin) added the `NLOHMANN_JSON_SERIALIZE_ENUM_STRICT` macro.
424. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include. 424. [Cosmin D.](https://github.com/drcosmin) fixed `std::filesystem::path` conversions and added an MSVC workaround for `std::unique_ptr`.
425. [Paul Dreik](https://github.com/pauldreik) fixed a test relying on implementation-specific behavior.
426. [Daniel Falk](https://github.com/daniel-falk) added missing copyright notices to the SBOM.
427. [Federico Sfriso](https://github.com/federicosfriso05-dotcom) added support for constructing JSON values from C++20 range views.
428. [Luke Banicevic](https://github.com/banaboi) fixed corrupt BSON output for lengths exceeding `INT32_MAX`, cleaned up the BSON writer, and improved the documentation.
429. [Patrick Armstrong](https://github.com/Patrick10199) updated the CBOR references and the half-precision float assertions.
430. [Yash Bavadiya](https://github.com/xevrion) added checks to all BSON reads.
431. [hum4nBeing](https://github.com/hum4nBeing) fixed the overflow handling of high-precision numbers in UBJSON.
432. [tomatotomata](https://github.com/tomatotomata) added checks for reading CBOR tagged subtypes.
433. [YingqiDuan](https://github.com/YingqiDuan) documented the BSON interoperability.
434. [KBS](https://github.com/youdie006) documented the standards compliance and the strictness of `parse()` and `operator>>`.
435. [Petr Bělohlávek](https://github.com/petrbel) added Clang 21 and 22 to the CI.
436. [Dmitry Rantovov](https://github.com/darkdi) fixed the placement of a CBOR documentation block.
437. [ljcjclljc](https://github.com/ljcjclljc) fixed the comparison of large unsigned integers with signed integers.
438. [Sahil Kamate](https://github.com/sahilkamate03) fixed the handling of CBOR tags 0-5 and 21-23.
439. [Krishnanand G](https://github.com/Krishnanand-G) made the UBJSON writer reject `use_type` without `use_size`.
440. [whn](https://github.com/Whning0513) documented the lenient BSON input handling and corrected the complexity of `to_bson`.
441. [elix3r](https://github.com/22elix3r) fixed `update()` with `merge_objects` when merging a primitive into an object.
442. [Avionic Harshit](https://github.com/avionicharshit-byte) made `diff()` linear when an array shrinks.
443. [Qatadaha Bin Matloob](https://github.com/qatcod) fixed comparisons between integers and floats and fixed unparsable BJData output.
444. [Wu Shuwen](https://github.com/dajiaohuang) removed an unused include.
Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone. Thanks a lot for helping out! Please [let me know](mailto:mail@nlohmann.me) if I forgot someone.
@@ -1850,28 +1879,21 @@ The library itself consists of a single header file licensed under the MIT licen
- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows - [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
- [**Artistic Style**](https://astyle.sourceforge.net) for automatic source code indentation - [**Artistic Style**](https://astyle.sourceforge.net) for automatic source code indentation
- [**Clang**](https://clang.llvm.org) for compilation with code sanitizers - [**Clang**](https://clang.llvm.org) for compilation with code sanitizers
- [**Clang-Tidy**](https://clang.llvm.org/extra/clang-tidy/) for static analysis and style checks
- [**CMake**](https://cmake.org) for build automation - [**CMake**](https://cmake.org) for build automation
- [**Codacy**](https://www.codacy.com) for further [code analysis](https://app.codacy.com/gh/nlohmann/json/dashboard) - [**Codacy**](https://www.codacy.com) for further [code analysis](https://app.codacy.com/gh/nlohmann/json/dashboard)
- [**CodeQL**](https://codeql.github.com) for code scanning
- [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json) - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
- [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json) - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
- [**cppcheck**](https://cppcheck.sourceforge.io) for static analysis - [**cppcheck**](https://cppcheck.sourceforge.io) for static analysis
- [**cpplint**](https://github.com/cpplint/cpplint) for style checks - [**doctest**](https://github.com/onqtam/doctest) for the unit tests
- [**doctest**](https://github.com/doctest/doctest) for the unit tests - [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
- [**Flawfinder**](https://dwheeler.com/flawfinder/) for static analysis
- [**GitHub Changelog Generator**](https://github.com/github-changelog-generator/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks - [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks
- [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros - [**Hedley**](https://nemequ.github.io/hedley/) to avoid re-inventing several compiler-agnostic feature macros
- [**include-what-you-use**](https://include-what-you-use.org) to check the included headers
- [**Infer**](https://fbinfer.com) for static analysis
- [**lcov**](https://github.com/linux-test-project/lcov) to process coverage information and create an HTML view - [**lcov**](https://github.com/linux-test-project/lcov) to process coverage information and create an HTML view
- [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz - [**libFuzzer**](https://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
- [**Material for MkDocs**](https://squidfunk.github.io/mkdocs-material/) for the style of the documentation site - [**Material for MkDocs**](https://squidfunk.github.io/mkdocs-material/) for the style of the documentation site
- [**MkDocs**](https://www.mkdocs.org) for the documentation site - [**MkDocs**](https://www.mkdocs.org) for the documentation site
- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json)) - [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json))
- [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments. - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
- [**Semgrep**](https://semgrep.dev) for static analysis
- [**Valgrind**](https://valgrind.org) to check for correct memory management - [**Valgrind**](https://valgrind.org) to check for correct memory management
## Notes ## Notes
@@ -1965,10 +1987,10 @@ If the testdata is not found, several test suites will fail like this:
``` ```
=============================================================================== ===============================================================================
json/tests/src/make_test_data_available.hpp:26: json/tests/src/make_test_data_available.hpp:21:
TEST CASE: check test suite is downloaded TEST CASE: check test suite is downloaded
json/tests/src/make_test_data_available.hpp:28: FATAL ERROR: REQUIRE( utils::check_testsuite_downloaded() ) is NOT correct! json/tests/src/make_test_data_available.hpp:23: FATAL ERROR: REQUIRE( utils::check_testsuite_downloaded() ) is NOT correct!
values: REQUIRE( false ) values: REQUIRE( false )
logged: Test data not found in 'json/cmake-build-debug/json_test_data'. logged: Test data not found in 'json/cmake-build-debug/json_test_data'.
Please execute target 'download_test_data' before running this test suite. Please execute target 'download_test_data' before running this test suite.
@@ -1979,10 +2001,10 @@ json/tests/src/make_test_data_available.hpp:28: FATAL ERROR: REQUIRE( utils::che
In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information. In case you have downloaded the library rather than checked out the code via Git, test `cmake_fetch_content_configure` will fail. Please execute `ctest -LE git_required` to skip these tests. See [issue #2189](https://github.com/nlohmann/json/issues/2189) for more information.
Some tests require network access to execute properly. They are labeled as `git_required`. Please execute `ctest -LE git_required` to skip these tests. See [issue #4851](https://github.com/nlohmann/json/issues/4851) for more information. Some tests are requiring network to be properly execute. They are labeled as `git_required`. Please execute `ctest -LE git_required` to skip these tests. See [issue #4851](https://github.com/nlohmann/json/issues/4851) for more information.
Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Furthermore, assertions must be switched off to ensure reproducible builds (see [discussion 4494](https://github.com/nlohmann/json/discussions/4494)). Some tests change the installed files and hence make the whole process not reproducible. Please execute `ctest -LE not_reproducible` to skip these tests. See [issue #2324](https://github.com/nlohmann/json/issues/2324) for more information. Furthermore, assertions must be switched off to ensure reproducible builds (see [discussion 4494](https://github.com/nlohmann/json/discussions/4494)).
Note you need to call `ctest -LE "not_reproducible|git_required"` to exclude both labels. See [issue #2596](https://github.com/nlohmann/json/issues/2596) for more information. Note you need to call `cmake -LE "not_reproducible|git_required"` to exclude both labels. See [issue #2596](https://github.com/nlohmann/json/issues/2596) for more information.
As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/fp-model-fp.html) then. As Intel compilers use unsafe floating point optimization by default, the unit tests may fail. Use flag [`/fp:precise`](https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/fp-model-fp.html) then.
+2 -8
View File
@@ -10,10 +10,10 @@ SPDX-FileCopyrightText = "2013-2026 Niels Lohmann <https://nlohmann.me>"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
[[annotations]] [[annotations]]
path = "include/nlohmann/thirdparty/hedley/hedley.hpp" path = "include/nlohmann/thirdparty/hedley.hpp"
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "2016-2021 Evan Nemerson <evan@nemerson.com>" SPDX-FileCopyrightText = "2016-2021 Evan Nemerson <evan@nemerson.com>"
SPDX-License-Identifier = "CC0-1.0" SPDX-License-Identifier = "CC0"
[[annotations]] [[annotations]]
path = "include/nlohmann/detail/meta/cpp_future.hpp" path = "include/nlohmann/detail/meta/cpp_future.hpp"
@@ -50,9 +50,3 @@ path = "tools/gdb_pretty_printer/**"
precedence = "aggregate" precedence = "aggregate"
SPDX-FileCopyrightText = "2020 Hannes Domani <https://github.com/ssbssa>" SPDX-FileCopyrightText = "2020 Hannes Domani <https://github.com/ssbssa>"
SPDX-License-Identifier = "MIT" SPDX-License-Identifier = "MIT"
[[annotations]]
path = "docs/docset/icon*.png"
precedence = "override"
SPDX-FileCopyrightText = "JSON logo (public domain), https://commons.wikimedia.org/wiki/File:JSON_vector_logo.svg"
SPDX-License-Identifier = "CC0-1.0"
+16 -13
View File
@@ -5,14 +5,15 @@ set(N 10)
# Needed tools. # Needed tools.
############################################################################### ###############################################################################
include(FindPython3)
find_package(Python3 COMPONENTS Interpreter) find_package(Python3 COMPONENTS Interpreter)
find_program(CLANG_TOOL NAMES clang++ clang++-HEAD clang++-22 clang++-21 clang++-20 clang++-19 clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++-13 clang++-12 clang++-11) find_program(CLANG_TOOL NAMES clang++-HEAD clang++ clang++-22 clang++-21 clang++-20 clang++-19 clang++-18 clang++-17 clang++-16 clang++-15 clang++-14 clang++-13 clang++-12 clang++-11 clang++)
execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION) execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION)
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}")
message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})") message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})")
find_program(CLANG_TIDY_TOOL NAMES clang-tidy clang-tidy-22 clang-tidy-21 clang-tidy-20 clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11) find_program(CLANG_TIDY_TOOL NAMES clang-tidy-22 clang-tidy-21 clang-tidy-20 clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy)
execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION) execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION)
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}")
message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})") message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})")
@@ -68,7 +69,9 @@ string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" VALGRIND_TOOL_VERSION "${VALGRIND_TOOL_V
message(STATUS "🔖 Valgrind ${VALGRIND_TOOL_VERSION} (${VALGRIND_TOOL})") message(STATUS "🔖 Valgrind ${VALGRIND_TOOL_VERSION} (${VALGRIND_TOOL})")
find_program(GENHTML_TOOL NAMES genhtml) find_program(GENHTML_TOOL NAMES genhtml)
find_program(SCAN_BUILD_TOOL NAMES scan-build scan-build-22 scan-build-21 scan-build-20 scan-build-19 scan-build-18 scan-build-17 scan-build-16 scan-build-15 scan-build-14 scan-build-13 scan-build-12 scan-build-11) find_program(PLOG_CONVERTER_TOOL NAMES plog-converter)
find_program(PVS_STUDIO_ANALYZER_TOOL NAMES pvs-studio-analyzer)
find_program(SCAN_BUILD_TOOL NAMES scan-build-15 scan-build-14 scan-build-13 scan-build-12 scan-build-11 scan-build)
# the individual source files # the individual source files
file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp) file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp)
@@ -364,7 +367,7 @@ add_custom_target(ci_test_coverage
COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused
COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused
COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept
COMMAND ${GENHTML_TOOL} --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept
COMMENT "Compile and test with coverage" COMMENT "Compile and test with coverage"
) )
@@ -389,8 +392,8 @@ add_custom_target(ci_test_clang_sanitizer
# Check if header is amalgamated and sources are properly indented. # Check if header is amalgamated and sources are properly indented.
############################################################################### ###############################################################################
# Same file set as .github/workflows/check_amalgamation.yml, so this CMake target (which can be run locally) is held # Same file set as .github/workflows/check_amalgamation.yml, so a direct push to develop/master/release/*
# to the same standard as the workflow (which runs for pull requests and for pushes to develop/master/release/*). # (which only this CMake target checks, not the pull_request-only workflow) is held to the same standard.
file(GLOB_RECURSE INDENT_FILES file(GLOB_RECURSE INDENT_FILES
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.hpp ${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.hpp
${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cpp ${PROJECT_SOURCE_DIR}/docs/mkdocs/docs/examples/*.cpp
@@ -420,7 +423,7 @@ add_custom_target(ci_test_amalgamation
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s . COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s .
COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s . COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s .
COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp ${include_dir}/json_literals.hpp COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp
COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/scripts/gen_bazel_build_file.cmake COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/scripts/gen_bazel_build_file.cmake
COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp
@@ -431,8 +434,6 @@ add_custom_target(ci_test_amalgamation
COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES} COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES}
COMMAND for FILE in `find . -name '*.orig'`\; do false \; done COMMAND for FILE in `find . -name '*.orig'`\; do false \; done
COMMAND rm -f ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ ${include_dir}/json_literals.hpp~ ${PROJECT_SOURCE_DIR}/BUILD.bazel~
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Check amalgamation, formatting, and BUILD.bazel" COMMENT "Check amalgamation, formatting, and BUILD.bazel"
) )
@@ -718,7 +719,7 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0 # the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
JSON_DisableTupleReferenceConversion JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
JSON_StrictNulHandling JSON_StrictBinaryUTF8 JSON_DeleteDeprecatedFunctions) JSON_StrictNulHandling JSON_StrictBinaryUTF8 JSON_DeleteDeprecatedFunctions)
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0}) set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0}) set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
@@ -775,14 +776,17 @@ add_custom_target(ci_cmake_flags
# Use more installed compilers. # Use more installed compilers.
############################################################################### ###############################################################################
foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6) foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 g++-11 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13 clang++-14 clang++-15 clang++-16 clang++-17 clang++-18 clang++-19 clang++-20)
find_program(COMPILER_TOOL NAMES ${COMPILER}) find_program(COMPILER_TOOL NAMES ${COMPILER})
if (COMPILER_TOOL) if (COMPILER_TOOL)
unset(ADDITIONAL_FLAGS)
add_custom_target(ci_test_compiler_${COMPILER} add_custom_target(ci_test_compiler_${COMPILER}
COMMAND CXX=${COMPILER} ${CMAKE_COMMAND} COMMAND CXX=${COMPILER} ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_BuildTests=ON -DJSON_FastTests=ON
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_${COMPILER}
${ADDITIONAL_FLAGS}
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER}
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_${COMPILER} && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure
COMMENT "Compile and test with ${COMPILER}" COMMENT "Compile and test with ${COMPILER}"
@@ -796,6 +800,7 @@ add_custom_target(ci_test_compiler_default
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_BuildTests=ON -DJSON_FastTests=ON -DJSON_BuildTests=ON -DJSON_FastTests=ON
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_default -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_compiler_default
${ADDITIONAL_FLAGS}
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_default --parallel ${N} COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_compiler_default --parallel ${N}
COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_default && ${CMAKE_CTEST_COMMAND} --parallel ${N} -LE git_required --output-on-failure COMMAND cd ${PROJECT_BINARY_DIR}/build_compiler_default && ${CMAKE_CTEST_COMMAND} --parallel ${N} -LE git_required --output-on-failure
COMMENT "Compile and test with default C++ compiler" COMMENT "Compile and test with default C++ compiler"
@@ -820,10 +825,8 @@ add_custom_target(ci_module_cpp20
COMMAND ${CMAKE_COMMAND} COMMAND ${CMAKE_COMMAND}
-DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_BUILD_TYPE=Debug -GNinja
-DJSON_CI=ON -DNLOHMANN_JSON_BUILD_MODULES=ON -DJSON_Install=ON -DJSON_CI=ON -DNLOHMANN_JSON_BUILD_MODULES=ON -DJSON_Install=ON
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
-S${PROJECT_SOURCE_DIR}/tests/module_cpp20 -B${PROJECT_BINARY_DIR}/ci_module_cpp20 -S${PROJECT_SOURCE_DIR}/tests/module_cpp20 -B${PROJECT_BINARY_DIR}/ci_module_cpp20
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/ci_module_cpp20 COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/ci_module_cpp20
VERBATIM
) )
############################################################################### ###############################################################################
-1
View File
@@ -49,7 +49,6 @@ cc_library(
hdrs = [ hdrs = [
"single_include/nlohmann/json.hpp", "single_include/nlohmann/json.hpp",
"single_include/nlohmann/json_fwd.hpp", "single_include/nlohmann/json_fwd.hpp",
"single_include/nlohmann/json_literals.hpp",
], ],
includes = ["single_include"], includes = ["single_include"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
+2 -2
View File
@@ -7,12 +7,12 @@ This URL provides the most recent documentation and also applies to previous ver
functions is not removed; instead, it is marked as deprecated. functions is not removed; instead, it is marked as deprecated.
If you want to view the documentation for a specific tag or commit hash, you can generate it locally as follows (example If you want to view the documentation for a specific tag or commit hash, you can generate it locally as follows (example
using tag `v3.12.0`): using tag `v3.10.2`):
```shell ```shell
git clone https://github.com/nlohmann/json.git git clone https://github.com/nlohmann/json.git
cd json cd json
git checkout v3.12.0 git checkout v3.10.2
make install_venv serve -C docs/mkdocs make install_venv serve -C docs/mkdocs
``` ```
+55 -11
View File
@@ -1,24 +1,48 @@
SHELL=/usr/bin/env bash SHELL=/usr/bin/env bash
PYTHON=../mkdocs/venv/bin/python3 SED ?= $(shell which gsed 2>/dev/null || which sed)
MKDOCS_PAGES=$(shell cd ../mkdocs/docs/ && find * -type f -name '*.md' | sort)
.PHONY: all .PHONY: all
all: JSON_for_Modern_C++.tgz all: JSON_for_Modern_C++.tgz
# generate the search index (the docset target does this itself, this is docSet.dsidx: docSet.sql
# only handy for inspecting the index) # generate index
docSet.dsidx: generate_docset.py sqlite3 docSet.dsidx <docSet.sql
$(PYTHON) generate_docset.py index docSet.dsidx
# build the documentation and turn it into a self-contained docset JSON_for_Modern_C++.docset: Info.plist docSet.dsidx
.PHONY: JSON_for_Modern_C++.docset rm -fr JSON_for_Modern_C++.docset JSON_for_Modern_C++.tgz
JSON_for_Modern_C++.docset: mkdir -p JSON_for_Modern_C++.docset/Contents/Resources/Documents/
cp icon*.png JSON_for_Modern_C++.docset
cp Info.plist JSON_for_Modern_C++.docset/Contents
# build and copy documentation
$(MAKE) install_venv -C ../mkdocs $(MAKE) install_venv -C ../mkdocs
$(MAKE) build -C ../mkdocs $(MAKE) build -C ../mkdocs
$(PYTHON) generate_docset.py docset ../mkdocs/site . cp -r ../mkdocs/site/* JSON_for_Modern_C++.docset/Contents/Resources/Documents
# patch CSS to hide navigation items
echo -e "\n\nheader, footer, nav.md-tabs, nav.md-tabs--active, div.md-sidebar--primary, a.md-content__button { display: none; }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
# fix spacing
echo -e "\n\ndiv.md-sidebar div.md-sidebar--secondary, div.md-main__inner { top: 0; margin-top: 0 }" >> "$$(ls JSON_for_Modern_C++.docset/Contents/Resources/Documents/assets/stylesheets/main.*.min.css)"
# remove "JSON for Modern C++" from page titles (fallback)
find JSON_for_Modern_C++.docset/Contents/Resources/Documents -type f -exec $(SED) -i 's| - JSON for Modern C++</title>|</title>|' {} +
# replace page titles with name from index, if available
for page in $(MKDOCS_PAGES); do \
case "$$page" in \
*/index.md) path=$${page/\/index.md/} ;; \
*) path=$${page/.md/} ;; \
esac; \
title=$$(sqlite3 docSet.dsidx "SELECT name FROM searchIndex WHERE path='$$path/index.html'" | tr '\n' ',' | $(SED) -e 's/,/, /g' -e 's/, $$/\n/'); \
if [ "x$$title" != "x" ]; then \
$(SED) -i "s%<title>.*</title>%<title>$$title</title>%" "JSON_for_Modern_C++.docset/Contents/Resources/Documents/$$path/index.html"; \
fi \
done
# clean up
rm JSON_for_Modern_C++.docset/Contents/Resources/Documents/sitemap.*
# copy index
cp docSet.dsidx JSON_for_Modern_C++.docset/Contents/Resources/
.PHONY: JSON_for_Modern_C++.tgz
JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset JSON_for_Modern_C++.tgz: JSON_for_Modern_C++.docset
$(PYTHON) generate_docset.py tgz . tar --exclude='.DS_Store' -cvzf JSON_for_Modern_C++.tgz JSON_for_Modern_C++.docset
# install docset for Zeal documentation browser (https://zealdocs.org/) # install docset for Zeal documentation browser (https://zealdocs.org/)
.PHONY: install_docset_zeal .PHONY: install_docset_zeal
@@ -28,6 +52,26 @@ install_docset_zeal: JSON_for_Modern_C++.docset
mkdir -p $$docset_root; \ mkdir -p $$docset_root; \
cp -r JSON_for_Modern_C++.docset $$docset_root/ cp -r JSON_for_Modern_C++.docset $$docset_root/
# both targets below compare the docset search index with the mkdocs page
# set. They share the same normalization (docs/foo/index.md and
# docs/foo.md both become foo/index.html, the URL mkdocs itself would
# give the page; the top-level index.md is excluded, as it is not part
# of the hand-curated docSet.sql) and use comm(1) on two sorted lists
# instead of running a sqlite3 query, or an O(n*m) nested shell loop,
# once per page.
DOCSET_INDEX_PATHS=$(shell sqlite3 docSet.dsidx "SELECT DISTINCT path FROM searchIndex" | sort)
DOCSET_PAGE_PATHS=$(shell echo '$(MKDOCS_PAGES)' | tr ' ' '\n' | grep -v '^index\.md$$' | $(SED) -E 's@/index\.md$$@/index.html@; s@\.md$$@/index.html@' | sort)
# list mkdocs pages missing from the docset index
.PHONY: list_missing_pages
list_missing_pages: docSet.dsidx
@comm -23 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
# list paths in the docset index without a corresponding mkdocs page
.PHONY: list_removed_paths
list_removed_paths: docSet.dsidx
@comm -13 <(echo '$(DOCSET_PAGE_PATHS)' | tr ' ' '\n') <(echo '$(DOCSET_INDEX_PATHS)' | tr ' ' '\n')
.PHONY: clean .PHONY: clean
clean: clean:
rm -f docSet.dsidx rm -f docSet.dsidx
+1 -3
View File
@@ -4,8 +4,7 @@ The folder contains the required files to create a [docset](https://kapeli.com/d
documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or documentation browsers like [Dash](https://kapeli.com/dash), [Velocity](https://velocity.silverlakesoftware.com), or
[Zeal](https://zealdocs.org). [Zeal](https://zealdocs.org).
The docset (pages and search index) is generated by `generate_docset.py` from the mkdocs site and `mkdocs.yml`. It The docset can be created with
can be created with
```sh ```sh
make JSON_for_Modern_C++.docset make JSON_for_Modern_C++.docset
@@ -13,7 +12,6 @@ make JSON_for_Modern_C++.docset
The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a The generated folder `JSON_for_Modern_C++.docset` can then be opened in the documentation browser. `make all` builds a
`JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly. `JSON_for_Modern_C++.tgz` archive instead, and `make install_docset_zeal` installs the docset for Zeal directly.
`make docSet.dsidx` builds only the search index.
A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B). A recent version is also part of the [Dash user contributions](https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/JSON_for_Modern_C%2B%2B).
+289
View File
@@ -0,0 +1,289 @@
DROP TABLE IF EXISTS searchIndex;
CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);
CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path);
-- API
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer', 'Class', 'api/adl_serializer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::from_json', 'Function', 'api/adl_serializer/from_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('adl_serializer::to_json', 'Function', 'api/adl_serializer/to_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype', 'Class', 'api/byte_container_with_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::byte_container_with_subtype', 'Constructor', 'api/byte_container_with_subtype/byte_container_with_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::clear_subtype', 'Method', 'api/byte_container_with_subtype/clear_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::has_subtype', 'Method', 'api/byte_container_with_subtype/has_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator!=', 'Operator', 'api/byte_container_with_subtype/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::operator==', 'Operator', 'api/byte_container_with_subtype/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::set_subtype', 'Method', 'api/byte_container_with_subtype/set_subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('byte_container_with_subtype::subtype', 'Method', 'api/byte_container_with_subtype/subtype/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json', 'Class', 'api/basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('format_as', 'Function', 'api/basic_json/format_as/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::accept', 'Function', 'api/basic_json/accept/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array', 'Function', 'api/basic_json/array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::array_t', 'Type', 'api/basic_json/array_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::as_base_class', 'Method', 'api/basic_json/as_base_class/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::at', 'Method', 'api/basic_json/at/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::back', 'Method', 'api/basic_json/back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::basic_json', 'Constructor', 'api/basic_json/basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::begin', 'Method', 'api/basic_json/begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary', 'Function', 'api/basic_json/binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::binary_t', 'Type', 'api/basic_json/binary_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::boolean_t', 'Type', 'api/basic_json/boolean_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbegin', 'Method', 'api/basic_json/cbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cbor_tag_handler_t', 'Enum', 'api/basic_json/cbor_tag_handler_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::cend', 'Method', 'api/basic_json/cend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::clear', 'Method', 'api/basic_json/clear/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::contains', 'Method', 'api/basic_json/contains/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::count', 'Method', 'api/basic_json/count/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crbegin', 'Method', 'api/basic_json/crbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::crend', 'Method', 'api/basic_json/crend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::default_object_comparator_t', 'Type', 'api/basic_json/default_object_comparator_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::diff', 'Function', 'api/basic_json/diff/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::dump', 'Method', 'api/basic_json/dump/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace', 'Method', 'api/basic_json/emplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::emplace_back', 'Method', 'api/basic_json/emplace_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::empty', 'Method', 'api/basic_json/empty/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end', 'Method', 'api/basic_json/end/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::end_pos', 'Method', 'api/basic_json/end_pos/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::erase', 'Method', 'api/basic_json/erase/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::error_handler_t', 'Enum', 'api/basic_json/error_handler_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::exception', 'Class', 'api/basic_json/exception/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::find', 'Method', 'api/basic_json/find/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::flatten', 'Method', 'api/basic_json/flatten/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bjdata', 'Function', 'api/basic_json/from_bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bson', 'Function', 'api/basic_json/from_bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_cbor', 'Function', 'api/basic_json/from_cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_msgpack', 'Function', 'api/basic_json/from_msgpack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_bon8', 'Function', 'api/basic_json/from_bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::from_ubjson', 'Function', 'api/basic_json/from_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::front', 'Method', 'api/basic_json/front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get', 'Method', 'api/basic_json/get/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_allocator', 'Function', 'api/basic_json/get_allocator/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_binary', 'Method', 'api/basic_json/get_binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ptr', 'Method', 'api/basic_json/get_ptr/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_ref', 'Method', 'api/basic_json/get_ref/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::get_to', 'Method', 'api/basic_json/get_to/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::input_format_t', 'Enum', 'api/basic_json/input_format_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::insert', 'Method', 'api/basic_json/insert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::invalid_iterator', 'Class', 'api/basic_json/invalid_iterator/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_array', 'Method', 'api/basic_json/is_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_binary', 'Method', 'api/basic_json/is_binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_boolean', 'Method', 'api/basic_json/is_boolean/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_discarded', 'Method', 'api/basic_json/is_discarded/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_null', 'Method', 'api/basic_json/is_null/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number', 'Method', 'api/basic_json/is_number/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_float', 'Method', 'api/basic_json/is_number_float/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_integer', 'Method', 'api/basic_json/is_number_integer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_number_unsigned', 'Method', 'api/basic_json/is_number_unsigned/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_object', 'Method', 'api/basic_json/is_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_primitive', 'Method', 'api/basic_json/is_primitive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_string', 'Method', 'api/basic_json/is_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::is_structured', 'Method', 'api/basic_json/is_structured/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::items', 'Method', 'api/basic_json/items/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_base_class_t', 'Type', 'api/basic_json/json_base_class_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::json_serializer', 'Class', 'api/basic_json/json_serializer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::max_size', 'Method', 'api/basic_json/max_size/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::merge_patch', 'Method', 'api/basic_json/merge_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::meta', 'Function', 'api/basic_json/meta/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_float_t', 'Type', 'api/basic_json/number_float_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_integer_t', 'Type', 'api/basic_json/number_integer_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::number_unsigned_t', 'Type', 'api/basic_json/number_unsigned_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object', 'Function', 'api/basic_json/object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_comparator_t', 'Type', 'api/basic_json/object_comparator_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::object_t', 'Type', 'api/basic_json/object_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator ValueType', 'Operator', 'api/basic_json/operator_ValueType/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator value_t', 'Operator', 'api/basic_json/operator_value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator[]', 'Operator', 'api/basic_json/operator[]/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator=', 'Operator', 'api/basic_json/operator=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator+=', 'Operator', 'api/basic_json/operator+=/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator==', 'Operator', 'api/basic_json/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator!=', 'Operator', 'api/basic_json/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<', 'Operator', 'api/basic_json/operator_lt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=', 'Operator', 'api/basic_json/operator_le/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>', 'Operator', 'api/basic_json/operator_gt/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator>=', 'Operator', 'api/basic_json/operator_ge/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::operator<=>', 'Operator', 'api/basic_json/operator_spaceship/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::out_of_range', 'Class', 'api/basic_json/out_of_range/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::other_error', 'Class', 'api/basic_json/other_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse', 'Function', 'api/basic_json/parse/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_error', 'Class', 'api/basic_json/parse_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parse_event_t', 'Enum', 'api/basic_json/parse_event_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::parser_callback_t', 'Type', 'api/basic_json/parser_callback_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch', 'Method', 'api/basic_json/patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::patch_inplace', 'Method', 'api/basic_json/patch_inplace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::push_back', 'Method', 'api/basic_json/push_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rbegin', 'Method', 'api/basic_json/rbegin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::rend', 'Method', 'api/basic_json/rend/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::sax_parse', 'Function', 'api/basic_json/sax_parse/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::size', 'Method', 'api/basic_json/size/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::start_pos', 'Method', 'api/basic_json/start_pos/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::string_t', 'Type', 'api/basic_json/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::swap', 'Method', 'api/basic_json/swap/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type', 'Method', 'api/basic_json/type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_error', 'Class', 'api/basic_json/type_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::type_name', 'Method', 'api/basic_json/type_name/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::unflatten', 'Method', 'api/basic_json/unflatten/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::update', 'Method', 'api/basic_json/update/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bjdata', 'Function', 'api/basic_json/to_bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bson', 'Function', 'api/basic_json/to_bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_cbor', 'Function', 'api/basic_json/to_cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_msgpack', 'Function', 'api/basic_json/to_msgpack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_bon8', 'Function', 'api/basic_json/to_bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_string', 'Method', 'api/basic_json/to_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::to_ubjson', 'Function', 'api/basic_json/to_ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value', 'Method', 'api/basic_json/value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::value_t', 'Enum', 'api/basic_json/value_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::with_t', 'Type', 'api/basic_json/with_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json::~basic_json', 'Method', 'api/basic_json/~basic_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json', 'Class', 'api/json/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');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::empty', 'Method', 'api/json_pointer/empty/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::front', 'Method', 'api/json_pointer/front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::json_pointer', 'Constructor', 'api/json_pointer/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator==', 'Operator', 'api/json_pointer/operator_eq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator!=', 'Operator', 'api/json_pointer/operator_ne/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/', 'Operator', 'api/json_pointer/operator_slash/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator/=', 'Operator', 'api/json_pointer/operator_slasheq/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator string_t', 'Operator', 'api/json_pointer/operator_string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::operator<=>', 'Operator', 'api/json_pointer/operator_spaceship/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::parent_pointer', 'Method', 'api/json_pointer/parent_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_back', 'Method', 'api/json_pointer/pop_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::pop_front', 'Method', 'api/json_pointer/pop_front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_back', 'Method', 'api/json_pointer/push_back/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::push_front', 'Method', 'api/json_pointer/push_front/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::string_t', 'Type', 'api/json_pointer/string_t/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_pointer::to_string', 'Method', 'api/json_pointer/to_string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax', 'Class', 'api/json_sax/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::binary', 'Method', 'api/json_sax/binary/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::boolean', 'Method', 'api/json_sax/boolean/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_array', 'Method', 'api/json_sax/end_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::end_object', 'Method', 'api/json_sax/end_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::key', 'Method', 'api/json_sax/key/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::null', 'Method', 'api/json_sax/null/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_float', 'Method', 'api/json_sax/number_float/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_integer', 'Method', 'api/json_sax/number_integer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::number_unsigned', 'Method', 'api/json_sax/number_unsigned/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::parse_error', 'Method', 'api/json_sax/parse_error/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_array', 'Method', 'api/json_sax/start_array/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::start_object', 'Method', 'api/json_sax/start_object/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('json_sax::string', 'Method', 'api/json_sax/string/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json', 'Literal', 'api/operator_literal_json/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator""_json_pointer', 'Literal', 'api/operator_literal_json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('operator<<', 'Operator', 'api/operator_ltlt/index.html');
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_map', 'Class', 'api/ordered_map/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::formatter<basic_json>', 'Class', 'api/basic_json/std_formatter/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::hash<basic_json>', 'Class', 'api/basic_json/std_hash/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('std::swap<basic_json>', 'Function', 'api/basic_json/std_swap/index.html');
-- Features
INSERT INTO searchIndex(name, type, path) VALUES ('Arbitrary Type Conversions', 'Guide', 'features/arbitrary_types/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats', 'Guide', 'features/binary_formats/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BJData', 'Guide', 'features/binary_formats/bjdata/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BSON', 'Guide', 'features/binary_formats/bson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: CBOR', 'Guide', 'features/binary_formats/cbor/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: MessagePack', 'Guide', 'features/binary_formats/messagepack/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: BON8', 'Guide', 'features/binary_formats/bon8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Formats: UBJSON', 'Guide', 'features/binary_formats/ubjson/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Binary Values', 'Guide', 'features/binary_values/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Comments', 'Guide', 'features/comments/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access', 'Guide', 'features/element_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Access with default value: value', 'Guide', 'features/element_access/default_value/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Checked access: at', 'Guide', 'features/element_access/checked_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Element Access: Unchecked access: operator[]', 'Guide', 'features/element_access/unchecked_access/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Exceptions', 'Guide', 'home/exceptions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Migration Guide', 'Guide', 'integration/migration_guide/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: CMake', 'Guide', 'integration/cmake/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Header only', 'Guide', 'integration/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Package Managers', 'Guide', 'integration/package_managers/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Integration: Pkg-config', 'Guide', 'integration/pkg-config/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Iterators', 'Guide', 'features/iterators/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Merge Patch', 'Guide', 'features/merge_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Patch and Diff', 'Guide', 'features/json_patch/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON Pointer', 'Guide', 'features/json_pointer/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('nlohmann Namespace', 'Guide', 'features/namespace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Types', 'Guide', 'features/types/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Types: Number Handling', 'Guide', 'features/types/number_handling/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Object Order', 'Guide', 'features/object_order/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Performance', 'Guide', 'features/performance/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing', 'Guide', 'features/parsing/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: JSON Lines', 'Guide', 'features/parsing/json_lines/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parser Callbacks', 'Guide', 'features/parsing/parser_callbacks/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Parsing and Exceptions', 'Guide', 'features/parsing/parse_exceptions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: SAX Interface', 'Guide', 'features/parsing/sax_interface/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Parsing: Untrusted Input', 'Guide', 'features/parsing/untrusted_input/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Runtime Assertions', 'Guide', 'features/assertions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Specializing enum conversion', 'Guide', 'features/enum_conversion/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Supported Macros', 'Guide', 'features/macros/index.html');
-- Macros
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_ASSERT', 'Macro', 'api/macros/json_assert/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_BRACE_INIT_COPY_SEMANTICS', 'Macro', 'api/macros/json_brace_init_copy_semantics/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_CATCH_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DELETE_DEPRECATED_FUNCTIONS', 'Macro', 'api/macros/json_delete_deprecated_functions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTICS', 'Macro', 'api/macros/json_diagnostics/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DIAGNOSTIC_POSITIONS', 'Macro', 'api/macros/json_diagnostic_positions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_ENUM_SERIALIZATION', 'Macro', 'api/macros/json_disable_enum_serialization/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_DISABLE_TUPLE_REFERENCE_CONVERSION', 'Macro', 'api/macros/json_disable_tuple_reference_conversion/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_11', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_14', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_17', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_20', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_23', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_CPP_26', 'Macro', 'api/macros/json_has_cpp_11/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_EXPERIMENTAL_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_FILESYSTEM', 'Macro', 'api/macros/json_has_filesystem/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_RANGES', 'Macro', 'api/macros/json_has_ranges/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STATIC_RTTI', 'Macro', 'api/macros/json_has_static_rtti/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_STD_FORMAT', 'Macro', 'api/macros/json_has_std_format/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_HAS_THREE_WAY_COMPARISON', 'Macro', 'api/macros/json_has_three_way_comparison/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NOEXCEPTION', 'Macro', 'api/macros/json_noexception/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_AUTOMATIC_UDLS', 'Macro', 'api/macros/json_no_automatic_udls/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_IO', 'Macro', 'api/macros/json_no_io/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_THREAD_LOCAL', 'Macro', 'api/macros/json_no_thread_local/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_BINARY_UTF8', 'Macro', 'api/macros/json_strict_binary_utf8/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_GLOBAL_UDLS', 'Macro', 'api/macros/json_use_global_udls/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_IMPLICIT_CONVERSIONS', 'Macro', 'api/macros/json_use_implicit_conversions/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON', 'Macro', 'api/macros/json_use_legacy_discarded_value_comparison/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS', 'Macro', 'api/macros/json_use_objects_for_enum_keyed_maps/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_USE_SIMDUTF', 'Macro', 'api/macros/json_use_simdutf/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('Macros', 'Macro', 'api/macros/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_derived_type/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT', 'Macro', 'api/macros/nlohmann_define_type_non_intrusive/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES', 'Macro', 'api/macros/nlohmann_define_type_with_names/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE', 'Macro', 'api/macros/nlohmann_json_namespace/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_BEGIN', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_END', 'Macro', 'api/macros/nlohmann_json_namespace_begin/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_NAMESPACE_NO_VERSION', 'Macro', 'api/macros/nlohmann_json_namespace_no_version/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM', 'Macro', 'api/macros/nlohmann_json_serialize_enum/index.html');
INSERT INTO searchIndex(name, type, path) VALUES ('NLOHMANN_JSON_SERIALIZE_ENUM_STRICT', 'Macro', 'api/macros/nlohmann_json_serialize_enum_strict/index.html');
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');
-532
View File
@@ -1,532 +0,0 @@
#!/usr/bin/env python3
"""Generate the Dash docset search index from the mkdocs sources."""
import argparse
import glob
import hashlib
import html
import os
import re
import shutil
import sqlite3
import sys
import tarfile
import urllib.parse
import urllib.request
import yaml
HERE = os.path.dirname(os.path.abspath(__file__))
MKDOCS_YML = os.path.join(HERE, '..', 'mkdocs', 'mkdocs.yml')
PAGES = os.path.join(HERE, '..', 'mkdocs', 'docs')
# api pages whose (name, type) cannot be derived by the heuristics
OVERRIDES = {
}
DOCSET = 'JSON_for_Modern_C++.docset'
TITLE_SUFFIX = ' - JSON for Modern C++</title>'
# CSS rules appended to the stylesheet: hide navigation items and fix spacing
# hide the navigation (the documentation browser has its own); Material's class selectors would win over element
# selectors, hence the classes and !important
CSS_PATCH = (
'\n\n.md-header, .md-footer, .md-tabs, .md-sidebar--primary, .md-content__button { display: none !important; }'
'\n\n.md-sidebar--secondary, .md-main__inner { top: 0 !important; margin-top: 0 !important; }'
)
USER_AGENT = ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/124.0 Safari/537.36')
CONTENT_TYPE_EXT = {'image/svg+xml': '.svg', 'image/png': '.png', 'image/jpeg': '.jpg',
'image/gif': '.gif', 'image/webp': '.webp'}
# remote loads that are allowed to remain (URL -> reason)
ALLOWED_REMOTE = {
# Material only loads this polyfill if the browser has no ResizeObserver
'https://unpkg.com/resize-observer-polyfill': 'fallback for browsers without ResizeObserver',
}
problems = []
def problem(page, reason) -> None:
"""Record a problem; all problems are reported at the end."""
problems.append(f'generate_docset.py: {page}: {reason}')
class Loader(yaml.SafeLoader):
"""YAML loader that tolerates the custom tags used in mkdocs.yml."""
Loader.add_multi_constructor('', lambda loader, suffix, node: None)
def walk_nav(items, groups=()):
"""Yield (group titles, nav title or None, md path) for all nav leaves."""
for item in items:
if isinstance(item, str):
yield list(groups), None, item
elif isinstance(item, dict):
for title, value in item.items():
if isinstance(value, list):
yield from walk_nav(value, groups + (str(title),))
else:
yield list(groups), str(title), value
def page_path(md_path) -> str:
"""Map a markdown path to the HTML path of the rendered page."""
if md_path.endswith('/index.md'):
return md_path[:-len('index.md')] + 'index.html'
return md_path[:-len('.md')] + '/index.html'
def read_page(md_path) -> str:
"""Read a page (resolving a snippet include), return '' if it does not exist."""
try:
with open(os.path.join(PAGES, md_path), encoding='utf-8') as f:
text = f.read()
m = re.match(r'--8<-- "(.+)"\s*$', text)
if m:
with open(os.path.join(PAGES, m.group(1)), encoding='utf-8') as f:
text = f.read()
return text
except OSError:
return ''
def clean(text) -> str:
"""Strip tags and entities from a heading and normalize whitespace."""
text = text.replace('\\>', '\x00') # escaped '>' is not the end of a tag
text = html.unescape(re.sub(r'</?[a-zA-Z][^>]*>', '', text)).replace('\x00', '>')
return re.sub(r'\s+', ' ', text).strip()
def strip_fences(text) -> str:
"""Remove fenced code blocks (their lines may start with '# ')."""
return re.sub(r'^(```|~~~).*?^\1[^\n]*$', '', text, flags=re.M | re.S)
def get_h1(text):
"""Return the cleaned first H1 of a page or None."""
body = strip_fences(text)
m = re.search(r'^# (.+)$', body, flags=re.M)
if m:
return clean(m.group(1))
m = re.search(r'<h1>(.*?)</h1>', body, flags=re.S)
return clean(m.group(1)) if m else None
def split_top_level(text, seps=','):
"""Split at separators that are not nested in <>, () or []."""
parts, depth, current = [], 0, ''
for c in text:
if c in '<([':
depth += 1
elif c in '>)]':
depth -= 1
if c in seps and depth <= 0:
parts.append(current)
current = ''
else:
current += c
parts.append(current)
return [p.strip() for p in parts if p.strip()]
OPERATOR_SYMBOLS = ('<=>', '<<', '>>', '<=', '>=', '<', '>')
def api_names(h1) -> list:
"""Derive the entry names from the H1 of an api page."""
# hide the angle brackets of operators from the nesting detection
for i, sym in enumerate(OPERATOR_SYMBOLS):
h1 = h1.replace('operator' + sym, f'operator\x01{i}\x01')
names = []
for part in split_top_level(h1):
name = part.replace('\\', '').replace('nlohmann::', '')
name = re.sub(r'\x01(\d)\x01', lambda m: OPERATOR_SYMBOLS[int(m.group(1))], name)
# drop qualifiers like "operator<<(basic_json)", but keep "operator()"
if not name.endswith('operator()'):
name = re.sub(r'(?<=\w|[<>=!+\-*/\[\]])\([^()]*\)$', '', name)
if name not in names:
names.append(name)
return names
def first_cpp_block(text):
"""Return the first ```cpp block following the H1."""
m = re.search(r'^# .*?^```cpp\n(.*?)^```', text, flags=re.M | re.S)
return m.group(1) if m else None
def api_type(name, decl):
"""Determine the Dash entry type from name and declaration."""
parts = name.split('::')
last = parts[-1]
if name.startswith('operator""'):
return 'Literal'
if last.startswith('operator'):
return 'Operator'
if decl is None:
return None
if re.search(r'\benum\b', decl):
return 'Enum'
if re.search(r'^\s*(template\s*<.*>\s*)?(class|struct)\s+\w+\s*(final\b|[:{;<]|$)', decl, flags=re.M):
return 'Class'
if re.search(r'\busing\s+\w+\s*=', decl) or 'typedef' in decl:
return 'Type'
if len(parts) > 1 and last == parts[-2]:
return 'Constructor'
if last.startswith('~'):
return 'Method'
if re.search(r'\bstatic\b', decl) or len(parts) == 1 or parts[0] == 'std':
return 'Function'
return 'Method'
def macro_names(h1) -> list:
"""Split the H1 of a macro page into macro names."""
return [n.strip() for n in re.split(r'[,/]', h1) if n.strip()]
def api_entries(md_path):
"""Return the (name, type) pairs of an api page."""
if md_path in OVERRIDES:
return OVERRIDES[md_path]
if md_path == 'api/macros/index.md':
return [('Macros', 'Macro')]
text = read_page(md_path)
h1 = get_h1(text)
if not h1:
problem(md_path, 'no H1 found')
return []
if md_path.startswith('api/macros/'):
return [(n, 'Macro') for n in macro_names(h1)]
names = api_names(h1)
if not names:
problem(md_path, 'no names found')
return []
decl = first_cpp_block(text)
result = []
for name in names:
kind = api_type(name, decl)
if kind is None:
problem(md_path, f'no type determinable for {name}')
else:
result.append((name, kind))
return result
def guide_entries(nav):
"""Yield (name, type, md path) for all non-api nav pages."""
for groups, title, md_path in walk_nav(nav):
if md_path.startswith('api/') or md_path == 'index.md':
continue
if not md_path.endswith('.md'):
continue
groups = groups[1:] # drop the top-level tab
if title is None:
title = get_h1(read_page(md_path))
if not title:
problem(md_path, 'no title found')
continue
# "Parsing: Parsing Untrusted Input" -> "Parsing: Untrusted Input"
if groups and title.startswith(groups[-1] + ' ') and title[len(groups[-1]) + 1:][:1].isupper():
title = title[len(groups[-1]) + 1:]
if md_path.endswith('/index.md') and groups and title == groups[-1]:
name = ': '.join(groups)
else:
name = ': '.join(groups + [title])
yield name, 'Guide', md_path
def build_entries() -> list:
"""Return the sorted list of (name, type, path) index entries."""
nav = load_mkdocs_yml()['nav']
entries = set()
for name, kind, md_path in guide_entries(nav):
entries.add((name, kind, page_path(md_path)))
api_root = os.path.join(PAGES, 'api')
on_disk = set()
for root, _, files in os.walk(api_root):
for file in files:
if file.endswith('.md'):
rel = os.path.relpath(os.path.join(root, file), PAGES)
on_disk.add(rel.replace(os.sep, '/'))
for _, _, md_path in walk_nav(nav):
if md_path.startswith('api/') and md_path not in on_disk:
problem(md_path, 'listed in nav but missing on disk')
for md_path in sorted(on_disk):
for name, kind in api_entries(md_path):
entries.add((name, kind, page_path(md_path)))
return sorted(entries)
def write_index(entries, out) -> None:
"""Write the entries into a SQLite search index."""
if os.path.exists(out):
os.remove(out)
con = sqlite3.connect(out)
con.execute('CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT)')
con.execute('CREATE UNIQUE INDEX anchor ON searchIndex (name, type, path)')
con.executemany('INSERT INTO searchIndex(name, type, path) VALUES (?, ?, ?)', entries)
con.commit()
con.close()
def html_files(root):
"""Yield all HTML files below root."""
for base, _, files in os.walk(root):
for file in files:
if file.endswith('.html'):
yield os.path.join(base, file)
def read(path) -> str:
with open(path, encoding='utf-8') as f:
return f.read()
def write(path, text) -> None:
with open(path, 'w', encoding='utf-8') as f:
f.write(text)
def remove_source_widget(docs) -> None:
"""Drop data-md-component=source so Material does not query api.github.com for the stars and version."""
pattern = re.compile(r'\sdata-md-component=(?:"source"|source\b)')
for path in html_files(docs):
text = read(path)
new = pattern.sub('', text)
if new != text:
write(path, new)
def patch_titles(docs, entries) -> None:
"""Strip the site name from all titles; use the index names where available."""
names = {}
for name, _, path in entries:
names.setdefault(path, []).append(name)
for file in html_files(docs):
rel = os.path.relpath(file, docs).replace(os.sep, '/')
text = read(file).replace(TITLE_SUFFIX, '</title>')
if rel in names:
title = html.escape(', '.join(names[rel]), quote=False)
text = re.sub(r'<title>.*?</title>', lambda _: f'<title>{title}</title>', text, count=1, flags=re.S)
write(file, text)
IMG_RE = re.compile(r'<img\b[^>]*>', re.I)
ATTR_RE = r'''(?:{0})\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))'''
def attr(tag, name):
"""Return the value of an attribute in a tag or None."""
m = re.search(r'(?<![\w-])' + ATTR_RE.format(name), tag, flags=re.I)
return next(g for g in m.groups() if g is not None) if m else None
def is_remote(url) -> bool:
return re.match(r'(https?:)?//', url.strip(), flags=re.I) is not None
def download(url, docs) -> str:
"""Download url into assets/external and return the path relative to docs."""
u = urllib.parse.urlparse(url if not url.startswith('//') else 'https:' + url)
if u.scheme.lower() not in ('http', 'https'):
raise ValueError(f'not an http(s) URL: {url}')
req = urllib.request.Request(u.geturl(), headers={'User-Agent': USER_AGENT})
# (the scheme is checked above)
with urllib.request.urlopen(req, timeout=20) as r: # nosec B310
data = r.read()
ctype = r.headers.get_content_type()
path = urllib.parse.unquote(u.path).lstrip('/')
ext = os.path.splitext(path)[1]
if u.query or not ext or path.endswith('/'):
digest = hashlib.sha1(url.encode(), usedforsecurity=False).hexdigest()[:12]
path = os.path.join(os.path.dirname(path), digest + CONTENT_TYPE_EXT.get(ctype, ext or '.bin'))
rel = os.path.normpath(os.path.join('assets', 'external', u.hostname, path))
out = os.path.join(docs, rel)
os.makedirs(os.path.dirname(out), exist_ok=True)
with open(out, 'wb') as f:
f.write(data)
return rel.replace(os.sep, '/')
def localize_images(docs) -> None:
"""Download remote images and rewrite their src; drop them on failure."""
cache = {}
for file in html_files(docs):
text = read(file)
def repl(m):
tag = m.group(0)
src = attr(tag, 'src')
if src is None or not is_remote(src):
return tag
if src not in cache:
try:
cache[src] = download(src, docs)
except Exception as e: # noqa: BLE001
print(f'generate_docset.py: warning: cannot download {src}: {e}', file=sys.stderr)
cache[src] = None
if cache[src] is None:
return attr(tag, 'alt') or ''
local = os.path.relpath(os.path.join(docs, cache[src]), os.path.dirname(file))
local = local.replace(os.sep, '/')
return re.sub(ATTR_RE.format('src'), lambda _: f'src="{local}"', tag, count=1, flags=re.I)
new = IMG_RE.sub(repl, text)
if new != text:
write(file, new)
def load_mkdocs_yml() -> dict:
"""Load mkdocs.yml, ignoring tags like !ENV and !!python/name."""
with open(MKDOCS_YML, encoding='utf-8') as f:
# (Loader is a yaml.SafeLoader)
return yaml.load(f, Loader=Loader) # nosec B506
def localize_site_urls(docs, site_url) -> None:
"""Load assets that the theme's JavaScript references by absolute site URL from the docset.
The privacy plugin rewrites the mermaid loader to "<site_url>assets/external/unpkg.com/mermaid@11/...", so the
docset would fetch mermaid from the live site. __md_scope is the site root that Material defines in every page.
"""
pattern = re.compile(r'"' + re.escape(site_url) + r'(assets/[^"]*)"')
for base, _, files in os.walk(docs):
for file in (f for f in files if f.endswith('.js')):
path = os.path.join(base, file)
text = read(path)
new = pattern.sub(r'new URL("\1",__md_scope).href', text)
if new != text:
write(path, new)
def remote_loads(docs, site_url) -> list:
"""Return 'file: url' strings for resources that would be loaded remotely."""
found = []
cdn = re.compile(r'https://(?:unpkg\.com|cdn\.jsdelivr\.net|cdnjs\.cloudflare\.com|'
r'fonts\.googleapis\.com|fonts\.gstatic\.com)/[^\s"\'`)\\]*')
css_url = re.compile(r'url\(\s*["\']?((?:https?:)?//[^)"\']+)', re.I)
css_import = re.compile(r'@import\s+(?:url\(\s*)?["\']?((?:https?:)?//[^)"\'; ]+)', re.I)
for base, _, files in os.walk(docs):
for file in files:
path = os.path.join(base, file)
rel = os.path.relpath(path, docs)
urls = []
if file.endswith('.html'):
text = read(path)
for m in re.finditer(r'<[a-zA-Z][^>]*>', text):
tag = m.group(0)
for name in ('src', 'poster'):
v = attr(tag, name)
if v and is_remote(v):
urls.append(v)
v = attr(tag, 'srcset')
if v:
urls += [c.split()[0] for c in v.split(',') if c.strip() and is_remote(c.strip())]
if re.match(r'<link\b', tag, flags=re.I):
rel_attr = (attr(tag, 'rel') or '').lower()
v = attr(tag, 'href')
if v and is_remote(v) and re.search(r'stylesheet|icon|preload|modulepreload|manifest', rel_attr):
urls.append(v)
urls += css_url.findall(text) + css_import.findall(text)
elif file.endswith('.css'):
text = read(path)
urls += css_url.findall(text) + css_import.findall(text)
elif file.endswith('.js'):
text = read(path)
urls += cdn.findall(text)
urls += re.findall(re.escape(site_url) + r'assets/[^\s"\'`)\\]*', text)
found += [f'{rel}: {u}' for u in urls if u not in ALLOWED_REMOTE]
return found
def make_docset(site, out_dir) -> int:
entries = build_entries()
if problems:
print('\n'.join(problems), file=sys.stderr)
return 1
docset = os.path.join(out_dir, DOCSET)
docs = os.path.join(docset, 'Contents', 'Resources', 'Documents')
if os.path.exists(docset):
shutil.rmtree(docset)
shutil.copytree(site, docs)
for icon in ('icon.png', 'icon@2x.png'):
shutil.copy(os.path.join(HERE, icon), docset)
shutil.copy(os.path.join(HERE, 'Info.plist'), os.path.join(docset, 'Contents'))
write_index(entries, os.path.join(docset, 'Contents', 'Resources', 'docSet.dsidx'))
# patch CSS to hide navigation items and fix spacing
css = glob.glob(os.path.join(docs, 'assets', 'stylesheets', 'main.*.min.css'))
if len(css) != 1:
print(f'generate_docset.py: expected exactly one main.*.min.css, found {len(css)}', file=sys.stderr)
return 1
with open(css[0], 'a', encoding='utf-8') as f:
f.write(CSS_PATCH)
patch_titles(docs, entries)
remove_source_widget(docs)
for sitemap in glob.glob(os.path.join(docs, 'sitemap.*')):
os.remove(sitemap)
# make the docset self-contained
site_url = load_mkdocs_yml()['site_url']
localize_images(docs)
localize_site_urls(docs, site_url)
remote = remote_loads(docs, site_url)
if remote:
print('generate_docset.py: remote resources remain in the docset:', file=sys.stderr)
print('\n'.join(' ' + r for r in remote), file=sys.stderr)
return 1
return 0
def make_tgz(out_dir) -> int:
docset = os.path.join(out_dir, DOCSET)
if not os.path.isdir(docset):
print(f'generate_docset.py: {docset} does not exist', file=sys.stderr)
return 1
with tarfile.open(os.path.join(out_dir, 'JSON_for_Modern_C++.tgz'), 'w:gz') as tar:
tar.add(docset, arcname=DOCSET, filter=lambda i: None if os.path.basename(i.name) == '.DS_Store' else i)
return 0
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
sub = parser.add_subparsers(dest='command', required=True)
sub.add_parser('list', help='print the index entries as TSV')
p = sub.add_parser('index', help='write the SQLite search index')
p.add_argument('out')
p = sub.add_parser('docset', help='build the docset from a built mkdocs site')
p.add_argument('site_dir')
p.add_argument('out_dir')
p = sub.add_parser('tgz', help='pack the docset into a tarball')
p.add_argument('out_dir')
args = parser.parse_args()
if args.command == 'docset':
return make_docset(args.site_dir, args.out_dir)
if args.command == 'tgz':
return make_tgz(args.out_dir)
entries = build_entries()
if problems:
print('\n'.join(problems), file=sys.stderr)
return 1
if args.command == 'list':
for entry in entries:
print('\t'.join(entry))
elif args.command == 'index':
write_index(entries, args.out)
return 0
if __name__ == '__main__':
sys.exit(main())
-1
View File
@@ -17,7 +17,6 @@ build: style_check
style_check: style_check:
@cd docs ; ../venv/bin/python3 ../scripts/check_structure.py @cd docs ; ../venv/bin/python3 ../scripts/check_structure.py
@venv/bin/python3 ../docset/generate_docset.py list > /dev/null
# check that all Mermaid diagrams parse (needs Node.js) # check that all Mermaid diagrams parse (needs Node.js)
# This target is used in the CI (ci_test_documentation_mermaid). # This target is used in the CI (ci_test_documentation_mermaid).
+1 -3
View File
@@ -241,6 +241,4 @@ Strong exception safety: if an exception occurs, the original value stays intact
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`operator[]`](operator[].md), [`value`](value.md), [`find`](find.md), and other lookup already supported by [`operator[]`](operator[].md), [`value`](value.md), [`find`](find.md), and other lookup
functions. functions.
4. Added in version 2.0.0. Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of 4. Added in version 2.0.0.
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is not
a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
+12 -1
View File
@@ -64,7 +64,18 @@ values of that type directly to a `basic_json` instance, and they will automatic
rather than arrays: rather than arrays:
```cpp ```cpp
using custom_json = nlohmann::ordered_json::with_binary_t<std::vector<std::byte>>; using custom_json = nlohmann::basic_json<
nlohmann::ordered_map, // ObjectType
std::vector, // ArrayType
std::string, // StringType
bool, // BooleanType
std::int64_t, // NumberIntegerType
std::uint64_t, // NumberUnsignedType
double, // NumberFloatType
std::allocator, // AllocatorType
nlohmann::adl_serializer,
std::vector<std::byte> // Custom BinaryType
>;
std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}}; std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}};
custom_json j = data; // Creates a binary value, not an array custom_json j = data; // Creates a binary value, not an array
@@ -10,10 +10,6 @@ bool contains(KeyType&& key) const;
// (3) // (3)
bool contains(const json_pointer& ptr) const; bool contains(const json_pointer& ptr) const;
// (4)
template<typename T>
bool contains(T) const = delete;
``` ```
1. Check whether an element exists in a JSON object with a key equivalent to `key`. If the element is not found or the 1. Check whether an element exists in a JSON object with a key equivalent to `key`. If the element is not found or the
@@ -21,9 +17,6 @@ bool contains(T) const = delete;
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and 2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
`#!cpp typename object_comparator_t::is_transparent` denotes a type. `#!cpp typename object_comparator_t::is_transparent` denotes a type.
3. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value. 3. Check whether the given JSON pointer `ptr` can be resolved in the current JSON value.
4. Deleted: this overload is only available if `T` is an integral type and is declared as deleted, so that a call with
an integer `key` (for example, `#!cpp j.contains(0)`) fails to compile. Otherwise, the integer literal `0` would convert to
a null `#!cpp const char*` and, from there, to the key type, causing undefined behavior at runtime.
## Template parameters ## Template parameters
@@ -46,7 +39,6 @@ bool contains(T) const = delete;
is not an object, `#!cpp false` is returned. is not an object, `#!cpp false` is returned.
2. See 1. 2. See 1.
3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise. 3. `#!cpp true` if the JSON pointer can be resolved to a stored value, `#!cpp false` otherwise.
4. Deleted; a call with an integral argument does not compile.
## Exception safety ## Exception safety
@@ -57,7 +49,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
1. The function does not throw exceptions. 1. The function does not throw exceptions.
2. The function does not throw exceptions. 2. The function does not throw exceptions.
3. The function does not throw exceptions. 3. The function does not throw exceptions.
4. Deleted; a call with an integral argument does not compile.
## Complexity ## Complexity
-7
View File
@@ -7,19 +7,12 @@ size_type count(const typename object_t::key_type& key) const;
// (2) // (2)
template<typename KeyType> template<typename KeyType>
size_type count(KeyType&& key) const; size_type count(KeyType&& key) const;
// (3)
template<typename T>
size_type count(T) const = delete;
``` ```
1. Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will 1. Returns the number of elements with key `key`. If `ObjectType` is the default `std::map` type, the return value will
always be `0` (`key` was not found) or `1` (`key` was found). always be `0` (`key` was not found) or `1` (`key` was found).
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and 2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
`#!cpp typename object_comparator_t::is_transparent` denotes a type. `#!cpp typename object_comparator_t::is_transparent` denotes a type.
3. Deleted: this overload is only available if `T` is an integral type and is declared as deleted, so that a call with
an integer `key` (for example, `#!cpp j.count(0)`) fails to compile. Otherwise, the integer literal `0` would convert to
a null `#!cpp const char*` and, from there, to the key type, causing undefined behavior at runtime.
## Template parameters ## Template parameters
@@ -37,8 +37,8 @@ ignore
: ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped : ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped
keep keep
: keep invalid UTF-8 sequences unchanged; only meaningful for the binary formats mentioned above, since : keep invalid UTF-8 sequences unchanged; only meaningful for the binary formats mentioned above, since [`dump`]
[`dump`](dump.md) itself must produce text, and `keep` there writes the ill-formed bytes to the output as is, so the (dump.md) itself must produce text, and `keep` there writes the ill-formed bytes to the output as is, so the
result is then not valid UTF-8 (but still equals the input bytes exactly, including around any well-formed result is then not valid UTF-8 (but still equals the input bytes exactly, including around any well-formed
characters, which are still escaped as usual) characters, which are still escaped as usual)
-9
View File
@@ -10,21 +10,12 @@ template<typename KeyType>
iterator find(KeyType&& key); iterator find(KeyType&& key);
template<typename KeyType> template<typename KeyType>
const_iterator find(KeyType&& key) const; const_iterator find(KeyType&& key) const;
// (3)
template<typename T>
iterator find(T) = delete;
template<typename T>
const_iterator find(T) const = delete;
``` ```
1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the 1. Finds an element in a JSON object with a key equivalent to `key`. If the element is not found or the
JSON value is not an object, `end()` is returned. JSON value is not an object, `end()` is returned.
2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and 2. See 1. This overload is only available if `KeyType` is comparable with `#!cpp typename object_t::key_type` and
`#!cpp typename object_comparator_t::is_transparent` denotes a type. `#!cpp typename object_comparator_t::is_transparent` denotes a type.
3. Deleted: this overload is only available if `T` is an integral type and is declared as deleted, so that a call with
an integer `key` (for example, `#!cpp j.find(0)`) fails to compile. Otherwise, the integer literal `0` would convert to
a null `#!cpp const char*` and, from there, to the key type, causing undefined behavior at runtime.
## Template parameters ## Template parameters
@@ -124,8 +124,7 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_bjdata` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_bjdata` with a pointer and a length as first two parameters, which has been
deprecated in version 3.13.0. In version 4.0.0, this overload deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_bjdata(ptr, len, ...);` with `#!cpp from_bjdata(ptr, ptr+len, ...);`. `#!cpp from_bjdata(ptr, len, ...);` with `#!cpp from_bjdata(ptr, ptr+len, ...);`.
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
+1 -2
View File
@@ -110,8 +110,7 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_bon8` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_bon8` with a pointer and a length as first two parameters, which has been
deprecated in version 3.13.0. In version 4.0.0, this overload deprecated in version 3.13.0. This overload will be removed in version 4.0.0. Please replace all calls like
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_bon8(ptr, len, ...);` with `#!cpp from_bon8(ptr, ptr+len, ...);`. `#!cpp from_bon8(ptr, len, ...);` with `#!cpp from_bon8(ptr, ptr+len, ...);`.
You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated You should be warned by your compiler with a `-Wdeprecated-declarations` warning if you are using a deprecated
+1 -2
View File
@@ -126,8 +126,7 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_bson` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_bson` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. In version 4.0.0, this overload deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_bson(ptr, len, ...);` with `#!cpp from_bson(ptr, ptr+len, ...);`. `#!cpp from_bson(ptr, len, ...);` with `#!cpp from_bson(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_bson` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_bson` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
+1 -2
View File
@@ -135,8 +135,7 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_cbor` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_cbor` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. In version 4.0.0, this overload deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_cbor(ptr, len, ...);` with `#!cpp from_cbor(ptr, ptr+len, ...);`. `#!cpp from_cbor(ptr, len, ...);` with `#!cpp from_cbor(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_cbor` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_cbor` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
@@ -127,8 +127,7 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_msgpack` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_msgpack` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. In version 4.0.0, this overload deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_msgpack(ptr, len, ...);` with `#!cpp from_msgpack(ptr, ptr+len, ...);`. `#!cpp from_msgpack(ptr, len, ...);` with `#!cpp from_msgpack(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_msgpack` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_msgpack` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
@@ -125,8 +125,7 @@ Linear in the size of the input.
!!! warning "Deprecation" !!! warning "Deprecation"
- Overload (2) replaces calls to `from_ubjson` with a pointer and a length as first two parameters, which has been - Overload (2) replaces calls to `from_ubjson` with a pointer and a length as first two parameters, which has been
deprecated in version 3.8.0. In version 4.0.0, this overload deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
will be deleted (`= delete`) rather than removed. Please replace all calls like
`#!cpp from_ubjson(ptr, len, ...);` with `#!cpp from_ubjson(ptr, ptr+len, ...);`. `#!cpp from_ubjson(ptr, len, ...);` with `#!cpp from_ubjson(ptr, ptr+len, ...);`.
- Overload (2) replaces calls to `from_ubjson` with a pair of iterators as their first parameter, which has been - Overload (2) replaces calls to `from_ubjson` with a pair of iterators as their first parameter, which has been
deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like deprecated in version 3.8.0. This overload will be removed in version 4.0.0. Please replace all calls like
@@ -62,5 +62,5 @@ same name. Hidden members remain accessible via [`as_base_class`](as_base_class.
## Version history ## Version history
- Added in version 3.11.3. - Added in version 3.12.0.
- Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class. - Made a public member type in version 3.13.0; it was private before, so it could not be named outside the class.
@@ -137,10 +137,13 @@ Strong exception safety: if an exception occurs, the original value stays intact
When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing
leaf), each missing level is created as an array or an object depending on whether the corresponding leaf), each missing level is created as an array or an object depending on whether the corresponding
pointer token parses as a non-negative integer: a numeric token creates an array, a non-numeric token pointer token is a valid array index: the token `0`, a sequence of digits that does not begin with `0`,
creates an object. For example, on an initially `#!json null` value, `/foo/0/0/0` creates nested arrays, or the token `-` creates an array, and every other token creates an object. For example, on an
while `/foo/one/one/one` creates nested objects. This is not specified by the JSON Pointer RFC; it is initially `#!json null` value, `/foo/0/0/0` creates nested arrays, while `/foo/one/one/one` creates
this library's own, intentional disambiguation rule. See also [JSON Pointer](../../features/json_pointer.md). nested objects. Tokens such as `01` or the empty token cannot be array indices (cf. RFC 6901, Sect. 4)
and therefore create objects, just as they would if the level already existed as an object. This is not
specified by the JSON Pointer RFC; it is this library's own, intentional disambiguation rule. See also
[JSON Pointer](../../features/json_pointer.md).
!!! warning "Deprecation" !!! warning "Deprecation"
@@ -286,6 +289,4 @@ Strong exception safety: if an exception occurs, the original value stays intact
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as 3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions. already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since 4. Added in version 2.0.0. A missing array index in the const version is guarded by a runtime assertion since
version 3.13.0. Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of version 3.13.0.
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is not
a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
-3
View File
@@ -112,6 +112,3 @@ is thrown. In any case, the original value is not changed: the patch is applied
location has a non-object/non-array parent in version 3.13.0. location has a non-object/non-array parent in version 3.13.0.
- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper - Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper
prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0. prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0.
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
@@ -110,6 +110,3 @@ function throws an exception.
location has a non-object/non-array parent in version 3.13.0. location has a non-object/non-array parent in version 3.13.0.
- Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper - Added [`out_of_range.414`](../../home/exceptions.md#jsonexceptionout_of_range414) and rejected a "move" operation whose "from" location is a proper
prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0. prefix of its "path" location instead of silently producing a corrupted result in version 3.13.0.
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
+1 -4
View File
@@ -96,9 +96,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
## Return value ## Return value
`#!cpp true` if the input was parsed without errors and no SAX event returned `#!cpp false`; `#!cpp false` otherwise. return value of the last processed SAX event
In particular, the result is `#!cpp false` for input with errors, even if the SAX parser recovered from all of them
(see [error recovery](../../features/parsing/error_recovery.md)).
## Exception safety ## Exception safety
@@ -147,7 +145,6 @@ A UTF-8 byte order mark is silently ignored.
- Added `ignore_trailing_commas` in version 3.13.0. - Added `ignore_trailing_commas` in version 3.13.0.
- Added `tag_handler` in version 3.13.0. - Added `tag_handler` in version 3.13.0.
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0. - Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
- Recovering from parse errors (see [`parse_error`](../json_sax/parse_error.md)) added in version 3.13.0.
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0. - Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
- `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right - `JSON_PRECISE_STREAM_POSITION` added in version 3.13.0 to optionally leave a `#!cpp std::istream` positioned right
after the parsed value when `strict` is `#!cpp false`. after the parsed value when `strict` is `#!cpp false`.
@@ -41,8 +41,6 @@ With (2), the bytes written before the exception remain in the output adapter.
above 9223372036854775807, which BON8 cannot represent above 9223372036854775807, which BON8 cannot represent
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not - Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if `j` contains a string that is not
valid UTF-8 valid UTF-8
- Throws [type_error.321](../../home/exceptions.md#jsonexceptiontype_error321) if `j` or a value nested in it is
discarded; example: `"cannot serialize discarded value to BON8"`
## Complexity ## Complexity
+2 -6
View File
@@ -36,9 +36,8 @@ The function can throw the following exceptions:
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a - Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) if an array index in a key is not a
number; example: `"array index 'one' is not a number"` number; example: `"array index 'one' is not a number"`
- Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array - Throws [`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) if a level becomes an array
(because one of its keys is `0`) and another key at that level begins with a digit but is not a valid array index (because one of its keys is `0`) and another key at that level cannot be an array index; example:
(such as `1a`), or is `-`; example: `"unresolved reference token 'x'"`
`"unresolved reference token '-'"`
## Complexity ## Complexity
@@ -81,6 +80,3 @@ Apart from these two cases, for a JSON value `j`, the following is always true:
- Added in version 2.0.0. - Added in version 2.0.0.
- Made the array/object decision independent of the object's iteration order in version 3.13.0. - Made the array/object decision independent of the object's iteration order in version 3.13.0.
- Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) instead of
[`out_of_range.404`](../../home/exceptions.md#jsonexceptionout_of_range404) for a one-character array index that is
not a digit (e.g., `/x`) in version 3.13.0, as it already did for longer ones.
+1 -11
View File
@@ -119,14 +119,6 @@ changes to any JSON value.
## Notes ## Notes
!!! warning "`null` members are not missing"
The default value is used only if the key (or JSON Pointer) does not exist. A member that exists but is
`#!json null` is converted like any other value, so `#!cpp j.value("k", 0)` throws a
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `"k"` is `#!json null`. See
[Access with default value](../../features/element_access/default_value.md)
for alternatives.
!!! warning "Return type" !!! warning "Return type"
The value function is a template, and the return type of the function is determined by the type of the provided The value function is a template, and the return type of the function is determined by the type of the provided
@@ -235,6 +227,4 @@ changes to any JSON value.
[`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and other lookup functions. [`operator[]`](operator[].md), [`at`](at.md), [`find`](find.md), and other lookup functions.
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving 3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or `ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
`default_value`, as documented). Throws [`parse_error.109`](../../home/exceptions.md#jsonexceptionparse_error109) `default_value`, as documented).
instead of returning `default_value` for a one-character array index that is not a digit (e.g., `/x`) in version
3.13.0, as it already did for longer ones.
+1 -2
View File
@@ -7,8 +7,7 @@ struct json_sax;
This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in This class describes the SAX interface used by [sax_parse](../basic_json/sax_parse.md). Each function is called in
different situations while the input is parsed. The boolean return value informs the parser whether to continue different situations while the input is parsed. The boolean return value informs the parser whether to continue
processing the input; for [`parse_error`](parse_error.md), it decides whether to processing the input.
[recover from the error](../../features/parsing/error_recovery.md).
For instance, parsing the JSON text `{"a": [1, true]}` triggers the following callbacks, in order: For instance, parsing the JSON text `{"a": [1, true]}` triggers the following callbacks, in order:
+2 -26
View File
@@ -21,18 +21,11 @@ A parse error occurred.
## Return value ## Return value
Whether to recover from the error: Whether parsing should proceed (**must return `#!cpp false`**).
- `#!cpp false` stops parsing.
- `#!cpp true` recovers from the error: the error is repaired and parsing continues. If that is not possible, which
happens in the binary formats when the end of the item with the error is unknown, the value read so far is completed
and parsing stops. See [error recovery](../../features/parsing/error_recovery.md) for how errors are repaired.
Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
## Examples ## Examples
??? example "Example: (1) the SAX interface" ??? example
The example below shows how the SAX interface is used. The example below shows how the SAX interface is used.
@@ -46,29 +39,12 @@ Either way, [`sax_parse`](../basic_json/sax_parse.md) returns `#!cpp false`.
--8<-- "examples/sax_parse.output" --8<-- "examples/sax_parse.output"
``` ```
??? example "Example: (2) recovering from errors"
The example below shows how a SAX parser recovers from errors.
```cpp
--8<-- "examples/sax_parse__error_recovery.cpp"
```
Output:
```
--8<-- "examples/sax_parse__error_recovery.output"
```
## See also ## See also
- [sax_parse](../basic_json/sax_parse.md) - SAX parser - [sax_parse](../basic_json/sax_parse.md) - SAX parser
- [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without - [Parsing and Exceptions](../../features/parsing/parse_exceptions.md) - the article on handling parse errors without
exceptions exceptions
- [Error Recovery](../../features/parsing/error_recovery.md) - the article on recovering from parse errors
## Version history ## Version history
- Added in version 3.2.0. - Added in version 3.2.0.
- Returning `#!cpp true` recovers from the error since version 3.13.0; before, parsing stopped, but the result of
[`sax_parse`](../basic_json/sax_parse.md) could be wrong.
-1
View File
@@ -27,7 +27,6 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard - [**JSON_HAS_CPP_11**<br>**JSON_HAS_CPP_14**<br>**JSON_HAS_CPP_17**<br>**JSON_HAS_CPP_20**](json_has_cpp_11.md) - set supported C++ standard
- [**JSON_HAS_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support - [**JSON_HAS_FILESYSTEM**<br>**JSON_HAS_EXPERIMENTAL_FILESYSTEM**](json_has_filesystem.md) - control `std::filesystem` support
- [**JSON_HAS_RANGE_VIEW_CONVERSION**](json_has_range_view_conversion.md) - control construction from `std::ranges` views
- [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support - [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support
- [**JSON_HAS_STATIC_RTTI**](json_has_static_rtti.md) - control RTTI (run time type information) support - [**JSON_HAS_STATIC_RTTI**](json_has_static_rtti.md) - control RTTI (run time type information) support
- [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support - [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support
@@ -49,6 +49,6 @@ The default value is detected based on preprocessor macros such as `#!cpp __cplu
## Version history ## Version history
- Added in version 3.10.0. - Added in version 3.10.5.
- Added `JSON_HAS_CPP_23` in version 3.12.0. - Added `JSON_HAS_CPP_23` in version 3.12.0.
- Added `JSON_HAS_CPP_26` in version 3.13.0. - Added `JSON_HAS_CPP_26` in version 3.13.0.
@@ -1,43 +0,0 @@
# JSON_HAS_RANGE_VIEW_CONVERSION
```cpp
#define JSON_HAS_RANGE_VIEW_CONVERSION /* value */
```
This macro indicates whether a JSON array can be constructed directly from a C++20 range view (`std::ranges::view`),
such as the result of `std::views::filter` or `std::views::transform`. Possible values are `1` when supported or `0`
when unsupported.
## Default definition
The default value is `1` if [`JSON_HAS_RANGES`](json_has_ranges.md) is `1` and the compiler is not MinGW (that is,
`#!cpp __MINGW32__` is not defined), and `0` otherwise.
When the macro is not defined, the library will define it to its default value.
!!! info "Known compiler/stdlib exclusions"
- **MinGW** -- disabled, because its `std::ranges` support is incomplete ([issue #4916](https://github.com/nlohmann/json/issues/4916)).
- All toolchains for which [`JSON_HAS_RANGES`](json_has_ranges.md#default-definition) is disabled.
## Examples
??? example
The code below forces the library to disable the conversion from range views:
```cpp
#define JSON_HAS_RANGE_VIEW_CONVERSION 0
#include <nlohmann/json.hpp>
...
```
## See also
- [JSON_HAS_RANGES](json_has_ranges.md) - control `std::ranges` support
- [Constructing from a C++20 range view](../../features/conversions.md#putting-values-in) - usage of the feature
## Version history
- Added in version 3.13.0.
@@ -20,7 +20,7 @@ When the macro is not defined, the library will define it to its default value.
- **GCC 11.1.0** — disabled (the shipped `<ranges>` header has a syntax error; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **GCC 11.1.0** — disabled (the shipped `<ranges>` header has a syntax error; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #5161](https://github.com/nlohmann/json/issues/5161)) - **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440)) - **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
- **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error - **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error
under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907)) under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907))
@@ -44,12 +44,9 @@ When the macro is not defined, the library will define it to its default value.
- [JSON_HAS_CPP_11 / JSON_HAS_CPP_14 / JSON_HAS_CPP_17 / JSON_HAS_CPP_20 / JSON_HAS_CPP_23 / - [JSON_HAS_CPP_11 / JSON_HAS_CPP_14 / JSON_HAS_CPP_17 / JSON_HAS_CPP_20 / JSON_HAS_CPP_23 /
JSON_HAS_CPP_26](json_has_cpp_11.md) - set supported C++ standard JSON_HAS_CPP_26](json_has_cpp_11.md) - set supported C++ standard
- [JSON_HAS_RANGE_VIEW_CONVERSION](json_has_range_view_conversion.md) - control construction from `std::ranges` views
- [JSON_HAS_STD_FORMAT](json_has_std_format.md) - a similar feature-detection macro, for `std::format`/`std::formatter` - [JSON_HAS_STD_FORMAT](json_has_std_format.md) - a similar feature-detection macro, for `std::format`/`std::formatter`
support support
## Version history ## Version history
- Added in version 3.11.0. - Added in version 3.11.0.
- Added the exclusions for libstdc++ < 11, Clang < 16 with libstdc++, libc++ < 16, and nvcc (CUDA) 12.0.x and 12.1.x in
version 3.13.0. Before, only GCC 11.1.0 was excluded.
@@ -20,10 +20,7 @@ The macro does not affect:
- The binary readers ([`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md), - The binary readers ([`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
[`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md), [`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md),
[`from_bson`](../basic_json/from_bson.md)): none of these formats requires a decoder to reject ill-formed UTF-8, so [`from_bson`](../basic_json/from_bson.md)): none of these formats requires a decoder to reject ill-formed UTF-8, so
by default they return the bytes unchanged (`error_handler_t::keep`), independent of this macro. Pass an they always return the bytes unchanged.
`error_handler` argument explicitly to validate or sanitize the strings they read.
- [`from_bon8`](../basic_json/from_bon8.md): BON8 always validates, because the UTF-8 lead bytes mark where a string
ends.
## Default definition ## Default definition
@@ -190,5 +190,3 @@ void to_json(BasicJsonType& j, const B& b) {
4. Added in version 3.12.0. 4. Added in version 3.12.0.
5. Added in version 3.12.0. 5. Added in version 3.12.0.
6. Added in version 3.12.0. 6. Added in version 3.12.0.
All six macros were changed to allow an empty member list in version 3.13.0.
@@ -186,6 +186,3 @@ See the examples below for the concrete generated code.
1. Added in version 3.9.0. 1. Added in version 3.9.0.
2. Added in version 3.11.0. 2. Added in version 3.11.0.
3. Added in version 3.11.3. 3. Added in version 3.11.3.
All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0,
and to allow an empty member list in version 3.13.0.
@@ -185,6 +185,3 @@ See the examples below for the concrete generated code.
1. Added in version 3.9.0. 1. Added in version 3.9.0.
2. Added in version 3.11.0. 2. Added in version 3.11.0.
3. Added in version 3.11.3. 3. Added in version 3.11.3.
All three macros were changed to work with any `basic_json` specialization (not only `nlohmann::json`) in version 3.12.0,
and to allow an empty member list in version 3.13.0.
@@ -38,6 +38,4 @@ the library.
## Version history ## Version history
- Added in version 3.11.0. Changed inline namespace name in version 3.11.2. Added the ABI tag `_dp` in - Added in version 3.11.0. Changed inline namespace name in version 3.11.2.
version 3.12.0, and the ABI tags `_bics`, `_psp`, `_snul`, `_sbu8`, and `_ekmo` in version 3.13.0; see
[`nlohmann` Namespace](../../features/namespace.md#structure).
@@ -14,7 +14,7 @@ These macros can be used to open and close the `nlohmann` namespace. See
## Default definition ## Default definition
The default definitions open and close the `nlohmann` namespace. The precise definition of The default definitions open and close the `nlohmann` namespace. The precise definition of
`NLOHMANN_JSON_NAMESPACE_BEGIN` varies as described [here](../../features/namespace.md#structure). [`NLOHMANN_JSON_NAMESPACE_BEGIN`] varies as described [here](../../features/namespace.md#structure).
1. Default definition of `NLOHMANN_JSON_NAMESPACE_BEGIN`: 1. Default definition of `NLOHMANN_JSON_NAMESPACE_BEGIN`:
+1 -1
View File
@@ -10,7 +10,7 @@ The library parses, stores, and serializes JSON values in memory. It does not op
files (it only reads from streams or `std::FILE*` handles that the caller has already opened), does not read environment files (it only reads from streams or `std::FILE*` handles that the caller has already opened), does not read environment
variables, and does not implement cryptography or handle credentials. variables, and does not implement cryptography or handle credentials.
The primary threat is therefore **untrusted input**: JSON text or binary data (BJData, BON8, BSON, CBOR, MessagePack, UBJSON) The primary threat is therefore **untrusted input**: JSON text or binary data (BJData, BSON, CBOR, MessagePack, UBJSON)
that an attacker controls, passed to [`parse`](../api/basic_json/parse.md), [`accept`](../api/basic_json/accept.md), that an attacker controls, passed to [`parse`](../api/basic_json/parse.md), [`accept`](../api/basic_json/accept.md),
[`sax_parse`](../api/basic_json/sax_parse.md), or one of the `from_*` functions such as [`sax_parse`](../api/basic_json/sax_parse.md), or one of the `from_*` functions such as
[`from_cbor`](../api/basic_json/from_cbor.md). Such input may try to [`from_cbor`](../api/basic_json/from_cbor.md). Such input may try to
@@ -12,7 +12,7 @@ violations will result in a failed build.
Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds. Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds.
Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md), [`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md), and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions. Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions.
- [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms, - [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms,
including the oldest versions known to compile the library. including the oldest versions known to compile the library.
@@ -107,7 +107,7 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa
- [x] The library is compiled with all C++ language revisions (C++11, C++14, C++17, C++20, C++23, and C++26) to detect - [x] The library is compiled with all C++ language revisions (C++11, C++14, C++17, C++20, C++23, and C++26) to detect
and fix language deprecations early. and fix language deprecations early.
- [x] The library is checked for compiler warnings: - [x] The library is checked for compiler warnings:
- On Clang, `-Weverything` is used with 7 exceptions. - On Clang, `-Weverything` is used with 8 exceptions.
??? abstract "Clang warnings" ??? abstract "Clang warnings"
+9 -23
View File
@@ -36,26 +36,13 @@ work items are tracked in the [GitHub milestones](https://github.com/nlohmann/js
## API stability ## API stability
Releases follow [semantic versioning](https://semver.org): a minor or patch release of version 3.x does not break code Releases follow [semantic versioning](https://semver.org): a minor or patch release of version 3.x does not break code
that uses the public API, unless that code opts in to a change with a macro as described [below](#version-40). In that uses the public API. In particular, a 3.x release does not:
particular, a 3.x release does not:
- make breaking changes to the signature of a function: the types or order of its existing parameters, its return type, - change the signature of a function (its parameter types, return type, number of parameters, or the const-ness of a
its `noexcept` or `constexpr` specifier, or the const-ness of a member function. New parameters may be added if they member function);
have a default value; - remove or rename a function or class;
- remove or rename a function or class, or change the template parameters of a public class template;
- change which exceptions a function throws, or the [exception ids](../home/exceptions.md); - change which exceptions a function throws, or the [exception ids](../home/exceptions.md);
- change access specifiers, or change or remove existing default arguments. New default arguments may be added; - change access specifiers or default arguments.
- change the JSON type that a valid input parses to, or the text that `dump()` produces for a valid value;
- accept input that was rejected before, or reject input that was accepted before;
- change the order in which the keys of an object are iterated. The default type sorts keys, and
[`ordered_json`](../api/ordered_json.md) keeps insertion order;
- change when iterators, pointers, or references are invalidated, or the state of a moved-from `basic_json`;
- add or remove implicit conversions from `basic_json`;
- change how `to_json` and `from_json` functions are found, or the behavior of
[`adl_serializer`](../api/adl_serializer/index.md);
- add pure virtual functions to the [`json_sax`](../api/json_sax/index.md) interface;
- remove, rename, renumber, or add enumerators of `value_t`;
- remove or rename a documented macro, CMake option, CMake target, or header, or change what a documented macro does.
Exceptions to these rules, for instance when fixing a bug requires changing the exception a function throws, are Exceptions to these rules, for instance when fixing a bug requires changing the exception a function throws, are
documented in the [release notes](../home/releases.md). documented in the [release notes](../home/releases.md).
@@ -64,14 +51,13 @@ The following are **not** part of the public API and may change in any release,
- The text of exception messages returned by `what()`. Use the [exception id](../home/exceptions.md) to tell errors - The text of exception messages returned by `what()`. Use the [exception id](../home/exceptions.md) to tell errors
apart. apart.
- The ABI, including `sizeof(basic_json)` and the memory layout of its values. The - The ABI, including `sizeof(basic_json)` and the memory layout of its values. Recompile your code when you upgrade the
[versioned inline namespace](../features/namespace.md) turns mixing versions into a link error. library. The [versioned inline namespace](../features/namespace.md) turns mixing versions into a link error.
- The hash values returned by `std::hash` for `basic_json`. Numbers that compare equal still hash equally.
- Everything in namespace `nlohmann::detail`, and macros and type traits that are not documented in the - Everything in namespace `nlohmann::detail`, and macros and type traits that are not documented in the
[API reference](../api/basic_json/index.md). [API reference](../api/basic_json/index.md).
Breaking changes are only added behind a macro whose default keeps the 3.x behavior. See [Version 4.0](#version-40) and Changes that would break the public API are only added behind a macro whose default keeps the 3.x behavior, see
the [macro overview](../features/macros.md). [Version 4.0](#version-40).
## Version 4.0 ## Version 4.0
+13 -1
View File
@@ -15,7 +15,19 @@ class base_class_with_hidden_members
} }
}; };
using json = nlohmann::json::with_base_class_t<base_class_with_hidden_members>; using json = nlohmann::basic_json <
std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
std::allocator,
nlohmann::adl_serializer,
std::vector<std::uint8_t>,
base_class_with_hidden_members
>;
int main() int main()
{ {
@@ -19,9 +19,25 @@ int main()
<< j.contains("/array/1"_json_pointer) << '\n' << j.contains("/array/1"_json_pointer) << '\n'
<< j.contains("/array/-"_json_pointer) << '\n' << j.contains("/array/-"_json_pointer) << '\n'
<< j.contains("/array/4"_json_pointer) << '\n' << j.contains("/array/4"_json_pointer) << '\n'
<< j.contains("/baz"_json_pointer) << '\n' << j.contains("/baz"_json_pointer) << std::endl;
// an array index with a leading '0' is not found
<< j.contains("/array/01"_json_pointer) << '\n' try
// an array index that is not a number is not found {
<< j.contains("/array/one"_json_pointer) << std::endl; // try to use an array index with leading '0'
j.contains("/array/01"_json_pointer);
}
catch (const json::parse_error& e)
{
std::cout << e.what() << '\n';
}
try
{
// try to use an array index that is not a number
j.contains("/array/one"_json_pointer);
}
catch (const json::parse_error& e)
{
std::cout << e.what() << '\n';
}
} }
@@ -5,5 +5,3 @@ true
false false
false false
false false
false
false
+1 -1
View File
@@ -8,7 +8,7 @@ int main()
try try
{ {
// parsing input with a syntax error // parsing input with a syntax error
json j = json::parse("[1,2,3,]"); json::parse("[1,2,3,]");
} }
catch (const json::parse_error& e) catch (const json::parse_error& e)
{ {
@@ -1,43 +0,0 @@
#include <iostream>
#include <iomanip>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// a SAX parser that creates a JSON value like json::parse does, but that
// recovers from parse errors instead of stopping at the first one
class recovering_parser : public nlohmann::detail::json_sax_dom_parser<json>
{
public:
explicit recovering_parser(json& result)
: nlohmann::detail::json_sax_dom_parser<json>(result, false)
{}
bool parse_error(std::size_t position,
const std::string& /*last_token*/,
const json::exception& ex)
{
std::cout << "byte " << position << ": " << ex.what() << '\n';
// repair the input and continue
return true;
}
};
int main()
{
// JSON text with several mistakes that ends too early
const std::string text = R"({
"name": "Hello World",
"tags": ["a" "b",],
"valid": tru,
"size": 1.,
"nested": {"x": 1)";
json result;
recovering_parser sax(result);
const bool valid = json::sax_parse(text, &sax);
std::cout << "\nvalid JSON: " << std::boolalpha << valid << '\n'
<< std::setw(4) << result << std::endl;
}
@@ -1,19 +0,0 @@
byte 49: [json.exception.parse_error.101] parse error at line 3, column 20: syntax error while parsing array - unexpected string literal; expected ']'
byte 51: [json.exception.parse_error.101] parse error at line 3, column 22: syntax error while parsing value - unexpected ']'; expected '[', '{', or a literal
byte 70: [json.exception.parse_error.101] parse error at line 4, column 17: syntax error while parsing value - invalid literal; last read: '"valid": tru,'
byte 86: [json.exception.parse_error.101] parse error at line 5, column 15: syntax error while parsing value - invalid number; expected digit after '.'; last read: '1.,'
byte 109: [json.exception.parse_error.101] parse error at line 6, column 22: syntax error while parsing object - unexpected end of input; expected '}'
valid JSON: false
{
"name": "Hello World",
"nested": {
"x": 1
},
"size": 1,
"tags": [
"a",
"b"
],
"valid": null
}
@@ -347,13 +347,6 @@ struct adl_serializer<boost::optional<T>> {
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
``` ```
!!! tip "`std::optional` needs no serializer"
Since version 3.12.0, `std::optional<T>` is supported out of the box when compiling with C++17
(`std::nullopt` is converted to and from `null`). Do not write an `adl_serializer` for it; this pattern is only
needed for types such as `boost::optional` or for custom semantics. See [Conversions](conversions.md) and
[Omitting a field when serializing `std::optional`](conversions.md#omitting-a-field-when-serializing-stdoptional).
!!! note "ABI compatibility" !!! note "ABI compatibility"
Use [`NLOHMANN_JSON_NAMESPACE_BEGIN`](../api/macros/nlohmann_json_namespace_begin.md) and `NLOHMANN_JSON_NAMESPACE_END` Use [`NLOHMANN_JSON_NAMESPACE_BEGIN`](../api/macros/nlohmann_json_namespace_begin.md) and `NLOHMANN_JSON_NAMESPACE_END`
+1 -2
View File
@@ -217,8 +217,7 @@ json j = numbers; // [1,2,3]
``` ```
This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
to incomplete C++20 ranges support there; see to incomplete C++20 ranges support there.
[`JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md).
## Your own types ## Your own types
@@ -30,47 +30,6 @@ you want to access and a default value in case there is no value stored with tha
| `#!cpp j.value("append", false)` | `#!json true` | | `#!cpp j.value("append", false)` | `#!json true` |
| `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` | | `#!cpp j.value("logLevel", "verbose")` | `#!json "verbose"` |
## Nested values
To read a value deep inside a document, pass a [JSON Pointer](../json_pointer.md) instead of a key. The default value is
returned if the value at the pointer does not exist, including the case that an intermediate key is missing. There is
no need to check each level with [`contains`](../../api/basic_json/contains.md) first.
```cpp
json j = {{"server", {{"port", 8080}}}, {"list", {10, 20}}};
int port = j.value("/server/port"_json_pointer, 80); // 8080
int timeout = j.value("/server/limits/timeout"_json_pointer, 30); // 30 (missing intermediate key)
int second = j.value("/list/1"_json_pointer, 0); // 20 (numeric tokens index arrays)
int third = j.value("/list/5"_json_pointer, 0); // 0 (index out of range)
bool has_port = j.contains("/server/port"_json_pointer); // true
bool has_host = j.contains("/server/host/name"_json_pointer); // false
```
If the path is only available as a dotted string such as `#!cpp "server.port"`, do not build the pointer by
concatenating `#!cpp "/"` and the parts: keys containing `/` or `~` would be misinterpreted. Append each part as a
reference token with [`operator/=`](../../api/json_pointer/operator_slasheq.md) instead. It escapes the token for you.
```cpp
json::json_pointer to_pointer(const std::string& dotted)
{
json::json_pointer ptr;
std::istringstream in(dotted);
for (std::string token; std::getline(in, token, '.');)
{
ptr /= token;
}
return ptr;
}
int port = j.value(to_pointer("server.port"), 80); // 8080
int first = j.value(to_pointer("list.0"), 0); // 10
```
The key `#!cpp "a/b"` yields the pointer `#!cpp "/a~1b"`; the dot-splitting itself is up to the caller, so keys
containing `.` need a different separator.
## Notes ## Notes
!!! failure "Exceptions" !!! failure "Exceptions"
@@ -78,31 +37,6 @@ containing `.` need a different separator.
- With string keys, `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown. - With string keys, `value` can only be used with objects. For other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
- With JSON Pointers, `value` can be used with both objects and arrays. For other types (null, boolean, number, string), a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown. - With JSON Pointers, `value` can be used with both objects and arrays. For other types (null, boolean, number, string), a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error306) is thrown.
!!! warning "`null` and mistyped members are not missing"
`value` returns the default value only if the key is **absent**. If the member exists, it is converted to the type
of the default value, even if it is `#!json null`. For `#!json {"k": null}`, the call `#!cpp j.value("k", 0)` throws
a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error302), and so does a member of another
type such as a string where a number is expected. The same holds for JSON Pointers.
To treat `#!json null` like a missing value, check for it explicitly:
```cpp
int n = (j.contains("k") && !j["k"].is_null()) ? j["k"].get<int>() : 0;
```
With C++17, [`get<std::optional<T>>()`](../../api/basic_json/get.md) maps `#!json null` to an empty optional. As
[`at`](../../api/basic_json/at.md) throws [`out_of_range`](../../home/exceptions.md#jsonexceptionout_of_range403)
for an absent key, use [`find`](../../api/basic_json/find.md) to cover both cases:
```cpp
std::optional<int> n; // empty if "k" is absent or null
if (const auto it = j.find("k"); it != j.end())
{
n = it->get<std::optional<int>>(); // still throws for a non-number such as "text"
}
```
!!! warning "Return type" !!! warning "Return type"
The value function is a template, and the return type of the function is determined by the type of the provided The value function is a template, and the return type of the function is determined by the type of the provided
@@ -129,6 +63,3 @@ containing `.` need a different separator.
- [`value`](../../api/basic_json/value.md) for access with default value - [`value`](../../api/basic_json/value.md) for access with default value
- documentation on [checked access](checked_access.md) - documentation on [checked access](checked_access.md)
- documentation on [JSON Pointer](../json_pointer.md)
- [`contains`](../../api/basic_json/contains.md) to check whether a key or JSON Pointer exists
- [`json_pointer::operator/=`](../../api/json_pointer/operator_slasheq.md) to build a pointer token by token
@@ -77,10 +77,6 @@ auto val2 = j.at(json::json_pointer("/nested/three/1")); // false
auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0 auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0
``` ```
To read a value with a fallback, use [`value`](../api/basic_json/value.md) with a JSON Pointer; to test for existence,
use [`contains`](../api/basic_json/contains.md). Neither needs intermediate checks, see
[Nested values](element_access/default_value.md#nested-values).
!!! note "Creating intermediate levels that don't exist" !!! note "Creating intermediate levels that don't exist"
See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is
@@ -130,8 +126,6 @@ auto j_original = j_flat.unflatten();
## See also ## See also
- Class [`json_pointer`](../api/json_pointer/index.md) - Class [`json_pointer`](../api/json_pointer/index.md)
- Functions [`value`](../api/basic_json/value.md), [`contains`](../api/basic_json/contains.md), and
[`at`](../api/basic_json/at.md) accept JSON Pointers; see [Nested values](element_access/default_value.md#nested-values)
- Function [`flatten`](../api/basic_json/flatten.md) - Function [`flatten`](../api/basic_json/flatten.md)
- Function [`unflatten`](../api/basic_json/unflatten.md) - Function [`unflatten`](../api/basic_json/unflatten.md)
- [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers - [JSON Patch](json_patch.md) - paths inside a patch are JSON Pointers
-39
View File
@@ -82,45 +82,6 @@ To override the built-in check, define `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERI
See [full documentation of `JSON_HAS_FILESYSTEM` and `JSON_HAS_EXPERIMENTAL_FILESYSTEM`](../api/macros/json_has_filesystem.md). See [full documentation of `JSON_HAS_FILESYSTEM` and `JSON_HAS_EXPERIMENTAL_FILESYSTEM`](../api/macros/json_has_filesystem.md).
## `JSON_HAS_RANGES`
The library uses `std::ranges` (and concepts) where available, for example, to parse from C++20 ranges and to
construct JSON arrays from range views. The library detects whether the standard library supports ranges and
disables the support on toolchains with an incomplete implementation. To override the built-in check, define
`JSON_HAS_RANGES` to `1` or `0`.
See [full documentation of `JSON_HAS_RANGES`](../api/macros/json_has_ranges.md).
## `JSON_HAS_RANGE_VIEW_CONVERSION`
When `JSON_HAS_RANGES` is enabled (and the compiler is not MinGW), a JSON array can be constructed directly from a C++20
range view such as `std::views::filter(...)`. To override the built-in check, define `JSON_HAS_RANGE_VIEW_CONVERSION` to
`1` or `0`.
See [full documentation of `JSON_HAS_RANGE_VIEW_CONVERSION`](../api/macros/json_has_range_view_conversion.md).
## `JSON_HAS_STATIC_RTTI`
The library detects whether the compiler supports run time type information (RTTI), which it needs, for instance, to
exclude `std::any` from the candidate types of the implicit conversion on C++17. To override the built-in check, define
`JSON_HAS_STATIC_RTTI` to `1` or `0`.
See [full documentation of `JSON_HAS_STATIC_RTTI`](../api/macros/json_has_static_rtti.md).
## `JSON_HAS_STD_FORMAT`
When compiling with C++20 and a standard library that provides `<format>`, the library provides a `std::formatter`
specialization for JSON values. To override the built-in check, define `JSON_HAS_STD_FORMAT` to `1` or `0`.
See [full documentation of `JSON_HAS_STD_FORMAT`](../api/macros/json_has_std_format.md).
## `JSON_HAS_THREE_WAY_COMPARISON`
When the compiler and standard library support 3-way comparison (the spaceship operator `<=>`), the library provides it
for JSON values. To override the built-in check, define `JSON_HAS_THREE_WAY_COMPARISON` to `1` or `0`.
See [full documentation of `JSON_HAS_THREE_WAY_COMPARISON`](../api/macros/json_has_three_way_comparison.md).
## `JSON_NOEXCEPTION` ## `JSON_NOEXCEPTION`
Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`. Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`.
-8
View File
@@ -9,13 +9,6 @@ syntax that closely mimics the document being modified. Unlike [JSON Patch](json
express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is express every kind of change (e.g., it cannot reorder array elements or remove a specific array element), but it is
easier to read and write for object-shaped documents. easier to read and write for object-shaped documents.
!!! tip "Not a general deep merge"
A merge patch is not a general deep merge: a `#!json null` value in the patch deletes the key from the target.
To merge two objects recursively (e.g., defaults and user settings), use
[`update`](../api/basic_json/update.md) with `merge_objects` set to `#!cpp true`; see
[Merging objects](modifying_values.md#merging-objects).
??? example ??? example
The following code shows how a JSON Merge Patch is applied to a JSON document. The following code shows how a JSON Merge Patch is applied to a JSON document.
@@ -35,4 +28,3 @@ easier to read and write for object-shaped documents.
- [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations - [JSON Patch and Diff](json_patch.md) - a more expressive alternative that describes a sequence of operations
- [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch - [JSON Pointer](json_pointer.md) - the addressing scheme used by JSON Patch
- Function [`merge_patch`](../api/basic_json/merge_patch.md) - Function [`merge_patch`](../api/basic_json/merge_patch.md)
- Function [`update`](../api/basic_json/update.md) - merge objects, optionally recursively
+6 -24
View File
@@ -35,12 +35,8 @@ the insertion happened — useful for "add if absent" semantics.
## Merging objects ## Merging objects
To merge one object into another, [`update`](../api/basic_json/update.md) copies all members from another object To merge one object into another, [`update`](../api/basic_json/update.md) copies all members from another object,
(similar to Python's `dict.update`). This is the idiomatic way to combine two objects. It has two modes: overwriting existing keys (similar to Python's `dict.update`). This is the idiomatic way to combine two objects.
- By default, the merge is shallow: existing keys are overwritten, even if both values are objects.
- With `merge_objects = #!cpp true`, keys whose values are objects in both JSON values are merged recursively.
Everything else is overwritten. In particular, arrays are replaced, not concatenated.
??? example ??? example
@@ -54,22 +50,9 @@ To merge one object into another, [`update`](../api/basic_json/update.md) copies
--8<-- "examples/update.output" --8<-- "examples/update.output"
``` ```
A common use of the recursive mode is combining defaults with user settings. Nested defaults that the user did not set For a recursive merge that follows [RFC 7386](https://tools.ietf.org/html/rfc7386), see
are kept: [JSON Merge Patch](merge_patch.md). To apply a sequence of well-defined edit operations, see
[JSON Patch](json_patch.md).
```cpp
json defaults = {{"log", {{"level", "info"}, {"file", "app.log"}}}, {"retries", 3}};
json user_settings = {{"log", {{"level", "debug"}}}};
json config = defaults;
config.update(user_settings, true);
// {"log":{"file":"app.log","level":"debug"},"retries":3}
```
[JSON Merge Patch](merge_patch.md) ([RFC 7386](https://tools.ietf.org/html/rfc7386)) also merges objects recursively,
but it is a different tool: a `#!json null` in the patch means "remove this key". It is meant for applying merge patch
documents (e.g., received via HTTP PATCH). To merge configuration-like objects, use `#!cpp update(..., true)`. To apply
a sequence of well-defined edit operations, see [JSON Patch](json_patch.md).
## Removing elements ## Removing elements
@@ -89,7 +72,6 @@ a.erase(1); // [1,3,4] (erase by index)
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array - [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
- [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent - [`emplace`](../api/basic_json/emplace.md) - insert into an object if the key is absent
- [`update`](../api/basic_json/update.md) - merge objects (shallow, or recursive with `merge_objects`) - [`update`](../api/basic_json/update.md) - merge objects
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7386 merge patch
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements - [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications - [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
-6
View File
@@ -101,9 +101,3 @@ follows:
- Introduced inline namespace (`json_v3_11_0[_abi-tag]*`) in version 3.11.0. - Introduced inline namespace (`json_v3_11_0[_abi-tag]*`) in version 3.11.0.
- Changed structure of inline namespace in version 3.11.2. - Changed structure of inline namespace in version 3.11.2.
- Added ABI tag `_dp` ([`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md)) in version 3.12.0.
- Added ABI tags `_bics` ([`JSON_BRACE_INIT_COPY_SEMANTICS`](../api/macros/json_brace_init_copy_semantics.md)), `_psp`
([`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md)), `_snul`
([`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md)), `_sbu8`
([`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md)), and `_ekmo`
([`JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS`](../api/macros/json_use_objects_for_enum_keyed_maps.md)) in version 3.13.0.
@@ -1,122 +0,0 @@
# Error Recovery
By default, parsing stops at the first error. With the [SAX interface](sax_interface.md), you can instead ask the
parser to *recover*: to repair the error and continue, so that you get as much as possible out of malformed input, for
instance a file that was cut off, JSON edited by hand, or the output of a language model.
## Recovering from errors
The SAX parser's [`parse_error`](../../api/json_sax/parse_error.md) function is called for every error. Its return value
decides what happens next:
- `#!cpp false` stops parsing. This is what the SAX parsers of the library do, so [`parse`](../../api/basic_json/parse.md)
and [`accept`](../../api/basic_json/accept.md) never recover.
- `#!cpp true` repairs the error and continues parsing.
When recovering, the SAX parser still receives well-formed events: every `start_object` or `start_array` is followed by
the matching `end_object` or `end_array`, and every `key` is followed by exactly one value. A SAX parser that creates a
JSON value, such as the one in the example below, therefore gets a complete value. Parsing always ends, and
[`sax_parse`](../../api/basic_json/sax_parse.md) returns `#!cpp false` for input that is not valid JSON, even if every
error was repaired. Each token is reported at most once, and the SAX parser can stop at any error by returning
`#!cpp false`.
!!! example
The example below derives a SAX parser from the library's parser for `json` values (`json_sax_dom_parser`),
and recovers from all errors.
```cpp
--8<-- "examples/sax_parse__error_recovery.cpp"
```
Output:
```
--8<-- "examples/sax_parse__error_recovery.output"
```
## How errors are repaired
Each error is repaired with the smallest local edit: a missing separator is inserted, a stray token is removed, what can
be read of a broken string or number is kept, and a value that cannot be read at all becomes `#!json null`.
| Mistake | Repair | Example | Result |
|---------------------------|--------------------------------------------------------------------------------|------------------------------------------|----------------------------|
| missing `,` or `:` | inserted | `#!json [1 2]`, `#!json {"a" 1}` | `[1,2]`, `{"a":1}` |
| missing value | `#!json null` for an object key or between commas in an array | `#!json {"a":}`, `#!json [1,,2]` | `{"a":null}`, `[1,null,2]` |
| trailing comma | removed | `#!json [1,2,]` | `[1,2]` |
| broken string | invalid escapes and bytes are replaced (see below); a line break ends the string | `#!json ["a\qb"]` | `["aqb"]` |
| broken number | the longest valid beginning is kept | `#!json [1., 2e+]` | `[1,2]` |
| unreadable value | `#!json null` | `#!json [1, NaN, tru]` | `[1,null,null]` |
| number too large | passed as infinity, together with its text | `#!json [1e999]` | infinity (see below) |
| stray `:` | removed | `#!json ["a":1]` | `["a",1]` |
| member without a key | skipped up to the next `,` or `}` | `#!json {1:2, "b":3}` | `{"b":3}` |
| wrong closing bracket | closes the innermost array or object | `#!json {"a":[1,2}, "b":3}` | `{"a":[1,2],"b":3}` |
| input ends too early | all open arrays and objects are closed | `#!json {"a":[1,2` | `{"a":[1,2]}` |
| text before the value | skipped | `#!json )]}'{"a":1}` | `{"a":1}` |
In a string, an unknown escape like `\q` stands for the escaped character (`q`), as in JavaScript. An invalid `\u`
escape, a lone surrogate, and ill-formed UTF-8 are each replaced by U+FFFD (REPLACEMENT CHARACTER), and control
characters are kept. A string without its closing quote ends at the next line break or at the end of the input.
The input after the top-level value is not repaired: as without recovery, it is reported as an error, and parsing stops.
## Binary formats
The binary formats ([BJData](../binary_formats/bjdata.md), [BON8](../binary_formats/bon8.md),
[BSON](../binary_formats/bson.md), [CBOR](../binary_formats/cbor.md), [MessagePack](../binary_formats/messagepack.md),
and [UBJSON](../binary_formats/ubjson.md)) have no delimiters to find the next value by. So what can be repaired depends
on whether the end of the item with the error is known, a distinction that
[RFC 8949, Section 5.3](https://www.rfc-editor.org/rfc/rfc8949.html#section-5.3) makes for CBOR, too.
If the item is complete, but cannot be passed on as it is, it is replaced, and parsing continues after it:
| Mistake | Formats | Repair |
|---------------------------------------------------------------------|-------------------------|-------------------------------------------------------------------------|
| tag, if `tag_handler` is `cbor_tag_handler_t::error` (the default) | CBOR | ignored |
| simple value other than `false`, `true`, and `null`, like undefined | CBOR | `#!json null` |
| number too large for a custom `number_float_t`, like `float` | all | infinity |
| character (`C`) that is not ASCII | BJData, UBJSON | U+FFFD |
| invalid high-precision number (`H`) | BJData, UBJSON | the longest valid beginning is kept, as for JSON text, or `#!json null` |
| high-precision number too large | BJData, UBJSON | passed as infinity, together with its text |
| object key that is not a string | BON8, CBOR, MessagePack | the member is skipped |
| element of a type the library does not read, like ObjectId or date | BSON | `#!json null` |
| string without its terminator | BSON | kept |
| document whose size does not match its content | BSON | kept |
CBOR tags and simple values are repaired as [RFC 8949, Section 6.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-6.1)
suggests for converting CBOR to JSON. By default, [`sax_parse`](../../api/basic_json/sax_parse.md) reports every tag as
an error; when recovering, tags are then ignored like with
[`cbor_tag_handler_t::ignore`](../../api/basic_json/cbor_tag_handler_t.md). Strings that are not valid UTF-8 are no
error: like [`from_cbor`](../../api/basic_json/from_cbor.md) and the other functions by default, `sax_parse` passes
them on as they are.
After any other error, the end of the item is unknown: the input ended, a byte is not a valid type marker, or a size
cannot be right. Parsing then stops, and the value read so far is completed: a key that waits for its value gets
`#!json null`, and all open arrays and objects are closed. This keeps everything before the error of an input that was
cut off. The exception is BSON, which stores the size of every document: an element whose end is unknown gets
`#!json null`, the rest of its document is skipped, and parsing continues after the document.
## Limitations
- A repair is a guess. For example, `#!json {"a" "b": 1}` could be meant as `#!json {"a": "b"}` or as
`#!json {"a": null, "b": 1}`; it is repaired to the former. Treat recovered values as a best effort, and check the
reported errors.
- A closing bracket always closes the innermost array or object. If a bracket is missing rather than wrong, the
repair differs from the intention: `#!json {"a": {"b": [1, 2}, "c": 3}` is repaired to
`#!json {"a": {"b": [1, 2], "c": 3}}`, although `#!json {"a": {"b": [1, 2]}, "c": 3}` may have been meant.
- Keys without quotes, and strings in single quotes, are not supported; such members are skipped.
- In the binary formats, a member that is skipped because its key is not a string is lost, and so are the elements of a
BSON document after one whose end is unknown.
- A number that is too large for `number_float_t` is passed as positive or negative infinity. The SAX parser's
`number_float` also gets the number's text, but a JSON value cannot store it, and
[`dump`](../../api/basic_json/dump.md) serializes infinity as `#!json null`.
- When parsing is not strict (see [`sax_parse`](../../api/basic_json/sax_parse.md)), a repair may read parts of the
input after the value, for instance of the next value in a stream of concatenated values.
## See also
- [SAX interface](sax_interface.md) - implement a custom SAX handler
- [`parse_error`](../../api/json_sax/parse_error.md) - the SAX event for parse errors
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
- [parsing and exceptions](parse_exceptions.md) - control error handling
+1 -2
View File
@@ -75,7 +75,7 @@ You can influence a DOM parse without switching to the SAX interface by passing
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used unavailable, the parser can instead return a discarded value, or [`accept`](../../api/basic_json/accept.md) can be used
to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available to only check whether an input is valid JSON. See [parsing and exceptions](parse_exceptions.md) for the available
options. To get as much as possible out of malformed input, a SAX parser can [recover from errors](error_recovery.md). options.
## See also ## See also
@@ -86,5 +86,4 @@ options. To get as much as possible out of malformed input, a SAX parser can [re
- [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function - [parser callbacks](parser_callbacks.md) - influence the parsing by a callback function
- [SAX interface](sax_interface.md) - implement a custom SAX handler - [SAX interface](sax_interface.md) - implement a custom SAX handler
- [parsing and exceptions](parse_exceptions.md) - control error handling - [parsing and exceptions](parse_exceptions.md) - control error handling
- [error recovery](error_recovery.md) - get as much as possible out of malformed input
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources - [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
@@ -64,8 +64,7 @@ bool parse_error(std::size_t position,
const json::exception& ex); const json::exception& ex);
``` ```
The return value decides whether to stop parsing (`#!cpp false`) or to repair the error and continue The return value indicates whether the parsing should continue, so the function should usually return `#!cpp false`.
(`#!cpp true`); see [error recovery](error_recovery.md) for the latter.
??? example "Example: report parse errors without exceptions" ??? example "Example: report parse errors without exceptions"
@@ -60,8 +60,7 @@ bool key(string_t& val);
bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex); bool parse_error(std::size_t position, const std::string& last_token, const json::exception& ex);
``` ```
The return value of each function determines whether parsing should proceed. For `parse_error`, returning The return value of each function determines whether parsing should proceed.
`#!cpp true` [recovers from the error](error_recovery.md).
To implement your own SAX handler, proceed as follows: To implement your own SAX handler, proceed as follows:
@@ -69,7 +68,7 @@ To implement your own SAX handler, proceed as follows:
2. Create an object of your SAX interface class, e.g. `my_sax`. 2. Create an object of your SAX interface class, e.g. `my_sax`.
3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface. 3. Call `#!cpp bool json::sax_parse(input, &my_sax);` where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
Note the `sax_parse` function only returns a `#!cpp bool` indicating whether the input was parsed without errors and no SAX event returned `#!cpp false`. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`. Note the `sax_parse` function only returns a `#!cpp bool` indicating the result of the last executed SAX event. It does not return `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file `json_sax.hpp`.
## See also ## See also
+1 -1
View File
@@ -34,7 +34,7 @@ flowchart LR
- **JSON text** is read by an [input adapter](#input-adapters), tokenized by the lexer, and turned into SAX events by - **JSON text** is read by an [input adapter](#input-adapters), tokenized by the lexer, and turned into SAX events by
the parser. the parser.
- **Binary formats** (BJData, BON8, BSON, CBOR, MessagePack, UBJSON) are read by an input adapter and turned into the same SAX - **Binary formats** (BJData, BSON, CBOR, MessagePack, UBJSON) are read by an input adapter and turned into the same SAX
events by the `binary_reader`. events by the `binary_reader`.
- A [SAX consumer](#sax-interface) receives the events. The one used by [`parse`](../api/basic_json/parse.md) builds a - A [SAX consumer](#sax-interface) receives the events. The one used by [`parse`](../api/basic_json/parse.md) builds a
`basic_json` value tree. `basic_json` value tree.
+2 -2
View File
@@ -6,7 +6,7 @@ There are myriads of [JSON](https://json.org) libraries out there, and each may
- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings. - **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](https://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [OpenSSF Best Practices](https://www.bestpractices.dev/projects/289). - **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/tests/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests against all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [OpenSSF Best Practices](https://www.bestpractices.dev/projects/289).
Other aspects were not so important to us: Other aspects were not so important to us:
@@ -14,4 +14,4 @@ Other aspects were not so important to us:
- **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set. - **Speed**. There are certainly [faster JSON libraries](https://github.com/miloyip/nativejson-benchmark#parsing-time) out there. However, if your goal is to speed up your development by adding JSON support with a single header, then this library is the way to go. If you know how to use a `std::vector` or `std::map`, you are already set.
See the [contribution guidelines](https://github.com/nlohmann/json/blob/develop/.github/CONTRIBUTING.md#please-dont) for more information. See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information.
+5 -13
View File
@@ -104,7 +104,7 @@ See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for
## Parse errors ## Parse errors
The library throws this exception when a parse error occurs. Parse errors The library throws this exception when a parse error occurs. Parse errors
can occur during the deserialization of JSON text or of one of the binary formats (BJData, BON8, BSON, CBOR, MessagePack, UBJSON), as well can occur during the deserialization of JSON text, CBOR, MessagePack, as well
as when using JSON Patch. as when using JSON Patch.
Exceptions have ids 1xx. Exceptions have ids 1xx.
@@ -190,7 +190,7 @@ This error indicates a syntax error while deserializing a JSON text. The error m
!!! tip !!! tip
- Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened. - Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened.
- Paste the input to a JSON validator like <https://jsonlint.com> or a tool like [jq](https://jqlang.github.io/jq/). - Paste the input to a JSON validator like <http://jsonlint.com> or a tool like [jq](https://stedolan.github.io/jq/).
### json.exception.parse_error.102 ### json.exception.parse_error.102
@@ -278,9 +278,7 @@ In a JSON Pointer, only `~0` and `~1` are valid escape sequences.
### json.exception.parse_error.109 ### json.exception.parse_error.109
A JSON Pointer array index must be a number. This exception is thrown for an array index that does not begin with a A JSON Pointer array index must be a number.
digit, except for `-` and the empty reference token, which throw [`out_of_range.404`](#jsonexceptionout_of_range404)
where they cannot be resolved.
!!! failure "Example messages" !!! failure "Example messages"
@@ -291,11 +289,6 @@ where they cannot be resolved.
[json.exception.parse_error.109] parse error: array index '+1' is not a number [json.exception.parse_error.109] parse error: array index '+1' is not a number
``` ```
!!! note
Before version 3.13.0, a one-character array index that is not a digit (e.g., `x`) threw
[`out_of_range.404`](#jsonexceptionout_of_range404) instead.
### json.exception.parse_error.110 ### json.exception.parse_error.110
When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has
@@ -817,7 +810,7 @@ does not list an enumerator and it is therefore converted like the first listed
A discarded value (one created by [`parse()`](../api/basic_json/parse.md) with a callback that returns `false` for the A discarded value (one created by [`parse()`](../api/basic_json/parse.md) with a callback that returns `false` for the
value, or by default-constructing a [`basic_json`](../api/basic_json/index.md) with value, or by default-constructing a [`basic_json`](../api/basic_json/index.md) with
[`value_t::discarded`](../api/basic_json/value_t.md)) was passed to a binary serialization function, either directly or [`value_t::discarded`](../api/basic_json/value_t.md)) was passed to a binary serialization function, either directly or
nested in an array or object. There is no way to represent a discarded value in CBOR, MessagePack, UBJSON, BJData, BSON, or BON8. nested in an array or object. There is no way to represent a discarded value in CBOR, MessagePack, UBJSON, BJData, or BSON.
!!! failure "Example message" !!! failure "Example message"
@@ -878,8 +871,7 @@ The provided key was not found in the JSON object.
### json.exception.out_of_range.404 ### json.exception.out_of_range.404
A reference token in a JSON Pointer could not be resolved, for instance an array index that begins with a digit but A reference token in a JSON Pointer could not be resolved.
contains other characters (e.g., `1a`), or `-` where it cannot be used.
!!! failure "Example message" !!! failure "Example message"
+1 -1
View File
@@ -26,7 +26,7 @@ Fixes bugs found in 3.11.3 and adds several features. All changes are backward-c
[BJData](../features/binary_formats/bjdata.md) draft 3 and unsigned 64-bit integers for [BJData](../features/binary_formats/bjdata.md) draft 3 and unsigned 64-bit integers for
[BSON](../features/binary_formats/bson.md). [BSON](../features/binary_formats/bson.md).
- Adds multidimensional C-array conversion and UTF-8 encoded `std::filesystem::path` conversions, and - Adds multidimensional C-array conversion and UTF-8 encoded `std::filesystem::path` conversions, and
raises the minimum [CMake](../integration/cmake.md) version to 3.5 and supports CMake 4.0. lowers the minimum [CMake](../integration/cmake.md) version to allow CMake 4.0.
[Full release notes](https://github.com/nlohmann/json/releases/tag/v3.12.0). [Full release notes](https://github.com/nlohmann/json/releases/tag/v3.12.0).
@@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.15)
include("cmake/HunterGate.cmake") include("cmake/HunterGate.cmake")
HunterGate( HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.26.12.tar.gz" URL "https://github.com/cpp-pm/hunter/archive/v0.23.297.tar.gz"
SHA1 "6498c5d0dec25d7fffb2b0574ebaf265179b894d" SHA1 "3319fe6a3b08090df7df98dee75134d68e2ef5a3"
) )
project(json_example) project(json_example)
+3 -28
View File
@@ -2,30 +2,12 @@
# Creates a `<path>.md` sibling of each HTML output (for example, # Creates a `<path>.md` sibling of each HTML output (for example,
# `features/comments/` becomes `features/comments.md`) so agents and tools can # `features/comments/` becomes `features/comments.md`) so agents and tools can
# fetch the raw Markdown directly instead of parsing rendered HTML. The # fetch the raw Markdown directly instead of parsing rendered HTML.
# `--8<-- "path"` lines of pymdownx.snippets are expanded with the extension's
# own preprocessor and the settings from mkdocs.yml, so the copies contain the
# included example code and files instead of the include directives.
import io
import os import os
import shutil
import markdown
from pymdownx.snippets import SnippetMissingError
_pages = [] _pages = []
_snippets = None
def on_config(config):
global _snippets
md = markdown.Markdown(
extensions=["pymdownx.snippets"],
extension_configs={"pymdownx.snippets": config["mdx_configs"].get("pymdownx.snippets", {})},
)
_snippets = md.preprocessors["snippet"]
_snippets.auto_append = [] # the glossary is only needed to render abbreviations
return config
def on_files(files, config): def on_files(files, config):
@@ -40,11 +22,4 @@ def on_post_build(config):
url = file.url.rstrip("/") url = file.url.rstrip("/")
target = os.path.join(site_dir, (url or "index") + ".md") target = os.path.join(site_dir, (url or "index") + ".md")
os.makedirs(os.path.dirname(target), exist_ok=True) os.makedirs(os.path.dirname(target), exist_ok=True)
with io.open(file.abs_src_path, encoding="utf-8", newline="") as source: shutil.copyfile(file.abs_src_path, target)
text = source.read()
try:
text = "\n".join(_snippets.run(text.split("\n")))
except (SnippetMissingError, OSError):
pass # keep the include directives; the regular build reports the missing file
with io.open(target, "w", encoding="utf-8", newline="") as copy:
copy.write(text)
+1 -1
View File
@@ -1,7 +1,7 @@
<!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary --> <!-- https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary -->
*[ADL]: Argument-dependent lookup *[ADL]: Argument-dependent lookup
*[API]: Application Programming Interface *[API]: Application Programming Interfaces
*[ASCII]: American Standard Code for Information Interchange *[ASCII]: American Standard Code for Information Interchange
*[BDFL]: Benevolent Dictator for Life *[BDFL]: Benevolent Dictator for Life
*[BJData]: Binary JData *[BJData]: Binary JData
-2
View File
@@ -88,7 +88,6 @@ nav:
- features/performance.md - features/performance.md
- Parsing: - Parsing:
- features/parsing/index.md - features/parsing/index.md
- features/parsing/error_recovery.md
- features/parsing/json_lines.md - features/parsing/json_lines.md
- features/parsing/parse_exceptions.md - features/parsing/parse_exceptions.md
- features/parsing/parser_callbacks.md - features/parsing/parser_callbacks.md
@@ -300,7 +299,6 @@ nav:
- 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md - 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION': api/macros/json_disable_tuple_reference_conversion.md
- 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20, JSON_HAS_CPP_23, JSON_HAS_CPP_26': api/macros/json_has_cpp_11.md - 'JSON_HAS_CPP_11, JSON_HAS_CPP_14, JSON_HAS_CPP_17, JSON_HAS_CPP_20, JSON_HAS_CPP_23, JSON_HAS_CPP_26': api/macros/json_has_cpp_11.md
- 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md - 'JSON_HAS_EXPERIMENTAL_FILESYSTEM, JSON_HAS_FILESYSTEM': api/macros/json_has_filesystem.md
- 'JSON_HAS_RANGE_VIEW_CONVERSION': api/macros/json_has_range_view_conversion.md
- 'JSON_HAS_RANGES': api/macros/json_has_ranges.md - 'JSON_HAS_RANGES': api/macros/json_has_ranges.md
- 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md - 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md
- 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md - 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md
+31
View File
@@ -288,6 +288,36 @@ def check_header_links() -> None:
f'link to "{match.group(0)}" does not point to a documentation page') f'link to "{match.group(0)}" does not point to a documentation page')
def check_docset() -> None:
"""Every API page and every macro has an entry in the docset index; no entry points to a missing page."""
entry_re = re.compile(r"VALUES \('((?:[^']|'')*)', '(\w+)', '([^']*)'\);")
names_by_path = {}
with open("../../docset/docSet.sql", encoding="utf-8") as sql:
for name, _, path in entry_re.findall(sql.read()):
names_by_path.setdefault(path, set()).add(name.replace("''", "'"))
def to_path(page):
if os.path.basename(page) == "index.md":
return page[:-len("index.md")] + "index.html"
return page[:-len(".md")] + "/index.html"
pages = sorted(glob.glob("**/*.md", recursive=True))
for path in sorted(set(names_by_path) - {to_path(p) for p in pages}):
report("docset/stale_entry", "../../docset/docSet.sql", f'entry "{path}" has no documentation page')
for page in (p for p in pages if p.startswith("api/")):
names = names_by_path.get(to_path(page))
if not names:
report("docset/missing_entry", page, "page has no entry in docs/docset/docSet.sql")
elif page.startswith("api/macros/") and os.path.basename(page) != "index.md":
with open(page, encoding="utf-8") as content:
text = content.read()
match = re.search(r"^# (.+)$", text, re.MULTILINE) or re.search(r"<h1>(.*?)</h1>", text, re.DOTALL)
title = re.sub(r"<[^>]+>|\s+", " ", match.group(1))
for macro in filter(None, (x.strip() for x in re.split(r"[,/]", title))):
if macro not in names:
report("docset/missing_macro", page, f'macro "{macro}" has no entry in docs/docset/docSet.sql')
if __name__ == "__main__": if __name__ == "__main__":
print(120 * "-") print(120 * "-")
check_structure() check_structure()
@@ -297,6 +327,7 @@ if __name__ == "__main__":
check_heading_levels() check_heading_levels()
check_image_alt_text() check_image_alt_text()
check_header_links() check_header_links()
check_docset()
print(120 * "-") print(120 * "-")
if warnings > 0: if warnings > 0:
@@ -48,7 +48,7 @@ inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_null())) if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{ {
throw_type_must_be("null", j); JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
} }
n = nullptr; n = nullptr;
} }
@@ -102,7 +102,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
throw_type_must_be("number", j); JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
} }
} }
@@ -111,7 +111,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{ {
throw_type_must_be("boolean", j); JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
} }
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
} }
@@ -121,7 +121,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
@@ -137,7 +137,7 @@ inline void from_json(const BasicJsonType& j, StringType& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@@ -183,7 +183,7 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
l.clear(); l.clear();
std::transform(j.rbegin(), j.rend(), std::transform(j.rbegin(), j.rend(),
@@ -200,7 +200,7 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
l.resize(j.size()); l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l), std::transform(j.begin(), j.end(), std::begin(l),
@@ -305,7 +305,7 @@ void())
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
from_json_array_impl(j, arr, priority_tag<3> {}); from_json_array_impl(j, arr, priority_tag<3> {});
@@ -324,7 +324,7 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {}); return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
@@ -335,7 +335,7 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{ {
throw_type_must_be("binary", j); JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
} }
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@@ -356,7 +356,7 @@ inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
} }
else else
{ {
throw_type_must_be("binary or array", j); JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
} }
} }
@@ -377,7 +377,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
throw_type_must_be("object", j); JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
} }
ConstructibleObjectType ret; ConstructibleObjectType ret;
@@ -432,7 +432,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
case value_t::binary: case value_t::binary:
case value_t::discarded: case value_t::discarded:
default: default:
throw_type_must_be("number", j); JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
} }
} }
@@ -504,7 +504,7 @@ auto from_json(const BasicJsonType& j, TupleRelated&& t)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {}); return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
@@ -517,14 +517,14 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
throw_type_must_be("array", j); JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
throw_type_must_be("array", p); JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
} }
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>()); m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
} }
@@ -591,7 +591,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
throw_type_must_be("string", j); JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
} }
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
// Checking for C++20 standard or later can be insufficient in case the // Checking for C++20 standard or later can be insufficient in case the
@@ -414,13 +414,13 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
BasicJsonType key = p.first; BasicJsonType key = p.first;
if (JSON_HEDLEY_UNLIKELY(!key.is_string())) if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
{ {
throw_type_must_be("string", key); JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
} }
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>(); auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second)) if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
{ {
JSON_THROW(type_error::create(exception_id::enum_key_duplicate, concat("duplicate object key '", key_string, "'"), &key)); JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
} }
} }
external_constructor<value_t::object>::construct(j, std::move(obj)); external_constructor<value_t::object>::construct(j, std::move(obj));
-144
View File
@@ -45,84 +45,6 @@ namespace detail
// exceptions // // exceptions //
//////////////// ////////////////
/*!
@brief the ids of the exceptions thrown by the library
@note The values are part of the public API: they are the `id` member of the
exceptions and appear in their `what()` messages.
@sa https://json.nlohmann.me/home/exceptions/
*/
enum class exception_id : int
{
// parse_error
syntax_error = 101, ///< unexpected token or invalid literal while parsing JSON
patch_not_an_array = 104, ///< a JSON Patch document is not an array of objects
patch_invalid_operation = 105, ///< a JSON Patch operation is malformed
pointer_index_leading_zero = 106, ///< a JSON Pointer array index has a leading zero
pointer_missing_slash = 107, ///< a JSON Pointer does not start with '/'
pointer_invalid_escape = 108, ///< a JSON Pointer contains an escape other than ~0 and ~1
pointer_index_not_a_number = 109, ///< a JSON Pointer array index is not a number
unexpected_end_of_input = 110, ///< a binary input ends early, or has bytes left at its end
unexpected_byte = 112, ///< a binary input contains an unexpected byte or invalid length
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
bson_unsupported_type = 114, ///< a BSON record type is not supported
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
// invalid_iterator
iterators_incompatible = 201, ///< the iterators of a range belong to different values
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
iterator_range_from_other_value = 203, ///< an iterator range does not belong to the value it is used with
iterator_range_out_of_range = 204, ///< an iterator range of a primitive value is not [begin, end)
iterator_out_of_range = 205, ///< an iterator of a primitive value is not begin()
iterator_range_of_null = 206, ///< an iterator range belongs to a null value
iterator_key_not_object = 207, ///< key() is called on an iterator of a non-object
iterator_subscript_on_object = 208, ///< operator[] is called on an iterator of an object
iterator_arithmetic_on_object = 209, ///< an offset operator is used on an iterator of an object
insert_range_incompatible = 210, ///< the iterators of an inserted range belong to different values
insert_range_into_itself = 211, ///< an inserted range belongs to the value it is inserted into
iterators_compare_different_values = 212, ///< iterators of different values are compared
iterator_order_on_object = 213, ///< iterators of an object are compared by order
iterator_value_unavailable = 214, ///< an iterator does not refer to a value
// type_error
object_from_non_pairs = 301, ///< an object is created from an initializer list that is not a list of pairs
type_mismatch = 302, ///< a value has the wrong type for a conversion
incompatible_reference_type = 303, ///< get_ref() is called with a reference type that does not match the value
at_wrong_type = 304, ///< at() is called on a value of the wrong type
subscript_wrong_type = 305, ///< operator[] is called on a value of the wrong type
value_wrong_type = 306, ///< value() is called on a value of the wrong type
erase_wrong_type = 307, ///< erase() is called on a value of the wrong type
push_back_wrong_type = 308, ///< push_back() or operator+= is called on a value of the wrong type
insert_wrong_type = 309, ///< insert() is called on a value of the wrong type
swap_wrong_type = 310, ///< swap() is called on a value of the wrong type
emplace_wrong_type = 311, ///< emplace() or emplace_back() is called on a value of the wrong type
update_wrong_type = 312, ///< update() is called on a value of the wrong type
unflatten_invalid_value = 313, ///< unflatten() finds conflicting paths
unflatten_not_object = 314, ///< unflatten() is called on a non-object
unflatten_value_not_primitive = 315, ///< unflatten() is called on an object with non-primitive values
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
discarded_value_used = 321, ///< a discarded value is used
// out_of_range
array_index_out_of_range = 401, ///< an array index is out of range
pointer_past_the_end_index = 402, ///< a JSON Pointer uses the array index '-'
key_not_found = 403, ///< an object key is not found
pointer_unresolved = 404, ///< a JSON Pointer reference token cannot be resolved
patch_on_root = 405, ///< the root of a value has no parent, e.g. for a JSON Patch 'remove' or 'add' at the root
number_overflow = 406, ///< a number cannot be stored without overflowing to NaN or INF
integer_too_large = 407, ///< an integer cannot be represented in the binary format
container_too_large = 408, ///< the size of a container in a binary input exceeds the maximal capacity
bson_key_with_null = 409, ///< a BSON key contains U+0000
value_out_of_range = 410, ///< an enum value is undefined, or a JSON Pointer array index exceeds size_type
patch_add_parent_not_container = 411, ///< the parent of a JSON Patch 'add' target is not a container
length_too_large = 412, ///< a length does not fit into the length field of a binary format
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
// other_error
internal_error = 500, ///< unreachable code was reached
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
size_marker_required = 502 ///< UBJSON/BJData output with type markers requires size markers
};
/// @brief general exception of the @ref basic_json class /// @brief general exception of the @ref basic_json class
/// @sa https://json.nlohmann.me/api/basic_json/exception/ /// @sa https://json.nlohmann.me/api/basic_json/exception/
class exception : public std::exception class exception : public std::exception
@@ -273,18 +195,6 @@ class parse_error : public exception
return {id_, byte_, w.c_str()}; return {id_, byte_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), pos, what_arg, context);
}
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static parse_error create(exception_id id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), byte_, what_arg, context);
}
/*! /*!
@brief byte index of the parse error @brief byte index of the parse error
@@ -319,12 +229,6 @@ class invalid_iterator : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static invalid_iterator create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
invalid_iterator(int id_, const char* what_arg) invalid_iterator(int id_, const char* what_arg)
@@ -343,12 +247,6 @@ class type_error : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static type_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -366,12 +264,6 @@ class out_of_range : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static out_of_range create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -389,12 +281,6 @@ class other_error : public exception
return {id_, w.c_str()}; return {id_, w.c_str()};
} }
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
static other_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
{
return create(static_cast<int>(id_), what_arg, context);
}
private: private:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
@@ -421,36 +307,6 @@ void templated_json_throw(ExceptionType exception)
(void)exception; (void)exception;
} }
/*!
@brief throws because @a j does not have the type a conversion expects
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
@param[in] j the value with the wrong type
@throw type_error.302 always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN inline void throw_type_must_be(const char* expected, const BasicJsonType& j)
{
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(j);
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
}
/*!
@brief throws because an operation is not supported for the type of @a j
@param[in] id_ the id of the type_error exception (at_wrong_type..update_wrong_type)
@param[in] operation the operation, e.g. "erase()"
@param[in] j the value the operation was called on
@throw type_error always
*/
template<typename BasicJsonType>
JSON_HEDLEY_NO_RETURN inline void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
{
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(operation);
static_cast<void>(j);
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
}
} // namespace detail } // namespace detail
NLOHMANN_JSON_NAMESPACE_END NLOHMANN_JSON_NAMESPACE_END
File diff suppressed because it is too large. Load diff
@@ -581,7 +581,7 @@ class wide_string_input_adapter
template<class T> template<class T>
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1) JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
{ {
JSON_THROW(parse_error::create(exception_id::unexpected_byte, 1, "wide string type cannot be interpreted as binary data", nullptr)); JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
} }
private: private:
@@ -793,7 +793,7 @@ inline file_input_adapter input_adapter(std::FILE* file)
{ {
if (file == nullptr) if (file == nullptr)
{ {
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return file_input_adapter(file); return file_input_adapter(file);
} }
@@ -802,7 +802,7 @@ inline input_stream_adapter input_adapter(std::istream& stream)
{ {
if (stream.rdbuf() == nullptr) if (stream.rdbuf() == nullptr)
{ {
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
return input_stream_adapter(stream); return input_stream_adapter(stream);
} }
@@ -827,7 +827,7 @@ contiguous_bytes_input_adapter input_adapter(CharT b)
{ {
if (b == nullptr) if (b == nullptr)
{ {
JSON_THROW(parse_error::create(exception_id::syntax_error, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr)); JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
auto length = std::strlen(reinterpret_cast<const char*>(b)); auto length = std::strlen(reinterpret_cast<const char*>(b));
const auto* ptr = reinterpret_cast<const char*>(b); const auto* ptr = reinterpret_cast<const char*>(b);
+80 -92
View File
@@ -132,9 +132,7 @@ struct json_sax
@param[in] position the position in the input where the error occurs @param[in] position the position in the input where the error occurs
@param[in] last_token the last read token @param[in] last_token the last read token
@param[in] ex an exception object describing the error @param[in] ex an exception object describing the error
@return whether to recover from the error: false stops parsing; true @return whether parsing should proceed (must return false)
repairs the error and continues, or, if that is not possible,
stops after completing the value read so far
*/ */
virtual bool parse_error(std::size_t position, virtual bool parse_error(std::size_t position,
const std::string& last_token, const std::string& last_token,
@@ -178,27 +176,6 @@ template<typename ArrayType>
inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/) inline void reserve_array(ArrayType& /*arr*/, std::size_t /*len*/, priority_tag<0> /*unused*/)
{} {}
/*!
@brief reports an object or array whose announced size exceeds max_size()
Shared by json_sax_dom_parser and json_sax_dom_callback_parser.
@param[in] sax the SAX parser to report the error to
@param[in] len the number of elements announced by the input, or unknown_size()
@param[in] kind "object" or "array"
@param[in] ref the object or array that was just created
@return whether parsing should continue (false after reporting out_of_range.408)
*/
template<typename SAX, typename BasicJsonType>
bool check_container_size(SAX& sax, std::size_t len, const char* kind, BasicJsonType* ref)
{
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref->max_size()))
{
return sax.parse_error(0, "", out_of_range::create(exception_id::container_too_large, concat("excessive ", kind, " size: ", std::to_string(len)), ref));
}
return true;
}
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
/*! /*!
@brief set the diagnostic positions of a value the DOM SAX parsers just stored @brief set the diagnostic positions of a value the DOM SAX parsers just stored
@@ -208,35 +185,6 @@ befriends this struct, as the position members are private.
*/ */
struct diagnostic_positions struct diagnostic_positions
{ {
/*!
@param[in,out] v the object or array whose opening brace or bracket was just read
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
*/
template<typename BasicJsonType, typename LexerType>
static void set_container_start(BasicJsonType& v, LexerType* lexer)
{
if (lexer)
{
// Lexer has read the first character of the container, so
// subtract 1 from the position to get the correct start position.
v.start_position = lexer->get_position() - 1;
}
}
/*!
@param[in,out] v the object or array whose closing brace or bracket was just read
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone
*/
template<typename BasicJsonType, typename LexerType>
static void set_container_end(BasicJsonType& v, LexerType* lexer)
{
if (lexer)
{
// Lexer's position is past the closing brace or bracket, so set that as the end position.
v.end_position = lexer->get_position();
}
}
/*! /*!
@param[in,out] v the value that was just parsed @param[in,out] v the value that was just parsed
@param[in] lexer the lexer that read it, or nullptr to leave @a v alone @param[in] lexer the lexer that read it, or nullptr to leave @a v alone
@@ -321,12 +269,9 @@ a pointer to the respective array or object for each recursion depth.
After successful parsing, the value that is passed by reference to the After successful parsing, the value that is passed by reference to the
constructor contains the parsed value. constructor contains the parsed value.
@tparam BasicJsonType the JSON type @tparam BasicJsonType the JSON type
@tparam InputAdapterType the input adapter of the lexer that can be passed to
the constructor to record diagnostic positions; it
does not matter if no lexer is passed
*/ */
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type> template<typename BasicJsonType, typename InputAdapterType>
class json_sax_dom_parser class json_sax_dom_parser
{ {
public: public:
@@ -404,11 +349,22 @@ class json_sax_dom_parser
ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the object here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer has read the first character of the object, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
return check_container_size(*this, len, "object", ref_stack.back()); if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
return true;
} }
bool key(string_t& val) bool key(string_t& val)
@@ -427,7 +383,11 @@ class json_sax_dom_parser
JSON_ASSERT(ref_stack.back()->is_object()); JSON_ASSERT(ref_stack.back()->is_object());
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing brace, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -440,13 +400,17 @@ class json_sax_dom_parser
ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the array here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back()))) if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{ {
return false; return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
} }
if (len != detail::unknown_size()) if (len != detail::unknown_size())
@@ -463,7 +427,11 @@ class json_sax_dom_parser
JSON_ASSERT(ref_stack.back()->is_array()); JSON_ASSERT(ref_stack.back()->is_array());
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing bracket, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -550,7 +518,7 @@ class json_sax_dom_parser
lexer_t* m_lexer_ref = nullptr; lexer_t* m_lexer_ref = nullptr;
}; };
template<typename BasicJsonType, typename InputAdapterType = string_input_adapter_type> template<typename BasicJsonType, typename InputAdapterType>
class json_sax_dom_callback_parser class json_sax_dom_callback_parser
{ {
public: public:
@@ -643,11 +611,21 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the object here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer has read the first character of the object, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
return check_container_size(*this, len, "object", ref_stack.back()); // check object limit
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{
return parse_error(0, "", out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
}
} }
return true; return true;
} }
@@ -717,7 +695,11 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing brace, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -728,7 +710,13 @@ class json_sax_dom_callback_parser
} }
} }
const string_t object_key = pop_container(); JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
const string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
{ {
@@ -754,13 +742,20 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
// Manually set the start position of the array here.
// Ensure this is after the call to handle_value to ensure correct start position. // Ensure this is after the call to handle_value to ensure correct start position.
diagnostic_positions::set_container_start(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer has read the first character of the array, so
// subtract 1 from the position to get the correct start position.
ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
}
#endif #endif
if (JSON_HEDLEY_UNLIKELY(!check_container_size(*this, len, "array", ref_stack.back()))) // check array limit
if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
{ {
return false; return parse_error(0, "", out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
} }
if (len != detail::unknown_size()) if (len != detail::unknown_size())
@@ -784,7 +779,11 @@ class json_sax_dom_callback_parser
{ {
#if JSON_DIAGNOSTIC_POSITIONS #if JSON_DIAGNOSTIC_POSITIONS
diagnostic_positions::set_container_end(*ref_stack.back(), m_lexer_ref); if (m_lexer_ref)
{
// Lexer's position is past the closing bracket, so set that as the end position.
ref_stack.back()->end_position = m_lexer_ref->get_position();
}
#endif #endif
ref_stack.back()->set_parents(); ref_stack.back()->set_parents();
@@ -810,7 +809,13 @@ class json_sax_dom_callback_parser
} }
} }
const string_t object_key = pop_container(); JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
const string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
// remove discarded value // remove discarded value
if (!ref_stack.empty() && ref_stack.back()) if (!ref_stack.empty() && ref_stack.back())
@@ -897,23 +902,6 @@ class json_sax_dom_callback_parser
return string_t{}; return string_t{};
} }
/*!
@brief leave the object or array that end_object()/end_array() just closed
@return the key the container is stored under in its parent (see
container_key_stack)
*/
string_t pop_container()
{
JSON_ASSERT(!ref_stack.empty());
JSON_ASSERT(!keep_stack.empty());
JSON_ASSERT(!container_key_stack.empty());
ref_stack.pop_back();
keep_stack.pop_back();
string_t object_key = std::move(container_key_stack.back());
container_key_stack.pop_back();
return object_key;
}
/*! /*!
@brief remove the discarded value the callback rejected from its parent, @brief remove the discarded value the callback rejected from its parent,
unless it is a duplicate key's slot with a stashed previous value, in unless it is a duplicate key's slot with a stashed previous value, in
+2 -591
View File
@@ -10,7 +10,7 @@
#include <array> // array #include <array> // array
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint8_t, uint32_t #include <cstdint> // uint32_t
#include <cstdio> // snprintf #include <cstdio> // snprintf
#include <initializer_list> // initializer_list #include <initializer_list> // initializer_list
#include <string> // char_traits, string #include <string> // char_traits, string
@@ -439,16 +439,8 @@ class lexer : public lexer_base<BasicJsonType>
if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
{ {
// expect next \uxxxx entry // expect next \uxxxx entry
if (JSON_HEDLEY_LIKELY(get() == '\\')) if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
{ {
if (JSON_HEDLEY_UNLIKELY(get() != 'u'))
{
// current is the character escaped by the backslash
error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
string_error_resume = resume_kind::escaped_character;
return token_type::parse_error;
}
const int codepoint2 = get_codepoint(); const int codepoint2 = get_codepoint();
if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
@@ -473,11 +465,7 @@ class lexer : public lexer_base<BasicJsonType>
} }
else else
{ {
// the second escape was read completely and is a
// code point of its own
error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
string_error_resume = resume_kind::after_escape;
string_error_codepoint = codepoint2;
return token_type::parse_error; return token_type::parse_error;
} }
} }
@@ -491,9 +479,7 @@ class lexer : public lexer_base<BasicJsonType>
{ {
if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
{ {
// the escape was read completely
error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
string_error_resume = resume_kind::after_escape;
return token_type::parse_error; return token_type::parse_error;
} }
} }
@@ -2147,574 +2133,6 @@ scan_number_done:
} }
} }
public:
/////////////////////
// error recovery
/////////////////////
/*!
@brief make the best of the token that scan() rejected
Called by the parser after scan() returned token_type::parse_error and the
SAX parser asked to recover from the error (see #3989). Keeps what can be
read of the token and skips the rest:
- A string keeps its characters. An unknown escape stands for the escaped
character itself (as in JavaScript), an invalid Unicode escape and ill-formed
UTF-8 become U+FFFD, and a control character is kept. A line break or the
end of the input ends a string that lacks its closing quote.
- A number keeps its longest valid prefix, e.g. `1` for `1.` or `1e+`.
- A block comment that is not closed runs to the end of the input.
- Anything else is skipped.
The rest of an invalid token is skipped up to the next delimiter
(whitespace, a structural character, or a quote). A delimiter that the
invalid token consumed is returned to the input, so that the next scan()
reads it.
@return token_type::value_string or a number token type if a string or a
number could be read, token_type::end_of_input for a block comment
that is not closed, token_type::uninitialized otherwise
*/
token_type recover_token()
{
const resume_kind resume = string_error_resume;
const int codepoint = string_error_codepoint;
string_error_resume = resume_kind::character;
string_error_codepoint = -1;
if (error_message_starts_with("invalid string"))
{
return recover_string(resume, codepoint);
}
if (error_message_starts_with("invalid number"))
{
return recover_number();
}
if (error_message_starts_with("invalid comment; missing"))
{
// the comment runs to the end of the input
return token_type::end_of_input;
}
skip_to_delimiter();
return token_type::uninitialized;
}
/*!
@brief return the token that scan() read last to the input, so that the
next scan() reads it again
Called by the parser when recovering from an error. The token must be a
single character (',', ':', '[', ']', '{', or '}') or the end of the
input, and scan() must have read it last.
*/
void unget_token()
{
JSON_ASSERT(!next_unget);
unget();
}
/*!
@brief let the token string for the next error begin at the current character
The token string of an error reaches back to the beginning of the last
string or number. After an error, the parser calls this function so that
the next error does not report (and, with many errors, copy) everything
read since then.
*/
void restart_token_string()
{
restart_token_string_impl(std::integral_constant<bool, lazy_token_string> {});
}
private:
/// how recover_string() continues after the error scan_string() reported
enum class resume_kind : std::uint8_t
{
/// current is the next character of the string (or the end of input)
character,
/// current is the character escaped by the preceding backslash
escaped_character,
/// current is the last character of a complete escape
after_escape
};
/// whether error_message begins with @a prefix
bool error_message_starts_with(const char* prefix) const noexcept
{
const char* message = error_message;
while (*prefix != '\0')
{
if (*message++ != *prefix++)
{
return false;
}
}
return true;
}
/// whether current ends an invalid token (see recover_token())
bool current_is_delimiter() const noexcept
{
switch (current)
{
case ' ':
case '\t':
case '\n':
case '\r':
case '[':
case ']':
case '{':
case '}':
case ',':
case ':':
case '\"':
#if !JSON_STRICT_NUL_HANDLING
case '\0':
#endif
case char_traits<char_type>::eof():
return true;
case '/':
return ignore_comments;
default:
return false;
}
}
/// skip the rest of an invalid token and return its delimiter to the input
void skip_to_delimiter()
{
while (!current_is_delimiter())
{
get();
}
if (current != char_traits<char_type>::eof())
{
unget();
}
}
/// append U+FFFD REPLACEMENT CHARACTER to token_buffer
void add_replacement_character()
{
add(0xEF);
add(0xBF);
add(0xBD);
}
/// append the UTF-8 encoding of @a codepoint (not a surrogate) to token_buffer
void add_codepoint(const int codepoint)
{
JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
const auto cp = static_cast<unsigned int>(codepoint);
if (cp < 0x80)
{
add(static_cast<char_int_type>(cp));
}
else if (cp <= 0x7FF)
{
add(static_cast<char_int_type>(0xC0u | (cp >> 6u)));
add(static_cast<char_int_type>(0x80u | (cp & 0x3Fu)));
}
else if (cp <= 0xFFFF)
{
add(static_cast<char_int_type>(0xE0u | (cp >> 12u)));
add(static_cast<char_int_type>(0x80u | ((cp >> 6u) & 0x3Fu)));
add(static_cast<char_int_type>(0x80u | (cp & 0x3Fu)));
}
else
{
add(static_cast<char_int_type>(0xF0u | (cp >> 18u)));
add(static_cast<char_int_type>(0x80u | ((cp >> 12u) & 0x3Fu)));
add(static_cast<char_int_type>(0x80u | ((cp >> 6u) & 0x3Fu)));
add(static_cast<char_int_type>(0x80u | (cp & 0x3Fu)));
}
}
/// append a code point read from a Unicode escape; a surrogate becomes U+FFFD
void add_escaped_codepoint(const int codepoint)
{
if (0xD800 <= codepoint && codepoint <= 0xDFFF)
{
add_replacement_character();
}
else
{
add_codepoint(codepoint);
}
}
/*!
@brief remove an incomplete UTF-8 sequence from the end of token_buffer
next_byte_in_range() adds the bytes of a sequence as it checks them, so
when it rejects a byte, the beginning of the sequence is already in
token_buffer, which otherwise holds only complete sequences.
@return whether an incomplete sequence was removed
*/
bool remove_incomplete_utf8_sequence()
{
std::size_t lead = token_buffer.size();
std::size_t continuation_bytes = 0;
while (lead > 0 && continuation_bytes < 3
&& (static_cast<unsigned char>(token_buffer[lead - 1]) & 0xC0u) == 0x80u)
{
--lead;
++continuation_bytes;
}
if (lead == 0)
{
return false;
}
const auto lead_byte = static_cast<unsigned char>(token_buffer[lead - 1]);
std::size_t expected = 0;
if (lead_byte >= 0xF0)
{
expected = 3;
}
else if (lead_byte >= 0xE0)
{
expected = 2;
}
else if (lead_byte >= 0xC0)
{
expected = 1;
}
if (continuation_bytes >= expected)
{
return false;
}
token_buffer.resize(lead - 1);
return true;
}
/*!
@brief read the UTF-8 sequence that begins with current, which is not ASCII
@return whether the next character must be read; false if current still
needs to be handled, because it does not belong to the sequence
*/
bool recover_utf8_sequence()
{
// the number of continuation bytes and the range of the first one;
// see the ranges in scan_string()
std::size_t count = 0;
char_int_type low = 0x80;
char_int_type high = 0xBF;
if (current >= 0xC2 && current <= 0xDF)
{
count = 1;
}
else if (current >= 0xE0 && current <= 0xEF)
{
count = 2;
low = (current == 0xE0) ? 0xA0 : 0x80;
high = (current == 0xED) ? 0x9F : 0xBF;
}
else if (current >= 0xF0 && current <= 0xF4)
{
count = 3;
low = (current == 0xF0) ? 0x90 : 0x80;
high = (current == 0xF4) ? 0x8F : 0xBF;
}
else
{
// an ill-formed byte
add_replacement_character();
return true;
}
const std::size_t start = token_buffer.size();
add(current);
for (std::size_t i = 0; i < count; ++i)
{
get();
if (current < low || current > high)
{
token_buffer.resize(start);
add_replacement_character();
return false;
}
add(current);
low = 0x80;
high = 0xBF;
}
return true;
}
/*!
@brief read the low surrogate that must follow the high surrogate @a high
@return whether the next character must be read; false if current still
needs to be handled
*/
bool recover_low_surrogate(int high)
{
while (true)
{
if (get() != '\\')
{
add_replacement_character();
return false;
}
if (get() != 'u')
{
add_replacement_character();
// not 'u', so this does not come back here
return recover_escape();
}
const int low = get_codepoint();
if (low == -1)
{
add_replacement_character();
return false;
}
if (0xDC00 <= low && low <= 0xDFFF)
{
add_codepoint(static_cast<int>((static_cast<unsigned int>(high) << 10u)
+ static_cast<unsigned int>(low) - 0x35FDC00u));
return true;
}
// high has no low surrogate
add_replacement_character();
if (low < 0xD800 || low > 0xDBFF)
{
add_codepoint(low);
return true;
}
// another high surrogate
high = low;
}
}
/*!
@brief read the escape whose backslash was read; current is the escaped character
@return whether the next character must be read; false if current still
needs to be handled
*/
bool recover_escape()
{
switch (current)
{
case '\"':
add('\"');
return true;
case '\\':
add('\\');
return true;
case '/':
add('/');
return true;
case 'b':
add('\b');
return true;
case 'f':
add('\f');
return true;
case 'n':
add('\n');
return true;
case 'r':
add('\r');
return true;
case 't':
add('\t');
return true;
case 'u':
{
const int codepoint = get_codepoint();
if (codepoint == -1)
{
add_replacement_character();
return false;
}
if (0xD800 <= codepoint && codepoint <= 0xDBFF)
{
return recover_low_surrogate(codepoint);
}
add_escaped_codepoint(codepoint);
return true;
}
// an unknown escape stands for the escaped character
default:
return false;
}
}
/*!
@brief read the rest of a string after scan_string() rejected it
token_buffer holds what scan_string() read before the error. See
recover_token() for how errors are repaired.
@param[in] resume how to continue, see resume_kind
@param[in] codepoint for a high surrogate followed by an escape of another
code point: that code point; -1 otherwise
*/
token_type recover_string(const resume_kind resume, const int codepoint)
{
// whether the next character must be read before it can be handled
bool fetch = false;
if (error_message_starts_with("invalid string: surrogate")
|| error_message_starts_with("invalid string: '\\u'")
|| (error_message_starts_with("invalid string: ill-formed UTF-8")
&& remove_incomplete_utf8_sequence()))
{
add_replacement_character();
}
switch (resume)
{
case resume_kind::escaped_character:
fetch = recover_escape();
break;
case resume_kind::after_escape:
if (0xD800 <= codepoint && codepoint <= 0xDBFF)
{
fetch = recover_low_surrogate(codepoint);
}
else
{
if (codepoint != -1)
{
add_escaped_codepoint(codepoint);
}
fetch = true;
}
break;
case resume_kind::character:
default:
break;
}
while (true)
{
if (fetch)
{
get();
}
fetch = true;
switch (current)
{
case '\"':
// a line break or the end of the input ends a string that
// lacks its closing quote
case '\n':
case '\r':
case char_traits<char_type>::eof():
return token_type::value_string;
#if !JSON_STRICT_NUL_HANDLING
case '\0':
// the end of the input, see scan()
unget();
return token_type::value_string;
#endif
case '\\':
get();
fetch = recover_escape();
break;
default:
if (current < 0x80)
{
// including control characters
add(current);
}
else
{
fetch = recover_utf8_sequence();
}
break;
}
}
}
/*!
@brief keep the longest valid prefix of a number that scan_number() rejected
token_buffer holds the characters scan_number() accepted before the error,
so the prefix ends at its last digit.
*/
token_type recover_number()
{
// only size(), operator[], and resize() are used, which every string
// type the library supports provides
std::size_t length = token_buffer.size();
while (length != 0 && (token_buffer[length - 1] < '0' || token_buffer[length - 1] > '9'))
{
--length;
}
token_buffer.resize(length);
if (length == 0)
{
skip_to_delimiter();
return token_type::uninitialized;
}
if (decimal_point_position >= length)
{
decimal_point_position = std::string::npos;
}
std::size_t exponent = std::string::npos;
for (std::size_t i = 0; i < length; ++i)
{
if (token_buffer[i] == 'e' || token_buffer[i] == 'E')
{
exponent = i;
break;
}
}
const std::size_t mantissa_end = (exponent == std::string::npos) ? length : exponent;
token_type number_type = token_type::value_unsigned;
if (decimal_point_position != std::string::npos || exponent != std::string::npos)
{
number_type = token_type::value_float;
}
else if (token_buffer[0] == '-')
{
number_type = token_type::value_integer;
}
const token_type result = convert_number(number_type, mantissa_end);
skip_to_delimiter();
return result;
}
/// seekable adapter: the token string begins at current, which was consumed
void restart_token_string_impl(std::true_type /*lazy*/) noexcept
{
const std::size_t consumed = ia.get_consumed_count();
token_string_start = (consumed > 0 && current != char_traits<char_type>::eof()) ? consumed - 1 : consumed;
}
/// streaming adapter: the token string begins at current; a character
/// that was put back is copied again when it is read again
void restart_token_string_impl(std::false_type /*lazy*/)
{
token_string.clear();
if (!next_unget && current != char_traits<char_type>::eof())
{
token_string.push_back(char_traits<char_type>::to_char_type(current));
}
}
/// input adapter /// input adapter
InputAdapterType ia; InputAdapterType ia;
@@ -2754,13 +2172,6 @@ scan_number_done:
/// a description of occurred lexer errors /// a description of occurred lexer errors
const char* error_message = ""; const char* error_message = "";
/// how recover_token() continues a string that scan_string() rejected;
/// set only on the error paths that need more than error_message
resume_kind string_error_resume = resume_kind::character;
/// the code point of the second escape when a high surrogate is followed
/// by an escape that is not a low surrogate; -1 otherwise
int string_error_codepoint = -1;
// number values // number values
number_integer_t value_integer = 0; number_integer_t value_integer = 0;
number_unsigned_t value_unsigned = 0; number_unsigned_t value_unsigned = 0;
+54 -745
View File
@@ -139,59 +139,26 @@ class parser
bool accept(const bool strict = true) bool accept(const bool strict = true)
{ {
json_sax_acceptor<BasicJsonType> sax_acceptor; json_sax_acceptor<BasicJsonType> sax_acceptor;
return sax_parse_impl<false>(&sax_acceptor, strict); return sax_parse(&sax_acceptor, strict);
} }
/*!
@brief public SAX interface
If the SAX parser's parse_error() returns true, the parser recovers from
the error: it repairs the input and continues (see #3989).
@param[in] sax the SAX parser
@param[in] strict whether to expect the last token to be EOF
@return whether the input was parsed without errors and no SAX event
returned false
*/
template<typename SAX> template<typename SAX>
JSON_HEDLEY_NON_NULL(2) JSON_HEDLEY_NON_NULL(2)
bool sax_parse(SAX* sax, const bool strict = true) bool sax_parse(SAX* sax, const bool strict = true)
{
return sax_parse_impl<true>(sax, strict);
}
private:
/// what sax_parse_internal() does after an object key was expected
enum class next_step : std::uint8_t
{
/// stop parsing
stop,
/// parse a value that begins with last_token
parse_value,
/// evaluate the state of the innermost container, which reads
/// last_token again
evaluate_state
};
template<bool AllowRecovery, typename SAX>
JSON_HEDLEY_NON_NULL(2)
bool sax_parse_impl(SAX* sax, const bool strict)
{ {
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {}; (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
const bool result = sax_parse_internal<AllowRecovery>(sax); const bool result = sax_parse_internal(sax);
if (result) if (result)
{ {
if (strict) if (strict)
{ {
// strict mode: next byte must be EOF; after recovering from an // strict mode: next byte must be EOF
// error, the end of the input may already have been read if (get_token() != token_type::end_of_input)
if (last_token != token_type::end_of_input && get_token() != token_type::end_of_input)
{ {
// the value is complete, so there is nothing to recover return sax->parse_error(m_lexer.get_position(),
static_cast<void>(report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr), m_lexer.get_token_string(),
std::integral_constant<bool, AllowRecovery> {})); parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
return false;
} }
} }
else else
@@ -202,9 +169,10 @@ class parser
} }
} }
return result && !error_reported; return result;
} }
private:
/*! /*!
@brief run a DOM SAX parser to completion and position the lexer @brief run a DOM SAX parser to completion and position the lexer
@@ -222,14 +190,17 @@ class parser
template<typename DomSax> template<typename DomSax>
bool parse_dom(DomSax& sdp, const bool strict) bool parse_dom(DomSax& sdp, const bool strict)
{ {
sax_parse_internal<false>(&sdp); sax_parse_internal(&sdp);
if (strict) if (strict)
{ {
// in strict mode, input must be completely read // in strict mode, input must be completely read
if (get_token() != token_type::end_of_input) if (get_token() != token_type::end_of_input)
{ {
syntax_error(sdp, exception_message(token_type::end_of_input, "value")); sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value"), nullptr));
} }
} }
else else
@@ -242,20 +213,10 @@ class parser
return !sdp.is_errored(); return !sdp.is_errored();
} }
/*! template<typename SAX>
@brief parse a JSON value and pass it to a SAX parser
@tparam AllowRecovery whether to recover from an error if the SAX parser's
parse_error() returns true; false for the SAX parsers
of parse() and accept(), which never do, so that no
code for recovering is generated for them
*/
template<bool AllowRecovery, typename SAX>
JSON_HEDLEY_NON_NULL(2) JSON_HEDLEY_NON_NULL(2)
bool sax_parse_internal(SAX* sax) bool sax_parse_internal(SAX* sax)
{ {
const std::integral_constant<bool, AllowRecovery> allow_recovery{};
// stack to remember the hierarchy of structured values we are parsing // stack to remember the hierarchy of structured values we are parsing
// true = array; false = object // true = array; false = object
std::vector<bool> states; std::vector<bool> states;
@@ -286,18 +247,12 @@ class parser
break; break;
} }
// remember we are now inside an object // parse key
states.push_back(false);
// parse key (the steps of parse_key(), which are
// repeated here and below for speed)
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{ {
if (!continue_after(key_error(sax, allow_recovery, false), skip_to_state_evaluation)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
}
continue;
} }
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{ {
@@ -307,13 +262,14 @@ class parser
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
{ {
if (!continue_after(key_error(sax, allow_recovery, true), skip_to_state_evaluation)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
}
continue;
} }
// remember we are now inside an object
states.push_back(false);
// parse values // parse values
get_token(); get_token();
continue; continue;
@@ -349,11 +305,9 @@ class parser
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
{ {
if (!overflow_error(sax, res, allow_recovery)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
}
break;
} }
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@@ -421,63 +375,23 @@ class parser
case token_type::parse_error: case token_type::parse_error:
{ {
// using "uninitialized" to avoid an "expected" message // using "uninitialized" to avoid an "expected" message
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr), allow_recovery)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
}
// recover: keep what can be read of the token
recover_token(allow_recovery);
if (last_token != token_type::uninitialized)
{
// a string or a number
continue;
}
if (states.empty())
{
// look for the value after the garbage
if (!skip_to_value(allow_recovery))
{
return false;
}
continue;
}
// nothing could be read
if (JSON_HEDLEY_UNLIKELY(!sax->null()))
{
return false;
}
break;
} }
case token_type::end_of_input: case token_type::end_of_input:
{ {
if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1)) if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
{ {
// there is nothing to recover return sax->parse_error(m_lexer.get_position(),
static_cast<void>(report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), m_lexer.get_token_string(),
"attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr), allow_recovery)); parse_error::create(101, m_lexer.get_position(),
return false; "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
} }
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr), allow_recovery)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
}
// recover: the input ends where a value is missing
if (states.empty())
{
// there is no value
return false;
}
if (!recover_missing_value(sax, states, allow_recovery))
{
return false;
}
// the state evaluation reads the token again
unget_token(allow_recovery);
skip_to_state_evaluation = true;
continue;
} }
case token_type::uninitialized: case token_type::uninitialized:
case token_type::end_array: case token_type::end_array:
@@ -487,35 +401,9 @@ class parser
case token_type::literal_or_value: case token_type::literal_or_value:
default: // the last token was unexpected default: // the last token was unexpected
{ {
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr), allow_recovery)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
}
// recover
if (states.empty())
{
// look for the value after the garbage
if (!skip_to_value(allow_recovery))
{
return false;
}
continue;
}
if (last_token == token_type::name_separator)
{
// a stray ':'; the value may follow
get_token();
continue;
}
if (!recover_missing_value(sax, states, allow_recovery))
{
return false;
}
// the state evaluation reads the token again
unget_token(allow_recovery);
skip_to_state_evaluation = true;
continue;
} }
} }
} }
@@ -566,30 +454,9 @@ class parser
continue; continue;
} }
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr), allow_recovery)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
}
// recover
if (last_token == token_type::end_of_input)
{
// the input ends inside the array
return close_containers(sax, states, allow_recovery);
}
if (last_token == token_type::end_object)
{
// a wrong closing bracket closes the innermost container
if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
{
return false;
}
states.pop_back();
skip_to_state_evaluation = true;
}
// otherwise, a missing ',' (or a stray ':', which value
// parsing drops): the next value begins here
continue;
} }
// states.back() is false -> object // states.back() is false -> object
@@ -606,12 +473,11 @@ class parser
// parse key // parse key
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{ {
if (!continue_after(key_error(sax, allow_recovery, false), skip_to_state_evaluation)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
}
continue;
} }
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{ {
return false; return false;
@@ -620,11 +486,9 @@ class parser
// parse separator (:) // parse separator (:)
if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator))) if (JSON_HEDLEY_UNLIKELY(!get_token_expecting(token_type::name_separator)))
{ {
if (!continue_after(key_error(sax, allow_recovery, true), skip_to_state_evaluation)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
}
continue;
} }
// parse values // parse values
@@ -652,546 +516,12 @@ class parser
continue; continue;
} }
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr), allow_recovery)) return sax->parse_error(m_lexer.get_position(),
{ m_lexer.get_token_string(),
return false; parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
}
// recover
if (last_token == token_type::end_of_input)
{
// the input ends inside the object
return close_containers(sax, states, allow_recovery);
}
if (last_token == token_type::end_array)
{
// a wrong closing bracket closes the innermost container
if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
{
return false;
}
states.pop_back();
skip_to_state_evaluation = true;
continue;
}
if (!continue_after(recover_member(sax, allow_recovery), skip_to_state_evaluation))
{
return false;
}
} }
} }
/*!
@brief continue sax_parse_internal() after a recovery
@return whether to continue parsing
*/
bool continue_after(const next_step step, bool& skip_to_state_evaluation)
{
if (step == next_step::evaluate_state)
{
// the state evaluation reads the token again
m_lexer.unget_token();
skip_to_state_evaluation = true;
}
return step != next_step::stop;
}
/// the parser for parse() and accept() never recovers: stop parsing
JSON_INTERNAL_ALWAYS_INLINE
static std::false_type continue_after(std::false_type /*step*/, bool& /*skip_to_state_evaluation*/) noexcept
{
return {};
}
/*!
@brief parse an object key and the name separator (:) after it
last_token is the token where the key is expected. sax_parse_internal()
repeats these steps rather than calling this function, which is used
when recovering from an error.
@return next_step::parse_value if the value follows, with last_token its
first token; next_step::evaluate_state if the object's state is
to be evaluated after recovering from an error; next_step::stop
to stop parsing
*/
template<typename SAX>
next_step parse_key(SAX* sax)
{
const std::true_type allow_recovery{};
if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
{
return key_error(sax, allow_recovery, false);
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
return next_step::stop;
}
// parse separator (:)
if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
{
return key_error(sax, allow_recovery, true);
}
// the value begins with the next token
get_token();
return next_step::parse_value;
}
/*!
@brief report a number that is too large for number_float_t, and recover
from the error by passing the value on; the SAX parser gets the
number's text as well
This is a separate function, as reading other numbers is measurably
slower if the error is handled where they are read.
@param[in] sax the SAX parser
@param[in] value the value that is not finite
@return whether to continue parsing
*/
template<typename SAX>
bool overflow_error(SAX* sax, const number_float_t value, std::true_type allow_recovery)
{
if (!report_error(sax, out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr), allow_recovery))
{
return false;
}
return sax->number_float(value, m_lexer.get_string());
}
/// @copydoc overflow_error
template<typename SAX>
JSON_INTERNAL_ALWAYS_INLINE
std::false_type overflow_error(SAX* sax, const number_float_t /*value*/, std::false_type allow_recovery)
{
return report_error(sax, out_of_range::create(exception_id::number_overflow, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr), allow_recovery);
}
/*!
@brief report a missing key, or a missing name separator (:) after the
key; the parser for parse() and accept() never recovers
@param[in] key_read whether the key was read, so that the name separator
is missing
@return std::false_type, see report_error()
*/
template<typename SAX>
JSON_INTERNAL_ALWAYS_INLINE
std::false_type key_error(SAX* sax, std::false_type allow_recovery, const bool key_read)
{
return report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), key_read
? exception_message(token_type::name_separator, "object separator")
: exception_message(token_type::value_string, "object key"), nullptr), allow_recovery);
}
/*!
@brief report a missing key, or a missing name separator (:) after the
key, and recover from it
@param[in] key_read whether the key was read, so that the name separator
is missing
*/
template<typename SAX>
next_step key_error(SAX* sax, std::true_type allow_recovery, const bool key_read)
{
if (!key_read)
{
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr), allow_recovery))
{
return next_step::stop;
}
return recover_key(sax);
}
if (!report_error(sax, parse_error::create(exception_id::syntax_error, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr), allow_recovery))
{
return next_step::stop;
}
return recover_name_separator(sax);
}
/////////////////////
// error recovery
/////////////////////
/*
The functions below repair an error after the SAX parser's parse_error()
returned true (see #3989). Each mistake is repaired by the smallest local
edit: a missing ',' or ':' is inserted, a stray token is removed, what can
be read of an invalid string or number is kept (see
lexer::recover_token()), a missing value becomes null, a wrong closing
bracket closes the innermost container, and the end of the input closes
all of them. The events stay balanced, and every key() is followed by
exactly one value.
A repair hands a token to the state evaluation, by returning it to the
lexer (lexer::unget_token()) so that the state evaluation reads it again,
only if it is ',', ']', '}', or the end of the input. The state evaluation
hands a token to value or key parsing only if it is none of them, so a
token is never handed back and forth. Every other step reads a token or
closes a container, so parsing always ends.
*/
/*!
@brief report an error to the SAX parser; the parser for parse() and
accept() never recovers
@return std::false_type rather than false: its value is known where the
function is called even if the call is not inlined, so the code
for recovering is not generated
*/
template<typename SAX, typename Exception>
JSON_INTERNAL_ALWAYS_INLINE
std::false_type report_error(SAX* sax, const Exception& ex, std::false_type /*allow_recovery*/)
{
static_cast<void>(sax); // MSVC 2015 does not count calling a static parse_error() as using it
error_reported = true;
static_cast<void>(sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), ex));
return {};
}
/*!
@brief report an error to the SAX parser
@return whether to recover from the error
*/
template<typename SAX, typename Exception>
bool report_error(SAX* sax, const Exception& ex, std::true_type /*allow_recovery*/)
{
static_cast<void>(sax); // MSVC 2015 does not count calling a static parse_error() as using it
const std::size_t position = m_lexer.get_position().chars_read_total;
if (error_reported && position == last_error_position && last_token == last_error_token)
{
// a repair handed on the token of the error it repaired; the
// token was reported already, and the SAX parser asked to recover
return true;
}
error_reported = true;
last_error_position = position;
last_error_token = last_token;
if (!sax->parse_error(m_lexer.get_position(), m_lexer.get_token_string(), ex))
{
return false;
}
// the token string of the next error begins here
m_lexer.restart_token_string();
return true;
}
/*!
@brief keep what can be read of the token that the lexer rejected
The error was reported for the rejected token, so it is not reported again
for the token it is repaired to (see lexer::recover_token()).
*/
token_type recover_token()
{
last_token = m_lexer.recover_token();
last_error_position = m_lexer.get_position().chars_read_total;
last_error_token = last_token;
return last_token;
}
// The functions below are called from sax_parse_internal() after an error
// was reported. The parser for parse() and accept() stopped there, so for
// it they are stubs: the code for recovering is then not even referenced,
// and unoptimized builds do not emit it either.
/// @copydoc recover_token()
JSON_INTERNAL_ALWAYS_INLINE
token_type recover_token(std::true_type /*allow_recovery*/)
{
return recover_token();
}
/// @copydoc recover_token()
JSON_INTERNAL_ALWAYS_INLINE
static std::false_type recover_token(std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/// return the token that was read last to the lexer (see lexer::unget_token())
JSON_INTERNAL_ALWAYS_INLINE
void unget_token(std::true_type /*allow_recovery*/)
{
m_lexer.unget_token();
}
/// @copydoc unget_token(std::true_type)
JSON_INTERNAL_ALWAYS_INLINE
static void unget_token(std::false_type /*allow_recovery*/) noexcept {}
/// @copydoc skip_to_value
JSON_INTERNAL_ALWAYS_INLINE
static std::false_type skip_to_value(std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/// @copydoc recover_missing_value
template<typename SAX>
JSON_INTERNAL_ALWAYS_INLINE
static std::false_type recover_missing_value(SAX* /*sax*/, const std::vector<bool>& /*states*/, std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/// pass the end events of all open containers
template<typename SAX>
bool close_containers(SAX* sax, std::vector<bool>& states, std::true_type /*allow_recovery*/)
{
while (!states.empty())
{
const bool is_array = states.back();
states.pop_back();
if (JSON_HEDLEY_UNLIKELY(is_array ? !sax->end_array() : !sax->end_object()))
{
return false;
}
}
return true;
}
/// @copydoc close_containers
template<typename SAX>
JSON_INTERNAL_ALWAYS_INLINE
static std::false_type close_containers(SAX* /*sax*/, std::vector<bool>& /*states*/, std::false_type /*allow_recovery*/) noexcept
{
return {};
}
/*!
@brief read tokens until one begins a value, skipping everything before
the top-level value
@return whether a value begins with last_token
*/
bool skip_to_value(std::true_type /*allow_recovery*/)
{
while (true)
{
switch (get_token())
{
case token_type::begin_array:
case token_type::begin_object:
case token_type::literal_false:
case token_type::literal_null:
case token_type::literal_true:
case token_type::value_float:
case token_type::value_integer:
case token_type::value_string:
case token_type::value_unsigned:
return true;
case token_type::end_of_input:
return false;
case token_type::parse_error:
recover_token();
if (last_token != token_type::uninitialized)
{
return true;
}
break;
case token_type::uninitialized:
case token_type::end_array:
case token_type::end_object:
case token_type::name_separator:
case token_type::value_separator:
case token_type::literal_or_value:
default:
break;
}
}
}
/*!
@brief skip the rest of an object member that cannot be read
Reads tokens, beginning with last_token, until a ',', '}', or ']' that is
not inside a container that begins in the skipped tokens, or the end of
the input.
*/
void skip_member()
{
std::size_t depth = 0;
while (true)
{
switch (last_token)
{
case token_type::begin_array:
case token_type::begin_object:
++depth;
break;
case token_type::end_array:
case token_type::end_object:
if (depth == 0)
{
return;
}
--depth;
break;
case token_type::value_separator:
if (depth == 0)
{
return;
}
break;
case token_type::end_of_input:
return;
case token_type::parse_error:
recover_token();
break;
case token_type::uninitialized:
case token_type::literal_true:
case token_type::literal_false:
case token_type::literal_null:
case token_type::value_string:
case token_type::value_unsigned:
case token_type::value_integer:
case token_type::value_float:
case token_type::name_separator:
case token_type::literal_or_value:
default:
break;
}
get_token();
}
}
/*!
@brief pass a value where it is missing
last_token is ',', ']', '}', or the end of the input, where a value was
expected. In an object, the key gets null; in an array, a ',' where a
value is missing stands for null (as in JavaScript), while an array that
ends there just ends.
*/
template<typename SAX>
bool recover_missing_value(SAX* sax, const std::vector<bool>& states, std::true_type /*allow_recovery*/)
{
JSON_ASSERT(!states.empty());
if (!states.back() || last_token == token_type::value_separator)
{
return sax->null();
}
return true;
}
/// recover from a missing key; last_token is where it was expected
template<typename SAX>
next_step recover_key(SAX* sax)
{
switch (last_token)
{
case token_type::value_separator:
case token_type::end_object:
case token_type::end_array:
case token_type::end_of_input:
// no member: the object's state handles the token
return next_step::evaluate_state;
case token_type::parse_error:
recover_token();
if (last_token == token_type::value_string)
{
// a key that could be repaired
return parse_key(sax);
}
skip_member();
return next_step::evaluate_state;
case token_type::uninitialized:
case token_type::literal_true:
case token_type::literal_false:
case token_type::literal_null:
case token_type::value_string:
case token_type::value_unsigned:
case token_type::value_integer:
case token_type::value_float:
case token_type::begin_array:
case token_type::begin_object:
case token_type::name_separator:
case token_type::literal_or_value:
default:
// a member without a key
skip_member();
return next_step::evaluate_state;
}
}
/// recover from a missing name separator (:) after the key; last_token
/// is where it was expected
template<typename SAX>
next_step recover_name_separator(SAX* sax)
{
switch (last_token)
{
case token_type::value_separator:
case token_type::end_object:
case token_type::end_array:
case token_type::end_of_input:
// the value is missing as well
return sax->null() ? next_step::evaluate_state : next_step::stop;
case token_type::uninitialized:
case token_type::literal_true:
case token_type::literal_false:
case token_type::literal_null:
case token_type::value_string:
case token_type::value_unsigned:
case token_type::value_integer:
case token_type::value_float:
case token_type::begin_array:
case token_type::begin_object:
case token_type::name_separator:
case token_type::parse_error:
case token_type::literal_or_value:
default:
// a missing ':'; the value begins here
return next_step::parse_value;
}
}
/// recover from a token after an object member that is neither ',' nor
/// '}' (nor ']' or the end of the input, which the caller handles)
template<typename SAX>
next_step recover_member(SAX* sax, std::true_type /*allow_recovery*/)
{
if (last_token == token_type::parse_error)
{
recover_token();
}
if (last_token == token_type::value_string)
{
// a missing ','; the next key begins here
return parse_key(sax);
}
skip_member();
return next_step::evaluate_state;
}
/// the parser for parse() and accept() never recovers (and does not come
/// here, as report_error() returned false)
template<typename SAX>
JSON_INTERNAL_ALWAYS_INLINE
std::false_type recover_member(SAX* /*sax*/, std::false_type /*allow_recovery*/) const noexcept
{
return {};
}
/// get next token from lexer /// get next token from lexer
token_type get_token() token_type get_token()
{ {
@@ -1205,21 +535,6 @@ class parser
return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type; return (last_token = m_lexer.scan_expecting(expected_type)) == expected_type;
} }
/*!
@brief reports a syntax error at the current token (parse_error.101)
@param[in] sax the SAX parser to report the error to
@param[in] message the error message
@return the result of the SAX parser's parse_error()
*/
template<typename SAX>
bool syntax_error(SAX& sax, const std::string& message)
{
// MSVC 2015 reports C4100 (unreferenced parameter) when SAX::parse_error is static
static_cast<void>(sax);
return sax.parse_error(m_lexer.get_position(), m_lexer.get_token_string(),
parse_error::create(exception_id::syntax_error, m_lexer.get_position(), message, nullptr));
}
std::string exception_message(const token_type expected, const std::string& context) std::string exception_message(const token_type expected, const std::string& context)
{ {
std::string error_msg = "syntax error "; std::string error_msg = "syntax error ";
@@ -1260,12 +575,6 @@ class parser
const bool allow_exceptions = true; const bool allow_exceptions = true;
/// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false) /// whether trailing commas in objects and arrays should be ignored (true) or signaled as errors (false)
const bool ignore_trailing_commas = false; const bool ignore_trailing_commas = false;
/// whether an error was reported to the SAX parser
bool error_reported = false;
/// the position of the last reported error
std::size_t last_error_position = 0;
/// the token of the last reported error
token_type last_error_token = token_type::uninitialized;
}; };
} // namespace detail } // namespace detail
+12 -18
View File
@@ -279,12 +279,6 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
} }
} }
/// @throw invalid_iterator.214 always
JSON_HEDLEY_NO_RETURN void throw_cannot_get_value() const
{
JSON_THROW(invalid_iterator::create(exception_id::iterator_value_unavailable, "cannot get value", m_object));
}
public: public:
/*! /*!
@brief return a reference to the value pointed to by the iterator @brief return a reference to the value pointed to by the iterator
@@ -309,7 +303,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
} }
case value_t::null: case value_t::null:
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
case value_t::string: case value_t::string:
case value_t::boolean: case value_t::boolean:
@@ -325,7 +319,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object; return *m_object;
} }
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
} }
} }
} }
@@ -367,7 +361,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_object; return m_object;
} }
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
} }
} }
} }
@@ -484,7 +478,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object)); JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
} }
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -533,7 +527,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(exception_id::iterators_compare_different_values, "cannot compare iterators of different containers", m_object)); JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
} }
// value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493 // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
@@ -546,7 +540,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_order_on_object, "cannot compare order of object iterators", m_object)); JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
case value_t::array: case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator); return (m_it.array_iterator < other.m_it.array_iterator);
@@ -602,7 +596,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object)); JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
case value_t::array: case value_t::array:
{ {
@@ -681,7 +675,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_arithmetic_on_object, "cannot use offsets with object iterators", m_object)); JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
case value_t::array: case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator; return m_it.array_iterator - other.m_it.array_iterator;
@@ -710,13 +704,13 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
switch (m_object->m_data.m_type) switch (m_object->m_data.m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(exception_id::iterator_subscript_on_object, "cannot use operator[] for object iterators", m_object)); JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
case value_t::array: case value_t::array:
return *std::next(m_it.array_iterator, n); return *std::next(m_it.array_iterator, n);
case value_t::null: case value_t::null:
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
case value_t::string: case value_t::string:
case value_t::boolean: case value_t::boolean:
@@ -732,7 +726,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return *m_object; return *m_object;
} }
throw_cannot_get_value(); JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
} }
} }
} }
@@ -750,7 +744,7 @@ class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-speci
return m_it.object_iterator->first; return m_it.object_iterator->first;
} }
JSON_THROW(invalid_iterator::create(exception_id::iterator_key_not_object, "cannot use key() for non-object iterators", m_object)); JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
} }
/*! /*!
+90 -215
View File
@@ -16,8 +16,8 @@
#include <iosfwd> // ostream #include <iosfwd> // ostream
#endif // JSON_NO_IO #endif // JSON_NO_IO
#include <limits> // max #include <limits> // max
#include <map> // map
#include <numeric> // accumulate #include <numeric> // accumulate
#include <set> // set
#include <string> // string #include <string> // string
#include <utility> // move #include <utility> // move
#include <vector> // vector #include <vector> // vector
@@ -166,7 +166,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
reference_tokens.erase(reference_tokens.begin()); reference_tokens.erase(reference_tokens.begin());
@@ -178,7 +178,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
return reference_tokens.front(); return reference_tokens.front();
@@ -204,7 +204,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
reference_tokens.pop_back(); reference_tokens.pop_back();
@@ -216,7 +216,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
return reference_tokens.back(); return reference_tokens.back();
@@ -244,21 +244,6 @@ class json_pointer
} }
private: private:
/// @throw out_of_range.405 always
JSON_HEDLEY_NO_RETURN static void throw_no_parent()
{
JSON_THROW(detail::out_of_range::create(detail::exception_id::patch_on_root, "JSON pointer has no parent", nullptr));
}
/// @throw out_of_range.404 always
template<typename BasicJsonContext>
JSON_HEDLEY_NO_RETURN static void throw_unresolved(const string_t& reference_token, BasicJsonContext context)
{
static_cast<void>(reference_token); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(context);
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_unresolved, detail::concat("unresolved reference token '", reference_token, "'"), context));
}
/*! /*!
@brief result of @ref parse_array_index @brief result of @ref parse_array_index
@@ -270,7 +255,7 @@ class json_pointer
{ {
ok, ///< @a s is a valid, representable array index ok, ///< @a s is a valid, representable array index
leading_zero, ///< @a s begins with '0' but has more than one character leading_zero, ///< @a s begins with '0' but has more than one character
not_a_number, ///< @a s is neither empty nor "-" and does not begin with a digit not_a_number, ///< @a s does not begin with a digit
unresolved, ///< @a s could not be converted to an integer unresolved, ///< @a s could not be converted to an integer
exceeds_size_type ///< @a s converts to an integer that exceeds size_type exceeds_size_type ///< @a s converts to an integer that exceeds size_type
}; };
@@ -297,10 +282,8 @@ class json_pointer
return array_index_status::leading_zero; return array_index_status::leading_zero;
} }
// error condition (cf. RFC 6901, Sect. 4); this also covers single- // error condition (cf. RFC 6901, Sect. 4)
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
// token are left to the conversion below and are reported as unresolved
if (JSON_HEDLEY_UNLIKELY(!s.empty() && s != "-" && !(s[0] >= '0' && s[0] <= '9')))
{ {
return array_index_status::not_a_number; return array_index_status::not_a_number;
} }
@@ -346,13 +329,13 @@ class json_pointer
// the branches differ in their messages, not after JSON_THROW's expansion // the branches differ in their messages, not after JSON_THROW's expansion
// NOLINTNEXTLINE(bugprone-branch-clone) // NOLINTNEXTLINE(bugprone-branch-clone)
case array_index_status::leading_zero: case array_index_status::leading_zero:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr)); JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
case array_index_status::not_a_number: case array_index_status::not_a_number:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", s, "' is not a number"), nullptr)); JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
case array_index_status::unresolved: case array_index_status::unresolved:
throw_unresolved(s, nullptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
case array_index_status::exceeds_size_type: case array_index_status::exceeds_size_type:
JSON_THROW(detail::out_of_range::create(detail::exception_id::value_out_of_range, detail::concat("array index ", s, " exceeds size_type"), nullptr)); JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr));
case array_index_status::ok: case array_index_status::ok:
default: default:
break; break;
@@ -366,7 +349,7 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(empty())) if (JSON_HEDLEY_UNLIKELY(empty()))
{ {
throw_no_parent(); JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
} }
json_pointer result = *this; json_pointer result = *this;
@@ -376,82 +359,33 @@ class json_pointer
private: private:
/*! /*!
@brief the pointer prefixes of a flattened object, and which of them denote arrays @brief the reference token sequences that denote arrays
@ref unflatten collects the pointer prefixes that have a reference token 0 @ref unflatten collects the pointer prefixes that have a reference token 0
among their children; @ref get_and_create creates arrays exactly below among their children; @ref get_and_create creates arrays exactly below
those prefixes and objects everywhere else. Deciding this up front keeps those prefixes and objects everywhere else. Deciding this up front keeps
the result independent of the order in which the flattened object is the result independent of the order in which the flattened object is
iterated, which is unspecified for some object types. iterated, which is unspecified for some object types.
The prefixes form a tree and are numbered, so each of them is stored only
once (as a node) rather than as a copy of all of its reference tokens.
*/ */
struct prefix_tree using array_parents_t = std::set<std::vector<string_t>>;
{
// children[id] maps a reference token to the number of the prefix
// extended by that token; number 0 is the empty prefix
std::vector<std::map<string_t, std::size_t>> children;
// is_array[id] is true iff some flattened key has the reference token
// 0 directly below the prefix with number id
std::vector<bool> is_array;
// start with the empty prefix only
prefix_tree()
: children(1)
, is_array(1, false)
{}
// return the number of the prefix with number id extended by
// reference_token, adding it if it is new
std::size_t add_child(std::size_t id, string_t&& reference_token)
{
if (reference_token == "0")
{
is_array[id] = true;
}
// read the number before the emplace_back below, which may
// reallocate children and invalidate the iterator
const std::size_t next = children.size();
const auto inserted = children[id].emplace(std::move(reference_token), next);
const std::size_t child = inserted.first->second;
if (inserted.second)
{
children.emplace_back();
is_array.push_back(false);
}
return child;
}
// return the number of the prefix with number id extended by
// reference_token, which must have been added before
std::size_t find_child(std::size_t id, const string_t& reference_token) const
{
const auto it = children[id].find(reference_token);
JSON_ASSERT(it != children[id].end());
return it->second;
}
};
/*! /*!
@brief create and return a reference to the pointed to value @brief create and return a reference to the pointed to value
Complexity: Linear in the number of reference tokens (times the logarithm Complexity: Linear in the number of reference tokens.
of the number of siblings for the prefix lookup).
@throw parse_error.106 if an array index begins with '0' @throw parse_error.106 if an array index begins with '0'
@throw parse_error.109 if array index is not a number @throw parse_error.109 if array index is not a number
@throw type_error.313 if value cannot be unflattened @throw type_error.313 if value cannot be unflattened
*/ */
template<typename BasicJsonType> template<typename BasicJsonType>
BasicJsonType& get_and_create(BasicJsonType& j, const prefix_tree& tree) const BasicJsonType& get_and_create(BasicJsonType& j, const array_parents_t& array_parents) const
{ {
auto* result = &j; auto* result = &j;
// the number of the prefix consumed so far; used to look up whether // the reference tokens that have been consumed so far; used to look up
// the value to be created below is an array or an object // whether the value to be created below is an array or an object
std::size_t id = 0; std::vector<string_t> prefix;
// in case no reference tokens exist, return a reference to the JSON value // in case no reference tokens exist, return a reference to the JSON value
// j which will be overwritten by a primitive value // j which will be overwritten by a primitive value
@@ -461,7 +395,7 @@ class json_pointer
{ {
case detail::value_t::null: case detail::value_t::null:
{ {
if (tree.is_array[id]) if (array_parents.find(prefix) != array_parents.end())
{ {
// some reference token below this position is 0, so the // some reference token below this position is 0, so the
// value is an array // value is an array
@@ -503,10 +437,10 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_invalid_value, "invalid value to unflatten", &j)); JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
} }
id = tree.find_child(id, reference_token); prefix.push_back(reference_token);
} }
return *result; return *result;
@@ -539,15 +473,19 @@ class json_pointer
// convert null values to arrays or objects before continuing // convert null values to arrays or objects before continuing
if (ptr->is_null()) if (ptr->is_null())
{ {
// check if the reference token is a number // check if the reference token is a valid array index, that is
const bool nums = // a nonempty sequence of digits without a leading '0'
std::all_of(reference_token.begin(), reference_token.end(), // (cf. RFC 6901, Sect. 4); tokens that could never be a valid
[](const unsigned char x) // array index (such as "01" or "") are treated as object keys
const bool nums = !reference_token.empty()
&& (reference_token.size() == 1 || reference_token[0] != '0')
&& std::all_of(reference_token.begin(), reference_token.end(),
[](const unsigned char x)
{ {
return std::isdigit(x); return std::isdigit(x);
}); });
// change value to an array for numbers or "-" or to object otherwise // change value to an array for array indices or "-" or to object otherwise
*ptr = (nums || reference_token == "-") *ptr = (nums || reference_token == "-")
? detail::value_t::array ? detail::value_t::array
: detail::value_t::object; : detail::value_t::object;
@@ -586,7 +524,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
throw_unresolved(reference_token, ptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
} }
} }
@@ -618,7 +556,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{ {
// "-" always fails the range check // "-" always fails the range check
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat( JSON_THROW(detail::out_of_range::create(402, detail::concat(
"array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
") is out of range"), ptr)); ") is out of range"), ptr));
} }
@@ -627,7 +565,7 @@ class json_pointer
// Bounds check before access to avoid exception with JSON_NOEXCEPTION // Bounds check before access to avoid exception with JSON_NOEXCEPTION
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size())) if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
{ {
JSON_THROW(detail::out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat( JSON_THROW(detail::out_of_range::create(401, detail::concat(
"array index ", std::to_string(idx), " is out of range"), ptr)); "array index ", std::to_string(idx), " is out of range"), ptr));
} }
ptr = &ptr->operator[](idx); ptr = &ptr->operator[](idx);
@@ -643,7 +581,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
throw_unresolved(reference_token, ptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
} }
} }
@@ -687,7 +625,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{ {
// "-" cannot be used for const access // "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr)); JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
} }
// use unchecked array access; the const operator[] // use unchecked array access; the const operator[]
@@ -705,7 +643,7 @@ class json_pointer
case detail::value_t::binary: case detail::value_t::binary:
case detail::value_t::discarded: case detail::value_t::discarded:
default: default:
throw_unresolved(reference_token, ptr); JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
} }
} }
@@ -760,9 +698,9 @@ class json_pointer
// the branches differ in their messages, not after JSON_THROW's expansion // the branches differ in their messages, not after JSON_THROW's expansion
// NOLINTNEXTLINE(bugprone-branch-clone) // NOLINTNEXTLINE(bugprone-branch-clone)
case array_index_status::leading_zero: case array_index_status::leading_zero:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_leading_zero, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr)); JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
case array_index_status::not_a_number: case array_index_status::not_a_number:
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_index_not_a_number, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr)); JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", reference_token, "' is not a number"), nullptr));
case array_index_status::unresolved: case array_index_status::unresolved:
case array_index_status::exceeds_size_type: case array_index_status::exceeds_size_type:
return nullptr; return nullptr;
@@ -889,7 +827,7 @@ class json_pointer
// check if a nonempty reference string begins with slash // check if a nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{ {
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_missing_slash, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr)); JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
} }
// extract the reference tokens: // extract the reference tokens:
@@ -925,7 +863,7 @@ class json_pointer
(reference_token[pos + 1] != '0' && (reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1'))) reference_token[pos + 1] != '1')))
{ {
JSON_THROW(detail::parse_error::create(detail::exception_id::pointer_invalid_escape, 0, "escape character '~' must be followed with '0' or '1'", nullptr)); JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
} }
} }
@@ -944,131 +882,64 @@ class json_pointer
@param[in,out] result the result object to insert values to @param[in,out] result the result object to insert values to
@note Empty objects or arrays are flattened to `null`. @note Empty objects or arrays are flattened to `null`.
The value is walked with an explicit stack rather than the call stack, so
arbitrarily deeply nested values can be flattened.
@sa https://github.com/nlohmann/json/issues/5393
*/ */
template<typename BasicJsonType> template<typename BasicJsonType>
static void flatten(const string_t& reference_string, static void flatten(const string_t& reference_string,
const BasicJsonType& value, const BasicJsonType& value,
BasicJsonType& result) BasicJsonType& result)
{ {
using object_const_iterator = typename BasicJsonType::object_t::const_iterator; switch (value.type())
// an array or object being walked: the container, the array index or
// object iterator of the next child, and the length of the path of the
// container itself
struct frame
{ {
frame(const BasicJsonType* container_, object_const_iterator member_, const std::size_t path_length_) noexcept case detail::value_t::array:
: container(container_), member(std::move(member_)), path_length(path_length_)
{}
const BasicJsonType* container;
std::size_t index = 0;
object_const_iterator member;
std::size_t path_length;
};
// The containers being flattened are kept on an explicit stack, and
// every child is flattened completely before the next one, so the
// entries come out in the same order as with a recursive walk. The
// path of the value being flattened is kept in one buffer that grows
// and shrinks with the stack, rather than in a new string per level.
std::vector<frame> stack;
string_t path = reference_string;
// flatten `v`, whose path is `path`: primitives and empty containers
// are added to the result right away; other containers get a frame
const auto enter = [&stack, &path, &result](const BasicJsonType & v)
{
switch (v.type())
{ {
case detail::value_t::array: if (value.m_data.m_value.array->empty())
{ {
if (v.m_data.m_value.array->empty()) // flatten empty array as null
{ result[reference_string] = nullptr;
// flatten empty array as null
result[path] = nullptr;
}
else
{
stack.emplace_back(&v, object_const_iterator(), path.size());
}
return;
} }
else
case detail::value_t::object:
{ {
if (v.m_data.m_value.object->empty()) // iterate array and use index as a reference string
for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
{ {
// flatten empty object as null flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
result[path] = nullptr; value.m_data.m_value.array->operator[](i), result);
} }
else
{
stack.emplace_back(&v, v.m_data.m_value.object->begin(), path.size());
}
return;
}
case detail::value_t::null:
case detail::value_t::string:
case detail::value_t::boolean:
case detail::value_t::number_integer:
case detail::value_t::number_unsigned:
case detail::value_t::number_float:
case detail::value_t::binary:
case detail::value_t::discarded:
default:
{
// add a primitive value with its reference string
result[path] = v;
return;
} }
break;
} }
};
enter(value); case detail::value_t::object:
while (!stack.empty())
{
// the frame is changed through stack.back(): enter() may push a
// frame, which would invalidate a reference to it
const BasicJsonType* const container = stack.back().container;
// drop the path of the previous child
path.resize(stack.back().path_length);
if (container->is_array())
{ {
const auto& array = *container->m_data.m_value.array; if (value.m_data.m_value.object->empty())
const std::size_t i = stack.back().index;
if (i == array.size())
{ {
stack.pop_back(); // flatten empty object as null
continue; result[reference_string] = nullptr;
} }
else
// iterate array and use index as a reference string {
++stack.back().index; // iterate object and use keys as reference string
detail::concat_into(path, '/', detail::to_string<string_t>(i)); for (const auto& element : *value.m_data.m_value.object)
enter(array[i]); {
flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
}
}
break;
} }
else
{
const object_const_iterator it = stack.back().member;
if (it == container->m_data.m_value.object->end())
{
stack.pop_back();
continue;
}
// iterate object and use keys as reference string case detail::value_t::null:
++stack.back().member; case detail::value_t::string:
detail::concat_into(path, '/', detail::escape(it->first)); case detail::value_t::boolean:
enter(it->second); case detail::value_t::number_integer:
case detail::value_t::number_unsigned:
case detail::value_t::number_float:
case detail::value_t::binary:
case detail::value_t::discarded:
default:
{
// add a primitive value with its reference string
result[reference_string] = value;
break;
} }
} }
} }
@@ -1089,22 +960,26 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(!value.is_object())) if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{ {
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_not_object, "only objects can be unflattened", &value)); JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
} }
BasicJsonType result; BasicJsonType result;
// collect the pointer prefixes that have a reference token 0 among // collect the pointer prefixes that have a reference token 0 among
// their children; the values below them are arrays, all others are // their children; the values below them are arrays, all others are
// objects (see prefix_tree) // objects (see array_parents_t)
prefix_tree tree; array_parents_t array_parents;
for (const auto& element : *value.m_data.m_value.object) for (const auto& element : *value.m_data.m_value.object)
{ {
json_pointer ptr(element.first); json_pointer ptr(element.first);
std::size_t id = 0; std::vector<string_t> prefix;
for (auto& reference_token : ptr.reference_tokens) for (auto& reference_token : ptr.reference_tokens)
{ {
id = tree.add_child(id, std::move(reference_token)); if (reference_token == "0")
{
array_parents.insert(prefix);
}
prefix.push_back(std::move(reference_token));
} }
} }
@@ -1113,14 +988,14 @@ class json_pointer
{ {
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{ {
JSON_THROW(detail::type_error::create(detail::exception_id::unflatten_value_not_primitive, "values in object must be primitive", &element.second)); JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
} }
// Assign the value to the reference pointed to by JSON pointer. Note // Assign the value to the reference pointed to by JSON pointer. Note
// that if the JSON pointer is "" (i.e., points to the whole value), // that if the JSON pointer is "" (i.e., points to the whole value),
// function get_and_create returns a reference to the result itself. // function get_and_create returns a reference to the result itself.
// An assignment will then create a primitive value. // An assignment will then create a primitive value.
json_pointer(element.first).get_and_create(result, tree) = element.second; json_pointer(element.first).get_and_create(result, array_parents) = element.second;
} }
return result; return result;
+2 -13
View File
@@ -199,17 +199,6 @@
#define JSON_NO_UNIQUE_ADDRESS #define JSON_NO_UNIQUE_ADDRESS
#endif #endif
// Inlines small functions even in unoptimized builds, so that they are not
// emitted. The parsers for parse(), accept(), and from_*() use it for the
// functions that stand in for the code recovering from errors (see #3989).
// MSVC is left to decide, as it warns (C4714) where it does not inline a
// __forceinline function.
#if defined(_MSC_VER) && !defined(__clang__)
#define JSON_INTERNAL_ALWAYS_INLINE
#else
#define JSON_INTERNAL_ALWAYS_INLINE JSON_HEDLEY_ALWAYS_INLINE
#endif
// Clang targeting MinGW does not survive the thread_local storage the copy // Clang targeting MinGW does not survive the thread_local storage the copy
// constructor uses to bound its descent: every test that copies a value // constructor uses to bound its descent: every test that copies a value
// segfaults with clang 11.0.1 and clang 18.1.8, while the same tests pass with // segfaults with clang 11.0.1 and clang 18.1.8, while the same tests pass with
@@ -323,7 +312,7 @@
return ej_pair.first == e; \ return ej_pair.first == e; \
}); \ }); \
if (it != std::end(m)) j = it->second; \ if (it != std::end(m)) j = it->second; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range,"enum value out of range for " #ENUM_TYPE, nullptr)); \ else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE, nullptr)); \
} \ } \
template<typename BasicJsonType> \ template<typename BasicJsonType> \
inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
@@ -338,7 +327,7 @@
return ej_pair.second == j; \ return ej_pair.second == j; \
}); \ }); \
if (it != std::end(m)) e = it->first; \ if (it != std::end(m)) e = it->first; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \ else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j)); \
} }
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They // Ugly macros to avoid uglier copy-paste when specializing basic_json. They
Loaded 100 of 146 files, more files were not shown because too many files have changed in this diff. Show more