mirror of
https://github.com/nlohmann/json.git
synced 2026-08-08 02:03:18 +00:00
Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a13902a33f | ||
|
|
c021a09b08 | ||
|
|
e4aaf46d38 | ||
|
|
ad94fb01cc | ||
|
|
c2e1cc50e0 | ||
|
|
173f2a7407 | ||
|
|
1c63a120b6 | ||
|
|
85889e8843 | ||
|
|
3c0a9a99fd | ||
|
|
e82724d87f | ||
|
|
5bc24e876b | ||
|
|
78821cd9c2 | ||
|
|
68f0722a19 | ||
|
|
5f121d8c50 | ||
|
|
585929bff9 | ||
|
|
31ba5208c8 | ||
|
|
da7b9bdb3d | ||
|
|
634f49bc5b | ||
|
|
2222d386c9 | ||
|
|
eaedec859a | ||
|
|
d94cbd99dc | ||
|
|
bc48951128 | ||
|
|
fd72ecfc8c | ||
|
|
de8a099ba5 | ||
|
|
dd24e2dffd | ||
|
|
868506dcc0 | ||
|
|
58ce09dcfd | ||
|
|
8dacb98041 | ||
|
|
2e23687092 | ||
|
|
227c5cdfb1 | ||
|
|
0832fd1cb4 | ||
|
|
8ec98e2c9e | ||
|
|
88b28ac43c | ||
|
|
e0c3c819e1 | ||
|
|
06ac77f4fd | ||
|
|
dfa51af692 | ||
|
|
d0d29039da | ||
|
|
9a3ebb9456 | ||
|
|
3296a3ad8c | ||
|
|
3565f40229 | ||
|
|
1c5a953de5 | ||
|
|
a03e65420c | ||
|
|
d6ede37088 | ||
|
|
722c03495f | ||
|
|
c197feff81 | ||
|
|
b2b47c69b1 | ||
|
|
6a406ee141 | ||
|
|
ca76c37650 | ||
|
|
fe2bcc080f | ||
|
|
c60217e801 | ||
|
|
6ba332c7df | ||
|
|
e9c3985f0a | ||
|
|
b630f5e9c7 | ||
|
|
4d8e7a7210 | ||
|
|
75e8fbac32 | ||
|
|
631e667fe5 | ||
|
|
d0a43141ea | ||
|
|
ecff144b3a | ||
|
|
855f511db4 | ||
|
|
d0de6a9111 | ||
|
|
f8e99e856c | ||
|
|
521a084827 | ||
|
|
ca91678af1 | ||
|
|
ff34a3fd2f |
@@ -15,6 +15,14 @@ guidance.
|
||||
|
||||
For vulnerabilities in third-party dependencies or modules, please report them directly to the respective maintainers.
|
||||
|
||||
## Unofficial packages
|
||||
|
||||
This project does not publish an official npm package. The npm package
|
||||
[`nlohmann-json`](https://www.npmjs.com/package/nlohmann-json) (or similarly named packages) is not maintained or
|
||||
endorsed by this project. See the
|
||||
[package managers documentation](https://json.nlohmann.me/integration/package_managers/#npm) for supported
|
||||
integration options.
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- Explore security-related topics and contribute to tools and projects through
|
||||
|
||||
@@ -39,14 +39,14 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout pull request
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
path: main
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Checkout tools
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
path: tools
|
||||
ref: develop
|
||||
|
||||
@@ -32,20 +32,20 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/init@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
with:
|
||||
languages: c-cpp
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/autobuild@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/analyze@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: 'Dependency Review'
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -43,6 +43,6 @@ jobs:
|
||||
output: 'flawfinder_results.sarif'
|
||||
|
||||
- name: Upload analysis results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
with:
|
||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run CMake
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run CMake
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
standard: [11, 14, 17, 20, 23, 26]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run CMake
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Install virtual environment
|
||||
run: make install_venv -C docs/mkdocs
|
||||
|
||||
@@ -41,12 +41,12 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
|
||||
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -76,6 +76,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
egress-policy: audit
|
||||
|
||||
# Checkout project source
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
|
||||
# Upload SARIF file generated in previous step
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/upload-sarif@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
|
||||
with:
|
||||
sarif_file: semgrep.sarif
|
||||
if: always()
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
||||
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
|
||||
with:
|
||||
stale-issue-label: 'state: stale'
|
||||
stale-pr-label: 'state: stale'
|
||||
|
||||
@@ -21,11 +21,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: gcc:latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -33,11 +33,21 @@ jobs:
|
||||
|
||||
ci_infer:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/nlohmann/json-ci:v2.4.0
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Install Infer
|
||||
run: |
|
||||
wget -q -O - "https://github.com/facebook/infer/releases/download/v1.3.0/infer-linux-x86_64-v1.3.0.tar.xz" | sudo tar -C /opt -xJ
|
||||
sudo ln -s /opt/infer-linux-x86_64-v1.3.0/bin/infer /usr/local/bin/infer
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -56,11 +66,11 @@ jobs:
|
||||
|
||||
- name: Install Valgrind
|
||||
run: sudo apt-get update ; sudo apt-get install -y valgrind
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -75,11 +85,11 @@ jobs:
|
||||
steps:
|
||||
- name: Install git, clang-tools, iwyu (ci_single_binaries), and unzip
|
||||
run: apt-get update ; apt-get install -y git clang-tools iwyu unzip
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -94,11 +104,11 @@ jobs:
|
||||
steps:
|
||||
- name: Install build-essential
|
||||
run: apt-get update ; apt-get install -y build-essential unzip wget git libssl-dev
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -112,7 +122,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install dependencies and de_DE locale
|
||||
@@ -132,7 +142,7 @@ jobs:
|
||||
name: code-coverage-report
|
||||
path: ${{ github.workspace }}/build/html
|
||||
- name: Publish report to Coveralls
|
||||
uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
|
||||
uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: ${{ github.workspace }}/build/json.info.filtered.noexcept
|
||||
@@ -143,11 +153,38 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: ['4.8', '4.9', '5', '6']
|
||||
container: ghcr.io/nlohmann/json-ci:v2.4.0
|
||||
# official gcc:4.8/4.9/5/6 images fail to check out code (too old for
|
||||
# actions/checkout); install the old compilers on top of official ubuntu:20.04
|
||||
# instead, mirroring what the (now retired) custom json-ci image did.
|
||||
container: ubuntu:20.04
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Install g++-${{ matrix.compiler }}
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends software-properties-common ca-certificates gnupg make git
|
||||
# add-apt-repository resolves the PPA through the Launchpad API,
|
||||
# which intermittently times out or fails the team lookup (the plain
|
||||
# "deb ..." sources below never hit Launchpad and never flake).
|
||||
# Retry with backoff so a transient Launchpad blip does not fail CI.
|
||||
for attempt in 1 2 3 4 5; do
|
||||
add-apt-repository -y ppa:ubuntu-toolchain-r/test && break
|
||||
echo "::warning::add-apt-repository ppa:ubuntu-toolchain-r/test failed (attempt ${attempt}/5); retrying"
|
||||
sleep $((attempt * 10))
|
||||
done
|
||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic main"
|
||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ bionic universe"
|
||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial main"
|
||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial universe"
|
||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates main"
|
||||
apt-add-repository -y "deb http://archive.ubuntu.com/ubuntu/ xenial-updates universe"
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends g++-${{ matrix.compiler }}
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: CXX=g++-${{ matrix.compiler }} cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -161,11 +198,11 @@ jobs:
|
||||
compiler: ['7', '8', '9', '10', '11', '12', '13', '14', '15', 'latest']
|
||||
container: gcc:${{ matrix.compiler }}
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -178,11 +215,11 @@ jobs:
|
||||
compiler: ['3.4', '3.5', '3.6', '3.7', '3.8', '3.9', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15-bullseye', '16', '17', '18', '19', '20', 'latest']
|
||||
container: silkeh/clang:${{ matrix.compiler }}
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- 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"
|
||||
if: ${{ matrix.compiler == '7' || matrix.compiler == '8' || matrix.compiler == '9' || matrix.compiler == '10' }}
|
||||
@@ -198,11 +235,11 @@ jobs:
|
||||
matrix:
|
||||
standard: [11, 14, 17, 20, 23, 26]
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -218,11 +255,11 @@ jobs:
|
||||
steps:
|
||||
- name: Install git and unzip
|
||||
run: apt-get update ; apt-get install -y git unzip
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build with libc++
|
||||
@@ -234,11 +271,22 @@ jobs:
|
||||
|
||||
ci_cuda_example:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/nlohmann/json-ci:v2.4.0
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# 11.8.0: newest pre-C++20 CUDA release, exercises the C++17 fallback
|
||||
# path (tests/cuda_example/CMakeLists.txt picks the standard per nvcc
|
||||
# version); 12.1.1: permanent regression guard for #3907 (nvcc 12.0/12.1
|
||||
# choke on enable_borrowed_range at C++20, fixed in 12.2); 12.6.3: recent
|
||||
# CUDA/C++20 coverage.
|
||||
cuda: ['11.8.0', '12.1.1', '12.6.3']
|
||||
container: nvidia/cuda:${{ matrix.cuda }}-devel-ubuntu22.04
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -251,14 +299,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.container }}
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
# The module test uses `import std;`, which needs CMake's experimental
|
||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||
- name: Get pinned CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
with:
|
||||
cmakeVersion: 4.3.4
|
||||
# Clang: the std library module is provided by libc++ (the image's libstdc++
|
||||
@@ -276,25 +324,30 @@ jobs:
|
||||
|
||||
ci_icpc:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/nlohmann/json-ci:v2.2.0
|
||||
# Intel discontinued the classic icc/icpc compiler in oneAPI 2024.0; this is
|
||||
# Intel's own last officially published image that still includes it.
|
||||
container: intel/oneapi-hpckit:2023.2.1-devel-ubuntu22.04
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
run: |
|
||||
. /opt/intel/oneapi/setvars.sh
|
||||
cmake --build build --target ci_icpc
|
||||
# No need to source setvars.sh here: unlike the old custom image, this
|
||||
# official image already has the oneAPI environment (icc/icpc on PATH)
|
||||
# baked in, and re-sourcing it fails with "already been run" (exit 3).
|
||||
run: cmake --build build --target ci_icpc
|
||||
|
||||
ci_icpx:
|
||||
runs-on: ubuntu-latest
|
||||
container: intel/oneapi-hpckit:latest
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -304,9 +357,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9-ubuntu22.04
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DJSON_CI=On
|
||||
- name: Build
|
||||
@@ -322,11 +375,11 @@ jobs:
|
||||
|
||||
- name: Install emscripten
|
||||
uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Run CMake
|
||||
run: cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -GNinja
|
||||
- name: Build
|
||||
@@ -343,7 +396,7 @@ jobs:
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run CMake
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
architecture: [x64, x86]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up MinGW
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set extra CXX_FLAGS for latest std_version
|
||||
@@ -86,9 +86,9 @@ jobs:
|
||||
runs-on: windows-2025
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Get latest CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
- name: Set extra CXX_FLAGS for latest std_version
|
||||
# /wd5285 silences C5285 emitted by the bundled third-party doctest.h, which
|
||||
# specializes std::tuple (newly diagnosed by the VS2026 v145 toolset)
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
runs-on: windows-11-arm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Run CMake (Release)
|
||||
run: cmake -S . -B build -G "Visual Studio 17 2022" -A ARM64 -DJSON_BuildTests=On -DCMAKE_CXX_FLAGS="/W4 /WX"
|
||||
if: matrix.build_type == 'Release'
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
version: [11.0.1, 12.0.1, 13.0.1, 14.0.6, 15.0.7, 16.0.6, 18.1.8, 19.1.7, 20.1.8]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Install Clang
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
architecture: [Win32, x64]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run CMake
|
||||
@@ -186,14 +186,14 @@ jobs:
|
||||
ci_module_cpp20:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
# The module test uses `import std;`, which needs CMake's experimental
|
||||
# import-std support. Its opt-in token is CMake-version-specific, so pin
|
||||
# CMake to the version whose token is set in tests/module_cpp20/CMakeLists.txt.
|
||||
- name: Get pinned CMake and ninja
|
||||
uses: lukka/get-cmake@f5b8fbb4d77cec1acc5a5f9f0df4beffaf5d98d9 # v4.3.4
|
||||
uses: lukka/get-cmake@e6906078ebd1ccb8ce51ab4626ac46a1b5a517e3 # v4.4.0
|
||||
with:
|
||||
cmakeVersion: 4.3.4
|
||||
- name: Run CMake (Debug)
|
||||
|
||||
@@ -90,7 +90,6 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl
|
||||
- [Steve Sperandeo](https://github.com/homer6)
|
||||
- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe)
|
||||
- [Steve Wagner](https://github.com/ciroque)
|
||||
- [Lion Yang](https://github.com/LionNatsu)
|
||||
|
||||
### Further support
|
||||
|
||||
@@ -1802,13 +1801,13 @@ The library itself consists of a single header file licensed under the MIT licen
|
||||
- [**amalgamate.py - Amalgamate C source and header files**](https://github.com/edlund/amalgamate) to create a single header file
|
||||
- [**American fuzzy lop**](https://lcamtuf.coredump.cx/afl/) for fuzz testing
|
||||
- [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
|
||||
- [**Artistic Style**](http://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
|
||||
- [**CMake**](https://cmake.org) for build automation
|
||||
- [**Codacy**](https://www.codacy.com) for further [code analysis](https://app.codacy.com/gh/nlohmann/json/dashboard)
|
||||
- [**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)
|
||||
- [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
|
||||
- [**cppcheck**](https://cppcheck.sourceforge.io) for static analysis
|
||||
- [**doctest**](https://github.com/onqtam/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)
|
||||
- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks
|
||||
@@ -1823,6 +1822,15 @@ The library itself consists of a single header file licensed under the MIT licen
|
||||
|
||||
## Notes
|
||||
|
||||
### Standards compliance
|
||||
|
||||
The library targets strict conformance with [RFC 8259](https://tools.ietf.org/html/rfc8259.html). Both the original [JSONTestSuite](https://github.com/nst/JSONTestSuite) and its updated revision are exercised in CI; their test data is downloaded from [`nlohmann/json_test_data`](https://github.com/nlohmann/json_test_data) at configure time rather than committed to this repository (see [`tests/src/unit-testsuites.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-testsuites.cpp)):
|
||||
|
||||
- The updated revision runs all mandatory `y_` (must-accept) and `n_` (must-reject) cases through the strict [`parse()`](https://json.nlohmann.me/api/basic_json/parse/) entry point; the original suite runs its `n_` cases through `parse()` and its `y_` cases through [`operator>>`](https://json.nlohmann.me/api/operator_gtgt/).
|
||||
- The `i_` (implementation-defined) cases are, by RFC 8259, free to be accepted *or* rejected, so "passing all `i_` cases" is not a meaningful conformance metric. The library makes deliberate, documented choices there: nesting depth is not artificially limited, a leading UTF-8 byte order mark is silently ignored, [Unicode noncharacters](https://www.unicode.org/faq/private_use.html#nonchar1) are forwarded unchanged, invalid UTF-8 and lone/unpaired UTF-16 surrogates are rejected (stricter than required), and a number that cannot be stored without becoming `NaN`/`INF` raises [`out_of_range.406`](https://json.nlohmann.me/home/exceptions/#jsonexceptionout_of_range406).
|
||||
|
||||
One behavioral nuance is worth calling out, because a superficial test often misreads it as non-compliance: [`parse()`](https://json.nlohmann.me/api/basic_json/parse/) is strict and rejects trailing data after a value, whereas [`operator>>`](https://json.nlohmann.me/api/operator_gtgt/) follows relaxed iostream semantics — it parses a single value and leaves the stream positioned right after it. Feeding "a valid document followed by trailing bytes" through `operator>>` reports success; the same input through `parse()` is rejected. This is a documented two-API design, not a conformance gap. See [**parsing**](https://json.nlohmann.me/features/parsing/) for details.
|
||||
|
||||
### Character encoding
|
||||
|
||||
The library supports **Unicode input** as follows:
|
||||
|
||||
+6
-2
@@ -669,7 +669,6 @@ add_custom_target(ci_test_compiler_default
|
||||
add_custom_target(ci_cuda_example
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DCMAKE_CUDA_HOST_COMPILER=g++-8
|
||||
-S${PROJECT_SOURCE_DIR}/tests/cuda_example -B${PROJECT_BINARY_DIR}/build_cuda_example
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_cuda_example
|
||||
)
|
||||
@@ -720,6 +719,11 @@ add_custom_target(ci_icpx
|
||||
# to zero and does not honor NaN ordering; -Kieee restores strict IEEE 754 behavior
|
||||
# (needed for the dtoa/grisu and NaN-comparison code paths).
|
||||
#
|
||||
# -tp=px pins the target processor to the generic x86-64 baseline (SSE2-only) to avoid
|
||||
# a nvc++ 25.5 / LLVM issue: when nvc++ auto-detects -tp from the runner's CPU (e.g. -tp znver4),
|
||||
# certain attribute combinations trigger an llc instruction-selection crash on std::ldexp<unsigned>.
|
||||
# Pinning to px removes this variability and is robust to future llc/nvc++ updates.
|
||||
#
|
||||
# The following tests are excluded as they trigger known nvc++ 25.5 defects (not
|
||||
# library bugs); see https://github.com/nlohmann/json for tracking. Only the
|
||||
# affected language-standard variants are excluded so coverage is otherwise kept:
|
||||
@@ -733,7 +737,7 @@ add_custom_target(ci_nvhpc
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCMAKE_BUILD_TYPE=Debug -GNinja
|
||||
-DCMAKE_C_COMPILER=nvc -DCMAKE_CXX_COMPILER=nvc++
|
||||
-DCMAKE_CXX_FLAGS=-Kieee
|
||||
-DCMAKE_CXX_FLAGS="-Kieee;-tp=px"
|
||||
-DJSON_BuildTests=ON -DJSON_FastTests=ON
|
||||
-S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_nvhpc
|
||||
COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_nvhpc
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
# -Wno-extra-semi-stmt The library uses assert which triggers this warning.
|
||||
# -Wno-padded We do not care about padding warnings.
|
||||
# -Wno-covered-switch-default All switches list all cases and a default case.
|
||||
# -Wno-unsafe-buffer-usage Otherwise Doctest would not compile.
|
||||
# -Wno-missing-noreturn We found no way to silence this warning otherwise, see PR #4871
|
||||
# -Wno-c2y-extensions Clang 22.1 diagnoses __COUNTER__ as a C2y extension, also in
|
||||
# C++ mode. The library does not use __COUNTER__; the warnings
|
||||
# all come from vendored Doctest (SECTION/TEST_CASE macros).
|
||||
# -Wno-unsafe-buffer-usage Pervasive: the library's own low-level numeric/buffer code
|
||||
# (to_chars, serializer, lexer, binary reader/writer, input
|
||||
# adapters, json_pointer) plus vendored Doctest itself (~208
|
||||
# distinct sites measured 2026-07-08 on clang trunk) all use
|
||||
# raw pointer arithmetic / libc string calls by necessity.
|
||||
|
||||
set(CLANG_CXXFLAGS
|
||||
-Werror
|
||||
@@ -17,6 +23,6 @@ set(CLANG_CXXFLAGS
|
||||
-Wno-extra-semi-stmt
|
||||
-Wno-padded
|
||||
-Wno-covered-switch-default
|
||||
-Wno-c2y-extensions
|
||||
-Wno-unsafe-buffer-usage
|
||||
-Wno-missing-noreturn
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"archive": "JSON_for_Modern_C++.tgz",
|
||||
"author": {
|
||||
"name": "Niels Lohmann",
|
||||
"link": "https://twitter.com/nlohmann"
|
||||
"link": "https://nlohmann.me"
|
||||
},
|
||||
"aliases": ["nlohmann/json"]
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ static bool accept(InputType&& i,
|
||||
const bool ignore_trailing_commas = false);
|
||||
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static bool accept(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static bool accept(IteratorType first, SentinelType last,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false);
|
||||
```
|
||||
@@ -17,10 +17,11 @@ static bool accept(IteratorType first, IteratorType last,
|
||||
Checks whether the input is valid JSON.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from a pair of character iterators
|
||||
2. Reads from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||
|
||||
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
||||
respectively as UTF-8, UTF-16, and UTF-32.
|
||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||
the iterator type with `operator!=`.
|
||||
|
||||
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
|
||||
(i.e., a parse error) nor creates diagnostic information.
|
||||
@@ -35,7 +36,8 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters (throws if null)
|
||||
- a `std::string`
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type, for instance.
|
||||
@@ -43,6 +45,12 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
||||
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
|
||||
- a pair of pointers such as `ptr` and `ptr + len`
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -60,7 +68,7 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
||||
: iterator to the start of the character range
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the character range
|
||||
: iterator to the end of the character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
## Return value
|
||||
|
||||
@@ -109,7 +117,9 @@ A UTF-8 byte order mark is silently ignored.
|
||||
- Added in version 3.0.0.
|
||||
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
||||
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
|
||||
- Added `ignore_trailing_commas` in version 3.12.x.
|
||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -115,7 +115,22 @@ basic_json(basic_json&& other) noexcept;
|
||||
|
||||
Function [`array()`](array.md) and [`object()`](object.md) force array and object creation from initializer lists,
|
||||
respectively.
|
||||
|
||||
|
||||
!!! warning "Brace initialization yields arrays"
|
||||
|
||||
Because this constructor takes an `initializer_list_t`, brace-initializing a `json`/`ordered_json` from
|
||||
another `json` value wraps it in a single-element array rather than copying it:
|
||||
|
||||
```cpp
|
||||
json j1 = "hello";
|
||||
json j2{j1}; // [!] j2 is ["hello"], NOT a copy of j1
|
||||
json j3(j1); // j3 is "hello" -- parentheses copy as expected
|
||||
```
|
||||
|
||||
See the FAQ entry on [brace initialization](../../home/faq.md#brace-initialization-yields-arrays) for the
|
||||
full explanation, an opt-in macro to change this behavior, and how to explicitly create a single-element
|
||||
array (`json::array({value})`) if that is what you want.
|
||||
|
||||
6. Constructs a JSON array value by creating `cnt` copies of a passed value. In case `cnt` is `0`, an empty array is
|
||||
created.
|
||||
|
||||
|
||||
@@ -13,9 +13,8 @@ is compatible with both of the binary data formats that use binary subtyping, (t
|
||||
incompatible with each other, and it is up to the user to translate between them). The subtype is added to `BinaryType`
|
||||
via the helper type [byte_container_with_subtype](../byte_container_with_subtype/index.md).
|
||||
|
||||
[CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type as:
|
||||
> Major type 2: a byte string. The string's length in bytes is represented following the rules for positive integers
|
||||
> (major type 0).
|
||||
[CBOR's RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) describes this type as:
|
||||
> Major type 2: A byte string. The number of bytes in the string is equal to the argument.
|
||||
|
||||
[MessagePack's documentation on the bin type
|
||||
family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) describes this type as:
|
||||
@@ -37,12 +36,52 @@ represent a byte array in modern C++.
|
||||
`BinaryType`
|
||||
: container type to store arrays
|
||||
|
||||
Although not formally expressed as a C++ concept, `BinaryType` must be default-constructible,
|
||||
copy/move-constructible, and support `push_back()`, `.data()`, and `.size()`, because
|
||||
[`byte_container_with_subtype`](../byte_container_with_subtype/index.md) derives directly from it. Its
|
||||
`value_type` must additionally be exactly one byte wide (e.g., `std::uint8_t`/`char`/`std::byte`): the binary
|
||||
serializers (CBOR, MessagePack, BSON, UBJSON) read and write the container's raw bytes via
|
||||
`reinterpret_cast`, which is only correct for byte-sized elements -- a container like
|
||||
`#!cpp std::vector<std::intptr_t>` will not work as `BinaryType`.
|
||||
|
||||
## Notes
|
||||
|
||||
#### Default type
|
||||
|
||||
The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`.
|
||||
|
||||
#### Custom BinaryType behavior
|
||||
|
||||
When a custom `BinaryType` is configured (other than the default `#!cpp std::vector<std::uint8_t>`), you can assign
|
||||
values of that type directly to a `basic_json` instance, and they will automatically be recognized as binary values
|
||||
rather than arrays:
|
||||
|
||||
```cpp
|
||||
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}};
|
||||
custom_json j = data; // Creates a binary value, not an array
|
||||
assert(j.is_binary());
|
||||
|
||||
// Round-tripping works seamlessly
|
||||
auto extracted = j.get<std::vector<std::byte>>();
|
||||
assert(extracted == data);
|
||||
```
|
||||
|
||||
This automatic type detection is a convenience feature that only applies to custom (non-default) `BinaryType` configurations.
|
||||
The default `nlohmann::json` continues to treat `#!cpp std::vector<std::uint8_t>` as arrays for backward compatibility.
|
||||
|
||||
#### Storage
|
||||
|
||||
Binary Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of the
|
||||
|
||||
@@ -43,6 +43,17 @@ Strong guarantee: if an exception is thrown, there are no changes to any JSON va
|
||||
Throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) if a string stored inside the JSON value
|
||||
is not UTF-8 encoded and `error_handler` is set to `strict`
|
||||
|
||||
!!! warning "Serializing untrusted input"
|
||||
|
||||
When serializing values that may contain invalid or untrusted UTF-8 (e.g., bytes taken directly from network
|
||||
input), `dump()` throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) in the default
|
||||
`strict` mode. To serialize such data without throwing, pass
|
||||
[`error_handler_t::replace`](error_handler_t.md) (substitutes U+FFFD) or
|
||||
[`error_handler_t::ignore`](error_handler_t.md). Callers that serialize untrusted input on a crash-sensitive path
|
||||
should either choose a non-strict error handler or wrap `dump()` in a `#!cpp try`/`#!cpp catch`.
|
||||
|
||||
See the [FAQ](../../home/faq.md#serializing-untrusted-or-invalid-utf-8) for details.
|
||||
|
||||
## Complexity
|
||||
|
||||
Linear.
|
||||
|
||||
@@ -92,4 +92,4 @@ std::string format_as(const BasicJsonType& j)
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_bjdata(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md).
|
||||
|
||||
@@ -29,11 +29,18 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -43,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -101,3 +108,5 @@ Linear in the size of the input.
|
||||
## Version history
|
||||
|
||||
- Added in version 3.11.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_bson(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md).
|
||||
|
||||
@@ -29,11 +29,18 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -43,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -101,6 +108,8 @@ Linear in the size of the input.
|
||||
## Version history
|
||||
|
||||
- Added in version 3.4.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ static basic_json from_cbor(InputType&& i,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||
@@ -19,7 +19,7 @@ static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md).
|
||||
|
||||
@@ -32,11 +32,18 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -46,7 +53,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -111,6 +118,8 @@ Linear in the size of the input.
|
||||
- Changed to consume input adapters, removed `start_index` parameter, and added `strict` parameter in version 3.0.0.
|
||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||
- Added `tag_handler` parameter in version 3.9.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_msgpack(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the MessagePack serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md).
|
||||
|
||||
@@ -29,11 +29,18 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -43,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -103,6 +110,8 @@ Linear in the size of the input.
|
||||
- Parameter `start_index` since version 2.1.1.
|
||||
- Changed to consume input adapters, removed `start_index` parameter, and added `strict` parameter in version 3.0.0.
|
||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_ubjson(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md).
|
||||
|
||||
@@ -29,11 +29,18 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -43,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -102,6 +109,8 @@ Linear in the size of the input.
|
||||
|
||||
- Added in version 3.1.0.
|
||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -114,6 +114,13 @@ overload (3).
|
||||
See [Number conversion](../../features/types/number_handling.md#number-conversion)
|
||||
for more information.
|
||||
|
||||
!!! note "`std::optional` conversions"
|
||||
|
||||
Prior to version 3.13.0, `#!cpp get<std::optional<T>>()` (and other conversions to `std::optional<T>`) failed to
|
||||
compile in every configuration, due to an internal implementation bug that made the `from_json` overload for
|
||||
`std::optional` unreachable regardless of the [`JSON_USE_IMPLICIT_CONVERSIONS`](../macros/json_use_implicit_conversions.md)
|
||||
setting. This has been fixed.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
@@ -46,6 +46,17 @@ for (auto& [key, val] : j_object.items())
|
||||
}
|
||||
```
|
||||
|
||||
If you need to name the type of the dereferenced element explicitly (e.g., to write a standalone function that
|
||||
takes it as a parameter, or to use `items()` with `std::for_each`), use `decltype`:
|
||||
|
||||
```cpp
|
||||
using element_type = decltype(*j_object.items().begin());
|
||||
```
|
||||
|
||||
The per-element type (`iteration_proxy_value`) lives in the library's internal `detail` namespace and is
|
||||
intentionally unspecified as a stable, named type -- `decltype` is the supported way to obtain it, but its exact
|
||||
name/definition may change between versions.
|
||||
|
||||
## Return value
|
||||
|
||||
iteration proxy object wrapping the current value with an interface to use in range-based for loops
|
||||
|
||||
@@ -63,7 +63,8 @@ behavior:
|
||||
object will agree on the name-value mappings.
|
||||
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be
|
||||
chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or
|
||||
`#!json {"key": 2}`.
|
||||
`#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see
|
||||
[this parsing recipe](../../features/parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
|
||||
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see
|
||||
[`dump`](dump.md)) in this order. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored
|
||||
and serialized as `#!json {"a": 2, "b": 1}`.
|
||||
|
||||
@@ -124,6 +124,15 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
filled with `#!json null`.
|
||||
- The special value `-` is treated as a synonym for the index past the end.
|
||||
|
||||
!!! note "Creating intermediate levels that don't exist yet"
|
||||
|
||||
When the JSON pointer traverses intermediate levels that don't exist at all yet (not just a missing
|
||||
leaf), each missing level is created as an array or an object depending on whether the corresponding
|
||||
pointer token parses as a non-negative integer: a numeric token creates an array, a non-numeric token
|
||||
creates an object. For example, on an initially `#!json null` value, `/foo/0/0/0` creates nested arrays,
|
||||
while `/foo/one/one/one` creates nested objects. This is not specified by the JSON Pointer RFC; it is
|
||||
this library's own, intentional disambiguation rule. See also [JSON Pointer](../../features/json_pointer.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example "Example: (1) access specified array element"
|
||||
@@ -251,5 +260,6 @@ Strong exception safety: if an exception occurs, the original value stays intact
|
||||
1. Added in version 1.0.0.
|
||||
2. Added in version 1.0.0. Added overloads for `T* key` in version 1.1.0. Removed overloads for `T* key` (replaced by 3)
|
||||
in version 3.11.0.
|
||||
3. Added in version 3.11.0.
|
||||
3. Added in version 3.11.0. Fixed in version 3.13.0 to consistently accept `std::string_view`-convertible keys, as
|
||||
already supported by [`at`](at.md), [`value`](value.md), [`find`](find.md), and other lookup functions.
|
||||
4. Added in version 2.0.0.
|
||||
|
||||
@@ -19,10 +19,8 @@ class basic_json {
|
||||
};
|
||||
```
|
||||
|
||||
1. Compares two JSON values for inequality according to the following rules:
|
||||
- The comparison always yields `#!cpp false` if (1) either operand is discarded, or (2) either operand is `NaN` and
|
||||
the other operand is either `NaN` or any other number.
|
||||
- Otherwise, returns the result of `#!cpp !(lhs == rhs)` (until C++20) or `#!cpp !(*this == rhs)` (since C++20).
|
||||
1. Compares two JSON values for inequality. Returns `#!cpp !(lhs == rhs)` (until C++20) or `#!cpp !(*this == rhs)` (since C++20).
|
||||
- This means the comparison is simply the logical negation of `operator==`, including for special values like `NaN` and `discarded`.
|
||||
|
||||
2. Compares a JSON value and a scalar or a scalar and a JSON value for inequality by converting the scalar to a JSON
|
||||
value and comparing both JSON values according to 1.
|
||||
@@ -54,13 +52,12 @@ Linear.
|
||||
|
||||
## Notes
|
||||
|
||||
!!! note "Comparing `NaN`"
|
||||
!!! note "Comparing `NaN` and `discarded`"
|
||||
|
||||
`NaN` values are unordered within the domain of numbers.
|
||||
The following comparisons all yield `#!cpp false`:
|
||||
1. Comparing a `NaN` with itself.
|
||||
2. Comparing a `NaN` with another `NaN`.
|
||||
3. Comparing a `NaN` and any other number.
|
||||
Since `operator!=` is defined as `!(a == b)`, the behavior for special values follows that of `operator==`:
|
||||
|
||||
- For `NaN` values: `NaN == NaN` yields `#!cpp false`, so `NaN != NaN` yields `#!cpp true`.
|
||||
- For `discarded` values: `discarded == x` yields `#!cpp false` for any `x`, so `discarded != x` yields `#!cpp true`.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -94,5 +91,7 @@ Linear.
|
||||
|
||||
## Version history
|
||||
|
||||
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
|
||||
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0.
|
||||
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
||||
special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`.
|
||||
2. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
||||
special-casing for `NaN` and `discarded` values; `operator!=` now consistently means `!(a == b)`.
|
||||
|
||||
@@ -10,8 +10,8 @@ static basic_json parse(InputType&& i,
|
||||
const bool ignore_trailing_commas = false);
|
||||
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json parse(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json parse(IteratorType first, SentinelType last,
|
||||
const parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -19,10 +19,11 @@ static basic_json parse(IteratorType first, IteratorType last,
|
||||
```
|
||||
|
||||
1. Deserialize from a compatible input.
|
||||
2. Deserialize from a pair of character iterators
|
||||
2. Deserialize from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||
|
||||
The `value_type` of the iterator must be an integral type with size of 1, 2, or 4 bytes, which will be interpreted
|
||||
respectively as UTF-8, UTF-16, and UTF-32.
|
||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||
the iterator type with `operator!=`.
|
||||
|
||||
## Template parameters
|
||||
|
||||
@@ -34,7 +35,8 @@ static basic_json parse(IteratorType first, IteratorType last,
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters (throws if null)
|
||||
- a `std::string`
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type, for instance.
|
||||
@@ -42,6 +44,12 @@ static basic_json parse(IteratorType first, IteratorType last,
|
||||
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
|
||||
- a pair of pointers such as `ptr` and `ptr + len`
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -66,7 +74,7 @@ static basic_json parse(IteratorType first, IteratorType last,
|
||||
: iterator to the start of a character range
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of a character range
|
||||
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
## Return value
|
||||
|
||||
@@ -235,7 +243,9 @@ Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
||||
- Overload for contiguous containers (1) added in version 2.0.3.
|
||||
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
||||
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
|
||||
- Added `ignore_trailing_commas` in version 3.12.x.
|
||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -29,7 +29,14 @@ Discarding a value (i.e., returning `#!cpp false`) has different effects dependi
|
||||
called:
|
||||
|
||||
- Discarded values in structured types are skipped. That is, the parser will behave as if the discarded value was never
|
||||
read.
|
||||
read. This holds for every value type and for both kinds of parent: a discarded element is removed from the
|
||||
surrounding array, and a discarded member is removed from the surrounding object together with its key.
|
||||
- Arrays and objects can be discarded either at their `parse_event_t::array_start`/`parse_event_t::object_start` event
|
||||
or at their `parse_event_t::array_end`/`parse_event_t::object_end` event, and both remove the whole value. Discarding
|
||||
it at the start event also means the callback is called neither for the content of the value nor for its matching end
|
||||
event.
|
||||
- Discarding a `parse_event_t::key` event discards the whole object member. The callback is still called for the
|
||||
associated value, but its return value has no further effect.
|
||||
- In case a value outside a structured type is skipped, it is replaced with `null`. This case happens if the top-level
|
||||
element is skipped.
|
||||
|
||||
@@ -49,7 +56,7 @@ called:
|
||||
## Return value
|
||||
|
||||
Whether the JSON value which called the function during parsing should be kept (`#!cpp true`) or not (`#!cpp false`). In
|
||||
the latter case, it is either skipped completely or replaced by an empty discarded object.
|
||||
the latter case, it is skipped completely, or replaced by `null` if it is the top-level value.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -68,6 +75,21 @@ the latter case, it is either skipped completely or replaced by an empty discard
|
||||
--8<-- "examples/parse__string__parser_callback_t.output"
|
||||
```
|
||||
|
||||
??? example
|
||||
|
||||
The example below shows where discarded values are removed. The array and the number are discarded in different
|
||||
ways, but in each case the parse result contains neither the value nor its key.
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/parser_callback_t.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/parser_callback_t.output"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [parse](parse.md) deserialize from a compatible input
|
||||
@@ -76,3 +98,5 @@ the latter case, it is either skipped completely or replaced by an empty discard
|
||||
## Version history
|
||||
|
||||
- Added in version 1.0.0.
|
||||
- Fixed in version 3.13.0 to also remove discarded values from a parent object; before, discarding an array or a value
|
||||
stored under an object key left a discarded member behind, which made the parse result serialize to invalid JSON.
|
||||
|
||||
@@ -74,4 +74,4 @@ is thrown. In any case, the original value is not changed: the patch is applied
|
||||
|
||||
- Added in version 2.0.0.
|
||||
- Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's
|
||||
target location has a non-object/non-array parent in version 3.12.x.
|
||||
target location has a non-object/non-array parent in version 3.13.0.
|
||||
|
||||
@@ -71,4 +71,4 @@ function throws an exception.
|
||||
|
||||
- Added in version 3.11.0.
|
||||
- Added [`out_of_range.411`](../../home/exceptions.md#jsonexceptionout_of_range411) and stopped relying on an internal assertion when an "add" operation's
|
||||
target location has a non-object/non-array parent in version 3.12.x.
|
||||
target location has a non-object/non-array parent in version 3.13.0.
|
||||
|
||||
@@ -11,8 +11,8 @@ static bool sax_parse(InputType&& i,
|
||||
const bool ignore_trailing_commas = false);
|
||||
|
||||
// (2)
|
||||
template<class IteratorType, class SAX>
|
||||
static bool sax_parse(IteratorType first, IteratorType last,
|
||||
template<class IteratorType, class SAX, class SentinelType = IteratorType>
|
||||
static bool sax_parse(IteratorType first, SentinelType last,
|
||||
SAX* sax,
|
||||
input_format_t format = input_format_t::json,
|
||||
const bool strict = true,
|
||||
@@ -23,10 +23,11 @@ static bool sax_parse(IteratorType first, IteratorType last,
|
||||
Read from input and generate SAX events
|
||||
|
||||
1. Read from a compatible input.
|
||||
2. Read from a pair of character iterators
|
||||
2. Read from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||
|
||||
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
||||
respectively as UTF-8, UTF-16, and UTF-32.
|
||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||
the iterator type with `operator!=`.
|
||||
|
||||
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
|
||||
|
||||
@@ -39,13 +40,19 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
||||
- a `FILE` pointer
|
||||
- a C-style array of characters
|
||||
- a pointer to a null-terminated string of single byte characters
|
||||
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of
|
||||
iterators.
|
||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||
|
||||
`IteratorType`
|
||||
: a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type
|
||||
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for overload (2), for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
`SAX`
|
||||
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
|
||||
|
||||
@@ -62,7 +69,8 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
||||
[`input_format_t`](input_format_t.md) for more information
|
||||
|
||||
`strict` (in)
|
||||
: whether the input has to be consumed completely (optional, `#!cpp true` by default)
|
||||
: whether the input has to be consumed completely (optional, `#!cpp true` by default); when `#!cpp false` and the
|
||||
input is a `#!cpp std::istream`, the stream is left positioned right after the parsed value
|
||||
|
||||
`ignore_comments` (in)
|
||||
: whether comments should be ignored and treated like whitespace (`#!cpp true`) or yield a parse error
|
||||
@@ -76,7 +84,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
||||
: iterator to the start of a character range
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of a character range
|
||||
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
## Return value
|
||||
|
||||
@@ -126,7 +134,11 @@ A UTF-8 byte order mark is silently ignored.
|
||||
|
||||
- Added in version 3.2.0.
|
||||
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
||||
- Added `ignore_trailing_commas` in version 3.12.x.
|
||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
- Changed in version 4.0.0 to leave a `#!cpp std::istream` positioned right after the parsed value when `strict` is
|
||||
`#!cpp false`; see [`operator>>`](../operator_gtgt.md#notes).
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -54,4 +54,4 @@ provides `<format>`, controlled by the [`JSON_HAS_STD_FORMAT`](../macros/json_ha
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -18,6 +18,11 @@ JSON class into byte-sized characters during deserialization.
|
||||
: the container to store strings (e.g., `std::string`). Note this container is used for keys/names in objects, see
|
||||
[object_t](object_t.md).
|
||||
|
||||
`StringType` must have a `char`-compatible `value_type`: the library relies on UTF-8/`char`-based storage and
|
||||
processing internally, so `std::wstring`, `std::u16string`, and `std::u32string` are **not** valid choices for
|
||||
`StringType`. To work with wide-character data, convert it to/from UTF-8 at the boundary instead -- see the
|
||||
FAQ's [wide string handling](../../home/faq.md#wide-string-handling) section for a conversion recipe.
|
||||
|
||||
## Notes
|
||||
|
||||
#### Default type
|
||||
|
||||
@@ -40,6 +40,9 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
||||
is not an object; example: `"to serialize to BSON, top-level type must be object, but is string"`
|
||||
- Throws [`out_of_range.409`](../../home/exceptions.md#jsonexceptionout_of_range409) if a key in the JSON object contains
|
||||
a null byte (code point U+0000); example: `"BSON key cannot contain code point U+0000 (at byte 2)"`
|
||||
- Throws [`out_of_range.412`](../../home/exceptions.md#jsonexceptionout_of_range412) if the length of a document, array,
|
||||
string, or binary value exceeds the range of the 32-bit BSON length field; example:
|
||||
`"BSON length 2147483661 exceeds maximum of 2147483647"`
|
||||
|
||||
## Complexity
|
||||
|
||||
|
||||
@@ -21,6 +21,12 @@ a string representation of the type ([`value_t`](value_t.md)):
|
||||
| array | `"array"` |
|
||||
| binary | `"binary"` |
|
||||
| discarded | `"discarded"` |
|
||||
| invalid (corrupted value) | `"invalid"` |
|
||||
|
||||
!!! note "The \"invalid\" type"
|
||||
|
||||
The `"invalid"` return value indicates a corrupted JSON value — this can occur if an enum value falls outside the
|
||||
range of valid `value_t` values. This is useful for diagnosing data corruption or internal errors.
|
||||
|
||||
## Exception safety
|
||||
|
||||
@@ -52,3 +58,4 @@ Constant.
|
||||
- Part of the public API version since 2.1.0.
|
||||
- Changed return value to `const char*` and added `noexcept` in version 3.0.0.
|
||||
- Added support for binary type in version 3.8.0.
|
||||
- Added `"invalid"` return value for corrupted JSON values in version 3.13.0.
|
||||
|
||||
@@ -17,6 +17,8 @@ ValueType value(const json_pointer& ptr,
|
||||
const ValueType& default_value) const;
|
||||
```
|
||||
|
||||
This is equivalent to Python's `dict.get(key, default)`.
|
||||
|
||||
1. Returns either a copy of an object's element at the specified key `key` or a given default value if no element with
|
||||
key `key` exists.
|
||||
|
||||
@@ -184,4 +186,6 @@ changes to any JSON value.
|
||||
|
||||
1. Added in version 1.0.0. Changed parameter `default_value` type from `const ValueType&` to `ValueType&&` in version 3.11.0.
|
||||
2. Added in version 3.11.0. Made `ValueType` the first template parameter in version 3.11.2.
|
||||
3. Added in version 2.0.2. Extended to work with arrays in version 3.12.x.
|
||||
3. Added in version 2.0.2. Extended to work with arrays in version 3.13.0, including fixing an issue where resolving
|
||||
`ptr` through an array unexpectedly threw `out_of_range` instead of returning the resolved element (or
|
||||
`default_value`, as documented).
|
||||
|
||||
@@ -36,4 +36,4 @@ Constant.
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -32,4 +32,4 @@ Linear in the number of reference tokens in the `json_pointer`.
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -35,4 +35,4 @@ Linear in the number of reference tokens in the `json_pointer`.
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -92,4 +92,4 @@ The default value is `0` (disabled — existing behavior is preserved).
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -38,7 +38,8 @@ When the macro is not defined, the library will define it to its default value.
|
||||
|
||||
Diagnostic messages can also be controlled with the CMake option
|
||||
[`JSON_Diagnostics`](../../integration/cmake.md#json_diagnostics) (`OFF` by default)
|
||||
which defines `JSON_DIAGNOSTICS` accordingly.
|
||||
which defines `JSON_DIAGNOSTICS` accordingly. Note this only applies when building the
|
||||
library from source — see the pre-installed-package caveat on that page.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -44,4 +44,4 @@ The default value is detected based on preprocessor macros such as `#!cpp __cplu
|
||||
|
||||
- Added in version 3.10.5.
|
||||
- Added `JSON_HAS_CPP_23` in version 3.12.0.
|
||||
- Added `JSON_HAS_CPP_26` in version 3.12.x.
|
||||
- Added `JSON_HAS_CPP_26` in version 3.13.0.
|
||||
|
||||
@@ -19,6 +19,20 @@ The default value is detected based on the preprocessor macros `#!cpp __cpp_lib_
|
||||
`#!cpp __cpp_lib_experimental_filesystem`, `#!cpp __has_include(<filesystem>)`, or
|
||||
`#!cpp __has_include(<experimental/filesystem>)`.
|
||||
|
||||
!!! info "Known compiler/stdlib exclusions"
|
||||
|
||||
Even when the feature-test macro indicates filesystem support is available, the library disables it on the following broken toolchains:
|
||||
|
||||
- **MinGW + GCC 8** — disabled entirely (broken `std::filesystem` implementation; [MinGW-w64 bug 737](https://sourceforge.net/p/mingw-w64/bugs/737/))
|
||||
- **GCC (non-Clang) < 8** — disabled (no filesystem support)
|
||||
- **Clang < 7** — disabled (no filesystem support)
|
||||
- **MSVC < 19.14** — disabled (no filesystem support)
|
||||
- **iOS < 13** — disabled (no filesystem support)
|
||||
- **macOS < Catalina (10.15)** — disabled (no filesystem support)
|
||||
|
||||
If `JSON_HAS_FILESYSTEM` or `JSON_HAS_EXPERIMENTAL_FILESYSTEM` is `0` despite `__cpp_lib_filesystem` being defined, one
|
||||
of the exclusions above likely applies to your toolchain.
|
||||
|
||||
## Notes
|
||||
|
||||
- Note that older compilers or older versions of libstdc++ also require the library `stdc++fs` to be linked to for
|
||||
|
||||
@@ -13,6 +13,20 @@ The default value is detected based on the preprocessor macro `#!cpp __cpp_lib_r
|
||||
|
||||
When the macro is not defined, the library will define it to its default value.
|
||||
|
||||
!!! info "Known compiler/stdlib exclusions"
|
||||
|
||||
Even when the feature-test macro `__cpp_lib_ranges` indicates ranges support is available, the library disables it on
|
||||
the following incomplete or broken toolchains:
|
||||
|
||||
- **GCC 11.1.0** — disabled (the shipped `<ranges>` header has a syntax error; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||
- **libstdc++ < 11** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||
- **Clang < 16 with libstdc++** — disabled (incomplete ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||
- **libc++ < 160000** — disabled (incomplete C++20 ranges support; [issue #4440](https://github.com/nlohmann/json/issues/4440))
|
||||
- **nvcc (CUDA) 12.0.x and 12.1.x** — disabled (the `enable_borrowed_range` variable-template syntax triggers a parse error
|
||||
under these two toolkit versions; fixed in CUDA 12.2; [issue #3907](https://github.com/nlohmann/json/issues/3907))
|
||||
|
||||
If `JSON_HAS_RANGES` is `0` despite `__cpp_lib_ranges` being defined, one of the exclusions above likely applies to your toolchain.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
@@ -38,4 +38,4 @@ When the macro is not defined, the library will define it to its default value.
|
||||
|
||||
## Version history
|
||||
|
||||
- Added in version 3.12.x.
|
||||
- Added in version 3.13.0.
|
||||
|
||||
@@ -62,6 +62,9 @@ See the examples below for the concrete generated code.
|
||||
|
||||
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
|
||||
with more than 63 member variables, you need to define the `to_json`/`from_json` functions manually.
|
||||
- These macros always produce object-style (named-key) JSON, one key per member. There is no macro variant
|
||||
that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by
|
||||
hand, building/reading a `json::array()` of the members in order.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ See the examples below for the concrete generated code.
|
||||
|
||||
- The current implementation is limited to at most 63 member variables. If you want to serialize/deserialize types
|
||||
with more than 63 member variables, you need to define the `to_json`/`from_json` functions manually.
|
||||
- These macros always produce object-style (named-key) JSON, one key per member. There is no macro variant
|
||||
that serializes a struct's members positionally into a JSON array; for that, write `to_json`/`from_json` by
|
||||
hand, building/reading a `json::array()` of the members in order.
|
||||
|
||||
## Examples
|
||||
|
||||
|
||||
@@ -75,4 +75,4 @@ For further information please refer to the corresponding macros without `WITH_N
|
||||
|
||||
## Version history
|
||||
|
||||
1. Added in version 3.12.x.
|
||||
1. Added in version 3.13.0.
|
||||
|
||||
@@ -102,4 +102,4 @@ inline void from_json(const BasicJsonType& j, type& e);
|
||||
|
||||
## Version history
|
||||
|
||||
Added in version 3.12.x.
|
||||
Added in version 3.13.0.
|
||||
|
||||
@@ -33,6 +33,30 @@ A UTF-8 byte order mark is silently ignored.
|
||||
Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
||||
[`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) with a detailed message.
|
||||
|
||||
`operator>>` parses exactly one JSON value and leaves the stream positioned right after it, so it can be called
|
||||
repeatedly to read a sequence of concatenated JSON values from the same stream:
|
||||
|
||||
```cpp
|
||||
std::istringstream input("1true[2]");
|
||||
json j1, j2, j3;
|
||||
input >> j1; // j1 == 1, stream now positioned right after it
|
||||
input >> j2; // j2 == true
|
||||
input >> j3; // j3 == [2]
|
||||
```
|
||||
|
||||
!!! note "Changed behavior for numbers"
|
||||
|
||||
A number is the only value whose end can be detected solely by reading the character that follows it. Up to
|
||||
version 3.13.0 that character was consumed and not put back, so the stream was left one byte too far whenever a
|
||||
number was immediately followed by another value: reading `1true` yielded `1` and left the stream at `rue`.
|
||||
Values had to be separated by whitespace to work around this.
|
||||
|
||||
The terminating character is now only looked at and left in the stream, so no separator is required. Code that
|
||||
relied on the extra byte being swallowed will observe it again.
|
||||
|
||||
Note that reading concatenated values does **not** work for [JSON Lines](../features/parsing/json_lines.md)
|
||||
(newline-delimited JSON) input -- see that page for why and for the recommended alternative.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
This function replaces function `#!cpp std::istream& operator<<(basic_json& j, std::istream& i)` which has
|
||||
@@ -63,3 +87,5 @@ Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
||||
## Version history
|
||||
|
||||
- Added in version 1.0.0.
|
||||
- Changed in version 4.0.0 to leave the character that terminates a number in the stream, so that the stream is
|
||||
positioned right after the parsed value for every value type.
|
||||
|
||||
@@ -64,4 +64,4 @@ Linear.
|
||||
|
||||
- Added in version 1.0.0.
|
||||
- Moved to namespace `nlohmann::literals::json_literals` in 3.11.0.
|
||||
- Added `char8_t*` overload in 3.12.x.
|
||||
- Added `char8_t*` overload in 3.13.0.
|
||||
|
||||
@@ -63,4 +63,4 @@ Linear.
|
||||
|
||||
- Added in version 2.0.0.
|
||||
- Moved to namespace `nlohmann::literals::json_literals` in 3.11.0.
|
||||
- Added `char8_t*` overload in 3.12.x.
|
||||
- Added `char8_t*` overload in 3.13.0.
|
||||
|
||||
@@ -13,6 +13,12 @@ Therefore, adding object elements can yield a reallocation in which case all ite
|
||||
[`end()`](basic_json/end.md) iterator) and all references to the elements are invalidated. Also, any iterator or
|
||||
reference after the insertion point will point to the same index, which is now a different value.
|
||||
|
||||
## Complexity
|
||||
|
||||
[`ordered_map`](ordered_map.md) has no lookup index: every key-based object operation is a linear scan, so building or
|
||||
parsing an object of `n` keys costs O(n²) rather than O(n log n). See
|
||||
[`ordered_map` complexity](ordered_map.md#complexity) for the per-operation table and for measured numbers.
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
@@ -56,6 +56,48 @@ std::equal_to<> // since C++14
|
||||
- **find**
|
||||
- **insert**
|
||||
|
||||
## Complexity
|
||||
|
||||
Because the elements are stored in a `std::vector` in insertion order, there is no index to look a key up by. Every
|
||||
key-based operation performs a **linear scan** over the stored elements. With `n` denoting the number of elements in the
|
||||
container:
|
||||
|
||||
| Operation | Complexity | Note |
|
||||
|----------------------------------------|----------------|----------------------------------------------------------|
|
||||
| **emplace** | O(n) | scans for an existing key, then appends (amortized O(1)) |
|
||||
| **operator\[\]** | O(n) | delegates to **emplace** (non-const) or **at** (const) |
|
||||
| **at** | O(n) | throws `#!cpp std::out_of_range` if the key is not found |
|
||||
| **find** | O(n) | |
|
||||
| **count** | O(n) | the result is always 0 or 1 |
|
||||
| **erase(key)** | O(n) | scan, then move the remaining elements one position down |
|
||||
| **erase(pos)**, **erase(first, last)** | O(n) | moves all elements after the erased range |
|
||||
| **insert(value)** | O(n) | equivalent to **emplace** |
|
||||
| **insert(first, last)** | O((n + m) * m) | for `m` inserted elements |
|
||||
|
||||
This differs from `#!cpp std::map`, where the same operations are O(log n).
|
||||
|
||||
!!! warning "Quadratic cost of building large objects"
|
||||
|
||||
Because every insertion scans all elements inserted so far, building an object of `n` distinct keys costs
|
||||
**O(n²)** in total. This applies to filling an [`ordered_json`](ordered_json.md) object key by key as well as to
|
||||
parsing one, since the parser inserts each key as it is read.
|
||||
|
||||
The cost is negligible for the object sizes typically found in configuration files or API payloads, but it grows
|
||||
steeply for machine-generated objects with many thousands of keys. Measured with `-O2 -DNDEBUG` for parsing a flat
|
||||
object of `n` keys, relative to `#!cpp nlohmann::json` (which uses `#!cpp std::map`):
|
||||
|
||||
| `n` | `json` | `ordered_json` | factor |
|
||||
|--------|--------|----------------|--------|
|
||||
| 2000 | 0.7 ms | 3.6 ms | 5× |
|
||||
| 4000 | 0.8 ms | 14.0 ms | 19× |
|
||||
| 8000 | 1.6 ms | 67.8 ms | 43× |
|
||||
| 16 000 | 3.3 ms | 181.6 ms | 54× |
|
||||
|
||||
If key order matters for objects of that size, consider a container with a lookup index, such as
|
||||
[`tsl::ordered_map`](https://github.com/Tessil/ordered-map)
|
||||
([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)), as the object type -- see
|
||||
[object order](../features/object_order.md).
|
||||
|
||||
## Examples
|
||||
|
||||
??? example
|
||||
|
||||
@@ -10,6 +10,10 @@ violations will result in a failed build.
|
||||
|
||||
Any compiler with complete C++11 support can compile the library without warnings.
|
||||
|
||||
Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds.
|
||||
|
||||
Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions.
|
||||
|
||||
- [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms,
|
||||
including the oldest versions known to compile the library.
|
||||
|
||||
@@ -62,12 +66,14 @@ violations will result in a failed build.
|
||||
| Clang 20.1.1 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| Clang 20.1.8 with GNU-like command-line | x86_64 | Windows Server 2022 (Build 20348) | GitHub |
|
||||
| Clang 21.1.8 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| CUDA 11.0.221 (nvcc) | x86_64 | Ubuntu 20.04 LTS | GitHub |
|
||||
| CUDA 11.8.0 (nvcc) | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
| CUDA 12.1.1 (nvcc) | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
| CUDA 12.6.3 (nvcc) | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
| Emscripten 4.0.6 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 4.8.5 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 4.9.3 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 5.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 6.4.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 4.8.5 | x86_64 | Ubuntu 20.04 LTS | GitHub |
|
||||
| GNU 4.9.3 | x86_64 | Ubuntu 20.04 LTS | GitHub |
|
||||
| GNU 5.5.0 | x86_64 | Ubuntu 20.04 LTS | GitHub |
|
||||
| GNU 6.4.0 | x86_64 | Ubuntu 20.04 LTS | GitHub |
|
||||
| GNU 7.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 8.5.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 9.3.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
@@ -84,7 +90,7 @@ violations will result in a failed build.
|
||||
| GNU 15.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 16.1.0 | x86_64 | Ubuntu 22.04.1 LTS | GitHub |
|
||||
| GNU 16.1.0 | arm64 | Linux 6.1.100 | Cirrus CI |
|
||||
| icpc (ICC) 2021.5.0 20211109 | x86_64 | Ubuntu 20.04.3 LTS | GitHub |
|
||||
| icpc (ICC) 2021.10.0 20230609 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
| icpx (Intel oneAPI DPC++/C++) 2025.3.2 | x86_64 | Ubuntu 24.04 LTS | GitHub |
|
||||
| nvc++ (NVIDIA HPC SDK) 25.5-0 | x86_64 | Ubuntu 22.04 LTS | GitHub |
|
||||
| MSVC 19.0.24241.7 | x86 | Windows 8.1 | AppVeyor |
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
int main()
|
||||
{
|
||||
// a JSON text with an array and a number inside an object
|
||||
auto text = R"({"IDs": [116, 943], "Width": 800})";
|
||||
|
||||
// discard the array when the parser reads its opening bracket
|
||||
json j_array_start = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & /*parsed*/)
|
||||
{
|
||||
return event != json::parse_event_t::array_start;
|
||||
});
|
||||
|
||||
// discard the same array when the parser reads its closing bracket
|
||||
json j_array_end = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & /*parsed*/)
|
||||
{
|
||||
return event != json::parse_event_t::array_end;
|
||||
});
|
||||
|
||||
// discard the number, but keep its key
|
||||
json j_value = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & parsed)
|
||||
{
|
||||
return !(event == json::parse_event_t::value && parsed == json(800));
|
||||
});
|
||||
|
||||
// discard the key of the number
|
||||
json j_key = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & parsed)
|
||||
{
|
||||
return !(event == json::parse_event_t::key && parsed == json("Width"));
|
||||
});
|
||||
|
||||
// discard the top-level object
|
||||
json j_root = json::parse(text, [](int /*depth*/, json::parse_event_t event, json & /*parsed*/)
|
||||
{
|
||||
return event != json::parse_event_t::object_end;
|
||||
});
|
||||
|
||||
// in every case, the discarded value is removed together with its key
|
||||
std::cout << j_array_start << '\n'
|
||||
<< j_array_end << '\n'
|
||||
<< j_value << '\n'
|
||||
<< j_key << '\n'
|
||||
<< j_root << '\n';
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{"Width":800}
|
||||
{"Width":800}
|
||||
{"IDs":[116,943]}
|
||||
{"IDs":[116,943]}
|
||||
null
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
json parse_strict(const std::string& input)
|
||||
{
|
||||
// one key set per nesting depth, reused across sibling objects
|
||||
std::vector<std::unordered_set<std::string>> keys;
|
||||
|
||||
auto reject_duplicate_keys = [&](int depth, json::parse_event_t event, json & parsed)
|
||||
{
|
||||
if (event == json::parse_event_t::object_start)
|
||||
{
|
||||
// keys of this object are reported at depth+1 (see the event table above)
|
||||
const auto child_depth = static_cast<std::size_t>(depth) + 1;
|
||||
if (keys.size() <= child_depth)
|
||||
{
|
||||
keys.resize(child_depth + 1);
|
||||
}
|
||||
keys[child_depth].clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event == json::parse_event_t::key)
|
||||
{
|
||||
auto& seen = keys[static_cast<std::size_t>(depth)];
|
||||
const auto& key = parsed.get_ref<const std::string&>();
|
||||
if (!seen.insert(key).second)
|
||||
{
|
||||
throw std::runtime_error("duplicate JSON object key: " + key);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return json::parse(input, reject_duplicate_keys);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// parsing succeeds when all keys are unique
|
||||
json j = parse_strict(R"({"one": 1, "two": 2})");
|
||||
std::cout << j << '\n';
|
||||
|
||||
// parsing throws when a key is repeated
|
||||
try
|
||||
{
|
||||
parse_strict(R"({"one": 1, "one": 2})");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"one":1,"two":2}
|
||||
duplicate JSON object key: one
|
||||
@@ -180,6 +180,49 @@ For _derived_ classes and structs, use the following macros
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning "Overriding conversions for natively-supported types"
|
||||
|
||||
The library already provides built-in `to_json`/`from_json` conversions for STL containers such as
|
||||
`std::vector`, `std::array`, and `std::map`. Defining your own free-function `to_json`/`from_json` overload
|
||||
for one of these container types directly (instead of for your own type) can conflict with the built-in
|
||||
overload during overload resolution, producing compiler errors ("no matching overloaded function",
|
||||
"call is ambiguous") that vary by compiler and library version. If you need different conversion behavior
|
||||
for a container type the library already handles, wrap it in your own type (or use `adl_serializer`
|
||||
specialization, as shown [above](#how-do-i-convert-third-party-types) for `boost::optional`) instead of
|
||||
trying to re-specialize `to_json`/`from_json` for the container type itself.
|
||||
|
||||
!!! warning "Raw C-style arrays"
|
||||
|
||||
Members declared as raw C-style arrays (e.g., `char buf[1024]`) do not round-trip safely through
|
||||
`NLOHMANN_DEFINE_TYPE_*` macros or the default (de)serializers: `to_json` serializes any `char` array as a
|
||||
JSON *string* (matching the `std::string`-constructible overload), but the `from_json` overload for
|
||||
fixed-size arrays expects a JSON *array* and iterates it element-wise, which fails with a `type_error` when
|
||||
given a string. Use `std::string`, `std::array<char, N>`, or a manually written `to_json`/`from_json` pair
|
||||
for such members instead.
|
||||
|
||||
!!! note "Macros and `nlohmann::ordered_json`"
|
||||
|
||||
The `NLOHMANN_DEFINE_TYPE_*`/`NLOHMANN_DEFINE_DERIVED_TYPE_*` macros are generic over any `basic_json`
|
||||
specialization, including `nlohmann::ordered_json`. Simply use `ordered_json` as the target type and members
|
||||
are serialized in declaration order -- no separate macro or extra code is needed.
|
||||
|
||||
```cpp
|
||||
namespace ns {
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(person, name, address, age)
|
||||
}
|
||||
|
||||
ns::person p{"Ned Flanders", "744 Evergreen Terrace", 60};
|
||||
nlohmann::ordered_json j = p; // keys appear in declaration order: name, address, age
|
||||
```
|
||||
|
||||
!!! note "No macro for non-default-constructible types"
|
||||
|
||||
There is currently no `NLOHMANN_DEFINE_TYPE_*`-style macro for types that are not
|
||||
[DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). This is not an
|
||||
intentional omission of documentation -- no such macro exists yet; see
|
||||
[How can I use `get()` for non-default constructible/non-copyable types?](#how-can-i-use-get-for-non-default-constructiblenon-copyable-types)
|
||||
for the manual pattern to use instead.
|
||||
|
||||
## How do I convert third-party types?
|
||||
|
||||
This requires a bit more advanced technique. But first, let us see how this conversion mechanism works:
|
||||
@@ -270,6 +313,49 @@ namespace nlohmann {
|
||||
}
|
||||
```
|
||||
|
||||
## Why can't I convert to/from `std::any`?
|
||||
|
||||
`std::any` is intentionally excluded from `get<T>()`/generic conversion support, so `get<std::any>()` and
|
||||
containers like `std::map<std::string, std::any>` fail to compile by design -- there is no way to know, from a
|
||||
`json` value alone, which concrete type to store inside the `std::any`. To work with heterogeneous JSON values,
|
||||
dispatch on the value's type manually and construct the `std::any` (or extract from it) yourself:
|
||||
|
||||
```cpp
|
||||
std::any value_to_any(const json& j) {
|
||||
if (j.is_boolean()) { return j.get<bool>(); }
|
||||
if (j.is_number_integer()) { return j.get<int>(); }
|
||||
if (j.is_number_float()) { return j.get<double>(); }
|
||||
if (j.is_string()) { return j.get<std::string>(); }
|
||||
// ... handle other types (arrays, objects) as needed for your use case
|
||||
return {};
|
||||
}
|
||||
|
||||
json any_to_json(const std::any& a) {
|
||||
if (a.type() == typeid(bool)) { return std::any_cast<bool>(a); }
|
||||
if (a.type() == typeid(int)) { return std::any_cast<int>(a); }
|
||||
if (a.type() == typeid(double)) { return std::any_cast<double>(a); }
|
||||
if (a.type() == typeid(std::string)) { return std::any_cast<std::string>(a); }
|
||||
return nullptr;
|
||||
}
|
||||
```
|
||||
|
||||
## Why does serializing a `std::map`/`std::unordered_map` with non-string keys produce an array?
|
||||
|
||||
A `std::map`/`std::unordered_map` whose key type is not string-like (e.g., `std::map<int, std::string>`) is
|
||||
serialized as a JSON *array* of 2-element `[key, value]` arrays, not as a JSON object -- JSON object keys must be
|
||||
strings, so the library cannot represent an integer-keyed map as an object.
|
||||
|
||||
```cpp
|
||||
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
|
||||
json j = m;
|
||||
// j is [[1,"one"],[2,"two"]], not {"1":"one","2":"two"}
|
||||
```
|
||||
|
||||
## Why does `std::wstring` convert or dump incorrectly?
|
||||
|
||||
The library assumes UTF-8 encoding internally, so `std::wstring` is not supported out of the box -- see the FAQ
|
||||
entry on [wide string handling](../home/faq.md#wide-string-handling) for why, and for a UTF-8 conversion recipe.
|
||||
|
||||
## Can I write my own serializer? (Advanced use)
|
||||
|
||||
Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-udt.cpp) in the test suite, to see a few examples.
|
||||
|
||||
@@ -35,6 +35,19 @@ The library uses the following mapping from JSON values types to BSON types:
|
||||
The mapping is **incomplete**, since only JSON-objects (and things contained therein) can be serialized to BSON.
|
||||
Also, keys may not contain U+0000, since they are serialized a zero-terminated c-strings.
|
||||
|
||||
!!! warning "BSON type 0x11 interoperability"
|
||||
|
||||
The BSON specification defines type `0x11` as a Timestamp. This library uses marker `0x11` when serializing
|
||||
`number_unsigned` values in the range `9223372036854775808..18446744073709551615`. Other BSON implementations may
|
||||
therefore interpret these values as Timestamps instead of unsigned integers.
|
||||
|
||||
!!! info "Binary values without a subtype"
|
||||
|
||||
BSON requires every binary value to have a subtype. If a binary value has no subtype, this library serializes it
|
||||
with the generic subtype `0x00`. After deserialization, `has_subtype()` returns `true` and `subtype()` returns `0`.
|
||||
As a result, serializing and deserializing a JSON object containing such a value produces a different JSON object,
|
||||
even though the binary data is unchanged.
|
||||
|
||||
??? example
|
||||
|
||||
```cpp
|
||||
@@ -82,8 +95,8 @@ The library maps BSON record types to JSON value types as follows:
|
||||
|
||||
!!! note "Handling of BSON type 0x11"
|
||||
|
||||
BSON type 0x11 is used to represent uint64 numbers. This library treats these values purely as uint64 numbers
|
||||
and does not parse them into date-related formats.
|
||||
This library deserializes BSON type `0x11` (Timestamp) as a `number_unsigned` value. The 64-bit value is preserved,
|
||||
but the Timestamp type information is not.
|
||||
|
||||
??? example
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ extremely small code sizes, fairly small message size, and extensibility without
|
||||
|
||||
- [CBOR Website](http://cbor.io) - the main source on CBOR
|
||||
- [CBOR Playground](http://cbor.me) - an interactive webpage to translate between JSON and CBOR
|
||||
- [RFC 7049](https://tools.ietf.org/html/rfc7049) - the CBOR specification
|
||||
- [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html) - the CBOR specification
|
||||
|
||||
## Serialization
|
||||
|
||||
The library uses the following mapping from JSON values types to CBOR types according to the CBOR specification
|
||||
([RFC 7049](https://www.rfc-editor.org/rfc/rfc7049.html)):
|
||||
([RFC 8949](https://www.rfc-editor.org/rfc/rfc8949.html)):
|
||||
|
||||
| JSON value type | value/range | CBOR type | first byte |
|
||||
|-----------------|--------------------------------------------|-----------------------------------|------------|
|
||||
@@ -66,7 +66,15 @@ see "binary" cells in the table above.
|
||||
|
||||
!!! info "NaN/infinity handling"
|
||||
|
||||
If NaN or Infinity are stored inside a JSON number, they are serialized properly. This behavior differs from the normal JSON serialization which serializes NaN or Infinity to `null`.
|
||||
`NaN`, `Infinity`, and `-Infinity` are serialized as a CBOR half-precision float (type 0xF9, 3 bytes total):
|
||||
`NaN` as `0xF9 0x7E 0x00`, `Infinity` as `0xF9 0x7C 0x00`, and `-Infinity` as `0xF9 0xFC 0x00`. This behavior
|
||||
differs from the normal JSON serialization which serializes NaN or Infinity to `null`.
|
||||
|
||||
!!! note
|
||||
|
||||
Prior to version 3.13.0, NaN and Infinity were instead serialized as a CBOR double-precision float (type 0xFB,
|
||||
9 bytes total), because the check used to select a smaller encoding compared magnitudes with NaN, which is
|
||||
always `false` and caused the intended half-precision path to be skipped.
|
||||
|
||||
!!! info "Unused CBOR types"
|
||||
|
||||
@@ -160,6 +168,13 @@ The library maps CBOR types to JSON value types as follows:
|
||||
- simple values (0xE0..0xF3, 0xF8)
|
||||
- undefined (0xF7)
|
||||
|
||||
!!! warning "Negative integer overflow"
|
||||
|
||||
CBOR negative integers (major type 1) are decoded as `-1 - n`. If the encoded magnitude `n` is too large for the
|
||||
result to fit into `number_integer_t` (`std::int64_t` by default), parsing fails with a
|
||||
[`parse_error.112`](../../home/exceptions.md#jsonexceptionparse_error112) exception rather than overflowing
|
||||
silently.
|
||||
|
||||
!!! warning "Object keys"
|
||||
|
||||
CBOR allows map keys of any type, whereas JSON only allows strings as keys in object values. Therefore, CBOR maps with keys other than UTF-8 strings are rejected.
|
||||
|
||||
@@ -67,8 +67,15 @@ specification:
|
||||
|
||||
!!! info "NaN/infinity handling"
|
||||
|
||||
If NaN or Infinity are stored inside a JSON number, they are serialized properly in contrast to the
|
||||
[dump](../../api/basic_json/dump.md) function which serializes NaN or Infinity to `null`.
|
||||
`NaN`, `Infinity`, and `-Infinity` are serialized as a MessagePack float 32 (type 0xCA, 5 bytes total),
|
||||
regardless of magnitude, in contrast to the [dump](../../api/basic_json/dump.md) function which serializes NaN
|
||||
or Infinity to `null`.
|
||||
|
||||
!!! note
|
||||
|
||||
Prior to version 3.13.0, NaN and Infinity were instead serialized as a MessagePack float 64 (type 0xCB, 9 bytes
|
||||
total), because the check used to select the smaller float 32 encoding compared magnitudes with NaN, which is
|
||||
always `false` and caused the float 32 path to be skipped.
|
||||
|
||||
??? example
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ This library does not support comments *by default*. It does so for three reason
|
||||
|
||||
3. It is dangerous for interoperability if some libraries add comment support while others do not. Please check [The Harmful Consequences of the Robustness Principle](https://tools.ietf.org/html/draft-iab-protocol-maintenance-01) on this.
|
||||
|
||||
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace.
|
||||
However, you can set parameter `ignore_comments` to `#!cpp true` in the [`parse`](../api/basic_json/parse.md) function to ignore `//` or `/* */` comments. Comments will then be treated as whitespace. Combined with `ignore_trailing_commas` (also a `parse` parameter), this covers what is commonly referred to as **JSONC** (JSON with Comments, as used e.g. by Visual Studio Code's `.jsonc` files) -- comments and trailing commas, nothing more. This is a different, smaller extension than [JSON5](https://json5.org), which additionally allows unquoted keys, single-quoted strings, and other syntax changes that this library does not support.
|
||||
|
||||
For more information, see [JSON With Commas and Comments (JWCC)](https://nigeltao.github.io/blog/2021/json-with-commas-comments.html).
|
||||
|
||||
|
||||
@@ -47,6 +47,34 @@ json j = {{"one", 1}, {"two", 2}};
|
||||
auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}}
|
||||
```
|
||||
|
||||
`#!cpp std::pair` and `#!cpp std::tuple` are also supported, converting positionally to and from a JSON array:
|
||||
|
||||
```cpp
|
||||
json j = {1.0, "hello", 42};
|
||||
auto t = j.get<std::tuple<double, std::string, int>>(); // {1.0, "hello", 42}
|
||||
```
|
||||
|
||||
!!! info "Extracting references into a tuple"
|
||||
|
||||
A tuple type may also hold references (e.g. `#!cpp std::tuple<double&, std::string&>`) to avoid copying: `get`
|
||||
then returns a tuple of references pointing directly at the elements stored inside the `basic_json` array,
|
||||
rather than a tuple of copies:
|
||||
|
||||
```cpp
|
||||
json j = {1.0, "hello"};
|
||||
auto refs = j.get<std::tuple<double&, std::string&>>();
|
||||
std::get<1>(refs) = "world"; // modifies j[1] in place
|
||||
```
|
||||
|
||||
A referenced element must name the type the library actually *stores* — one of [`boolean_t`](../api/basic_json/boolean_t.md),
|
||||
[`number_integer_t`](../api/basic_json/number_integer_t.md), [`number_unsigned_t`](../api/basic_json/number_unsigned_t.md),
|
||||
[`number_float_t`](../api/basic_json/number_float_t.md), [`string_t`](../api/basic_json/string_t.md),
|
||||
[`binary_t`](../api/basic_json/binary_t.md), [`array_t`](../api/basic_json/array_t.md), or
|
||||
[`object_t`](../api/basic_json/object_t.md). There is nothing else to refer to, so a reference to any other type is a
|
||||
compile error even when a conversion would exist: `#!cpp std::tuple<int&>` is rejected, because the library stores a
|
||||
`#!cpp number_integer_t` (`#!cpp std::int64_t` by default) and not an `#!cpp int`. This restriction applies only to
|
||||
reference elements — a plain `#!cpp std::tuple<int>` converts by value as usual.
|
||||
|
||||
## Implicit conversions
|
||||
|
||||
By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted:
|
||||
@@ -66,6 +94,84 @@ which forces the explicit `get` form and can catch unintended conversions at com
|
||||
floating-point value as an integer truncates it, and narrowing conversions may overflow. See
|
||||
[number conversion](types/number_handling.md#number-conversion) for details and how to guard against it.
|
||||
|
||||
!!! warning "std::optional direct construction from JSON null throws"
|
||||
|
||||
Constructing or assigning `std::optional<T>` directly from a JSON value does not correctly produce
|
||||
`std::nullopt` for a JSON `null`:
|
||||
|
||||
```cpp
|
||||
json j_null;
|
||||
std::optional<std::string> opt = j_null; // ❌ throws type_error 302
|
||||
```
|
||||
|
||||
This is due to C++ language rules: `std::optional<T>` has its own converting constructor that is chosen over
|
||||
`basic_json::operator T()` when both are viable. Use `get<std::optional<T>>()` or `get_to()` instead:
|
||||
|
||||
```cpp
|
||||
auto opt = j_null.get<std::optional<std::string>>(); // ✅ std::nullopt
|
||||
j_null.get_to(opt); // ✅ std::nullopt
|
||||
```
|
||||
|
||||
!!! warning "`static_cast` and `get<std::optional<T>>()` are not guaranteed equivalent"
|
||||
|
||||
`operator ValueType()` (used by `static_cast` and implicit conversions) intentionally excludes
|
||||
`std::optional<T>` from delegating to `get<T>()`, to avoid a constructor ambiguity with
|
||||
`std::optional<T>`'s own converting constructor from `basic_json`. As a result,
|
||||
`static_cast<std::optional<T>>(json_value)` goes through `std::optional<T>`'s own converting
|
||||
constructor rather than through `get<std::optional<T>>()`, which can behave differently -- for example,
|
||||
with a custom `adl_serializer<std::optional<T>>` specialization. Prefer `get<std::optional<T>>()`/`get_to()`
|
||||
over `static_cast` for optional types.
|
||||
|
||||
!!! warning "Converting to a fixed-size destination does not check the array size"
|
||||
|
||||
Some destination types have a size that is fixed by their C++ type rather than by the JSON value:
|
||||
`#!cpp std::pair<A, B>`, `#!cpp std::tuple<Ts...>`, `#!cpp std::array<T, N>`, C arrays `#!cpp T[N]`, and
|
||||
`#!cpp std::map`/`#!cpp std::unordered_map` with a non-string key type (which is read from an array of
|
||||
two-element arrays). All of them read exactly as many elements as they need via
|
||||
[`at`](../api/basic_json/at.md) and **never compare the JSON array's size to that number**. The two
|
||||
mismatch directions therefore behave differently:
|
||||
|
||||
- The JSON array has **too many** elements: the surplus is **silently discarded**, and no exception is
|
||||
thrown.
|
||||
- The JSON array has **too few** elements: `at` throws
|
||||
[`out_of_range.401`](../home/exceptions.md#jsonexceptionout_of_range401) for the first missing index --
|
||||
an out-of-range error, not a [`type_error`](../home/exceptions.md#type-errors), even though the cause
|
||||
is a shape mismatch.
|
||||
|
||||
```cpp
|
||||
json j = {1, 2, 3, 4, 5};
|
||||
|
||||
auto a = j.get<std::array<int, 3>>(); // {1, 2, 3} -- elements 4 and 5 silently dropped
|
||||
auto p = j.get<std::pair<int, int>>(); // (1, 2) -- elements 3, 4, and 5 silently dropped
|
||||
|
||||
json k = {1};
|
||||
auto q = k.get<std::pair<int, int>>(); // ❌ throws out_of_range.401
|
||||
```
|
||||
|
||||
If a size mismatch is an error in your application, check the size yourself before converting.
|
||||
|
||||
## Omitting a field when serializing `std::optional`
|
||||
|
||||
By default, `to_json` for `std::optional<T>` writes either the value or `#!json null` -- there is no built-in way
|
||||
to make a field disappear from the serialized object entirely when the `std::optional` is `std::nullopt`. Because
|
||||
a specialization of `adl_serializer<std::optional<T>>` only controls how the *value* is converted (it cannot
|
||||
prevent the containing object's `to_json` from inserting the key in the first place), omission has to be
|
||||
implemented in the *containing* type's `to_json`:
|
||||
|
||||
```cpp
|
||||
struct person {
|
||||
std::string name;
|
||||
std::optional<int> age;
|
||||
};
|
||||
|
||||
void to_json(json& j, const person& p) {
|
||||
j = json{{"name", p.name}};
|
||||
if (p.age) {
|
||||
j["age"] = *p.age; // key is only inserted when the optional has a value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Putting values in
|
||||
|
||||
The reverse direction works the same way: assigning or constructing a `json` from a C++ value converts it to JSON.
|
||||
@@ -75,6 +181,20 @@ std::vector<int> numbers = {1, 2, 3};
|
||||
json j = numbers; // [1,2,3]
|
||||
```
|
||||
|
||||
!!! info "Constructing from a C++20 range view"
|
||||
|
||||
A `json` array can also be constructed directly from a C++20 range view (`std::ranges::view`), such as the result
|
||||
of `std::views::filter` or `std::views::transform` -- no intermediate container is needed:
|
||||
|
||||
```cpp
|
||||
std::vector<int> nums{1, 2, 37, 42, 21};
|
||||
auto filtered = nums | std::views::filter([](int i) { return i > 10; });
|
||||
json j(filtered); // [37,42,21]
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Your own types
|
||||
|
||||
The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
In many situations, such as configuration files, missing values are not exceptional, but may be treated as if a default
|
||||
value was present. For this case, use [`value(key, default_value)`](../../api/basic_json/value.md) which takes the key
|
||||
you want to access and a default value in case there is no value stored with that key.
|
||||
you want to access and a default value in case there is no value stored with that key. This is equivalent to Python's
|
||||
`dict.get(key, default)`.
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -102,6 +102,20 @@ that the passed index is the new maximal index. Intermediate values are filled w
|
||||
`operator[]` can only be used with objects (with a string argument) or with arrays (with a numeric argument). For
|
||||
other types, a [`basic_json::type_error`](../../home/exceptions.md#jsonexceptiontype_error305) is thrown.
|
||||
|
||||
## Performance: reserving array capacity
|
||||
|
||||
There is no public `reserve(count)` member on `basic_json` for pre-allocating array capacity. If you are building
|
||||
a large array incrementally (e.g., via repeated `push_back()`) and know its final size ahead of time, you can
|
||||
reserve capacity via `get_ref()` to access the underlying `array_t` directly:
|
||||
|
||||
```cpp
|
||||
json j = json::array();
|
||||
j.get_ref<json::array_t&>().reserve(1000);
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
j.push_back(i);
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
| scenario | non-const value | const value |
|
||||
|
||||
@@ -77,6 +77,11 @@ auto val2 = j.at(json::json_pointer("/nested/three/1")); // false
|
||||
auto val3 = j.value(json::json_pointer("/nested/four"), 0); // 0
|
||||
```
|
||||
|
||||
!!! note "Creating intermediate levels that don't exist"
|
||||
|
||||
See the [`operator[]` notes](../api/basic_json/operator%5B%5D.md#return-value) for how array vs. object is
|
||||
decided when a pointer creates intermediate levels that don't exist yet.
|
||||
|
||||
## Flatten / unflatten
|
||||
|
||||
The library implements a function [`flatten`](../api/basic_json/flatten.md) to convert any JSON document into a JSON
|
||||
|
||||
@@ -27,6 +27,7 @@ json data = json::parse(f);
|
||||
It should be noted that as modules do not export macros, the `nlohmann.json` module will not export any macros.
|
||||
|
||||
## Exported symbols
|
||||
|
||||
Only the following symbols are exported from `nlohmann.json`:
|
||||
|
||||
- `nlohmann::adl_serializer`
|
||||
@@ -38,3 +39,21 @@ Only the following symbols are exported from `nlohmann.json`:
|
||||
- `nlohmann::to_string`
|
||||
- `nlohmann::literals::json_literals::operator""_json`
|
||||
- `nlohmann::literals::json_literals::operator""_json_pointer`
|
||||
|
||||
Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue
|
||||
([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API,
|
||||
and should not be used directly:
|
||||
|
||||
- `nlohmann::detail::json_sax_dom_callback_parser`
|
||||
- `nlohmann::detail::unknown_size`
|
||||
|
||||
## Known issues
|
||||
|
||||
C++20 modules support is exercised in CI against current GCC and Clang on Ubuntu, and the default MSVC toolset on Windows Server 2022 — there is no documented minimum compiler version, unlike feature-test-macro-gated features such as [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md).
|
||||
|
||||
!!! info "Known compiler issues"
|
||||
|
||||
- **GCC** may emit "redefinition" errors when `#include <nlohmann/json.hpp>` appears in a module preamble together with other imports. This is an upstream GCC bug, not yet resolved as of GCC 16. Workarounds: include `nlohmann/json.hpp` before other `#include`s, use `import nlohmann.json;` instead, or upgrade GCC. ([issue #5103](https://github.com/nlohmann/json/issues/5103))
|
||||
- **MSVC** could fail with `C2039: 'json_sax_dom_callback_parser' is not a member of ... detail`; fixed by exporting the required internal symbols from `json.cppm` (see [Exported symbols](#exported-symbols) above). ([issue #3970](https://github.com/nlohmann/json/issues/3970))
|
||||
|
||||
If you hit a different module-related build failure, search [existing issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+modules) before filing a new one.
|
||||
|
||||
@@ -53,6 +53,12 @@ If you do want to preserve the **insertion order**, you can use the type [`nlohm
|
||||
|
||||
Alternatively, you can use a more sophisticated ordered map like [`tsl::ordered_map`](https://github.com/Tessil/ordered-map) ([integration](https://github.com/nlohmann/json/issues/546#issuecomment-304447518)) or [`nlohmann::fifo_map`](https://github.com/nlohmann/fifo_map) ([integration](https://github.com/nlohmann/json/issues/485#issuecomment-333652309)).
|
||||
|
||||
The [`ordered_map`](../api/ordered_map.md) behind `nlohmann::ordered_json` is deliberately minimal and has no lookup
|
||||
index, so every key access is a linear scan and building an object of `n` keys costs O(n²). This is unnoticeable at
|
||||
typical object sizes but becomes significant for objects with many thousands of keys; see
|
||||
[`ordered_map` complexity](../api/ordered_map.md#complexity). The alternatives above keep a lookup index and do not
|
||||
have this cost.
|
||||
|
||||
### Notes on parsing
|
||||
|
||||
Note that you also need to call the right [`parse`](../api/basic_json/parse.md) function when reading from a file.
|
||||
|
||||
@@ -28,6 +28,22 @@ Inputs consisting of multiple values separated by newlines are handled by the [J
|
||||
By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse`
|
||||
function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md).
|
||||
|
||||
## Strictness and trailing data
|
||||
|
||||
[`parse`](../../api/basic_json/parse.md) reads a single JSON value and requires the whole input to be consumed: any
|
||||
non-whitespace data after the value is reported as a parse error. Use it when you want to guarantee that an input is
|
||||
exactly one complete JSON document.
|
||||
|
||||
[`operator>>`](../../api/operator_gtgt.md) follows relaxed `#!cpp std::istream` semantics instead: it parses one JSON
|
||||
value and leaves the stream positioned right after it, without requiring the rest of the stream to be consumed. This is
|
||||
what makes it possible to read several concatenated values from the same stream, but it also means that "a valid
|
||||
document followed by trailing bytes" is accepted rather than rejected. If you are validating conformance, or need to
|
||||
reject any input that is not exactly one JSON document, prefer `parse`.
|
||||
|
||||
When using `operator>>` to read several concatenated values this way, a value that is a number must be followed by
|
||||
whitespace, because `operator>>` consumes the character that terminates a number — see the
|
||||
[`operator>>` notes](../../api/operator_gtgt.md#notes) for details and examples.
|
||||
|
||||
## SAX vs. DOM parsing
|
||||
|
||||
The library offers two parsing models:
|
||||
|
||||
@@ -47,3 +47,7 @@ JSON Lines input with more than one value is treated as invalid JSON by the [`pa
|
||||
```
|
||||
|
||||
with a JSON Lines input does not work, because the parser will try to parse one value after the last one.
|
||||
|
||||
This is different from parsing a stream of *concatenated* (non-newline-delimited) JSON values, for which
|
||||
`operator>>` does work, provided that a value that is a number is followed by whitespace -- see its
|
||||
[notes](../../api/operator_gtgt.md#notes) for details.
|
||||
|
||||
@@ -58,6 +58,14 @@ table describes the values of the parameters `depth`, `event`, and `parsed`.
|
||||
| `array_end` | 1 | `#!json [52.519444,13.406667]` |
|
||||
| `object_end` | 0 | `#!json {"location":[52.519444,13.406667],"name":"Berlin"}` |
|
||||
|
||||
!!! note "No built-in nesting depth limit"
|
||||
|
||||
The library has no built-in limit on recursion/nesting depth while parsing. A parser callback can only
|
||||
*discard* content it has already parsed (by returning `#!c false`); it cannot make parsing fail once a
|
||||
nesting limit is exceeded partway through reading a deeply nested value. If you need to reject over-deep
|
||||
untrusted input outright, track `depth` in a callback and `throw` from it once your limit is exceeded (a
|
||||
thrown exception propagates out of `parse()` as usual).
|
||||
|
||||
## Return value
|
||||
|
||||
Discarding a value (i.e., returning `#!c false`) has different effects depending on the context in which the function
|
||||
@@ -81,3 +89,82 @@ was called:
|
||||
```json
|
||||
--8<-- "examples/parse__string__parser_callback_t.output"
|
||||
```
|
||||
|
||||
## Recipe: rejecting duplicate object keys
|
||||
|
||||
The JSON specification leaves the handling of objects with repeated keys up to the implementation. As described in
|
||||
[`object_t`](../../api/basic_json/object_t.md#behavior), it is unspecified which value for a repeated key ends up in
|
||||
the resulting `#!c json` value -- once parsing has produced that value, the duplicate is already gone, because object
|
||||
storage maps each key to a single value. If duplicate keys should instead be treated as an error, a parser callback
|
||||
can detect them while the object is still being read, before that ambiguity ever applies.
|
||||
|
||||
??? example
|
||||
|
||||
```cpp
|
||||
--8<-- "examples/reject_duplicate_keys.cpp"
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```json
|
||||
--8<-- "examples/reject_duplicate_keys.output"
|
||||
```
|
||||
|
||||
This approach has two limitations:
|
||||
|
||||
- The depth-indexed bookkeeping must account for the fact that `object_start` reports the depth of the *parent* of
|
||||
the object, while the `key` events inside that object are reported one depth deeper (see the event table above);
|
||||
it is easy to get this off by one for nested objects.
|
||||
- The thrown exception cannot carry a `parse_error`-style byte offset, because position tracking only exists inside
|
||||
the parser and lexer, not at the callback layer.
|
||||
|
||||
For strict validation with precise error positions, implementing a [SAX interface](sax_interface.md) instead gives
|
||||
access to the parser's position information directly.
|
||||
|
||||
## Recipe: streaming a large homogeneous array
|
||||
|
||||
A common use case is a huge top-level array of many similarly-shaped objects, too large to hold entirely in
|
||||
memory as a `#!c json` value. A parser callback can hand off each completed element to a user function and then
|
||||
discard it, so memory usage stays bounded by a single element (plus the not-yet-parsed tail of the input) rather
|
||||
than the whole document. Since the top-level array's `array_start`/`array_end` are reported at `depth == 0` (its
|
||||
parent is the document root), the object elements it contains are reported at `depth == 1`:
|
||||
|
||||
??? example
|
||||
|
||||
```cpp
|
||||
std::ifstream input("large_array.json");
|
||||
|
||||
auto callback = [](int depth, json::parse_event_t event, json& parsed) -> bool {
|
||||
if (depth == 1 && event == json::parse_event_t::object_end) {
|
||||
handle_element(parsed); // process the element, e.g. write it elsewhere
|
||||
return false; // discard it -- frees its memory before the next one is parsed
|
||||
}
|
||||
return true; // keep everything else, including the (by then empty) top-level array
|
||||
};
|
||||
|
||||
json::parse(input, callback);
|
||||
```
|
||||
|
||||
If the array's elements are scalars or nested arrays instead of objects, check for `parse_event_t::value` or
|
||||
`parse_event_t::array_end` at `depth == 1` instead. The same approach works for a top-level *object* of many
|
||||
homogeneous values by checking `object_end`/`value` events at `depth == 1` there too.
|
||||
|
||||
## Recipe: max nesting depth via a callback
|
||||
|
||||
Since there is no built-in nesting-depth limit (see the note above), a callback can enforce one manually by
|
||||
tracking the maximum `depth` seen and throwing once it is exceeded:
|
||||
|
||||
??? example
|
||||
|
||||
```cpp
|
||||
constexpr int max_depth = 32;
|
||||
|
||||
auto callback = [](int depth, json::parse_event_t /*event*/, json& /*parsed*/) -> bool {
|
||||
if (depth > max_depth) {
|
||||
throw std::runtime_error("maximum nesting depth exceeded");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
json::parse(input, callback);
|
||||
```
|
||||
|
||||
@@ -131,7 +131,7 @@ std::map<
|
||||
The choice of `object_t` influences the behavior of the JSON class. With the default type, objects have the following behavior:
|
||||
|
||||
- When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings.
|
||||
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`.
|
||||
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see [this parsing recipe](../parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
|
||||
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see `dump`) in this order. For instance, both `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored and serialized as `#!json {"a": 2, "b": 1}`.
|
||||
- When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be treated as equal.
|
||||
|
||||
@@ -151,6 +151,18 @@ In this class, the object's limit of nesting is not explicitly constrained. Howe
|
||||
|
||||
Objects are stored as pointers in a `basic_json` type. That is, for any access to object values, a pointer of type `object_t*` must be dereferenced.
|
||||
|
||||
### Converting maps with non-string keys
|
||||
|
||||
A `std::map`/`std::unordered_map` whose key type is not string-like (e.g., `std::map<int, std::string>`) is
|
||||
converted to a JSON *array* of 2-element `[key, value]` arrays rather than a JSON object, because JSON object
|
||||
keys must be strings:
|
||||
|
||||
```cpp
|
||||
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
|
||||
json j = m;
|
||||
// j is [[1,"one"],[2,"two"]], not {"1":"one","2":"two"}
|
||||
```
|
||||
|
||||
|
||||
## Arrays
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ In the default [`json`](../../api/json.md) type, numbers are stored as `#!c std:
|
||||
number without loss of precision. If this is impossible (e.g., if the number is too large), the number is stored as
|
||||
`#!c double`.
|
||||
|
||||
Positive integers are stored as `#!c std::uint64_t`, while negative integers are stored as `#!c std::int64_t`. This
|
||||
distinction is determined at parse time: if the JSON number has a leading minus sign, it uses signed integer storage;
|
||||
otherwise, it uses unsigned integer storage.
|
||||
|
||||
!!! info "Notes"
|
||||
|
||||
- Numbers with a decimal digit or scientific notation are always stored as `#!c double`.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Debugging
|
||||
|
||||
This page collects the library's built-in debugger integrations and other debugging-related features. They are
|
||||
not linked from a single place elsewhere in the docs, so are collected here.
|
||||
|
||||
## Visual Studio (natvis)
|
||||
|
||||
The repository ships [`nlohmann_json.natvis`](https://github.com/nlohmann/json/blob/develop/nlohmann_json.natvis)
|
||||
at its root, a [Natvis](https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects)
|
||||
file that gives `json`/`ordered_json` values a friendly, key/value debugger view instead of showing raw internal
|
||||
fields, when debugging with the MSVC debug engine (`cppvsdbg`) in Visual Studio or VS Code.
|
||||
|
||||
Debug engines that wrap LLDB instead of the MSVC debug engine (for example, `codelldb` in VS Code) only have
|
||||
partial/experimental Natvis support, and commonly fall back to showing raw internal fields even with the
|
||||
`.natvis` file present. Switching to `cppvsdbg` where available, or checking your debug extension's own Natvis
|
||||
support/version, are the next things to try if this happens. There is currently no bundled LLDB-native
|
||||
pretty-printer script in this repository.
|
||||
|
||||
## GDB
|
||||
|
||||
The repository ships a [GDB Python pretty printer](https://github.com/nlohmann/json/tree/develop/tools/gdb_pretty_printer)
|
||||
under `tools/gdb_pretty_printer`, with its own usage instructions in that directory's `README.md`.
|
||||
|
||||
## Extended exception diagnostics
|
||||
|
||||
Defining [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) before including the library augments
|
||||
`type_error`/`out_of_range`-style exceptions with a JSON Pointer to the offending value, which can help pinpoint
|
||||
where in a large document a runtime error occurred. This only applies to exceptions thrown *after* a value
|
||||
exists (e.g. during element access); parse errors, which happen before any value exists to point at, are not
|
||||
covered by this mechanism -- see [Parsing and exceptions](../features/parsing/parse_exceptions.md) for how parse
|
||||
errors report their own location instead.
|
||||
@@ -291,9 +291,10 @@ A JSON Pointer array index must be a number.
|
||||
|
||||
### json.exception.parse_error.110
|
||||
|
||||
When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.
|
||||
When parsing a [binary format](../features/binary_formats/index.md), the byte vector ends before the complete value has
|
||||
been read.
|
||||
|
||||
!!! failure "Example message"
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input
|
||||
@@ -301,6 +302,9 @@ When parsing CBOR or MessagePack, the byte vector ends before the complete value
|
||||
```
|
||||
[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing UBJSON value: expected end of input; last byte: 0x5A
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.110] parse error at byte 8: syntax error while parsing BSON number: unexpected end of input
|
||||
```
|
||||
|
||||
### json.exception.parse_error.112
|
||||
|
||||
@@ -326,10 +330,17 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 15: syntax error while parsing BSON binary: byte array length cannot be negative, is -1
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 9: syntax error while parsing CBOR value: negative integer overflow
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.112] parse error at byte 5: syntax error while parsing BSON document: document size 6 does not match the number of bytes read (5)
|
||||
```
|
||||
|
||||
### json.exception.parse_error.113
|
||||
|
||||
While parsing a map key, a value that is not a string has been read.
|
||||
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
||||
string was read where one was required (for instance as a map key), or the string's length specification is invalid.
|
||||
|
||||
!!! failure "Example messages"
|
||||
|
||||
@@ -342,6 +353,9 @@ While parsing a map key, a value that is not a string has been read.
|
||||
```
|
||||
[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON char: byte after 'C' must be in range 0x00..0x7F; last byte: 0x82
|
||||
```
|
||||
```
|
||||
[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative
|
||||
```
|
||||
|
||||
### json.exception.parse_error.114
|
||||
|
||||
@@ -850,13 +864,21 @@ and this exception no longer occurs.
|
||||
|
||||
### json.exception.out_of_range.408
|
||||
|
||||
The size (following `#`) of an UBJSON array or object exceeds the maximal capacity.
|
||||
The size of an array or object in a [binary format](../features/binary_formats/index.md) exceeds the maximal capacity:
|
||||
the size following `#` for [UBJSON](../features/binary_formats/ubjson.md)/[BJData](../features/binary_formats/bjdata.md),
|
||||
or the encoded length for [CBOR](../features/binary_formats/cbor.md).
|
||||
|
||||
!!! failure "Example message"
|
||||
!!! failure "Example messages"
|
||||
|
||||
```
|
||||
excessive array size: 8658170730974374167
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.408] syntax error while parsing CBOR size: excessive array size
|
||||
```
|
||||
```
|
||||
[json.exception.out_of_range.408] syntax error while parsing CBOR size: excessive map size
|
||||
```
|
||||
|
||||
### json.exception.out_of_range.409
|
||||
|
||||
@@ -893,7 +915,23 @@ A JSON Patch `add` operation cannot be applied because the target location's par
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.12.x. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
|
||||
This exception was added in version 3.13.0. Before that, this situation hit an internal assertion (aborting the program in debug builds) or was silently ignored when assertions were disabled.
|
||||
|
||||
### json.exception.out_of_range.412
|
||||
|
||||
BSON stores the length of documents, arrays, strings, and binary values in a signed 32-bit integer. This exception is thrown when a value is too large to be described by such a length field.
|
||||
|
||||
!!! failure "Example message"
|
||||
|
||||
```
|
||||
BSON length 2147483661 exceeds maximum of 2147483647
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
This exception was added in version 3.13.0. Before that, the length was silently truncated, and
|
||||
[`to_bson`](../api/basic_json/to_bson.md) produced documents with negative length prefixes that
|
||||
[`from_bson`](../api/basic_json/from_bson.md) rejected.
|
||||
|
||||
## Further exceptions
|
||||
|
||||
|
||||
@@ -129,6 +129,29 @@ As described [above](#parse-errors-reading-non-ascii-characters), the library as
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Thread safety
|
||||
|
||||
!!! question
|
||||
|
||||
Is `basic_json` thread-safe?
|
||||
|
||||
No. `basic_json` provides no built-in synchronization, the same as `std::map` or `std::vector`. Concurrent reads of
|
||||
the same value from multiple threads are safe, as are concurrent (non-overlapping) accesses to independent `json`
|
||||
objects. However, any concurrent write to a `json` object -- or a concurrent read while another thread writes to the
|
||||
same object -- is a data race and requires external synchronization (e.g., a `std::mutex`) by the caller.
|
||||
|
||||
### Schema validation
|
||||
|
||||
!!! question
|
||||
|
||||
Does this library support JSON Schema validation?
|
||||
|
||||
Not directly, but the companion project [json-schema-validator](https://github.com/pboettch/json-schema-validator)
|
||||
builds JSON Schema (draft 4, 6, 7, and 2019-09) validation on top of this library and is a common recommendation
|
||||
for this use case.
|
||||
|
||||
## Exceptions
|
||||
|
||||
### Parsing without exceptions
|
||||
@@ -171,6 +194,27 @@ The library uses `std::numeric_limits<number_float_t>::digits10` (15 for IEEE `d
|
||||
|
||||
See [this section](../features/types/number_handling.md#number-serialization) on the library's number handling for more information.
|
||||
|
||||
### Serializing untrusted or invalid UTF-8
|
||||
|
||||
!!! question "Questions"
|
||||
|
||||
- Why does `dump()` throw when I serialize data that came from the network?
|
||||
- Is CVE-2024-34363 a vulnerability in this library?
|
||||
|
||||
Crashes reported against this library that stem from an uncaught
|
||||
[`type_error.316`](exceptions.md#jsonexceptiontype_error316) while serializing unvalidated input (e.g.,
|
||||
CVE-2024-34363) are a usage issue, not a library vulnerability:
|
||||
[`dump()`](../api/basic_json/dump.md) throws in its default `strict` mode because
|
||||
[RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-8.1) requires JSON text to be valid UTF-8.
|
||||
|
||||
The recommended pattern is to pass a non-strict [`error_handler`](../api/basic_json/error_handler_t.md) or to handle the
|
||||
exception:
|
||||
|
||||
```cpp
|
||||
// replace invalid sequences with U+FFFD instead of throwing
|
||||
const auto s = j.dump(-1, ' ', false, json::error_handler_t::replace);
|
||||
```
|
||||
|
||||
### Using JSON values with `std::format` or `fmt`
|
||||
|
||||
!!! question
|
||||
@@ -178,7 +222,7 @@ See [this section](../features/types/number_handling.md#number-serialization) on
|
||||
- Can I use `std::format("{}", j)` on a JSON value?
|
||||
- Can I use `fmt::format("{}", j)` or `fmt::print("{}", j)` (the [{fmt}](https://github.com/fmtlib/fmt) library) on a JSON value?
|
||||
|
||||
`std::format` works out of the box since version 3.12.x, as long as the standard library provides
|
||||
`std::format` works out of the box since version 3.13.0, as long as the standard library provides
|
||||
`<format>` (see [`JSON_HAS_STD_FORMAT`](../api/macros/json_has_std_format.md)); see
|
||||
[`std::formatter<basic_json>`](../api/basic_json/std_formatter.md) for details, including the `#!cpp "{:#}"`
|
||||
pretty-print spec, indent widths (`#!cpp "{:2}"`), and custom indent characters (`#!cpp "{:.>#}"`).
|
||||
|
||||
@@ -14,6 +14,5 @@ You can sponsor this library at [GitHub Sponsors](https://github.com/sponsors/nl
|
||||
- [Steve Sperandeo](https://github.com/homer6)
|
||||
- [Robert Jefe Lindstädt](https://github.com/eljefedelrodeodeljefe)
|
||||
- [Steve Wagner](https://github.com/ciroque)
|
||||
- [Lion Yang](https://github.com/LionNatsu)
|
||||
|
||||
Thanks everyone!
|
||||
|
||||
@@ -135,6 +135,31 @@ Enable CI build targets. The exact targets are used during the several CI steps
|
||||
|
||||
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
|
||||
|
||||
!!! warning "Does not apply to a pre-installed package"
|
||||
|
||||
This option only takes effect when building nlohmann/json from source as part of your own
|
||||
CMake project (e.g. via [`FetchContent`](#fetchcontent) or [`add_subdirectory`](#external)).
|
||||
It has **no effect** on a package that was already built and installed elsewhere (Homebrew,
|
||||
vcpkg, a system package, etc.) — the resulting compile definition is baked into the exported
|
||||
`nlohmann_jsonTargets.cmake` at install time, and `set(JSON_Diagnostics ON)` before
|
||||
`find_package()` does not change it (verified against the Homebrew-installed package: the
|
||||
exported target still carries a fixed `$<$<BOOL:OFF>:JSON_DIAGNOSTICS=1>`, regardless of any
|
||||
variable set in the consuming project).
|
||||
|
||||
To enable extended diagnostics for a pre-installed package, override the imported target's
|
||||
property directly after `find_package()`:
|
||||
|
||||
```cmake
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "JSON_DIAGNOSTICS=1")
|
||||
```
|
||||
|
||||
This only works cleanly when your project is the sole consumer of that imported target. If
|
||||
nlohmann_json is pulled in from more than one place in your dependency graph with different
|
||||
`JSON_DIAGNOSTICS` values, you may see a `"JSON_DIAGNOSTICS" redefined` compiler error, since
|
||||
conflicting `-D` flags can end up on the same compile command line.
|
||||
|
||||
### `JSON_Diagnostic_Positions`
|
||||
|
||||
Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default.
|
||||
@@ -181,3 +206,15 @@ Execute the test suite with [Valgrind](https://valgrind.org). This option is `OF
|
||||
|
||||
Build the experimental [C++ module](../features/modules.md) `nlohmann.json` (requires CMake 3.28 or later and C++20).
|
||||
This option is `OFF` by default.
|
||||
|
||||
A consuming project must link the dedicated `nlohmann_json_modules` CMake target (not just
|
||||
`nlohmann_json::nlohmann_json`) for `import nlohmann.json;` to resolve:
|
||||
|
||||
```cmake
|
||||
set(NLOHMANN_JSON_BUILD_MODULES ON)
|
||||
add_subdirectory(path/to/json)
|
||||
|
||||
add_executable(myproject main.cpp)
|
||||
target_link_libraries(myproject PRIVATE nlohmann_json_modules)
|
||||
target_compile_definitions(myproject PRIVATE NLOHMANN_JSON_BUILD_MODULES)
|
||||
```
|
||||
|
||||
@@ -930,6 +930,21 @@ If you are using [CocoaPods](https://cocoapods.org), you can use the library by
|
||||
to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues
|
||||
[here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open).
|
||||
|
||||
## npm
|
||||
|
||||
This project does not publish an official [npm](https://www.npmjs.com) package. The npm package
|
||||
[`nlohmann-json`](https://www.npmjs.com/package/nlohmann-json) (or similarly named packages) is not maintained or
|
||||
endorsed by this project. Use one of the package managers listed above, or integrate the single header directly.
|
||||
|
||||
## ESP-IDF and PlatformIO
|
||||
|
||||
There is no official package published to the [ESP-IDF Component Registry](https://components.espressif.com) or the
|
||||
[PlatformIO Registry](https://registry.platformio.org). A community-maintained fork,
|
||||
[Johboh/nlohmann-json](https://github.com/Johboh/nlohmann-json), publishes this library to both registries on each
|
||||
new release and can be used as an unofficial component/package for ESP-IDF and PlatformIO projects. As the library
|
||||
is header-only, it can otherwise be used directly by adding its `include/` directory to your component's/project's
|
||||
include paths, like any other integration method described on this page.
|
||||
|
||||

|
||||
|
||||
!!! warning
|
||||
|
||||
@@ -56,6 +56,7 @@ nav:
|
||||
- home/architecture.md
|
||||
- home/customers.md
|
||||
- home/sponsors.md
|
||||
- home/debugging.md
|
||||
- Features:
|
||||
- features/index.md
|
||||
- features/arbitrary_types.md
|
||||
|
||||
@@ -2,7 +2,7 @@ wheel==0.47.0
|
||||
|
||||
mkdocs==1.6.1 # documentation framework
|
||||
mkdocs-git-revision-date-localized-plugin==1.5.3 # plugin "git-revision-date-localized"
|
||||
mkdocs-material==9.7.6 # theme for mkdocs
|
||||
mkdocs-material==9.7.7 # theme for mkdocs
|
||||
mkdocs-material-extensions==1.3.1 # extensions
|
||||
mkdocs-minify-plugin==0.8.0 # plugin "minify"
|
||||
mkdocs-redirects==1.2.3 # plugin "redirects"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <unordered_map> // unordered_map
|
||||
#include <utility> // pair, declval
|
||||
#include <valarray> // valarray
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
@@ -332,6 +333,7 @@ template < typename BasicJsonType, typename ConstructibleArrayType,
|
||||
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
||||
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!is_basic_json<ConstructibleArrayType>::value,
|
||||
int > = 0 >
|
||||
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
||||
@@ -377,6 +379,25 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
|
||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||
int > = 0 >
|
||||
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
||||
{
|
||||
if (j.is_binary())
|
||||
{
|
||||
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
|
||||
}
|
||||
else if (j.is_array())
|
||||
{
|
||||
from_json_array_impl(j, bin, priority_tag<3> {});
|
||||
}
|
||||
else
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||
|
||||
@@ -177,8 +177,11 @@ struct external_constructor<value_t::array>
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
|
||||
int > = 0 >
|
||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
, int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
{
|
||||
using std::begin;
|
||||
@@ -218,6 +221,25 @@ struct external_constructor<value_t::array>
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
// std::ranges does not work properly on MinGW due to incomplete C++20 support
|
||||
// see https://github.com/nlohmann/json/issues/4916
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -355,19 +377,44 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
||||
!is_basic_json<CompatibleArrayType>::value,
|
||||
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!is_basic_json<CompatibleArrayType>::value
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
,
|
||||
int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, arr);
|
||||
}
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
template < typename BasicJsonType, typename T,
|
||||
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
|
||||
&& !is_compatible_string_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
||||
&& !is_compatible_object_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
||||
&& !is_basic_json<std::remove_cvref_t<T>>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, T && arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, std::forward<T>(arr));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
|
||||
{
|
||||
external_constructor<value_t::binary>::construct(j, bin);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||
int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
|
||||
{
|
||||
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
|
||||
@@ -163,14 +163,44 @@ class binary_reader
|
||||
// BSON //
|
||||
//////////
|
||||
|
||||
/*!
|
||||
@brief Validate a BSON document's declared size against the bytes read.
|
||||
|
||||
A BSON document starts with an int32 that counts its own total length in
|
||||
bytes, including that prefix and the trailing 0x00. The reader is driven
|
||||
by the terminator rather than the declared length, so without this check a
|
||||
nested document could declare a length that disagrees with where its
|
||||
terminator actually falls and quietly hand the bytes in between to the
|
||||
enclosing document. A well-formed document is at least 5 bytes (the prefix
|
||||
plus the terminator); the equality also rejects those impossible sizes,
|
||||
since at least 5 bytes are always consumed.
|
||||
|
||||
@param[in] document_start value of chars_read before the size prefix
|
||||
@param[in] document_size the declared document size
|
||||
@return whether the declared size matches the number of bytes read
|
||||
*/
|
||||
bool check_bson_document_size(const std::size_t document_start, const std::int32_t document_size)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(document_size < 0 || static_cast<std::size_t>(document_size) != chars_read - document_start))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
|
||||
exception_message(input_format_t::bson, concat("document size ", std::to_string(document_size), " does not match the number of bytes read (", std::to_string(chars_read - document_start), ")"), "document"), nullptr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Reads in a BSON-object and passes it to the SAX-parser.
|
||||
@return whether a valid BSON-value was passed to the SAX parser
|
||||
*/
|
||||
bool parse_bson_internal()
|
||||
{
|
||||
const std::size_t document_start = chars_read;
|
||||
std::int32_t document_size{};
|
||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
||||
if (!get_number<std::int32_t, true>(input_format_t::bson, document_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
|
||||
{
|
||||
@@ -182,6 +212,11 @@ class binary_reader
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(document_start, document_size)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return sax->end_object();
|
||||
}
|
||||
|
||||
@@ -255,7 +290,10 @@ class binary_reader
|
||||
|
||||
// All BSON binary values have a subtype
|
||||
std::uint8_t subtype{};
|
||||
get_number<std::uint8_t>(input_format_t::bson, subtype);
|
||||
if (JSON_HEDLEY_UNLIKELY(!get_number<std::uint8_t>(input_format_t::bson, subtype)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
result.set_subtype(subtype);
|
||||
|
||||
return get_binary(input_format_t::bson, len, result);
|
||||
@@ -308,7 +346,8 @@ class binary_reader
|
||||
|
||||
case 0x08: // boolean
|
||||
{
|
||||
return sax->boolean(get() != 0);
|
||||
std::uint8_t value{};
|
||||
return get_number<std::uint8_t>(input_format_t::bson, value) && sax->boolean(value != 0);
|
||||
}
|
||||
|
||||
case 0x0A: // null
|
||||
@@ -397,8 +436,12 @@ class binary_reader
|
||||
*/
|
||||
bool parse_bson_array()
|
||||
{
|
||||
const std::size_t document_start = chars_read;
|
||||
std::int32_t document_size{};
|
||||
get_number<std::int32_t, true>(input_format_t::bson, document_size);
|
||||
if (!get_number<std::int32_t, true>(input_format_t::bson, document_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
|
||||
{
|
||||
@@ -410,6 +453,11 @@ class binary_reader
|
||||
return false;
|
||||
}
|
||||
|
||||
if (JSON_HEDLEY_UNLIKELY(!check_bson_document_size(document_start, document_size)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return sax->end_array();
|
||||
}
|
||||
|
||||
@@ -656,13 +704,15 @@ class binary_reader
|
||||
case 0x9A: // array (four-byte uint32_t for n follow)
|
||||
{
|
||||
std::uint32_t len{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "array") && get_cbor_array(size, tag_handler);
|
||||
}
|
||||
|
||||
case 0x9B: // array (eight-byte uint64_t for n follow)
|
||||
{
|
||||
std::uint64_t len{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "array") && get_cbor_array(size, tag_handler);
|
||||
}
|
||||
|
||||
case 0x9F: // array (indefinite length)
|
||||
@@ -710,13 +760,15 @@ class binary_reader
|
||||
case 0xBA: // map (four-byte uint32_t for n follow)
|
||||
{
|
||||
std::uint32_t len{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "map") && get_cbor_object(size, tag_handler);
|
||||
}
|
||||
|
||||
case 0xBB: // map (eight-byte uint64_t for n follow)
|
||||
{
|
||||
std::uint64_t len{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
|
||||
std::size_t size{};
|
||||
return get_number(input_format_t::cbor, len) && get_cbor_container_size(len, size, "map") && get_cbor_object(size, tag_handler);
|
||||
}
|
||||
|
||||
case 0xBF: // map (indefinite length)
|
||||
@@ -759,25 +811,37 @@ class binary_reader
|
||||
case 0xD8:
|
||||
{
|
||||
std::uint8_t subtype_to_ignore{};
|
||||
get_number(input_format_t::cbor, subtype_to_ignore);
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xD9:
|
||||
{
|
||||
std::uint16_t subtype_to_ignore{};
|
||||
get_number(input_format_t::cbor, subtype_to_ignore);
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xDA:
|
||||
{
|
||||
std::uint32_t subtype_to_ignore{};
|
||||
get_number(input_format_t::cbor, subtype_to_ignore);
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xDB:
|
||||
{
|
||||
std::uint64_t subtype_to_ignore{};
|
||||
get_number(input_format_t::cbor, subtype_to_ignore);
|
||||
if (!get_number(input_format_t::cbor, subtype_to_ignore))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -795,28 +859,40 @@ class binary_reader
|
||||
case 0xD8:
|
||||
{
|
||||
std::uint8_t subtype{};
|
||||
get_number(input_format_t::cbor, subtype);
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
case 0xD9:
|
||||
{
|
||||
std::uint16_t subtype{};
|
||||
get_number(input_format_t::cbor, subtype);
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
case 0xDA:
|
||||
{
|
||||
std::uint32_t subtype{};
|
||||
get_number(input_format_t::cbor, subtype);
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
case 0xDB:
|
||||
{
|
||||
std::uint64_t subtype{};
|
||||
get_number(input_format_t::cbor, subtype);
|
||||
if (!get_number(input_format_t::cbor, subtype))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
|
||||
break;
|
||||
}
|
||||
@@ -858,7 +934,7 @@ class binary_reader
|
||||
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||
|
||||
// Code from RFC 7049, Appendix D, Figure 3:
|
||||
// Code from RFC 8949, Appendix D, Figure 3:
|
||||
// As half-precision floating-point numbers were only added
|
||||
// to IEEE 754 in 2008, today's programming platforms often
|
||||
// still only have limited support for them. It is very
|
||||
@@ -871,8 +947,8 @@ class binary_reader
|
||||
{
|
||||
const int exp = (half >> 10u) & 0x1Fu;
|
||||
const unsigned int mant = half & 0x3FFu;
|
||||
JSON_ASSERT(0 <= exp&& exp <= 32);
|
||||
JSON_ASSERT(mant <= 1024);
|
||||
JSON_ASSERT(exp <= 31);
|
||||
JSON_ASSERT(mant <= 1023);
|
||||
switch (exp)
|
||||
{
|
||||
case 0:
|
||||
@@ -1107,6 +1183,31 @@ class binary_reader
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief narrow a definite CBOR array/map length to std::size_t
|
||||
|
||||
A definite length is rejected if it does not fit in std::size_t or if it
|
||||
equals detail::unknown_size(), which is reserved to mark an indefinite-
|
||||
length container and would otherwise make the length read as indefinite.
|
||||
Both cases exceed any container's max_size(), so no representable input
|
||||
is affected.
|
||||
|
||||
@param[in] len the declared length
|
||||
@param[out] result the length narrowed to std::size_t
|
||||
@param[in] context "array" or "map", for the error message
|
||||
@return whether the length is usable
|
||||
*/
|
||||
bool get_cbor_container_size(const std::uint64_t len, std::size_t& result, const char* context)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::size_t>(len) || len == detail::unknown_size()))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
|
||||
exception_message(input_format_t::cbor, concat("excessive ", context, " size"), "size"), nullptr));
|
||||
}
|
||||
result = conditional_static_cast<std::size_t>(len);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] len the length of the array or detail::unknown_size() for an
|
||||
array of indefinite size
|
||||
@@ -1846,6 +1947,29 @@ class binary_reader
|
||||
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reject a negative UBJSON/BJData string length
|
||||
|
||||
String and key lengths are written with signed integer markers (i, I, l,
|
||||
L). A negative value is malformed; without this check get_string() would
|
||||
silently treat it as an empty string and leave the following bytes to be
|
||||
misread as the next value. This mirrors the non-negative check the
|
||||
optimized-container count path already performs in get_ubjson_size_value.
|
||||
|
||||
@param[in] len the string length read from the input
|
||||
@return whether the length is valid (non-negative)
|
||||
*/
|
||||
template<typename NumberType>
|
||||
bool check_ubjson_string_length(const NumberType len)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||
{
|
||||
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||
exception_message(input_format, "string length must not be negative", "string"), nullptr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief reads a UBJSON string
|
||||
|
||||
@@ -1883,25 +2007,25 @@ class binary_reader
|
||||
case 'i':
|
||||
{
|
||||
std::int8_t len{};
|
||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||
}
|
||||
|
||||
case 'I':
|
||||
{
|
||||
std::int16_t len{};
|
||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||
}
|
||||
|
||||
case 'l':
|
||||
{
|
||||
std::int32_t len{};
|
||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||
}
|
||||
|
||||
case 'L':
|
||||
{
|
||||
std::int64_t len{};
|
||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||
}
|
||||
|
||||
case 'u':
|
||||
@@ -2423,7 +2547,7 @@ class binary_reader
|
||||
const auto byte1 = static_cast<unsigned char>(byte1_raw);
|
||||
const auto byte2 = static_cast<unsigned char>(byte2_raw);
|
||||
|
||||
// Code from RFC 7049, Appendix D, Figure 3:
|
||||
// Code from RFC 8949, Appendix D, Figure 3:
|
||||
// As half-precision floating-point numbers were only added
|
||||
// to IEEE 754 in 2008, today's programming platforms often
|
||||
// still only have limited support for them. It is very
|
||||
@@ -2436,8 +2560,8 @@ class binary_reader
|
||||
{
|
||||
const int exp = (half >> 10u) & 0x1Fu;
|
||||
const unsigned int mant = half & 0x3FFu;
|
||||
JSON_ASSERT(0 <= exp&& exp <= 32);
|
||||
JSON_ASSERT(mant <= 1024);
|
||||
JSON_ASSERT(exp <= 31);
|
||||
JSON_ASSERT(mant <= 1023);
|
||||
switch (exp)
|
||||
{
|
||||
case 0:
|
||||
@@ -2754,7 +2878,17 @@ class binary_reader
|
||||
case token_type::value_unsigned:
|
||||
return sax->number_unsigned(number_lexer.get_number_unsigned());
|
||||
case token_type::value_float:
|
||||
return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
|
||||
{
|
||||
const auto parsed_float = number_lexer.get_number_float();
|
||||
if (JSON_HEDLEY_UNLIKELY(!std::isfinite(parsed_float)))
|
||||
{
|
||||
return sax->parse_error(
|
||||
chars_read,
|
||||
number_string,
|
||||
out_of_range::create(406, concat("number overflow parsing '", number_string, '\''), nullptr));
|
||||
}
|
||||
return sax->number_float(parsed_float, std::move(number_string));
|
||||
}
|
||||
case token_type::uninitialized:
|
||||
case token_type::literal_true:
|
||||
case token_type::literal_false:
|
||||
|
||||
@@ -101,6 +101,9 @@ class input_stream_adapter
|
||||
// maintain ifstream flags, except eof
|
||||
if (is != nullptr)
|
||||
{
|
||||
// consume the character last returned by get_character() unless it
|
||||
// was given back with release_lookahead()
|
||||
commit_lookahead();
|
||||
is->clear(is->rdstate() & std::ios::eofbit);
|
||||
}
|
||||
}
|
||||
@@ -115,29 +118,60 @@ class input_stream_adapter
|
||||
input_stream_adapter& operator=(input_stream_adapter&&) = delete;
|
||||
|
||||
input_stream_adapter(input_stream_adapter&& rhs) noexcept
|
||||
: is(rhs.is), sb(rhs.sb)
|
||||
: is(rhs.is), sb(rhs.sb), lookahead(rhs.lookahead)
|
||||
{
|
||||
rhs.is = nullptr;
|
||||
rhs.sb = nullptr;
|
||||
rhs.lookahead = false;
|
||||
}
|
||||
|
||||
// Whether the character last returned by get_character() can be given back
|
||||
// to the input with release_lookahead().
|
||||
static constexpr bool supports_lookahead = true;
|
||||
|
||||
// std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
|
||||
// ensure that std::char_traits<char>::eof() and the character 0xFF do not
|
||||
// end up as the same value, e.g., 0xFFFFFFFF.
|
||||
//
|
||||
// The character is peeked rather than consumed: it is only stepped over
|
||||
// once the next character is requested, or when the adapter is destroyed.
|
||||
// Until then, release_lookahead() can leave it in the input.
|
||||
std::char_traits<char>::int_type get_character()
|
||||
{
|
||||
auto res = sb->sbumpc();
|
||||
if (lookahead)
|
||||
{
|
||||
// step over the character returned by the previous call
|
||||
sb->sbumpc();
|
||||
}
|
||||
|
||||
auto res = sb->sgetc();
|
||||
// set eof manually, as we don't use the istream interface.
|
||||
if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
|
||||
{
|
||||
// there is nothing to step over next time
|
||||
lookahead = false;
|
||||
is->clear(is->rdstate() | std::ios::eofbit);
|
||||
}
|
||||
else
|
||||
{
|
||||
lookahead = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Leave the character last returned by get_character() in the input, so
|
||||
// that the next read from the stream - by this adapter or by the caller
|
||||
// once parsing is done - sees it again. Unlike putting a consumed
|
||||
// character back, this cannot fail.
|
||||
void release_lookahead() noexcept
|
||||
{
|
||||
lookahead = false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::size_t get_elements(T* dest, std::size_t count = 1)
|
||||
{
|
||||
commit_lookahead();
|
||||
auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
|
||||
if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
|
||||
{
|
||||
@@ -147,15 +181,31 @@ class input_stream_adapter
|
||||
}
|
||||
|
||||
private:
|
||||
// Step over the character last returned by get_character(). The character
|
||||
// has already been peeked successfully, so for every streambuf with a get
|
||||
// area this is a pointer increment that cannot fail.
|
||||
void commit_lookahead()
|
||||
{
|
||||
if (lookahead)
|
||||
{
|
||||
lookahead = false;
|
||||
sb->sbumpc();
|
||||
}
|
||||
}
|
||||
|
||||
/// the associated input stream
|
||||
std::istream* is = nullptr;
|
||||
std::streambuf* sb = nullptr;
|
||||
/// whether get_character() peeked a character that is not consumed yet
|
||||
bool lookahead = false;
|
||||
};
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
// General-purpose iterator-based adapter. It might not be as fast as
|
||||
// theoretically possible for some containers, but it is extremely versatile.
|
||||
template<typename IteratorType>
|
||||
// SentinelType defaults to IteratorType for backward compatibility, but may
|
||||
// be a different type (e.g., a C++20 sentinel or counted_iterator).
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
class iterator_input_adapter
|
||||
{
|
||||
public:
|
||||
@@ -169,9 +219,10 @@ class iterator_input_adapter
|
||||
// in wide_string_input_adapter, which does not expose this).
|
||||
static constexpr bool supports_seek =
|
||||
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
||||
&& std::is_same<IteratorType, SentinelType>::value
|
||||
&& sizeof(char_type) == 1;
|
||||
|
||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
||||
iterator_input_adapter(IteratorType first, SentinelType last)
|
||||
: begin(first), current(std::move(first)), end(std::move(last))
|
||||
{}
|
||||
|
||||
@@ -216,19 +267,30 @@ class iterator_input_adapter
|
||||
private:
|
||||
// whether IteratorType refers to a contiguous range and therefore supports
|
||||
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
||||
// library iterators such as those of std::vector and std::string)
|
||||
// library iterators such as those of std::vector and std::string).
|
||||
// Computing the available element count needs either same-type iterators
|
||||
// (plain std::distance) or, in C++20, a sized sentinel (std::ranges::distance),
|
||||
// e.g. std::counted_iterator paired with std::default_sentinel_t.
|
||||
static constexpr bool iterator_is_contiguous =
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
std::contiguous_iterator<IteratorType> ||
|
||||
(std::is_same<IteratorType, SentinelType>::value || std::sized_sentinel_for<SentinelType, IteratorType>)
|
||||
&& (std::contiguous_iterator<IteratorType> || std::is_pointer<IteratorType>::value);
|
||||
#else
|
||||
std::is_same<IteratorType, SentinelType>::value && std::is_pointer<IteratorType>::value;
|
||||
#endif
|
||||
std::is_pointer<IteratorType>::value;
|
||||
|
||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||
template<class T>
|
||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
||||
{
|
||||
const std::size_t wanted = count * sizeof(T);
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
// std::ranges::distance also supports sized sentinels of a different
|
||||
// type (e.g. std::counted_iterator + std::default_sentinel_t)
|
||||
const std::size_t available = static_cast<std::size_t>(std::ranges::distance(current, end)) * sizeof(char_type);
|
||||
#else
|
||||
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
||||
#endif
|
||||
const std::size_t copied = (std::min)(wanted, available);
|
||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||
{
|
||||
@@ -267,7 +329,7 @@ class iterator_input_adapter
|
||||
|
||||
IteratorType begin;
|
||||
IteratorType current;
|
||||
IteratorType end;
|
||||
SentinelType end;
|
||||
|
||||
template<typename BaseInputAdapter, size_t T>
|
||||
friend struct wide_string_input_helper;
|
||||
@@ -381,17 +443,30 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||
// A supplementary code point is a high surrogate (0xD800..0xDBFF)
|
||||
// followed by a low surrogate (0xDC00..0xDFFF). A lone low
|
||||
// surrogate, a high surrogate at the end of the input, or a high
|
||||
// surrogate followed by any other unit is malformed UTF-16. In
|
||||
// that case the offending unit is passed through unchanged so the
|
||||
// UTF-8 decoder rejects it, matching how \uXXXX surrogate escapes
|
||||
// are handled in the lexer.
|
||||
bool valid_pair = false;
|
||||
if (wc <= 0xDBFF && JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||
{
|
||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||
utf8_bytes_filled = 4;
|
||||
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||
{
|
||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||
utf8_bytes_filled = 4;
|
||||
valid_pair = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!valid_pair)
|
||||
{
|
||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||
utf8_bytes_filled = 1;
|
||||
@@ -430,7 +505,7 @@ class wide_string_input_adapter
|
||||
|
||||
// parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
|
||||
template<class T>
|
||||
std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||
JSON_HEDLEY_NO_RETURN std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
|
||||
{
|
||||
JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
|
||||
}
|
||||
@@ -453,19 +528,54 @@ class wide_string_input_adapter
|
||||
std::size_t utf8_bytes_filled = 0;
|
||||
};
|
||||
|
||||
template<typename IteratorType, typename Enable = void>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
|
||||
struct iterator_input_adapter_factory
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = SentinelType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
static adapter_type create(IteratorType first, SentinelType last)
|
||||
{
|
||||
return adapter_type(std::move(first), std::move(last));
|
||||
}
|
||||
};
|
||||
|
||||
// Detection: whether IteratorType and SentinelType can be compared with !=
|
||||
template<typename IteratorType, typename SentinelType, typename = void>
|
||||
struct can_compare_ne_impl : std::false_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_impl < IteratorType, SentinelType,
|
||||
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
|
||||
: std::true_type {};
|
||||
|
||||
// Workaround for reversed operator order
|
||||
template<typename IteratorType, typename SentinelType, typename = void>
|
||||
struct can_compare_ne_reversed : std::false_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_reversed < IteratorType, SentinelType,
|
||||
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_either_order : std::integral_constant < bool,
|
||||
can_compare_ne_impl<IteratorType, SentinelType>::value ||
|
||||
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
|
||||
|
||||
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
|
||||
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
|
||||
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
|
||||
// SFINAE the `operator!=` detection above for std::nullptr_t against
|
||||
// container/string types, which would otherwise make such calls ambiguous
|
||||
// with the compatible-input overload.
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne : std::integral_constant < bool,
|
||||
!std::is_same<SentinelType, std::nullptr_t>::value &&
|
||||
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
|
||||
|
||||
template<typename T>
|
||||
struct is_iterator_of_multibyte
|
||||
{
|
||||
@@ -476,25 +586,31 @@ struct is_iterator_of_multibyte
|
||||
};
|
||||
};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = SentinelType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
static adapter_type create(IteratorType first, SentinelType last)
|
||||
{
|
||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
||||
}
|
||||
};
|
||||
|
||||
// General purpose iterator-based input
|
||||
template<typename IteratorType>
|
||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
||||
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||
// defaults to IteratorType for the common same-type case, but may differ for
|
||||
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||
// be compared with !=.
|
||||
template < typename IteratorType, typename SentinelType = IteratorType,
|
||||
typename = typename std::enable_if <
|
||||
can_compare_ne<IteratorType, SentinelType>::value >::type >
|
||||
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
|
||||
{
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
|
||||
return factory_type::create(first, last);
|
||||
}
|
||||
|
||||
@@ -517,18 +633,18 @@ struct container_input_adapter_factory< ContainerType,
|
||||
{
|
||||
using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
|
||||
|
||||
static adapter_type create(const ContainerType& container)
|
||||
static adapter_type create(ContainerType&& container)
|
||||
{
|
||||
return input_adapter(begin(container), end(container));
|
||||
return input_adapter(begin(std::forward<ContainerType>(container)), end(std::forward<ContainerType>(container)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace container_input_adapter_factory_impl
|
||||
|
||||
template<typename ContainerType>
|
||||
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
|
||||
typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(ContainerType&& container)
|
||||
{
|
||||
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
|
||||
return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(std::forward<ContainerType>(container));
|
||||
}
|
||||
|
||||
// specialization for std::string
|
||||
|
||||
@@ -626,14 +626,7 @@ class json_sax_dom_callback_parser
|
||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
|
||||
{
|
||||
// remove discarded value
|
||||
for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
|
||||
{
|
||||
if (it->is_discarded())
|
||||
{
|
||||
ref_stack.back()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
remove_discarded_value(*ref_stack.back());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -674,8 +667,9 @@ class json_sax_dom_callback_parser
|
||||
bool end_array()
|
||||
{
|
||||
bool keep = true;
|
||||
const bool stored = ref_stack.back() != nullptr;
|
||||
|
||||
if (ref_stack.back())
|
||||
if (stored)
|
||||
{
|
||||
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
|
||||
if (keep)
|
||||
@@ -709,9 +703,19 @@ class json_sax_dom_callback_parser
|
||||
keep_stack.pop_back();
|
||||
|
||||
// remove discarded value
|
||||
if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
|
||||
if (!ref_stack.empty() && ref_stack.back())
|
||||
{
|
||||
ref_stack.back()->m_data.m_value.array->pop_back();
|
||||
if (!keep && ref_stack.back()->is_array())
|
||||
{
|
||||
ref_stack.back()->m_data.m_value.array->pop_back();
|
||||
}
|
||||
else if ((!keep || !stored) && ref_stack.back()->is_object())
|
||||
{
|
||||
// the array is either still stored under its key or was never
|
||||
// stored, leaving the placeholder key() wrote; both show up as
|
||||
// a discarded member of the parent object
|
||||
remove_discarded_value(*ref_stack.back());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -801,6 +805,19 @@ class json_sax_dom_callback_parser
|
||||
}
|
||||
#endif
|
||||
|
||||
/// remove the discarded value the callback rejected from its parent
|
||||
static void remove_discarded_value(BasicJsonType& parent)
|
||||
{
|
||||
for (auto it = parent.begin(); it != parent.end(); ++it)
|
||||
{
|
||||
if (it->is_discarded())
|
||||
{
|
||||
parent.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@param[in] v value to add to the JSON value we build during parsing
|
||||
@param[in] skip_callback whether we should skip calling the callback
|
||||
@@ -841,6 +858,18 @@ class json_sax_dom_callback_parser
|
||||
// do not handle this value if we just learnt it shall be discarded
|
||||
if (!keep)
|
||||
{
|
||||
// if the value was to become an object member, key() already
|
||||
// stored a placeholder for it that has to be removed again
|
||||
if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_object())
|
||||
{
|
||||
JSON_ASSERT(!key_keep_stack.empty());
|
||||
const bool placeholder_stored = key_keep_stack.back();
|
||||
key_keep_stack.pop_back();
|
||||
if (placeholder_stored)
|
||||
{
|
||||
remove_discarded_value(*ref_stack.back());
|
||||
}
|
||||
}
|
||||
return {false, nullptr};
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,24 @@ constexpr bool input_adapter_supports_seek(std::false_type /*detected*/)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect whether an input adapter reads with one character of lookahead that
|
||||
// can be left in the input (see input_stream_adapter::supports_lookahead),
|
||||
// detected like supports_seek above.
|
||||
template<typename InputAdapterType>
|
||||
using detect_supports_lookahead = decltype(InputAdapterType::supports_lookahead);
|
||||
|
||||
template<typename InputAdapterType>
|
||||
constexpr bool input_adapter_supports_lookahead(std::true_type /*detected*/)
|
||||
{
|
||||
return InputAdapterType::supports_lookahead;
|
||||
}
|
||||
|
||||
template<typename InputAdapterType>
|
||||
constexpr bool input_adapter_supports_lookahead(std::false_type /*detected*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief lexical analysis
|
||||
|
||||
@@ -146,6 +164,12 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
static constexpr bool lazy_token_string =
|
||||
input_adapter_supports_seek<InputAdapterType>(is_detected<detect_supports_seek, InputAdapterType> {});
|
||||
|
||||
/// whether a simulated unget can be passed on to the input adapter, which
|
||||
/// then leaves the character in the input; see
|
||||
/// input_adapter_supports_lookahead
|
||||
static constexpr bool can_release_lookahead =
|
||||
input_adapter_supports_lookahead<InputAdapterType>(is_detected<detect_supports_lookahead, InputAdapterType> {});
|
||||
|
||||
public:
|
||||
using token_type = typename lexer_base<BasicJsonType>::token_type;
|
||||
|
||||
@@ -1456,6 +1480,21 @@ scan_number_done:
|
||||
uncapture_char(std::integral_constant<bool, lazy_token_string> {});
|
||||
}
|
||||
|
||||
/// adapter without lookahead: nothing to do (see release_lookahead)
|
||||
void release_lookahead_impl(std::false_type /*can_release*/) const noexcept {}
|
||||
|
||||
/// adapter with lookahead: leave the character in the input instead
|
||||
void release_lookahead_impl(std::true_type /*can_release*/)
|
||||
{
|
||||
if (next_unget)
|
||||
{
|
||||
// the character is read from the input again rather than replayed
|
||||
// from current, so the adapter must not step over it
|
||||
next_unget = false;
|
||||
ia.release_lookahead();
|
||||
}
|
||||
}
|
||||
|
||||
/// seekable adapter: nothing was captured, so nothing to undo
|
||||
void uncapture_char(std::true_type /*lazy*/) const noexcept {}
|
||||
|
||||
@@ -1519,6 +1558,29 @@ scan_number_done:
|
||||
return position;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief pass a pending simulated unget on to the input
|
||||
|
||||
unget() only rewinds the lexer's own bookkeeping, so the character that
|
||||
terminated the last token (e.g. the character after a number) would still
|
||||
be stepped over when the input adapter is done. Callers that hand the
|
||||
input back to the user afterwards - operator>> and non-strict sax_parse -
|
||||
call this once when scanning is done, so that the input is positioned
|
||||
right after the value.
|
||||
|
||||
Adapters without lookahead (see input_adapter_supports_lookahead) are not
|
||||
handed back to the user, so this is a no-op for them.
|
||||
|
||||
Scanning may continue after this call: @a next_unget is cleared, and the
|
||||
character is read from the input again instead of being replayed from
|
||||
@a current. A pending unget of EOF needs no special case, because reaching
|
||||
EOF leaves no lookahead to release.
|
||||
*/
|
||||
void release_lookahead()
|
||||
{
|
||||
release_lookahead_impl(std::integral_constant<bool, can_release_lookahead> {});
|
||||
}
|
||||
|
||||
/// seekable adapter: rebuild the last read token from the input on demand
|
||||
const std::vector<char_type>& collect_token_chars(std::vector<char_type>& out, std::true_type /*lazy*/) const
|
||||
{
|
||||
|
||||
@@ -99,8 +99,14 @@ class parser
|
||||
json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
if (!strict)
|
||||
{
|
||||
// the caller keeps using the input: position it right after
|
||||
// the value by leaving the character that terminated it
|
||||
m_lexer.release_lookahead();
|
||||
}
|
||||
// in strict mode, input must be completely read
|
||||
if (strict && (get_token() != token_type::end_of_input))
|
||||
else if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
sdp.parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
@@ -127,8 +133,13 @@ class parser
|
||||
json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
|
||||
sax_parse_internal(&sdp);
|
||||
|
||||
if (!strict)
|
||||
{
|
||||
// see above
|
||||
m_lexer.release_lookahead();
|
||||
}
|
||||
// in strict mode, input must be completely read
|
||||
if (strict && (get_token() != token_type::end_of_input))
|
||||
else if (get_token() != token_type::end_of_input)
|
||||
{
|
||||
sdp.parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
@@ -165,8 +176,14 @@ class parser
|
||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||
const bool result = sax_parse_internal(sax);
|
||||
|
||||
if (result && !strict)
|
||||
{
|
||||
// the caller keeps using the input: position it right after the
|
||||
// value by leaving the character that terminated it
|
||||
m_lexer.release_lookahead();
|
||||
}
|
||||
// strict mode: next byte must be EOF
|
||||
if (result && strict && (get_token() != token_type::end_of_input))
|
||||
else if (result && strict && (get_token() != token_type::end_of_input))
|
||||
{
|
||||
return sax->parse_error(m_lexer.get_position(),
|
||||
m_lexer.get_token_string(),
|
||||
|
||||
@@ -146,6 +146,11 @@
|
||||
#define JSON_HAS_RANGES 0
|
||||
#elif defined(_LIBCPP_VERSION) && _LIBCPP_VERSION < 160000
|
||||
#define JSON_HAS_RANGES 0
|
||||
// nvcc CUDA 12.0/12.1 chokes on the enable_borrowed_range variable-template
|
||||
// syntax when compiling as CUDA source; fixed in CUDA 12.2 (issue #3907)
|
||||
#elif defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) && __CUDACC_VER_MAJOR__ == 12 \
|
||||
&& defined(__CUDACC_VER_MINOR__) && (__CUDACC_VER_MINOR__ == 0 || __CUDACC_VER_MINOR__ == 1)
|
||||
#define JSON_HAS_RANGES 0
|
||||
#elif defined(__cpp_lib_ranges)
|
||||
#define JSON_HAS_RANGES 1
|
||||
#else
|
||||
|
||||
@@ -13,11 +13,15 @@
|
||||
#include <tuple> // tuple
|
||||
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
||||
#include <utility> // declval
|
||||
#include <vector> // vector
|
||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||
#include <cstddef> // byte
|
||||
#endif
|
||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#include <optional> // optional
|
||||
#endif
|
||||
#include <nlohmann/detail/meta/call_std/begin.hpp>
|
||||
#include <nlohmann/detail/meta/call_std/end.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
@@ -227,7 +231,9 @@ struct char_traits<signed char> : std::char_traits<char>
|
||||
// Redefine to_int_type function
|
||||
static int_type to_int_type(char_type c) noexcept
|
||||
{
|
||||
return static_cast<int_type>(c);
|
||||
// cast via unsigned char: sign-extending a negative char_type would make
|
||||
// byte 0xFF indistinguishable from eof()
|
||||
return static_cast<int_type>(static_cast<unsigned char>(c));
|
||||
}
|
||||
|
||||
static char_type to_char_type(int_type i) noexcept
|
||||
@@ -450,6 +456,51 @@ struct is_constructible_string_type
|
||||
value_type_t, laundered_type >>::value;
|
||||
};
|
||||
|
||||
// Forward declarations: iteration_proxy.hpp includes this file, so we cannot
|
||||
// include it here.
|
||||
template<typename IteratorType> class iteration_proxy;
|
||||
template<typename IteratorType> class iteration_proxy_value;
|
||||
|
||||
// Identifies nlohmann's internal iteration-proxy types. These must be excluded
|
||||
// before evaluating any std::ranges concept to avoid circular constraints.
|
||||
template<typename T> struct is_iteration_proxy_type : std::false_type {};
|
||||
template<typename T> struct is_iteration_proxy_type<iteration_proxy<T>> : std::true_type {};
|
||||
template<typename T> struct is_iteration_proxy_type<iteration_proxy_value<T>> : std::true_type {};
|
||||
|
||||
// In C++26, std::optional satisfies std::ranges::view; exclude it so the
|
||||
// range-view overload does not hijack the optional serializer.
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
template<typename T> struct is_range_view_optional_type : std::false_type {};
|
||||
template<typename T> struct is_range_view_optional_type<std::optional<T>> : std::true_type {};
|
||||
#else
|
||||
template<typename T> struct is_range_view_optional_type : std::false_type {};
|
||||
#endif
|
||||
|
||||
// std::ranges does not work properly on MinGW due to incomplete C++20 support
|
||||
// see https://github.com/nlohmann/json/issues/4916
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
|
||||
// SafeToCheck guards against types that trigger circular constraints when
|
||||
// std::ranges::view<T> is evaluated on GCC 12 / libstdc++ 12:
|
||||
// - iteration_proxy / iteration_proxy_value directly
|
||||
// - views wrapping the above (e.g. owning_view<iteration_proxy<...>>)
|
||||
// - views wrapping basic_json (e.g. ref_view<json>) — same circularity
|
||||
// via json's constructors → is_compatible_array_type → here
|
||||
// nlohmann's plain range_value_t (iterator_traits-based) is safe to call
|
||||
// before any std::ranges concept is touched, so we use it for the checks.
|
||||
template < typename T, bool SafeToCheck =
|
||||
!is_iteration_proxy_type<T>::value &&
|
||||
!is_iteration_proxy_type<detected_t<range_value_t, T>>::value &&
|
||||
!is_basic_json<detected_t<range_value_t, T>>::value &&
|
||||
!is_range_view_optional_type<T>::value >
|
||||
struct is_compatible_range_view : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_compatible_range_view<T, true>
|
||||
: std::bool_constant<std::ranges::view<T>> {};
|
||||
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
|
||||
struct is_compatible_array_type_impl : std::false_type {};
|
||||
|
||||
@@ -461,13 +512,38 @@ struct is_compatible_array_type_impl <
|
||||
is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
|
||||
// special case for types like std::filesystem::path whose iterator's value_type are themselves
|
||||
// c.f. https://github.com/nlohmann/json/pull/3073
|
||||
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
|
||||
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value
|
||||
// When range-view support is enabled, std::ranges::view types (e.g. std::string_view,
|
||||
// filter_view) can match BOTH this iterator-based specialization AND the view-based one
|
||||
// below, causing ambiguity. Exclude views here so the two specializations are mutually
|
||||
// exclusive: this one handles plain iterable containers, the other handles views.
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
>>
|
||||
{
|
||||
static constexpr bool value =
|
||||
is_constructible<BasicJsonType,
|
||||
range_value_t<CompatibleArrayType>>::value;
|
||||
};
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type_impl <
|
||||
BasicJsonType, CompatibleArrayType,
|
||||
enable_if_t < is_compatible_range_view<CompatibleArrayType>::value
|
||||
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, char>::value
|
||||
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, wchar_t>::value >>
|
||||
{
|
||||
// CompatibleArrayType is a std::ranges::view here, so std::ranges::range_value_t
|
||||
// is safe and correctly handles C++20 iterators that may lack classic iterator_traits.
|
||||
static constexpr bool value =
|
||||
is_constructible<BasicJsonType,
|
||||
std::ranges::range_value_t<CompatibleArrayType>>::value;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type
|
||||
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
|
||||
@@ -559,6 +635,14 @@ template<typename BasicJsonType, typename CompatibleType>
|
||||
struct is_compatible_type
|
||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_binary_type
|
||||
{
|
||||
static constexpr bool value =
|
||||
std::is_same<typename BasicJsonType::binary_t::container_type, CompatibleArrayType>::value &&
|
||||
!std::is_same<typename BasicJsonType::binary_t::container_type, std::vector<std::uint8_t>>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleReferenceType>
|
||||
struct is_compatible_reference_type_impl
|
||||
{
|
||||
@@ -617,21 +701,35 @@ struct is_json_pointer_of<A, ::nlohmann::json_pointer<A>> : std::true_type {};
|
||||
template <typename A>
|
||||
struct is_json_pointer_of<A, ::nlohmann::json_pointer<A>&> : std::true_type {};
|
||||
|
||||
// checks if A and B are comparable using Compare functor
|
||||
// checks if A and B are comparable using Compare functor, assuming that
|
||||
// neither A nor B is a json_pointer type (that case is handled by
|
||||
// is_comparable below, which never instantiates this helper otherwise)
|
||||
template<typename Compare, typename A, typename B, typename = void>
|
||||
struct is_comparable : std::false_type {};
|
||||
struct is_comparable_no_json_pointer : std::false_type {};
|
||||
|
||||
// We exclude json_pointer here, because the checks using Compare(A, B) will
|
||||
// use json_pointer::operator string_t() which triggers a deprecation warning
|
||||
// for GCC. See https://github.com/nlohmann/json/issues/4621. The call to
|
||||
// is_json_pointer_of can be removed once the deprecated function has been
|
||||
// removed.
|
||||
template<typename Compare, typename A, typename B>
|
||||
struct is_comparable < Compare, A, B, enable_if_t < !is_json_pointer_of<A, B>::value
|
||||
&& std::is_constructible <decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>()))>::value
|
||||
struct is_comparable_no_json_pointer < Compare, A, B, enable_if_t <
|
||||
std::is_constructible <decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>()))>::value
|
||||
&& std::is_constructible <decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))>::value
|
||||
>> : std::true_type {};
|
||||
|
||||
// checks if A and B are comparable using Compare functor
|
||||
// We dispatch on is_json_pointer_of as a plain bool (rather than folding it
|
||||
// into a single enable_if_t condition together with the checks below) so
|
||||
// that the Compare(A, B) checks are only ever written - and thus only ever
|
||||
// instantiated - when A/B are not a json_pointer/string pair. Those checks
|
||||
// use json_pointer::operator string_t() (GCC, see #4621) resp. the
|
||||
// deprecated json_pointer/string operator== (Clang, see #5288), and merely
|
||||
// naming them as later operands of a plain && chain is not sufficient to
|
||||
// avoid their instantiation on all compilers, even when the first operand
|
||||
// is false. The dispatch on is_json_pointer_of can be removed once the
|
||||
// deprecated json_pointer comparison operators have been removed.
|
||||
template<typename Compare, typename A, typename B, bool = is_json_pointer_of<A, B>::value>
|
||||
struct is_comparable : std::false_type {};
|
||||
|
||||
template<typename Compare, typename A, typename B>
|
||||
struct is_comparable<Compare, A, B, false> : is_comparable_no_json_pointer<Compare, A, B> {};
|
||||
|
||||
template<typename T>
|
||||
using detect_is_transparent = typename T::is_transparent;
|
||||
|
||||
|
||||
@@ -979,13 +979,28 @@ class binary_writer
|
||||
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Checks that @a size fits into the 32-bit length field used by BSON
|
||||
@return The size as a signed 32-bit integer
|
||||
@throw out_of_range.412 if @a size exceeds the range of std::int32_t
|
||||
*/
|
||||
static std::int32_t to_bson_length(const std::size_t size)
|
||||
{
|
||||
if (JSON_HEDLEY_UNLIKELY(!value_in_range_of<std::int32_t>(size)))
|
||||
{
|
||||
JSON_THROW(out_of_range::create(412, concat("BSON length ", std::to_string(size), " exceeds maximum of ", std::to_string((std::numeric_limits<std::int32_t>::max)())), nullptr));
|
||||
}
|
||||
|
||||
return static_cast<std::int32_t>(size);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Writes the given @a element_type and @a name to the output adapter
|
||||
*/
|
||||
void write_bson_entry_header(const string_t& name,
|
||||
const std::uint8_t element_type)
|
||||
{
|
||||
oa->write_character(to_char_type(element_type)); // boolean
|
||||
oa->write_character(to_char_type(element_type));
|
||||
oa->write_characters(
|
||||
reinterpret_cast<const CharType*>(name.c_str()),
|
||||
name.size() + 1u);
|
||||
@@ -1027,7 +1042,7 @@ class binary_writer
|
||||
{
|
||||
write_bson_entry_header(name, 0x02);
|
||||
|
||||
write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
|
||||
write_number<std::int32_t>(to_bson_length(value.size() + 1ul), true);
|
||||
oa->write_characters(
|
||||
reinterpret_cast<const CharType*>(value.c_str()),
|
||||
value.size() + 1);
|
||||
@@ -1070,7 +1085,7 @@ class binary_writer
|
||||
}
|
||||
|
||||
/*!
|
||||
@return The size of the BSON-encoded unsigned integer in @a j
|
||||
@return The size of the BSON-encoded unsigned integer @a value
|
||||
*/
|
||||
static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
|
||||
{
|
||||
@@ -1083,22 +1098,22 @@ class binary_writer
|
||||
@brief Writes a BSON element with key @a name and unsigned @a value
|
||||
*/
|
||||
void write_bson_unsigned(const string_t& name,
|
||||
const BasicJsonType& j)
|
||||
const std::uint64_t value)
|
||||
{
|
||||
if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||
if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
|
||||
{
|
||||
write_bson_entry_header(name, 0x10 /* int32 */);
|
||||
write_number<std::int32_t>(static_cast<std::int32_t>(j.m_data.m_value.number_unsigned), true);
|
||||
write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
|
||||
}
|
||||
else if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||
else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
|
||||
{
|
||||
write_bson_entry_header(name, 0x12 /* int64 */);
|
||||
write_number<std::int64_t>(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned), true);
|
||||
write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
write_bson_entry_header(name, 0x11 /* uint64 */);
|
||||
write_number<std::uint64_t>(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned), true);
|
||||
write_number<std::uint64_t>(value, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1142,7 +1157,7 @@ class binary_writer
|
||||
const typename BasicJsonType::array_t& value)
|
||||
{
|
||||
write_bson_entry_header(name, 0x04); // array
|
||||
write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
|
||||
write_number<std::int32_t>(to_bson_length(calc_bson_array_size(value)), true);
|
||||
|
||||
std::size_t array_index = 0ul;
|
||||
|
||||
@@ -1162,7 +1177,7 @@ class binary_writer
|
||||
{
|
||||
write_bson_entry_header(name, 0x05);
|
||||
|
||||
write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
|
||||
write_number<std::int32_t>(to_bson_length(value.size()), true);
|
||||
write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
|
||||
|
||||
oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
|
||||
@@ -1244,7 +1259,7 @@ class binary_writer
|
||||
return write_bson_integer(name, j.m_data.m_value.number_integer);
|
||||
|
||||
case value_t::number_unsigned:
|
||||
return write_bson_unsigned(name, j);
|
||||
return write_bson_unsigned(name, j.m_data.m_value.number_unsigned);
|
||||
|
||||
case value_t::string:
|
||||
return write_bson_string(name, *j.m_data.m_value.string);
|
||||
@@ -1284,7 +1299,7 @@ class binary_writer
|
||||
*/
|
||||
void write_bson_object(const typename BasicJsonType::object_t& value)
|
||||
{
|
||||
write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
|
||||
write_number<std::int32_t>(to_bson_length(calc_bson_object_size(value)), true);
|
||||
|
||||
for (const auto& el : value)
|
||||
{
|
||||
@@ -1647,7 +1662,15 @@ class binary_writer
|
||||
std::size_t len = (value.at(key).empty() ? 0 : 1);
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
len *= static_cast<std::size_t>(el.m_data.m_value.number_unsigned);
|
||||
// a dimension is read as an unsigned value below, so anything that
|
||||
// is not a non-negative integer is rejected: a non-integer entry
|
||||
// would pun unrelated bytes as the dimension, and a negative one
|
||||
// would wrap into a nonsensical length
|
||||
if (!el.is_number_integer() || (!el.is_number_unsigned() && el.template get<std::int64_t>() < 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
len *= static_cast<std::size_t>(el.template get<std::uint64_t>());
|
||||
}
|
||||
|
||||
key = "_ArrayData_";
|
||||
@@ -1656,6 +1679,24 @@ class binary_writer
|
||||
return true;
|
||||
}
|
||||
|
||||
// every element is written below as the number kind dtype names, so it
|
||||
// has to actually be a number of that category: an element of any other
|
||||
// type would reinterpret unrelated bytes, e.g. a string's heap pointer,
|
||||
// as that number. Such an object falls back to a plain object encoding.
|
||||
// dtype names the wire type, not the storage type: whether an integer
|
||||
// is held as number_integer or number_unsigned depends on how the value
|
||||
// was built (parsing stores non-negative integers as unsigned, the C++
|
||||
// API stores int literals as signed), so both are accepted here and the
|
||||
// writes below go through get<>, which reads the member that is active.
|
||||
const bool ndarray_is_float = (dtype == 'd' || dtype == 'D');
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
if (ndarray_is_float ? !el.is_number_float() : !el.is_number_integer())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
oa->write_character('[');
|
||||
oa->write_character('$');
|
||||
oa->write_character(dtype);
|
||||
@@ -1669,70 +1710,70 @@ class binary_writer
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::uint8_t>(el.m_data.m_value.number_unsigned), true);
|
||||
write_number(static_cast<std::uint8_t>(el.template get<std::uint64_t>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'i')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::int8_t>(el.m_data.m_value.number_integer), true);
|
||||
write_number(static_cast<std::int8_t>(el.template get<std::int64_t>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'u')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::uint16_t>(el.m_data.m_value.number_unsigned), true);
|
||||
write_number(static_cast<std::uint16_t>(el.template get<std::uint64_t>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'I')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::int16_t>(el.m_data.m_value.number_integer), true);
|
||||
write_number(static_cast<std::int16_t>(el.template get<std::int64_t>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'm')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::uint32_t>(el.m_data.m_value.number_unsigned), true);
|
||||
write_number(static_cast<std::uint32_t>(el.template get<std::uint64_t>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'l')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::int32_t>(el.m_data.m_value.number_integer), true);
|
||||
write_number(static_cast<std::int32_t>(el.template get<std::int64_t>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'M')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::uint64_t>(el.m_data.m_value.number_unsigned), true);
|
||||
write_number(el.template get<std::uint64_t>(), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'L')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<std::int64_t>(el.m_data.m_value.number_integer), true);
|
||||
write_number(el.template get<std::int64_t>(), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'd')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<float>(el.m_data.m_value.number_float), true);
|
||||
write_number(static_cast<float>(el.template get<double>()), true);
|
||||
}
|
||||
}
|
||||
else if (dtype == 'D')
|
||||
{
|
||||
for (const auto& el : value.at(key))
|
||||
{
|
||||
write_number(static_cast<double>(el.m_data.m_value.number_float), true);
|
||||
write_number(el.template get<double>(), true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
+32
-39
@@ -3776,17 +3776,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return *this == basic_json(rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: not equal
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||
bool operator!=(const_reference rhs) const noexcept
|
||||
{
|
||||
if (compares_unordered(rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
/// @brief comparison: 3-way
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
|
||||
std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
|
||||
@@ -3892,10 +3881,6 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
|
||||
friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
|
||||
{
|
||||
if (compares_unordered(lhs, rhs, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@@ -4098,12 +4083,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief deserialize from a pair of character iterators
|
||||
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json parse(IteratorType first,
|
||||
IteratorType last,
|
||||
SentinelType last,
|
||||
parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -4137,10 +4123,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
||||
}
|
||||
|
||||
/// @brief check if the input is valid JSON
|
||||
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||
template<typename IteratorType>
|
||||
static bool accept(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
static bool accept(IteratorType first, SentinelType last,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
@@ -4172,11 +4159,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events
|
||||
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
||||
template<class IteratorType, class SAX>
|
||||
template<class IteratorType, class SAX, class SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
|
||||
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
|
||||
input_format_t format = input_format_t::json,
|
||||
const bool strict = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -4476,11 +4464,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in CBOR format
|
||||
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
@@ -4533,11 +4522,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format
|
||||
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -4587,11 +4577,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format
|
||||
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -4641,11 +4632,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format
|
||||
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -4671,11 +4663,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format
|
||||
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
|
||||
+713
-150
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user