mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 05:00:30 +00:00
Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168aa3723c | ||
|
|
4289a38275 | ||
|
|
73b8a6a9c8 | ||
|
|
9df8de47a5 | ||
|
|
57371b0977 | ||
|
|
11e75a5428 | ||
|
|
e400780533 | ||
|
|
2c65caa5bf | ||
|
|
38528b4a38 | ||
|
|
9cbe62efb0 | ||
|
|
471c7678cf | ||
|
|
4fabfb27f7 | ||
|
|
71b4eaf743 | ||
|
|
2790175d79 | ||
|
|
cc9fafa0e1 | ||
|
|
8d6d1264a7 | ||
|
|
d75f9ab161 | ||
|
|
c3774be4e2 | ||
|
|
91365ca3b2 | ||
|
|
605cc6c81d | ||
|
|
f012b85d06 | ||
|
|
243b6e20e3 | ||
|
|
0a0d5623be | ||
|
|
22c9ce3f21 | ||
|
|
0d2240ad1e |
@@ -0,0 +1,53 @@
|
|||||||
|
name: "Cancel runs of closed pull requests"
|
||||||
|
|
||||||
|
# The concurrency groups of the other workflows cancel superseded runs when a
|
||||||
|
# pull request gets new commits, but nothing stops the runs of its last commit
|
||||||
|
# once the pull request is merged or closed. They then keep the runners busy
|
||||||
|
# for hours while the queue of the open pull requests waits.
|
||||||
|
#
|
||||||
|
# pull_request_target is needed to get a token that can cancel runs for pull
|
||||||
|
# requests from forks. This is safe because the workflow never checks out or
|
||||||
|
# runs code from the pull request; it only calls the API.
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cancel:
|
||||||
|
permissions:
|
||||||
|
actions: write
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Harden Runner
|
||||||
|
uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1
|
||||||
|
with:
|
||||||
|
egress-policy: audit
|
||||||
|
|
||||||
|
- name: Cancel unfinished runs of the pull request's head commit
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
SELF: ${{ github.run_id }}
|
||||||
|
# only runs triggered by the pull request: when a branch is pushed to
|
||||||
|
# develop directly, its push runs share the head commit
|
||||||
|
run: |
|
||||||
|
gh api --paginate "repos/$GH_REPO/actions/runs?head_sha=$HEAD_SHA&per_page=100" \
|
||||||
|
--jq ".workflow_runs[]
|
||||||
|
| select(.status != \"completed\" and .id != $SELF)
|
||||||
|
| select(.event == \"pull_request\" or .event == \"pull_request_target\")
|
||||||
|
| \"\(.id) \(.name)\"" |
|
||||||
|
while read -r id name; do
|
||||||
|
echo "Cancelling run $id ($name)"
|
||||||
|
# a run may finish between listing and cancelling; that is not an error
|
||||||
|
gh run cancel "$id" || true
|
||||||
|
done
|
||||||
@@ -2,16 +2,23 @@ name: "Check amalgamation"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
# also check develop itself: a PR can be merged before its own run of this
|
||||||
|
# workflow completes (e.g. while it is still queued), leaving single_include
|
||||||
|
# stale on develop without any failing check
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.ref || github.run_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
save:
|
save:
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
@@ -43,11 +50,11 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: Checkout pull request
|
- name: Checkout pull request or pushed commit
|
||||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
path: main
|
path: main
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Checkout tools
|
- name: Checkout tools
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
comment:
|
comment:
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
|
# push runs on develop have no PR to comment on (and no "pr" artifact)
|
||||||
|
if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event == 'pull_request' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -31,6 +31,48 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --target ci_test_gcc
|
run: cmake --build build --target ci_test_gcc
|
||||||
|
|
||||||
|
ci_meson_install:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Get latest CMake and ninja
|
||||||
|
uses: lukka/get-cmake@fffaaafeea488556c2c12dad60690008bc1caacb # v4.4.2
|
||||||
|
- name: Check that Meson and pkg-config offer the CMake options
|
||||||
|
run: make check_build_options
|
||||||
|
- name: Install Meson
|
||||||
|
run: pip install meson
|
||||||
|
- name: Install with Meson
|
||||||
|
run: |
|
||||||
|
meson setup build-meson --prefix=${{ github.workspace }}/install
|
||||||
|
meson install -C build-meson
|
||||||
|
- name: Use the installed package with find_package
|
||||||
|
run: |
|
||||||
|
cmake -S tests/cmake_import/project -B build-import -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install
|
||||||
|
cmake --build build-import
|
||||||
|
- name: Install with Meson and non-default options
|
||||||
|
run: |
|
||||||
|
meson setup build-meson-options --prefix=${{ github.workspace }}/install-options -DMultipleHeaders=true -DDiagnostics=true -DGlobalUDLs=false -DDisableTupleReferenceConversion=true
|
||||||
|
meson install -C build-meson-options
|
||||||
|
- name: Check that the options reach the installed files
|
||||||
|
run: |
|
||||||
|
test -d install-options/include/nlohmann/detail
|
||||||
|
cflags=$(PKG_CONFIG_PATH=${{ github.workspace }}/install-options/share/pkgconfig pkg-config --cflags nlohmann_json)
|
||||||
|
echo "$cflags"
|
||||||
|
echo "$cflags" | grep -q -- '-DJSON_DIAGNOSTICS=1'
|
||||||
|
echo "$cflags" | grep -q -- '-DJSON_USE_GLOBAL_UDLS=0'
|
||||||
|
echo "$cflags" | grep -q -- '-DJSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1'
|
||||||
|
grep -q 'JSON_USE_GLOBAL_UDLS=0;JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1;JSON_DIAGNOSTICS=1' install-options/share/cmake/nlohmann_json/nlohmann_jsonTargets.cmake
|
||||||
|
cmake -S tests/cmake_import/project -B build-import-options -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-options
|
||||||
|
cmake --build build-import-options
|
||||||
|
- name: Install with Meson and the include directory outside the prefix
|
||||||
|
run: |
|
||||||
|
meson setup build-meson-split --prefix=${{ github.workspace }}/install-split --includedir=${{ github.workspace }}/install-split-dev/include
|
||||||
|
meson install -C build-meson-split
|
||||||
|
cmake -S tests/cmake_import/project -B build-import-split -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install-split
|
||||||
|
cmake --build build-import-split
|
||||||
|
|
||||||
ci_infer:
|
ci_infer:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
+27
-7
@@ -61,7 +61,6 @@ option(JSON_Install "Install CMake targets during install
|
|||||||
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." ON)
|
||||||
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
option(JSON_SystemInclude "Include as system headers (skip for clang-tidy)." OFF)
|
||||||
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
option(JSON_StrictNulHandling "Build with strict NUL-byte handling enabled." OFF)
|
||||||
option(JSON_StrictBinaryUTF8 "Build with UTF-8 checks in the CBOR, UBJSON, BJData, and BSON writers enabled." OFF)
|
|
||||||
|
|
||||||
if (JSON_CI)
|
if (JSON_CI)
|
||||||
include(ci)
|
include(ci)
|
||||||
@@ -119,10 +118,6 @@ if (JSON_StrictNulHandling)
|
|||||||
message(STATUS "Strict NUL-byte handling enabled (JSON_STRICT_NUL_HANDLING=1)")
|
message(STATUS "Strict NUL-byte handling enabled (JSON_STRICT_NUL_HANDLING=1)")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (JSON_StrictBinaryUTF8)
|
|
||||||
message(STATUS "Strict UTF-8 checks in binary writers enabled (JSON_STRICT_BINARY_UTF8=1)")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (JSON_Diagnostic_Positions)
|
if (JSON_Diagnostic_Positions)
|
||||||
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
message(STATUS "Diagnostic positions enabled (JSON_DIAGNOSTIC_POSITIONS=1)")
|
||||||
endif()
|
endif()
|
||||||
@@ -158,7 +153,6 @@ target_compile_definitions(
|
|||||||
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
$<$<BOOL:${JSON_Diagnostic_Positions}>:JSON_DIAGNOSTIC_POSITIONS=1>
|
||||||
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
$<$<BOOL:${JSON_LegacyDiscardedValueComparison}>:JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1>
|
||||||
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
$<$<BOOL:${JSON_StrictNulHandling}>:JSON_STRICT_NUL_HANDLING=1>
|
||||||
$<$<BOOL:${JSON_StrictBinaryUTF8}>:JSON_STRICT_BINARY_UTF8=1>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
@@ -180,7 +174,33 @@ if (MSVC)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install a pkg-config file, so other tools can find this.
|
# Install a pkg-config file, so other tools can find this. It carries the same
|
||||||
|
# compile definitions as the target above.
|
||||||
|
set(NLOHMANN_JSON_PKGCONFIG_CFLAGS "")
|
||||||
|
if (NOT JSON_GlobalUDLs)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_GLOBAL_UDLS=0")
|
||||||
|
endif()
|
||||||
|
if (NOT JSON_ImplicitConversions)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_IMPLICIT_CONVERSIONS=0")
|
||||||
|
endif()
|
||||||
|
if (JSON_DisableEnumSerialization)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DISABLE_ENUM_SERIALIZATION=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_DisableTupleReferenceConversion)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_Diagnostics)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DIAGNOSTICS=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_Diagnostic_Positions)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_DIAGNOSTIC_POSITIONS=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_LegacyDiscardedValueComparison)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1")
|
||||||
|
endif()
|
||||||
|
if (JSON_StrictNulHandling)
|
||||||
|
string(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -DJSON_STRICT_NUL_HANDLING=1")
|
||||||
|
endif()
|
||||||
configure_file(
|
configure_file(
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/pkg-config.pc.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
|
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
|
||||||
|
|||||||
@@ -249,9 +249,31 @@ make BUILD.bazel
|
|||||||
|
|
||||||
The "Check amalgamation" workflow fails if the file is out of date.
|
The "Check amalgamation" workflow fails if the file is out of date.
|
||||||
|
|
||||||
### `meson.build`
|
### `meson.build` and `meson_options.txt`
|
||||||
|
|
||||||
The build definition for the [Meson](https://mesonbuild.com) build system.
|
Meson build definitions suitable for use as a subproject ("wrap" in Meson terminology).
|
||||||
|
|
||||||
|
Projects wishing to use the wrap can execute:
|
||||||
|
```sh
|
||||||
|
meson wrap install nlohmann_json
|
||||||
|
```
|
||||||
|
|
||||||
|
Which allows Meson to build from source when a system provided dependency isn't available.
|
||||||
|
|
||||||
|
To build directly:
|
||||||
|
```sh
|
||||||
|
meson setup builddir
|
||||||
|
ninja -C builddir
|
||||||
|
```
|
||||||
|
|
||||||
|
`meson_options.txt` defines the options, which mirror the CMake options that change the library's target (for example,
|
||||||
|
`-DDiagnostics=true`). Meson requires this file next to `meson.build`, so it is also part of `include.zip`. `make check_build_options`
|
||||||
|
([`tools/check_build_options`](tools/check_build_options/README.md)) checks in CI that both files and the pkg-config files
|
||||||
|
stay in sync with the CMake options.
|
||||||
|
|
||||||
|
When installing, `meson.build` installs the headers, a pkg-config file, and the CMake package config files, so that
|
||||||
|
`find_package(nlohmann_json)` works. As Meson cannot generate `nlohmann_jsonTargets.cmake` itself, it is created from
|
||||||
|
the template `cmake/nlohmann_jsonTargets.cmake.in`, which is only used by Meson.
|
||||||
|
|
||||||
### `Package.swift`
|
### `Package.swift`
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check
|
.PHONY: pretty clean ChangeLog.md release update_hedley update_hedley_undef BUILD.bazel natvis macro_builder_check check_build_options
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# configuration
|
# configuration
|
||||||
@@ -124,6 +124,10 @@ macro_builder_check:
|
|||||||
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
diff "$$TMPDIR/paste.hpp" "$$TMPDIR/paste_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_EXPAND..NLOHMANN_JSON_DOUBLE_PASTE63) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1); \
|
||||||
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
diff "$$TMPDIR/type_body.hpp" "$$TMPDIR/type_body_actual.hpp" || (echo "===================================================================\n $(MACRO_SCOPE_HPP) (NLOHMANN_JSON_TYPE_BODY) is out of date!\n Regenerate it, see tools/macro_builder/README.md.\n===================================================================" ; exit 1)
|
||||||
|
|
||||||
|
# check that the Meson build and the pkg-config files offer the options of the CMake target
|
||||||
|
check_build_options:
|
||||||
|
python3 tools/check_build_options/check_build_options.py .
|
||||||
|
|
||||||
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
# check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources
|
||||||
check-amalgamation:
|
check-amalgamation:
|
||||||
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
@mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~
|
||||||
@@ -181,7 +185,7 @@ json.tar.xz:
|
|||||||
# We use `-X` to make the resulting ZIP file reproducible, see
|
# We use `-X` to make the resulting ZIP file reproducible, see
|
||||||
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
# <https://content.pivotal.io/blog/barriers-to-deterministic-reproducible-zip-files>.
|
||||||
include.zip: BUILD.bazel
|
include.zip: BUILD.bazel
|
||||||
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT
|
zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build meson_options.txt LICENSE.MIT
|
||||||
|
|
||||||
# Create the files for a release and add signatures and hashes.
|
# Create the files for a release and add signatures and hashes.
|
||||||
release: include.zip json.tar.xz
|
release: include.zip json.tar.xz
|
||||||
|
|||||||
+1
-1
@@ -691,7 +691,7 @@ ci_get_cmake(4.0.0 CMAKE_4_0_0_BINARY)
|
|||||||
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
# the tests require CMake 3.13 or later, so they are excluded for CMake 3.5.0
|
||||||
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
set(JSON_CMAKE_FLAGS_3_5_0 JSON_Diagnostics JSON_Diagnostic_Positions JSON_GlobalUDLs JSON_ImplicitConversions JSON_DisableEnumSerialization
|
||||||
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
JSON_LegacyDiscardedValueComparison JSON_Install JSON_MultipleHeaders JSON_SystemInclude JSON_Valgrind
|
||||||
JSON_StrictNulHandling JSON_StrictBinaryUTF8)
|
JSON_StrictNulHandling)
|
||||||
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
set(JSON_CMAKE_FLAGS_3_31_6 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
set(JSON_CMAKE_FLAGS_4_0_0 JSON_BuildTests ${JSON_CMAKE_FLAGS_3_5_0})
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Imported target for installations made with Meson (see meson.build).
|
||||||
|
#
|
||||||
|
# CMake installations generate this file with install(EXPORT ...). Meson cannot
|
||||||
|
# do that, but as the library is header-only, the target only needs an include
|
||||||
|
# directory, the C++ standard, and the compile definitions of the options that
|
||||||
|
# differ from their defaults. Paths are computed relative to this file so that
|
||||||
|
# the installation can be relocated (e.g., into a sysroot), unless includedir or
|
||||||
|
# datadir is outside the prefix.
|
||||||
|
|
||||||
|
if(TARGET @PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_DIR}/@NLOHMANN_JSON_CONFIG_TO_PREFIX@" ABSOLUTE)
|
||||||
|
# As in CMake's generated file: avoid "//include" for an installation to "/".
|
||||||
|
if(_IMPORT_PREFIX STREQUAL "/")
|
||||||
|
set(_IMPORT_PREFIX "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ INTERFACE IMPORTED)
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "@NLOHMANN_JSON_INCLUDE_DIR@"
|
||||||
|
)
|
||||||
|
if(CMAKE_VERSION VERSION_LESS 3.8)
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_COMPILE_FEATURES cxx_range_for
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_COMPILE_FEATURES cxx_std_11
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(_NLOHMANN_JSON_COMPILE_DEFINITIONS "@NLOHMANN_JSON_COMPILE_DEFINITIONS@")
|
||||||
|
if(_NLOHMANN_JSON_COMPILE_DEFINITIONS)
|
||||||
|
set_target_properties(@PROJECT_NAME@::@NLOHMANN_JSON_TARGET_NAME@ PROPERTIES
|
||||||
|
INTERFACE_COMPILE_DEFINITIONS "${_NLOHMANN_JSON_COMPILE_DEFINITIONS}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
unset(_NLOHMANN_JSON_COMPILE_DEFINITIONS)
|
||||||
|
unset(_IMPORT_PREFIX)
|
||||||
@@ -4,4 +4,4 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
|||||||
Name: @PROJECT_NAME@
|
Name: @PROJECT_NAME@
|
||||||
Description: JSON for Modern C++
|
Description: JSON for Modern C++
|
||||||
Version: @PROJECT_VERSION@
|
Version: @PROJECT_VERSION@
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}@NLOHMANN_JSON_PKGCONFIG_CFLAGS@
|
||||||
|
|||||||
@@ -25,12 +25,10 @@ and `ensure_ascii` parameters.
|
|||||||
result consists of ASCII characters only.
|
result consists of ASCII characters only.
|
||||||
|
|
||||||
`error_handler` (in)
|
`error_handler` (in)
|
||||||
: how to react on decoding errors; there are four possible values (see [`error_handler_t`](error_handler_t.md):
|
: how to react on decoding errors; there are three possible values (see [`error_handler_t`](error_handler_t.md):
|
||||||
`strict` (throws an exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences
|
`strict` (throws an exception in case a decoding error occurs; default), `replace` (replace invalid UTF-8 sequences
|
||||||
with U+FFFD), `ignore` (ignore invalid UTF-8 sequences during serialization; all valid bytes are copied to the
|
with U+FFFD), and `ignore` (ignore invalid UTF-8 sequences during serialization; all valid bytes are copied to the
|
||||||
output unchanged, and invalid bytes are dropped), and `keep` (write the ill-formed bytes to the output as is,
|
output unchanged, and invalid bytes are dropped)).
|
||||||
without escaping them, even if `ensure_ascii` is `#!cpp true`; the result is then not valid UTF-8, but equals the
|
|
||||||
input bytes exactly, and well-formed characters around the ill-formed bytes are still escaped as usual)).
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
@@ -96,4 +94,3 @@ Binary values are serialized as an object containing two keys:
|
|||||||
- Indentation character `indent_char`, option `ensure_ascii` and exceptions added in version 3.0.0.
|
- Indentation character `indent_char`, option `ensure_ascii` and exceptions added in version 3.0.0.
|
||||||
- Error handlers added in version 3.4.0.
|
- Error handlers added in version 3.4.0.
|
||||||
- Serialization of binary values added in version 3.8.0.
|
- Serialization of binary values added in version 3.8.0.
|
||||||
- Error handler `keep` added in version 3.13.0.
|
|
||||||
|
|||||||
@@ -4,31 +4,15 @@
|
|||||||
enum class error_handler_t {
|
enum class error_handler_t {
|
||||||
strict,
|
strict,
|
||||||
replace,
|
replace,
|
||||||
ignore,
|
ignore
|
||||||
keep
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
This enumeration is used to choose how to treat ill-formed UTF-8 in a string value or object key:
|
This enumeration is used in the [`dump`](dump.md) function to choose how to treat decoding errors while serializing a
|
||||||
|
`basic_json` value. Three values are differentiated:
|
||||||
- [`dump`](dump.md) uses it while serializing a `basic_json` value to text.
|
|
||||||
- [`to_cbor`](to_cbor.md), [`to_msgpack`](to_msgpack.md), [`to_ubjson`](to_ubjson.md), [`to_bjdata`](to_bjdata.md),
|
|
||||||
and [`to_bson`](to_bson.md) use it while serializing a `basic_json` value to that binary format. Their default is
|
|
||||||
`keep`, as no binary writer checked before this parameter was added. CBOR, UBJSON, BJData, and BSON require valid
|
|
||||||
UTF-8, so for these four the default is `strict` if [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md)
|
|
||||||
is enabled; MessagePack's specification explicitly allows a string to contain ill-formed UTF-8, so `to_msgpack`
|
|
||||||
stays at `keep`. `to_bon8` does not take this parameter: BON8 always validates, since UTF-8 lead bytes are
|
|
||||||
structural to that format.
|
|
||||||
- [`from_cbor`](from_cbor.md), [`from_msgpack`](from_msgpack.md), [`from_ubjson`](from_ubjson.md),
|
|
||||||
[`from_bjdata`](from_bjdata.md), and [`from_bson`](from_bson.md) use it while parsing that binary format, to decide
|
|
||||||
whether to check a string value or object key for well-formed UTF-8 at all; by default (`keep`) they do not, as no
|
|
||||||
binary reader did before this parameter was added. `from_bon8` does not take this parameter, for the same reason
|
|
||||||
`to_bon8` does not.
|
|
||||||
|
|
||||||
Four values are differentiated:
|
|
||||||
|
|
||||||
strict
|
strict
|
||||||
: throw a `type_error`/`parse_error` exception in case of invalid UTF-8
|
: throw a `type_error` exception in case of invalid UTF-8
|
||||||
|
|
||||||
replace
|
replace
|
||||||
: replace invalid UTF-8 sequences with U+FFFD (� REPLACEMENT CHARACTER)
|
: replace invalid UTF-8 sequences with U+FFFD (� REPLACEMENT CHARACTER)
|
||||||
@@ -36,12 +20,6 @@ replace
|
|||||||
ignore
|
ignore
|
||||||
: ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped
|
: ignore invalid UTF-8 sequences; all valid bytes are copied to the output unchanged, and invalid bytes are dropped
|
||||||
|
|
||||||
keep
|
|
||||||
: keep invalid UTF-8 sequences unchanged; only meaningful for the binary formats mentioned above, since [`dump`]
|
|
||||||
(dump.md) itself must produce text, and `keep` there writes the ill-formed bytes to the output as is, so the
|
|
||||||
result is then not valid UTF-8 (but still equals the input bytes exactly, including around any well-formed
|
|
||||||
characters, which are still escaped as usual)
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
@@ -62,5 +40,3 @@ keep
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
- Added `keep`, and made this enumeration apply to the binary readers and writers in addition to `dump`, in version
|
|
||||||
3.13.0.
|
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
static basic_json from_bjdata(InputType&& i,
|
static basic_json from_bjdata(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
|
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
|
||||||
@@ -60,12 +58,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`allow_exceptions` (in)
|
`allow_exceptions` (in)
|
||||||
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string value or object key that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
BJData does not require a decoder to reject ill-formed UTF-8, so checking is opt-in: the default, `keep`, does not
|
|
||||||
check at all, as every binary reader did before this parameter was added; `strict` checks and throws;
|
|
||||||
`replace`/`ignore` sanitize the string the same way [`dump`](dump.md) would
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
||||||
@@ -81,7 +73,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
the end of the file was not reached when `strict` was set to true
|
the end of the file was not reached when `strict` was set to true
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
|
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
|
||||||
successfully, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict`
|
successfully
|
||||||
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
|
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
|
||||||
or n-dimensional array cannot be represented by `std::size_t`
|
or n-dimensional array cannot be represented by `std::size_t`
|
||||||
|
|
||||||
@@ -119,4 +111,3 @@ Linear in the size of the input.
|
|||||||
- Added in version 3.11.0.
|
- 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 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.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0.
|
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
static basic_json from_bson(InputType&& i,
|
static basic_json from_bson(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
|
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
|
||||||
@@ -60,12 +58,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`allow_exceptions` (in)
|
`allow_exceptions` (in)
|
||||||
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string value or object key that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
BSON does not require a decoder to reject ill-formed UTF-8, so checking is opt-in: the default, `keep`, does not
|
|
||||||
check at all, as every binary reader did before this parameter was added; `strict` checks and throws;
|
|
||||||
`replace`/`ignore` sanitize the string the same way [`dump`](dump.md) would
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
||||||
@@ -83,8 +75,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
invalid string or byte array length)
|
invalid string or byte array length)
|
||||||
- Throws [`parse_error.114`](../../home/exceptions.md#jsonexceptionparse_error114) if an unsupported BSON record type is
|
- Throws [`parse_error.114`](../../home/exceptions.md#jsonexceptionparse_error114) if an unsupported BSON record type is
|
||||||
encountered
|
encountered
|
||||||
- Throws [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) if a string value or object key is
|
|
||||||
not valid UTF-8 and `error_handler` is `strict`
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -121,7 +111,6 @@ Linear in the size of the input.
|
|||||||
- Added in version 3.4.0.
|
- 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 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.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0.
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -6,16 +6,14 @@ template<typename InputType>
|
|||||||
static basic_json from_cbor(InputType&& i,
|
static basic_json from_cbor(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error,
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error,
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
|
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
|
||||||
@@ -67,12 +65,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: how to treat CBOR tags (optional, `error` by default); see [`cbor_tag_handler_t`](cbor_tag_handler_t.md) for more
|
: how to treat CBOR tags (optional, `error` by default); see [`cbor_tag_handler_t`](cbor_tag_handler_t.md) for more
|
||||||
information
|
information
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string value or object key that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
CBOR does not require a decoder to reject ill-formed UTF-8, so checking is opt-in: the default, `keep`, does not
|
|
||||||
check at all, as every binary reader did before this parameter was added; `strict` checks and throws;
|
|
||||||
`replace`/`ignore` sanitize the string the same way [`dump`](dump.md) would
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
||||||
@@ -88,9 +80,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
the end of the file was not reached when `strict` was set to true
|
the end of the file was not reached when `strict` was set to true
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from CBOR were
|
||||||
used in the given input or if the input is not valid CBOR
|
used in the given input or if the input is not valid CBOR
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of
|
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of other
|
||||||
other types are not supported, as JSON object keys are always strings), or if a string value or object key is not
|
types are not supported, as JSON object keys are always strings) or a string is malformed
|
||||||
valid UTF-8 and `error_handler` is `strict`
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -130,7 +121,6 @@ Linear in the size of the input.
|
|||||||
- Added `tag_handler` parameter in version 3.9.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 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.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0.
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
static basic_json from_msgpack(InputType&& i,
|
static basic_json from_msgpack(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Deserializes a given input to a JSON value using the MessagePack serialization format.
|
Deserializes a given input to a JSON value using the MessagePack serialization format.
|
||||||
@@ -60,12 +58,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`allow_exceptions` (in)
|
`allow_exceptions` (in)
|
||||||
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string value or object key that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
MessagePack's specification explicitly allows ill-formed UTF-8, so checking is opt-in: the default, `keep`, does
|
|
||||||
not check at all, as every binary reader did before this parameter was added; `strict` checks and throws;
|
|
||||||
`replace`/`ignore` sanitize the string the same way [`dump`](dump.md) would
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
||||||
@@ -81,9 +73,8 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
the end of the file was not reached when `strict` was set to true
|
the end of the file was not reached when `strict` was set to true
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if unsupported features from
|
||||||
MessagePack were used in the given input or if the input is not valid MessagePack
|
MessagePack were used in the given input or if the input is not valid MessagePack
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of
|
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a map key is not a string (keys of other
|
||||||
other types are not supported, as JSON object keys are always strings), or if a string value or object key is not
|
types are not supported, as JSON object keys are always strings) or a string is malformed
|
||||||
valid UTF-8 and `error_handler` is `strict`
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -122,7 +113,6 @@ Linear in the size of the input.
|
|||||||
- Added `allow_exceptions` parameter in version 3.2.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 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.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0.
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -5,14 +5,12 @@
|
|||||||
template<typename InputType>
|
template<typename InputType>
|
||||||
static basic_json from_ubjson(InputType&& i,
|
static basic_json from_ubjson(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
|
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
|
||||||
@@ -60,12 +58,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`allow_exceptions` (in)
|
`allow_exceptions` (in)
|
||||||
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
: whether to throw exceptions in case of a parse error (optional, `#!cpp true` by default)
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string value or object key that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
UBJSON does not require a decoder to reject ill-formed UTF-8, so checking is opt-in: the default, `keep`, does not
|
|
||||||
check at all, as every binary reader did before this parameter was added; `strict` checks and throws;
|
|
||||||
`replace`/`ignore` sanitize the string the same way [`dump`](dump.md) would
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
deserialized JSON value; in case of a parse error and `allow_exceptions` set to `#!cpp false`, the return value will be
|
||||||
@@ -81,7 +73,7 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
the end of the file was not reached when `strict` was set to true
|
the end of the file was not reached when `strict` was set to true
|
||||||
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
|
- Throws [parse_error.112](../../home/exceptions.md#jsonexceptionparse_error112) if a parse error occurs
|
||||||
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
|
- Throws [parse_error.113](../../home/exceptions.md#jsonexceptionparse_error113) if a string could not be parsed
|
||||||
successfully, or if a string value or object key is not valid UTF-8 and `error_handler` is `strict`
|
successfully
|
||||||
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
|
- Throws [out_of_range.408](../../home/exceptions.md#jsonexceptionout_of_range408) if the size of an optimized container
|
||||||
or n-dimensional array cannot be represented by `std::size_t`
|
or n-dimensional array cannot be represented by `std::size_t`
|
||||||
|
|
||||||
@@ -120,7 +112,6 @@ Linear in the size of the input.
|
|||||||
- Added `allow_exceptions` parameter in version 3.2.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 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.
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0.
|
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -5,18 +5,15 @@
|
|||||||
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
|
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
|
||||||
const bool use_size = false,
|
const bool use_size = false,
|
||||||
const bool use_type = false,
|
const bool use_type = false,
|
||||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
const bjdata_version_t version = bjdata_version_t::draft2);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false,
|
||||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
const bjdata_version_t version = bjdata_version_t::draft2);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
|
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false,
|
||||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
const bjdata_version_t version = bjdata_version_t::draft2);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Serializes a given JSON value `j` to a byte vector using the BJData (Binary JData) serialization format. BJData aims to
|
Serializes a given JSON value `j` to a byte vector using the BJData (Binary JData) serialization format. BJData aims to
|
||||||
@@ -46,12 +43,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: which version of BJData to use (see note on "Binary values" on [BJData](../../features/binary_formats/bjdata.md));
|
: which version of BJData to use (see note on "Binary values" on [BJData](../../features/binary_formats/bjdata.md));
|
||||||
optional, `#!cpp bjdata_version_t::draft2` by default.
|
optional, `#!cpp bjdata_version_t::draft2` by default.
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string or object key in `j` that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
The default, `keep`, writes the ill-formed bytes to the output as is, as every version of `to_bjdata` did before
|
|
||||||
this parameter was added; `strict` throws; `replace`/`ignore` sanitize it the same way [`dump`](dump.md) would.
|
|
||||||
If [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled, the default is `strict` instead.
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
1. BJData serialization as byte vector
|
1. BJData serialization as byte vector
|
||||||
@@ -65,9 +56,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
is false.
|
is false.
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
|
||||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -101,7 +89,4 @@ Linear in the size of the JSON value `j`.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.11.0.
|
- Added in version 3.11.0.
|
||||||
- BJData version parameter (for draft3 binary encoding) added in version 3.12.0.
|
- BJData version parameter (for draft3 binary encoding) added in version 3.12.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
|
||||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
|
||||||
@@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// (1)
|
// (1)
|
||||||
static std::vector<std::uint8_t> to_bson(const basic_json& j,
|
static std::vector<std::uint8_t> to_bson(const basic_json& j);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
static void to_bson(const basic_json& j, detail::output_adapter<char> o);
|
||||||
static void to_bson(const basic_json& j, detail::output_adapter<char> o,
|
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are stored as a single entity (a
|
BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are stored as a single entity (a
|
||||||
@@ -28,12 +25,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`o` (in)
|
`o` (in)
|
||||||
: output adapter to write serialization to
|
: output adapter to write serialization to
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string or object key in `j` that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
The default, `keep`, writes the ill-formed bytes to the output as is, as every version of `to_bson` did before
|
|
||||||
this parameter was added; `strict` throws; `replace`/`ignore` sanitize it the same way [`dump`](dump.md) would.
|
|
||||||
If [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled, the default is `strict` instead.
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
1. BSON serialization as a byte vector
|
1. BSON serialization as a byte vector
|
||||||
@@ -55,9 +46,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
||||||
exceeds 255, the maximum of the BSON binary subtype; example:
|
exceeds 255, the maximum of the BSON binary subtype; example:
|
||||||
`"subtype 70000 is too large for the BSON binary subtype (max 255)"`
|
`"subtype 70000 is too large for the BSON binary subtype (max 255)"`
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key is
|
|
||||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -94,7 +82,3 @@ pass before anything is written.
|
|||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
- Linear in the size of `j`, and no longer limited by the call stack for deeply nested values, since version 3.13.0.
|
||||||
- `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0.
|
- `out_of_range.415` is now detected before anything is written, like the other exceptions above, since version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
|
||||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316` before anything
|
|
||||||
is written.
|
|
||||||
|
|||||||
@@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// (1)
|
// (1)
|
||||||
static std::vector<std::uint8_t> to_cbor(const basic_json& j,
|
static std::vector<std::uint8_t> to_cbor(const basic_json& j);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
static void to_cbor(const basic_json& j, detail::output_adapter<char> o);
|
||||||
static void to_cbor(const basic_json& j, detail::output_adapter<char> o,
|
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Serializes a given JSON value `j` to a byte vector using the CBOR (Concise Binary Object Representation) serialization
|
Serializes a given JSON value `j` to a byte vector using the CBOR (Concise Binary Object Representation) serialization
|
||||||
@@ -29,12 +26,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`o` (in)
|
`o` (in)
|
||||||
: output adapter to write serialization to
|
: output adapter to write serialization to
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string or object key in `j` that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
The default, `keep`, writes the ill-formed bytes to the output as is, as every version of `to_cbor` did before
|
|
||||||
this parameter was added; `strict` throws; `replace`/`ignore` sanitize it the same way [`dump`](dump.md) would.
|
|
||||||
If [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled, the default is `strict` instead.
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
1. CBOR serialization as a byte vector
|
1. CBOR serialization as a byte vector
|
||||||
@@ -44,12 +35,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||||
|
|
||||||
## Exceptions
|
|
||||||
|
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
|
||||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`.
|
Linear in the size of the JSON value `j`.
|
||||||
@@ -83,6 +68,3 @@ Linear in the size of the JSON value `j`.
|
|||||||
|
|
||||||
- Added in version 2.0.9.
|
- Added in version 2.0.9.
|
||||||
- Compact representation of floating-point numbers added in version 3.8.0.
|
- Compact representation of floating-point numbers added in version 3.8.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
|
||||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
|
||||||
|
|||||||
@@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// (1)
|
// (1)
|
||||||
static std::vector<std::uint8_t> to_msgpack(const basic_json& j,
|
static std::vector<std::uint8_t> to_msgpack(const basic_json& j);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
static void to_msgpack(const basic_json& j, detail::output_adapter<char> o);
|
||||||
static void to_msgpack(const basic_json& j, detail::output_adapter<char> o,
|
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Serializes a given JSON value `j` to a byte vector using the MessagePack serialization format. MessagePack is a binary
|
Serializes a given JSON value `j` to a byte vector using the MessagePack serialization format. MessagePack is a binary
|
||||||
@@ -28,13 +25,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`o` (in)
|
`o` (in)
|
||||||
: output adapter to write serialization to
|
: output adapter to write serialization to
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string or object key in `j` that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
The default, `keep`, writes the ill-formed bytes to the output as is, as every version of `to_msgpack` did before
|
|
||||||
this parameter was added and as the MessagePack specification allows; `strict` throws; `replace`/`ignore` sanitize
|
|
||||||
it the same way [`dump`](dump.md) would. Unlike the other binary writers, the default stays `keep` even if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled.
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
1. MessagePack serialization as a byte vector
|
1. MessagePack serialization as a byte vector
|
||||||
@@ -52,8 +42,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
- Throws [`out_of_range.415`](../../home/exceptions.md#jsonexceptionout_of_range415) if the subtype of a binary value
|
||||||
exceeds 255, the maximum of the MessagePack ext type; example:
|
exceeds 255, the maximum of the MessagePack ext type; example:
|
||||||
`"subtype 70000 is too large for the MessagePack ext type (max 255)"`
|
`"subtype 70000 is too large for the MessagePack ext type (max 255)"`
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
|
||||||
not valid UTF-8 and `error_handler` is `strict`
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -88,8 +76,6 @@ Linear in the size of the JSON value `j`.
|
|||||||
|
|
||||||
- Added in version 2.0.9.
|
- Added in version 2.0.9.
|
||||||
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
|
- Throws `out_of_range.412` and `out_of_range.415` since version 3.13.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
|
||||||
that is not valid UTF-8 unchanged, as before.
|
|
||||||
- Fixed in version 3.13.0 to serialize `number_integer_t`/`number_unsigned_t` pairs of different width correctly;
|
- Fixed in version 3.13.0 to serialize `number_integer_t`/`number_unsigned_t` pairs of different width correctly;
|
||||||
before, integers could be serialized with the wrong value if `number_integer_t` was narrower than
|
before, integers could be serialized with the wrong value if `number_integer_t` was narrower than
|
||||||
`number_unsigned_t`.
|
`number_unsigned_t`.
|
||||||
|
|||||||
@@ -4,16 +4,13 @@
|
|||||||
// (1)
|
// (1)
|
||||||
static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
|
static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
|
||||||
const bool use_size = false,
|
const bool use_size = false,
|
||||||
const bool use_type = false,
|
const bool use_type = false);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
|
static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false);
|
||||||
const error_handler_t error_handler = error_handler_t::keep);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Serializes a given JSON value `j` to a byte vector using the UBJSON (Universal Binary JSON) serialization format. UBJSON
|
Serializes a given JSON value `j` to a byte vector using the UBJSON (Universal Binary JSON) serialization format. UBJSON
|
||||||
@@ -39,12 +36,6 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: whether to add type annotations to container types (must be combined with `#!cpp use_size = true`); optional,
|
: whether to add type annotations to container types (must be combined with `#!cpp use_size = true`); optional,
|
||||||
`#!cpp false` by default.
|
`#!cpp false` by default.
|
||||||
|
|
||||||
`error_handler` (in)
|
|
||||||
: how to treat a string or object key in `j` that is not valid UTF-8; see [`error_handler_t`](error_handler_t.md).
|
|
||||||
The default, `keep`, writes the ill-formed bytes to the output as is, as every version of `to_ubjson` did before
|
|
||||||
this parameter was added; `strict` throws; `replace`/`ignore` sanitize it the same way [`dump`](dump.md) would.
|
|
||||||
If [`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled, the default is `strict` instead.
|
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
1. UBJSON serialization as a byte vector
|
1. UBJSON serialization as a byte vector
|
||||||
@@ -58,9 +49,6 @@ Strong guarantee: if an exception is thrown, there are no changes in the JSON va
|
|||||||
|
|
||||||
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
is false.
|
is false.
|
||||||
- Throws [type_error.316](../../home/exceptions.md#jsonexceptiontype_error316) if a string or object key in `j` is
|
|
||||||
not valid UTF-8 and `error_handler` is `strict` (the default only if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled)
|
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
@@ -94,6 +82,3 @@ Linear in the size of the JSON value `j`.
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.1.0.
|
- Added in version 3.1.0.
|
||||||
- Added `error_handler` parameter in version 3.13.0. Its default, `keep`, writes the bytes of a string or object key
|
|
||||||
that is not valid UTF-8 unchanged, as before; `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../macros/json_strict_binary_utf8.md) is enabled) throws `type_error.316`.
|
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ header. See also the [macro overview page](../../features/macros.md).
|
|||||||
|
|
||||||
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
- [**JSON_PRECISE_STREAM_POSITION**](json_precise_stream_position.md) - opt in to leaving an input stream positioned
|
||||||
right after a parsed number
|
right after a parsed number
|
||||||
- [**JSON_STRICT_BINARY_UTF8**](json_strict_binary_utf8.md) - opt in to checking strings for valid UTF-8 in the CBOR,
|
|
||||||
UBJSON, BJData, and BSON writers
|
|
||||||
- [**JSON_STRICT_NUL_HANDLING**](json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input instead of
|
- [**JSON_STRICT_NUL_HANDLING**](json_strict_nul_handling.md) - opt in to rejecting a NUL byte in the input instead of
|
||||||
treating it as end of input
|
treating it as end of input
|
||||||
|
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
# JSON_STRICT_BINARY_UTF8
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_STRICT_BINARY_UTF8 /* value */
|
|
||||||
```
|
|
||||||
|
|
||||||
When defined to `1`, the `error_handler` parameter of the binary writers [`to_cbor`](../basic_json/to_cbor.md),
|
|
||||||
[`to_ubjson`](../basic_json/to_ubjson.md), [`to_bjdata`](../basic_json/to_bjdata.md), and
|
|
||||||
[`to_bson`](../basic_json/to_bson.md) defaults to [`error_handler_t::strict`](../basic_json/error_handler_t.md) instead
|
|
||||||
of `error_handler_t::keep`. These writers then check every string value and object key for valid UTF-8 and throw
|
|
||||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for ill-formed UTF-8, like
|
|
||||||
[`dump`](../basic_json/dump.md) does. Without it, they write the bytes unchanged. An `error_handler` passed explicitly
|
|
||||||
always takes precedence.
|
|
||||||
|
|
||||||
The macro does not affect:
|
|
||||||
|
|
||||||
- [`to_msgpack`](../basic_json/to_msgpack.md): the MessagePack specification allows a `str` value to contain bytes that
|
|
||||||
are not valid UTF-8, so its `error_handler` always defaults to `keep`.
|
|
||||||
- [`to_bon8`](../basic_json/to_bon8.md): BON8 always checks, because the UTF-8 lead bytes mark where a string ends.
|
|
||||||
- The binary readers ([`from_cbor`](../basic_json/from_cbor.md), [`from_msgpack`](../basic_json/from_msgpack.md),
|
|
||||||
[`from_ubjson`](../basic_json/from_ubjson.md), [`from_bjdata`](../basic_json/from_bjdata.md),
|
|
||||||
[`from_bson`](../basic_json/from_bson.md)): none of these formats requires a decoder to reject ill-formed UTF-8, so
|
|
||||||
they always return the bytes unchanged.
|
|
||||||
|
|
||||||
## Default definition
|
|
||||||
|
|
||||||
The default value is `0` (disabled, the behavior of version 3.12.0 and earlier is preserved).
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_STRICT_BINARY_UTF8 0
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
!!! note "Background"
|
|
||||||
|
|
||||||
CBOR, UBJSON, BJData, and BSON all require strings to be UTF-8. Up to version 3.12.0, the writers did not check
|
|
||||||
this, so they could produce output that other decoders reject. Checking by default would break code that stores
|
|
||||||
other encodings (for instance ISO 8859-1) in a string and only ever writes it to a binary format. You can pass
|
|
||||||
`error_handler_t::strict` to each call, or use this macro to check by default ahead of version 4.0.0, where
|
|
||||||
`strict` is planned to become the default (see
|
|
||||||
[#5529](https://github.com/nlohmann/json/issues/5529) and [#5651](https://github.com/nlohmann/json/issues/5651)).
|
|
||||||
|
|
||||||
!!! warning "Opt-in only"
|
|
||||||
|
|
||||||
This macro must be defined **before** including `<nlohmann/json.hpp>`. Defining it after the include has no
|
|
||||||
effect.
|
|
||||||
|
|
||||||
!!! note "ABI compatibility"
|
|
||||||
|
|
||||||
The value of this macro is encoded in the [namespace](../../features/namespace.md) (tag `_sbu8`), resulting in
|
|
||||||
distinct symbol names. Translation units compiled with and without it can therefore be linked into the same program
|
|
||||||
without One Definition Rule (ODR) violations, but they cannot exchange instances of library types.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
??? example "Default behavior (macro not defined)"
|
|
||||||
|
|
||||||
Without the macro, the bytes are written unchanged:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
auto v = json::to_cbor(json("\xFF"));
|
|
||||||
// v is {0x61, 0xFF}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
??? example "Opt-in check (macro defined to 1)"
|
|
||||||
|
|
||||||
With the macro, ill-formed UTF-8 is rejected:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#define JSON_STRICT_BINARY_UTF8 1
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
auto v = json::to_cbor(json("\xFF"));
|
|
||||||
// throws type_error.316: invalid UTF-8 byte at index 0: 0xFF
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## See also
|
|
||||||
|
|
||||||
- [**to_cbor**](../basic_json/to_cbor.md) - create a CBOR serialization of a JSON value
|
|
||||||
- [**to_ubjson**](../basic_json/to_ubjson.md) - create a UBJSON serialization of a JSON value
|
|
||||||
- [**to_bjdata**](../basic_json/to_bjdata.md) - create a BJData serialization of a JSON value
|
|
||||||
- [**to_bson**](../basic_json/to_bson.md) - create a BSON serialization of a JSON value
|
|
||||||
- [**error_handler_t**](../basic_json/error_handler_t.md) - how [`dump`](../basic_json/dump.md) treats ill-formed UTF-8
|
|
||||||
|
|
||||||
## Version history
|
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
|
||||||
- Planned to become the default (with the macro removed) in version 4.0.0.
|
|
||||||
@@ -20,6 +20,5 @@ int main()
|
|||||||
<< j_invalid.dump(-1, ' ', false, json::error_handler_t::replace)
|
<< j_invalid.dump(-1, ' ', false, json::error_handler_t::replace)
|
||||||
<< "\nstring with ignored invalid characters: "
|
<< "\nstring with ignored invalid characters: "
|
||||||
<< j_invalid.dump(-1, ' ', false, json::error_handler_t::ignore)
|
<< j_invalid.dump(-1, ' ', false, json::error_handler_t::ignore)
|
||||||
<< "\nstring with the invalid byte kept as is (" << j_invalid.dump(-1, ' ', false, json::error_handler_t::keep).size()
|
<< '\n';
|
||||||
<< " bytes, not valid UTF-8 itself)\n";
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9
|
[json.exception.type_error.316] invalid UTF-8 byte at index 2: 0xA9
|
||||||
string with replaced invalid characters: "ä�ü"
|
string with replaced invalid characters: "ä�ü"
|
||||||
string with ignored invalid characters: "äü"
|
string with ignored invalid characters: "äü"
|
||||||
string with the invalid byte kept as is (7 bytes, not valid UTF-8 itself)
|
|
||||||
|
|||||||
@@ -63,15 +63,6 @@ The library uses the following mapping from JSON values types to BJData types ac
|
|||||||
|
|
||||||
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
- strings with more than 18446744073709551615 bytes, i.e., 2<sup>64</sup>-1 bytes (theoretical)
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values and object keys"
|
|
||||||
|
|
||||||
BJData strings must use UTF-8 encoding. By default (the [`error_handler`](../../api/basic_json/to_bjdata.md)
|
|
||||||
parameter left at `keep`), `to_bjdata()` writes the bytes of string values and object keys unchanged, even if they
|
|
||||||
are not valid UTF-8. With `error_handler_t::strict`, it throws
|
|
||||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for ill-formed UTF-8 instead;
|
|
||||||
`replace`/`ignore` sanitize the string. [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md)
|
|
||||||
makes `strict` the default.
|
|
||||||
|
|
||||||
!!! info "Unused BJData markers"
|
!!! info "Unused BJData markers"
|
||||||
|
|
||||||
The following markers are not used in the conversion:
|
The following markers are not used in the conversion:
|
||||||
@@ -217,19 +208,6 @@ The library maps BJData types to JSON value types as follows:
|
|||||||
|
|
||||||
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
The mapping is **complete** in the sense that any BJData value can be converted to a JSON value.
|
||||||
|
|
||||||
!!! warning "Ill-formed UTF-8 in string values and object keys"
|
|
||||||
|
|
||||||
BJData strings must use UTF-8 encoding, but checking it on read is opt-in: with the
|
|
||||||
[`error_handler`](../../api/basic_json/from_bjdata.md) parameter left at `keep` (the default), `from_bjdata()`
|
|
||||||
accepts a string value or object key whose bytes are not valid UTF-8 and hands them back unchanged. Passing
|
|
||||||
`error_handler_t::strict` makes `from_bjdata()` check and throw
|
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) for ill-formed UTF-8, and
|
|
||||||
`replace`/`ignore` sanitize the string instead of keeping it. However,
|
|
||||||
[`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
|
||||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for a value read with the default
|
|
||||||
`keep` handler, unless an error handler is passed that replaces or ignores the ill-formed bytes. `to_bjdata()`'s
|
|
||||||
own `error_handler` parameter defaults to `keep` (see above), so such a value is written back unchanged.
|
|
||||||
|
|
||||||
!!! info "Round trips"
|
!!! info "Round trips"
|
||||||
|
|
||||||
A value returned by [`from_bjdata`](../../api/basic_json/from_bjdata.md) can be serialized with
|
A value returned by [`from_bjdata`](../../api/basic_json/from_bjdata.md) can be serialized with
|
||||||
|
|||||||
@@ -109,21 +109,14 @@ The library maps BSON record types to JSON value types as follows:
|
|||||||
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
||||||
`from_bson()`.
|
`from_bson()`.
|
||||||
|
|
||||||
!!! warning "Ill-formed UTF-8 in string values"
|
!!! warning "UTF-8 validation of string values"
|
||||||
|
|
||||||
The BSON specification requires `string` values (type `0x02`) to be valid UTF-8, but this is not required of a
|
The BSON specification requires `string` values (type `0x02`) to be valid UTF-8. This library validates the
|
||||||
decoder, so checking is opt-in: with the [`error_handler`](../../api/basic_json/from_bson.md) parameter left at
|
bytes of every such string at decode time and rejects ill-formed UTF-8 with a
|
||||||
`keep` (the default), `from_bson()` accepts a `string` value whose bytes are not valid UTF-8 and hands them back
|
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with `allow_exceptions`
|
||||||
unchanged. Passing `error_handler_t::strict` makes `from_bson()` check and throw
|
set to `false`, a discarded value), rather than only failing later when the resulting value is dumped. Element
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) for ill-formed UTF-8, and
|
(key) names and `binary` values (type `0x05`) are unaffected and are never validated, since they are read
|
||||||
`replace`/`ignore` sanitize the string instead of keeping it. However, [`dump()`](../../api/basic_json/dump.md)
|
byte-by-byte as a C string, or are not required to hold text, respectively.
|
||||||
still requires valid UTF-8 and throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for a
|
|
||||||
value read with the default `keep` handler, unless an error handler is passed that replaces or ignores the
|
|
||||||
ill-formed bytes. `to_bson()`'s own `error_handler` parameter defaults to `keep`, so such a string value or element
|
|
||||||
(key) name is written unchanged; with `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception
|
|
||||||
instead. Element (key) names are never validated on read, since they are read byte-by-byte as a C string. `binary`
|
|
||||||
values (type `0x05`) are unaffected, since they are not required to hold text.
|
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -189,21 +189,15 @@ The library maps CBOR types to JSON value types as follows:
|
|||||||
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
|
([RFC 8392](https://www.rfc-editor.org/rfc/rfc8392.html)), cannot be read with this library and need a
|
||||||
general-purpose CBOR library instead.
|
general-purpose CBOR library instead.
|
||||||
|
|
||||||
!!! warning "Ill-formed UTF-8 in text strings"
|
!!! warning "UTF-8 validation of text strings"
|
||||||
|
|
||||||
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings (major
|
[RFC 8949, Section 3.1](https://www.rfc-editor.org/rfc/rfc8949.html#section-3.1) requires CBOR text strings
|
||||||
type 3) to be valid UTF-8, but leaves it up to the decoder whether to enforce this, so checking is opt-in: with the
|
(major type 3) to be valid UTF-8. This library validates the bytes of every text string (object keys included) at
|
||||||
[`error_handler`](../../api/basic_json/from_cbor.md) parameter left at `keep` (the default), `from_cbor()` accepts a
|
decode time and rejects ill-formed UTF-8 with a
|
||||||
text string (object keys included) whose bytes are not valid UTF-8 and hands them back unchanged. Passing
|
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or, with
|
||||||
`error_handler_t::strict` makes `from_cbor()` check and throw
|
`allow_exceptions` set to `false`, a discarded value), rather than only failing later when the resulting value is
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) for ill-formed UTF-8, and
|
dumped. Byte strings (major type 2) are unaffected and are never validated, since they are not required to hold
|
||||||
`replace`/`ignore` sanitize the string instead of keeping it. However, [`dump()`](../../api/basic_json/dump.md)
|
text.
|
||||||
still requires valid UTF-8 and throws [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for a
|
|
||||||
value read with the default `keep` handler, unless an error handler is passed that replaces or ignores the
|
|
||||||
ill-formed bytes. `to_cbor()`'s own [`error_handler`](../../api/basic_json/to_cbor.md) parameter defaults to `keep`,
|
|
||||||
so such a value is written back unchanged; with `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md) is enabled), it throws the same exception
|
|
||||||
instead. Byte strings (major type 2) are unaffected, since they are not required to hold text.
|
|
||||||
|
|
||||||
!!! warning "Tagged items"
|
!!! warning "Tagged items"
|
||||||
|
|
||||||
|
|||||||
@@ -153,23 +153,14 @@ The library maps MessagePack types to JSON value types as follows:
|
|||||||
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
|
This applies to the [SAX interface](../parsing/sax_interface.md) as well, as the key is read before it is passed
|
||||||
on. Such input needs a general-purpose MessagePack library instead.
|
on. Such input needs a general-purpose MessagePack library instead.
|
||||||
|
|
||||||
!!! warning "Ill-formed UTF-8 in string values"
|
!!! warning "UTF-8 validation of string values"
|
||||||
|
|
||||||
The MessagePack specification explicitly allows a `str` value (`fixstr`, `str 8`, `str 16`, `str 32`) to contain
|
The MessagePack specification requires `str` values (`fixstr`, `str 8`, `str 16`, `str 32`) to be valid UTF-8.
|
||||||
a byte sequence that is not valid UTF-8, and expects a deserializer to hand the original bytes back unchanged.
|
This library validates the bytes of every such string (object keys included) at decode time and rejects
|
||||||
This library follows that by default: with its
|
ill-formed UTF-8 with a [`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) exception (or,
|
||||||
[`error_handler`](../../api/basic_json/from_msgpack.md) parameter left at `keep` (the default),
|
with `allow_exceptions` set to `false`, a discarded value), rather than only failing later when the resulting
|
||||||
`from_msgpack()` reads `str` bytes (object keys included) as-is, without validating them, so such a value
|
value is dumped. `bin`/`ext`/`fixext` values are unaffected and are never validated, since they are not required
|
||||||
round-trips through `from_msgpack(to_msgpack(j))` byte for byte. Passing `error_handler_t::strict` makes
|
to hold text.
|
||||||
`from_msgpack()` check anyway and throw
|
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) for ill-formed UTF-8, and
|
|
||||||
`replace`/`ignore` sanitize the string instead of keeping it. `to_msgpack()` also writes `str` bytes as-is by
|
|
||||||
default, since the specification permits it; its [`error_handler`](../../api/basic_json/to_msgpack.md) parameter
|
|
||||||
can be set to `strict` to throw [`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) instead, or
|
|
||||||
to `replace`/`ignore` to sanitize the string, for instance for a decoder that rejects ill-formed UTF-8. However,
|
|
||||||
[`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
|
||||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for a value read this way with the
|
|
||||||
default `keep` handler, unless an error handler is passed that replaces or ignores the ill-formed bytes.
|
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
|
|||||||
@@ -47,15 +47,6 @@ The library uses the following mapping from JSON values types to UBJSON types ac
|
|||||||
|
|
||||||
- strings with more than 9223372036854775807 bytes (theoretical)
|
- strings with more than 9223372036854775807 bytes (theoretical)
|
||||||
|
|
||||||
!!! warning "UTF-8 validation of string values and object keys"
|
|
||||||
|
|
||||||
UBJSON's required string encoding is UTF-8. By default (the [`error_handler`](../../api/basic_json/to_ubjson.md)
|
|
||||||
parameter left at `keep`), `to_ubjson()` writes the bytes of string values and object keys unchanged, even if they
|
|
||||||
are not valid UTF-8. With `error_handler_t::strict`, it throws
|
|
||||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for ill-formed UTF-8 instead;
|
|
||||||
`replace`/`ignore` sanitize the string. [`JSON_STRICT_BINARY_UTF8`](../../api/macros/json_strict_binary_utf8.md)
|
|
||||||
makes `strict` the default.
|
|
||||||
|
|
||||||
!!! info "Unused UBJSON markers"
|
!!! info "Unused UBJSON markers"
|
||||||
|
|
||||||
The following markers are not used in the conversion:
|
The following markers are not used in the conversion:
|
||||||
@@ -129,19 +120,6 @@ The library maps UBJSON types to JSON value types as follows:
|
|||||||
|
|
||||||
The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
|
The mapping is **complete** in the sense that any UBJSON value can be converted to a JSON value.
|
||||||
|
|
||||||
!!! warning "Ill-formed UTF-8 in string values and object keys"
|
|
||||||
|
|
||||||
UBJSON's required string encoding is UTF-8, but checking it on read is opt-in: with the
|
|
||||||
[`error_handler`](../../api/basic_json/from_ubjson.md) parameter left at `keep` (the default), `from_ubjson()`
|
|
||||||
accepts a string value or object key whose bytes are not valid UTF-8 and hands them back unchanged. Passing
|
|
||||||
`error_handler_t::strict` makes `from_ubjson()` check and throw
|
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113) for ill-formed UTF-8, and
|
|
||||||
`replace`/`ignore` sanitize the string instead of keeping it. However,
|
|
||||||
[`dump()`](../../api/basic_json/dump.md) still requires valid UTF-8 and throws
|
|
||||||
[`type_error.316`](../../home/exceptions.md#jsonexceptiontype_error316) for a value read with the default
|
|
||||||
`keep` handler, unless an error handler is passed that replaces or ignores the ill-formed bytes. `to_ubjson()`'s
|
|
||||||
own `error_handler` parameter defaults to `keep` (see above), so such a value is written back unchanged.
|
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -138,20 +138,6 @@ using the library with compilers that do not fully support C++11 and may only wo
|
|||||||
|
|
||||||
See [full documentation of `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`](../api/macros/json_skip_unsupported_compiler_check.md).
|
See [full documentation of `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`](../api/macros/json_skip_unsupported_compiler_check.md).
|
||||||
|
|
||||||
## `JSON_STRICT_BINARY_UTF8`
|
|
||||||
|
|
||||||
When defined to `1`, [`to_cbor`](../api/basic_json/to_cbor.md), [`to_ubjson`](../api/basic_json/to_ubjson.md),
|
|
||||||
[`to_bjdata`](../api/basic_json/to_bjdata.md), and [`to_bson`](../api/basic_json/to_bson.md) throw
|
|
||||||
[`type_error.316`](../home/exceptions.md#jsonexceptiontype_error316) for a string value or object key that is not
|
|
||||||
valid UTF-8. The default value is `0`, which writes the bytes unchanged as before version 3.13.0; this is planned to
|
|
||||||
become the default in version 4.0.0.
|
|
||||||
|
|
||||||
The check can also be enabled with the CMake option
|
|
||||||
[`JSON_StrictBinaryUTF8`](../integration/cmake.md#json_strictbinaryutf8) (`OFF` by default) which sets
|
|
||||||
`JSON_STRICT_BINARY_UTF8` accordingly.
|
|
||||||
|
|
||||||
See [full documentation of `JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md).
|
|
||||||
|
|
||||||
## `JSON_STRICT_NUL_HANDLING`
|
## `JSON_STRICT_NUL_HANDLING`
|
||||||
|
|
||||||
When defined to `1`, a `'\0'` (NUL) byte anywhere in the input is rejected with `parse_error.101`, like any other
|
When defined to `1`, a `'\0'` (NUL) byte anywhere in the input is rejected with `parse_error.101`, like any other
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ The complete default namespace name is derived as follows:
|
|||||||
`_bics`.
|
`_bics`.
|
||||||
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
- [`JSON_PRECISE_STREAM_POSITION`](../api/macros/json_precise_stream_position.md) defined non-zero appends `_psp`.
|
||||||
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
- [`JSON_STRICT_NUL_HANDLING`](../api/macros/json_strict_nul_handling.md) defined non-zero appends `_snul`.
|
||||||
- [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) defined non-zero appends `_sbu8`.
|
|
||||||
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
- The inline namespace ends with the suffix `_v` followed by the 3 components of the version number separated by
|
||||||
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
underscores. To omit the version component, see [Disabling the version component](#disabling-the-version-component)
|
||||||
below.
|
below.
|
||||||
|
|||||||
@@ -341,10 +341,7 @@ An unexpected byte was read in a [binary format](../features/binary_formats/inde
|
|||||||
|
|
||||||
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
A string could not be read from a [binary format](../features/binary_formats/index.md): either a value that is not a
|
||||||
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
string was read where one was required (for instance as a map key), the string's length specification is invalid, or
|
||||||
the string's bytes are not valid UTF-8 and the `error_handler` parameter of the corresponding `from_*` function is
|
the string's bytes are not valid UTF-8.
|
||||||
set to `strict`. By default (`error_handler_t::keep`), the bytes of a string are not checked for valid UTF-8 on read;
|
|
||||||
see the ill-formed UTF-8 notes on the individual [binary format](../features/binary_formats/index.md) pages for how
|
|
||||||
such a string is handled depending on `error_handler`.
|
|
||||||
|
|
||||||
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
CBOR and MessagePack allow map keys of any type, but JSON object keys are always strings. Maps with keys of any other
|
||||||
type (for instance integers or `null`) are therefore not supported; see the notes on
|
type (for instance integers or `null`) are therefore not supported; see the notes on
|
||||||
@@ -752,12 +749,6 @@ The `unflatten()` function only works for an object whose keys are JSON Pointers
|
|||||||
|
|
||||||
The `dump()` function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded.
|
The `dump()` function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded.
|
||||||
|
|
||||||
The binary writers [`to_cbor()`](../api/basic_json/to_cbor.md), [`to_ubjson()`](../api/basic_json/to_ubjson.md),
|
|
||||||
[`to_bjdata()`](../api/basic_json/to_bjdata.md), and [`to_bson()`](../api/basic_json/to_bson.md) throw this exception
|
|
||||||
as well for a string value or object key that is not valid UTF-8 if their `error_handler` is `strict` (the default if
|
|
||||||
[`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md) is enabled). So does
|
|
||||||
[`to_msgpack()`](../api/basic_json/to_msgpack.md) if `error_handler_t::strict` is passed.
|
|
||||||
|
|
||||||
!!! failure "Example message"
|
!!! failure "Example message"
|
||||||
|
|
||||||
Calling `dump()` on a JSON value containing an ISO 8859-1 encoded string:
|
Calling `dump()` on a JSON value containing an ISO 8859-1 encoded string:
|
||||||
|
|||||||
@@ -204,11 +204,6 @@ Use the non-amalgamated version of the library. This option is `ON` by default.
|
|||||||
|
|
||||||
Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to check for this library by tools like Clang-Tidy. This option is `OFF` by default.
|
Treat the library headers like system headers (i.e., adding `SYSTEM` to the [`target_include_directories`](https://cmake.org/cmake/help/latest/command/target_include_directories.html) call) to check for this library by tools like Clang-Tidy. This option is `OFF` by default.
|
||||||
|
|
||||||
### `JSON_StrictBinaryUTF8`
|
|
||||||
|
|
||||||
Check string values and object keys for valid UTF-8 in the CBOR, UBJSON, BJData, and BSON writers, by defining the
|
|
||||||
macro [`JSON_STRICT_BINARY_UTF8`](../api/macros/json_strict_binary_utf8.md). This option is `OFF` by default.
|
|
||||||
|
|
||||||
### `JSON_StrictNulHandling`
|
### `JSON_StrictNulHandling`
|
||||||
|
|
||||||
Reject a `'\0'` (NUL) byte in the input instead of treating it as end of input, by defining the macro
|
Reject a `'\0'` (NUL) byte in the input instead of treating it as end of input, by defining the macro
|
||||||
|
|||||||
@@ -121,10 +121,23 @@ meson wrap install nlohmann_json
|
|||||||
Please see the Meson project for any issues regarding the packaging.
|
Please see the Meson project for any issues regarding the packaging.
|
||||||
|
|
||||||
The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in
|
The provided `meson.build` can also be used as an alternative to CMake for installing `nlohmann_json` system-wide in
|
||||||
which case a pkg-config file is installed. To use it, have your build system require the `nlohmann_json`
|
which case a pkg-config file and the CMake package config files are installed. To use it, have your build system require
|
||||||
pkg-config dependency. In Meson, it is preferred to use the
|
the `nlohmann_json` pkg-config dependency, or use [`find_package(nlohmann_json)`](cmake.md#external) in CMake. In Meson,
|
||||||
[`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a subproject fallback, rather than
|
it is preferred to use the [`dependency()`](https://mesonbuild.com/Reference-manual.html#dependency) object with a
|
||||||
using the subproject directly.
|
subproject fallback, rather than using the subproject directly.
|
||||||
|
|
||||||
|
The options that change the library's configuration are available in Meson as well, named like the
|
||||||
|
[CMake options](cmake.md#cmake-options) without the `JSON_` prefix: `MultipleHeaders`, `GlobalUDLs`,
|
||||||
|
`ImplicitConversions`, `DisableEnumSerialization`, `DisableTupleReferenceConversion`, `Diagnostics`,
|
||||||
|
`Diagnostic_Positions`, `LegacyDiscardedValueComparison`, and `StrictNulHandling`. They have the same defaults as in CMake, except that
|
||||||
|
`MultipleHeaders` is `false`. Set them with `-D` when setting up the build, or with the subproject name as prefix when
|
||||||
|
the library is used as a subproject:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
meson setup build -Dnlohmann_json:Diagnostics=true
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting compile definitions are part of the Meson dependency, the pkg-config file, and the CMake target.
|
||||||
|
|
||||||
??? example "Example: Wrap"
|
??? example "Example: Wrap"
|
||||||
|
|
||||||
|
|||||||
@@ -301,7 +301,6 @@ nav:
|
|||||||
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
|
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
|
||||||
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
|
||||||
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
|
||||||
- 'JSON_STRICT_BINARY_UTF8': api/macros/json_strict_binary_utf8.md
|
|
||||||
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
- 'JSON_STRICT_NUL_HANDLING': api/macros/json_strict_nul_handling.md
|
||||||
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
- 'JSON_USE_GLOBAL_UDLS': api/macros/json_use_global_udls.md
|
||||||
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
- 'JSON_USE_IMPLICIT_CONVERSIONS': api/macros/json_use_implicit_conversions.md
|
||||||
|
|||||||
@@ -46,10 +46,6 @@
|
|||||||
#define JSON_STRICT_NUL_HANDLING 0
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_STRICT_BINARY_UTF8
|
|
||||||
#define JSON_STRICT_BINARY_UTF8 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -86,20 +82,14 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_STRICT_BINARY_UTF8
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8 _sbu8
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -108,8 +98,7 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/is_sax.hpp>
|
#include <nlohmann/detail/meta/is_sax.hpp>
|
||||||
#include <nlohmann/detail/meta/type_traits.hpp>
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
||||||
#include <nlohmann/detail/output/error_handler.hpp>
|
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
#include <nlohmann/detail/value_t.hpp>
|
#include <nlohmann/detail/value_t.hpp>
|
||||||
@@ -109,16 +108,8 @@ class binary_reader
|
|||||||
@brief create a binary reader
|
@brief create a binary reader
|
||||||
|
|
||||||
@param[in] adapter input adapter to read from
|
@param[in] adapter input adapter to read from
|
||||||
@param[in] format the binary format to parse
|
|
||||||
@param[in] error_handler how to treat text strings and object keys that
|
|
||||||
are not well-formed UTF-8; none of the supported formats
|
|
||||||
requires a decoder to reject those, so the default is to
|
|
||||||
@ref error_handler_t::keep them unchanged, as every binary
|
|
||||||
reader did before this parameter existed
|
|
||||||
*/
|
*/
|
||||||
explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json,
|
explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)
|
||||||
const error_handler_t error_handler = error_handler_t::keep) noexcept
|
|
||||||
: ia(std::move(adapter)), input_format(format), error_handler(error_handler)
|
|
||||||
{
|
{
|
||||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||||
}
|
}
|
||||||
@@ -437,7 +428,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
if (get_bson_cstr_bulk(result, std::integral_constant<bool, bulk_scan> {}))
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto out = std::back_inserter(result);
|
auto out = std::back_inserter(result);
|
||||||
@@ -450,7 +441,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
if (current == 0x00)
|
if (current == 0x00)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, "key");
|
return true;
|
||||||
}
|
}
|
||||||
*out++ = static_cast<typename string_t::value_type>(current);
|
*out++ = static_cast<typename string_t::value_type>(current);
|
||||||
}
|
}
|
||||||
@@ -531,7 +522,7 @@ class binary_reader
|
|||||||
"string"), nullptr));
|
"string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return check_string_utf8(result, "string");
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -1158,7 +1149,7 @@ class binary_reader
|
|||||||
|
|
||||||
@return whether string creation completed
|
@return whether string creation completed
|
||||||
*/
|
*/
|
||||||
bool get_cbor_string(string_t& result, const char* context = "string")
|
bool get_cbor_string(string_t& result)
|
||||||
{
|
{
|
||||||
// number of indefinite-length strings that have been opened and not
|
// number of indefinite-length strings that have been opened and not
|
||||||
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
// closed yet. RFC 8949, Section 3.2.3 does not permit nesting them,
|
||||||
@@ -1188,7 +1179,7 @@ class binary_reader
|
|||||||
{
|
{
|
||||||
if (--open == 0)
|
if (--open == 0)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, context);
|
return true;
|
||||||
}
|
}
|
||||||
get();
|
get();
|
||||||
continue;
|
continue;
|
||||||
@@ -1201,7 +1192,7 @@ class binary_reader
|
|||||||
|
|
||||||
if (open == 0)
|
if (open == 0)
|
||||||
{
|
{
|
||||||
return check_string_utf8(result, context);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
get();
|
get();
|
||||||
@@ -1225,7 +1216,7 @@ class binary_reader
|
|||||||
// EOF and major type 3 (text string) are left to get_cbor_string
|
// EOF and major type 3 (text string) are left to get_cbor_string
|
||||||
if (current == char_traits<char_type>::eof() || (static_cast<unsigned int>(current) & 0xE0u) == 0x60u)
|
if (current == char_traits<char_type>::eof() || (static_cast<unsigned int>(current) & 0xE0u) == 0x60u)
|
||||||
{
|
{
|
||||||
return get_cbor_string(result, "key");
|
return get_cbor_string(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* found = nullptr;
|
const char* found = nullptr;
|
||||||
@@ -2013,7 +2004,7 @@ class binary_reader
|
|||||||
|
|
||||||
@return whether string creation completed
|
@return whether string creation completed
|
||||||
*/
|
*/
|
||||||
bool get_msgpack_string(string_t& result, const char* context = "string")
|
bool get_msgpack_string(string_t& result)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
|
if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
|
||||||
{
|
{
|
||||||
@@ -2056,25 +2047,25 @@ class binary_reader
|
|||||||
case 0xBE:
|
case 0xBE:
|
||||||
case 0xBF:
|
case 0xBF:
|
||||||
{
|
{
|
||||||
return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result) && check_string_utf8(result, context);
|
return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xD9: // str 8
|
case 0xD9: // str 8
|
||||||
{
|
{
|
||||||
std::uint8_t len{};
|
std::uint8_t len{};
|
||||||
return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result) && check_string_utf8(result, context);
|
return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDA: // str 16
|
case 0xDA: // str 16
|
||||||
{
|
{
|
||||||
std::uint16_t len{};
|
std::uint16_t len{};
|
||||||
return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result) && check_string_utf8(result, context);
|
return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xDB: // str 32
|
case 0xDB: // str 32
|
||||||
{
|
{
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result) && check_string_utf8(result, context);
|
return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -2152,7 +2143,7 @@ class binary_reader
|
|||||||
// byte 0xC1 are left to get_msgpack_string
|
// byte 0xC1 are left to get_msgpack_string
|
||||||
if (current == char_traits<char_type>::eof())
|
if (current == char_traits<char_type>::eof())
|
||||||
{
|
{
|
||||||
return get_msgpack_string(result, "key");
|
return get_msgpack_string(result);
|
||||||
}
|
}
|
||||||
if (current <= 0x7F || current >= 0xE0)
|
if (current <= 0x7F || current >= 0xE0)
|
||||||
{
|
{
|
||||||
@@ -2168,7 +2159,7 @@ class binary_reader
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return get_msgpack_string(result, "key");
|
return get_msgpack_string(result);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2414,7 +2405,7 @@ class binary_reader
|
|||||||
if (top.is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, true, "key") || !sax->key(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2436,7 +2427,7 @@ class binary_reader
|
|||||||
if (top.is_object)
|
if (top.is_object)
|
||||||
{
|
{
|
||||||
key.clear();
|
key.clear();
|
||||||
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false, "key") || !sax->key(key)))
|
if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2504,7 +2495,7 @@ class binary_reader
|
|||||||
|
|
||||||
@return whether string creation completed
|
@return whether string creation completed
|
||||||
*/
|
*/
|
||||||
bool get_ubjson_string(string_t& result, const bool get_char = true, const char* context = "string")
|
bool get_ubjson_string(string_t& result, const bool get_char = true)
|
||||||
{
|
{
|
||||||
if (get_char)
|
if (get_char)
|
||||||
{
|
{
|
||||||
@@ -2525,31 +2516,31 @@ class binary_reader
|
|||||||
case 'U':
|
case 'U':
|
||||||
{
|
{
|
||||||
std::uint8_t len{};
|
std::uint8_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -2559,7 +2550,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint16_t len{};
|
std::uint16_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'm':
|
case 'm':
|
||||||
@@ -2569,7 +2560,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint32_t len{};
|
std::uint32_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
@@ -2579,7 +2570,7 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
std::uint64_t len{};
|
std::uint64_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result) && check_string_utf8(result, context);
|
return get_number(input_format, len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -4040,50 +4031,27 @@ class binary_reader
|
|||||||
const NumberType len,
|
const NumberType len,
|
||||||
string_t& result)
|
string_t& result)
|
||||||
{
|
{
|
||||||
// Strings are taken as is by default: none of CBOR (RFC 8949 §3.1
|
// get_bytes() appends to result, and CBOR indefinite-length strings
|
||||||
// leaves the choice to the decoder), MessagePack (whose spec
|
// collect all their chunks in the same result; validating only the
|
||||||
// explicitly allows a str object to contain an invalid byte
|
// newly read bytes keeps the check linear in the input size
|
||||||
// sequence), UBJSON, BJData, or BSON requires a decoder to reject
|
const std::size_t old_size = result.size();
|
||||||
// ill-formed UTF-8. Checking (and, with @ref error_handler_t::strict,
|
if (JSON_HEDLEY_UNLIKELY(!get_bytes(format, len, "string", result)))
|
||||||
// rejecting, or with `replace`/`ignore`, sanitizing) is opt-in via
|
|
||||||
// @ref error_handler, applied once the whole string (all chunks of
|
|
||||||
// an indefinite-length CBOR string included) has been assembled, by
|
|
||||||
// @ref check_string_utf8 at the call site.
|
|
||||||
return get_bytes(format, len, "string", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief validate a decoded text string (value or object key) against @ref error_handler
|
|
||||||
|
|
||||||
None of the binary formats requires a decoder to reject ill-formed UTF-8
|
|
||||||
in a text string (see @ref get_string), so by default
|
|
||||||
(@ref error_handler_t::keep) this does nothing. A stricter
|
|
||||||
@ref error_handler opts into the same well-formedness check @ref
|
|
||||||
serializer::dump_escaped_impl applies when dumping a string:
|
|
||||||
@ref error_handler_t::strict rejects ill-formed input with
|
|
||||||
parse_error.113 (honoring `allow_exceptions` via @a sax), while
|
|
||||||
@ref error_handler_t::replace / @ref error_handler_t::ignore sanitize
|
|
||||||
@a result in place, using the exact same rules.
|
|
||||||
|
|
||||||
@param[in,out] result the already assembled string to check
|
|
||||||
@param[in] context further context information (for diagnostics)
|
|
||||||
@return whether @a result is acceptable (always true for `keep`)
|
|
||||||
*/
|
|
||||||
bool check_string_utf8(string_t& result, const char* context)
|
|
||||||
{
|
|
||||||
if (error_handler == error_handler_t::keep || is_valid_utf8(result))
|
|
||||||
{
|
{
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error_handler == error_handler_t::strict)
|
// RFC 8949 (CBOR) §3.1 and the MessagePack/BSON/UBJSON specifications
|
||||||
|
// all require text strings to be valid UTF-8; reject anything else
|
||||||
|
// right here so malformed input is caught at decode time instead of
|
||||||
|
// only surfacing later as a type_error.316 when the value is dumped
|
||||||
|
// (which would defeat allow_exceptions=false / strict discarding).
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(!is_valid_utf8(result, old_size)))
|
||||||
{
|
{
|
||||||
auto last_token = get_token_string();
|
return sax->parse_error(chars_read, get_token_string(),
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
parse_error::create(113, chars_read,
|
||||||
exception_message(input_format, "invalid string: ill-formed UTF-8 byte", context), nullptr));
|
exception_message(format, "invalid string: ill-formed UTF-8 byte", "string"), nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
result = sanitize_utf8(result, error_handler);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4258,9 +4226,6 @@ class binary_reader
|
|||||||
/// input format
|
/// input format
|
||||||
const input_format_t input_format = input_format_t::json;
|
const input_format_t input_format = input_format_t::json;
|
||||||
|
|
||||||
/// how to treat text strings/object keys that are not well-formed UTF-8
|
|
||||||
const error_handler_t error_handler = error_handler_t::keep;
|
|
||||||
|
|
||||||
/// the SAX parser
|
/// the SAX parser
|
||||||
json_sax_t* sax = nullptr;
|
json_sax_t* sax = nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@
|
|||||||
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
#undef JSON_BRACE_INIT_COPY_SEMANTICS
|
||||||
#undef JSON_PRECISE_STREAM_POSITION
|
#undef JSON_PRECISE_STREAM_POSITION
|
||||||
#undef JSON_STRICT_NUL_HANDLING
|
#undef JSON_STRICT_NUL_HANDLING
|
||||||
#undef JSON_STRICT_BINARY_UTF8
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
#include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#include <nlohmann/detail/input/binary_reader.hpp>
|
#include <nlohmann/detail/input/binary_reader.hpp>
|
||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
#include <nlohmann/detail/input/string_scan.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/output/error_handler.hpp>
|
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
#include <nlohmann/detail/string_utils.hpp>
|
#include <nlohmann/detail/string_utils.hpp>
|
||||||
@@ -94,12 +93,8 @@ class binary_writer
|
|||||||
@param[in] sink output sink to write to (a value-type sink such as
|
@param[in] sink output sink to write to (a value-type sink such as
|
||||||
output_vector_sink, or output_adapter_sink wrapping a
|
output_vector_sink, or output_adapter_sink wrapping a
|
||||||
type-erased output adapter)
|
type-erased output adapter)
|
||||||
@param[in] error_handler_ how to treat a string value or object key that
|
|
||||||
is not valid UTF-8 (CBOR, MessagePack, UBJSON, BJData, and BSON;
|
|
||||||
never consulted by @ref write_bon8)
|
|
||||||
*/
|
*/
|
||||||
explicit binary_writer(OutputSinkType sink, const error_handler_t error_handler_ = binary_writer_default_error_handler())
|
explicit binary_writer(OutputSinkType sink) : oa(std::move(sink))
|
||||||
: oa(std::move(sink)), error_handler(error_handler_)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -112,20 +107,14 @@ class binary_writer
|
|||||||
from one.
|
from one.
|
||||||
|
|
||||||
@param[in] adapter output adapter to write to
|
@param[in] adapter output adapter to write to
|
||||||
@param[in] error_handler_ how to treat a string value or object key that
|
|
||||||
is not valid UTF-8 (CBOR, MessagePack, UBJSON, BJData, and BSON;
|
|
||||||
never consulted by @ref write_bon8)
|
|
||||||
*/
|
*/
|
||||||
template < typename SinkType = OutputSinkType,
|
template < typename SinkType = OutputSinkType,
|
||||||
typename std::enable_if < std::is_constructible<SinkType, output_adapter_t<CharType>>::value, int >::type = 0 >
|
typename std::enable_if < std::is_constructible<SinkType, output_adapter_t<CharType>>::value, int >::type = 0 >
|
||||||
explicit binary_writer(output_adapter_t<CharType> adapter, const error_handler_t error_handler_ = binary_writer_default_error_handler())
|
explicit binary_writer(output_adapter_t<CharType> adapter) : oa(SinkType(std::move(adapter)))
|
||||||
: oa(SinkType(std::move(adapter))), error_handler(error_handler_)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
@throw type_error.316 if a string value or an object key is not valid
|
|
||||||
UTF-8
|
|
||||||
@throw type_error.317 if @a j is not an object
|
@throw type_error.317 if @a j is not an object
|
||||||
*/
|
*/
|
||||||
void write_bson(const BasicJsonType& j)
|
void write_bson(const BasicJsonType& j)
|
||||||
@@ -156,8 +145,6 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] j JSON value to serialize
|
@param[in] j JSON value to serialize
|
||||||
@throw type_error.316 if a string value or an object key is not valid
|
|
||||||
UTF-8
|
|
||||||
*/
|
*/
|
||||||
void write_cbor(const BasicJsonType& j)
|
void write_cbor(const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
@@ -224,16 +211,13 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
string_t storage;
|
|
||||||
const string_t& value = sanitize_utf8_for_write(*j.m_data.m_value.string, j, storage);
|
|
||||||
|
|
||||||
// step 1: write control byte and the string length
|
// step 1: write control byte and the string length
|
||||||
write_cbor_head(0x60, value.size());
|
write_cbor_head(0x60, j.m_data.m_value.string->size());
|
||||||
|
|
||||||
// step 2: write the string
|
// step 2: write the string
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(value.data()),
|
reinterpret_cast<const CharType*>(j.m_data.m_value.string->data()),
|
||||||
value.size());
|
j.m_data.m_value.string->size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,17 +287,6 @@ class binary_writer
|
|||||||
// step 2: write each element
|
// step 2: write each element
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
// el.first is checked here, against the object as
|
|
||||||
// diagnostics context, because write_cbor(el.first)
|
|
||||||
// converts it to a temporary basic_json that would be
|
|
||||||
// used as the context instead; for error_handler_t::keep
|
|
||||||
// and ::replace/::ignore the recursive write_cbor(el.first)
|
|
||||||
// call below handles the key like any other string, so no
|
|
||||||
// separate check is needed here for those
|
|
||||||
if (error_handler == error_handler_t::strict)
|
|
||||||
{
|
|
||||||
check_utf8(el.first, j);
|
|
||||||
}
|
|
||||||
write_cbor(el.first);
|
write_cbor(el.first);
|
||||||
write_cbor(el.second);
|
write_cbor(el.second);
|
||||||
}
|
}
|
||||||
@@ -461,11 +434,8 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
string_t storage;
|
|
||||||
const string_t& value = sanitize_utf8_for_write(*j.m_data.m_value.string, j, storage);
|
|
||||||
|
|
||||||
// step 1: write control byte and the string length
|
// step 1: write control byte and the string length
|
||||||
const auto N = to_msgpack_length(value.size(), j);
|
const auto N = to_msgpack_length(j.m_data.m_value.string->size(), j);
|
||||||
if (N <= 31)
|
if (N <= 31)
|
||||||
{
|
{
|
||||||
// fixstr
|
// fixstr
|
||||||
@@ -492,8 +462,8 @@ class binary_writer
|
|||||||
|
|
||||||
// step 2: write the string
|
// step 2: write the string
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(value.data()),
|
reinterpret_cast<const CharType*>(j.m_data.m_value.string->data()),
|
||||||
value.size());
|
j.m_data.m_value.string->size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,13 +610,6 @@ class binary_writer
|
|||||||
// step 2: write each element
|
// step 2: write each element
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
// as in write_cbor, el.first is checked here against the
|
|
||||||
// object as diagnostics context; the recursive call below
|
|
||||||
// handles keep/replace/ignore like any other string
|
|
||||||
if (error_handler == error_handler_t::strict)
|
|
||||||
{
|
|
||||||
check_utf8(el.first, j);
|
|
||||||
}
|
|
||||||
write_msgpack(el.first);
|
write_msgpack(el.first);
|
||||||
write_msgpack(el.second);
|
write_msgpack(el.second);
|
||||||
}
|
}
|
||||||
@@ -666,8 +629,6 @@ class binary_writer
|
|||||||
@param[in] add_prefix whether prefixes need to be used for this value
|
@param[in] add_prefix whether prefixes need to be used for this value
|
||||||
@param[in] use_bjdata whether write in BJData format, default is false
|
@param[in] use_bjdata whether write in BJData format, default is false
|
||||||
@param[in] bjdata_version which BJData version to use, default is draft2
|
@param[in] bjdata_version which BJData version to use, default is draft2
|
||||||
@throw type_error.316 if a string value or an object key is not valid
|
|
||||||
UTF-8
|
|
||||||
*/
|
*/
|
||||||
void write_ubjson(const BasicJsonType& j, const bool use_count,
|
void write_ubjson(const BasicJsonType& j, const bool use_count,
|
||||||
const bool use_type, const bool add_prefix = true,
|
const bool use_type, const bool add_prefix = true,
|
||||||
@@ -717,17 +678,14 @@ class binary_writer
|
|||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
{
|
{
|
||||||
string_t storage;
|
|
||||||
const string_t& value = sanitize_utf8_for_write(*j.m_data.m_value.string, j, storage);
|
|
||||||
|
|
||||||
if (add_prefix)
|
if (add_prefix)
|
||||||
{
|
{
|
||||||
oa.write_character(to_char_type('S'));
|
oa.write_character(to_char_type('S'));
|
||||||
}
|
}
|
||||||
write_number_with_ubjson_prefix(value.size(), true, use_bjdata);
|
write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata);
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(value.data()),
|
reinterpret_cast<const CharType*>(j.m_data.m_value.string->data()),
|
||||||
value.size());
|
j.m_data.m_value.string->size());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,12 +840,10 @@ class binary_writer
|
|||||||
|
|
||||||
for (const auto& el : *j.m_data.m_value.object)
|
for (const auto& el : *j.m_data.m_value.object)
|
||||||
{
|
{
|
||||||
string_t storage;
|
write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
|
||||||
const string_t& key = sanitize_utf8_for_write(el.first, j, storage);
|
|
||||||
write_number_with_ubjson_prefix(key.size(), true, use_bjdata);
|
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(key.data()),
|
reinterpret_cast<const CharType*>(el.first.data()),
|
||||||
key.size());
|
el.first.size());
|
||||||
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version);
|
write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,12 +884,8 @@ class binary_writer
|
|||||||
/*!
|
/*!
|
||||||
@return The size of a BSON document entry header, including the id marker
|
@return The size of a BSON document entry header, including the id marker
|
||||||
and the entry name size (and its null-terminator).
|
and the entry name size (and its null-terminator).
|
||||||
@throw out_of_range.409 if @a name contains U+0000, before anything is
|
|
||||||
written
|
|
||||||
@throw type_error.316 if @a name is not valid UTF-8, before anything is
|
|
||||||
written
|
|
||||||
*/
|
*/
|
||||||
std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
|
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
const auto it = name.find(static_cast<typename string_t::value_type>(0));
|
const auto it = name.find(static_cast<typename string_t::value_type>(0));
|
||||||
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
|
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
|
||||||
@@ -941,10 +893,8 @@ class binary_writer
|
|||||||
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
string_t storage;
|
static_cast<void>(j);
|
||||||
const string_t& sanitized = sanitize_utf8_for_write(name, j, storage);
|
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
|
||||||
|
|
||||||
return /*id*/ 1ul + sanitized.size() + /*zero-terminator*/1u;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -964,28 +914,14 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Writes the given @a element_type and @a name to the output adapter
|
@brief Writes the given @a element_type and @a name to the output adapter
|
||||||
|
|
||||||
@a name has already been validated (and, for @ref error_handler_t::strict,
|
|
||||||
found well-formed) by @ref calc_bson_entry_header_size during the earlier
|
|
||||||
size pass, so only @ref error_handler_t::replace / @ref
|
|
||||||
error_handler_t::ignore need to sanitize it again here, to actually write
|
|
||||||
the bytes that size was computed from.
|
|
||||||
*/
|
*/
|
||||||
void write_bson_entry_header(const string_t& name,
|
void write_bson_entry_header(const string_t& name,
|
||||||
const std::uint8_t element_type)
|
const std::uint8_t element_type)
|
||||||
{
|
{
|
||||||
oa.write_character(to_char_type(element_type));
|
oa.write_character(to_char_type(element_type));
|
||||||
|
oa.write_characters(
|
||||||
if (error_handler == error_handler_t::keep || error_handler == error_handler_t::strict || is_valid_utf8(name))
|
reinterpret_cast<const CharType*>(name.data()),
|
||||||
{
|
name.size());
|
||||||
oa.write_characters(reinterpret_cast<const CharType*>(name.data()), name.size());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const string_t sanitized = sanitize_utf8(name, error_handler);
|
|
||||||
oa.write_characters(reinterpret_cast<const CharType*>(sanitized.data()), sanitized.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// the terminating null byte is written explicitly rather than taken
|
// the terminating null byte is written explicitly rather than taken
|
||||||
// from the buffer, so that string_t::data() need not be null-terminated
|
// from the buffer, so that string_t::data() need not be null-terminated
|
||||||
oa.write_character(to_char_type(0x00));
|
oa.write_character(to_char_type(0x00));
|
||||||
@@ -1013,50 +949,24 @@ class binary_writer
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
@return The size of the BSON-encoded string in @a value
|
@return The size of the BSON-encoded string in @a value
|
||||||
@throw type_error.316 if @a value is not valid UTF-8, before anything is
|
|
||||||
written
|
|
||||||
|
|
||||||
@note The UTF-8 check is skipped if @a value is already too long for the
|
|
||||||
32-bit BSON length field (@ref to_bson_length rejects it later, once
|
|
||||||
the size of the whole document is known); this also keeps the check
|
|
||||||
from reading past a StringType that reports a size larger than what
|
|
||||||
it actually holds.
|
|
||||||
*/
|
*/
|
||||||
std::size_t calc_bson_string_size(const string_t& value, const BasicJsonType& j)
|
static std::size_t calc_bson_string_size(const string_t& value)
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_LIKELY(value_in_range_of<std::int32_t>(value.size())))
|
|
||||||
{
|
|
||||||
string_t storage;
|
|
||||||
const string_t& sanitized = sanitize_utf8_for_write(value, j, storage);
|
|
||||||
return sizeof(std::int32_t) + sanitized.size() + 1ul;
|
|
||||||
}
|
|
||||||
return sizeof(std::int32_t) + value.size() + 1ul;
|
return sizeof(std::int32_t) + value.size() + 1ul;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief Writes a BSON element with key @a name and string value @a value
|
@brief Writes a BSON element with key @a name and string value @a value
|
||||||
|
|
||||||
@a value has already been validated (and, for @ref error_handler_t::strict,
|
|
||||||
found well-formed) by @ref calc_bson_string_size during the earlier size
|
|
||||||
pass, so only @ref error_handler_t::replace / @ref error_handler_t::ignore
|
|
||||||
need to sanitize it again here, to actually write the bytes that size was
|
|
||||||
computed from.
|
|
||||||
*/
|
*/
|
||||||
void write_bson_string(const string_t& name,
|
void write_bson_string(const string_t& name,
|
||||||
const string_t& value)
|
const string_t& value)
|
||||||
{
|
{
|
||||||
write_bson_entry_header(name, 0x02);
|
write_bson_entry_header(name, 0x02);
|
||||||
|
|
||||||
const bool sanitize = error_handler != error_handler_t::keep
|
write_number<std::int32_t>(to_bson_length(value.size() + 1ul), true);
|
||||||
&& error_handler != error_handler_t::strict
|
|
||||||
&& !is_valid_utf8(value);
|
|
||||||
const string_t sanitized = sanitize ? sanitize_utf8(value, error_handler) : string_t{};
|
|
||||||
const string_t& written = sanitize ? sanitized : value;
|
|
||||||
|
|
||||||
write_number<std::int32_t>(to_bson_length(written.size() + 1ul), true);
|
|
||||||
oa.write_characters(
|
oa.write_characters(
|
||||||
reinterpret_cast<const CharType*>(written.data()),
|
reinterpret_cast<const CharType*>(value.data()),
|
||||||
written.size());
|
value.size());
|
||||||
// the terminating null byte is written explicitly rather than taken
|
// the terminating null byte is written explicitly rather than taken
|
||||||
// from the buffer, so that string_t::data() need not be null-terminated
|
// from the buffer, so that string_t::data() need not be null-terminated
|
||||||
oa.write_character(to_char_type(0x00));
|
oa.write_character(to_char_type(0x00));
|
||||||
@@ -1170,10 +1080,8 @@ class binary_writer
|
|||||||
is neither an object nor an array
|
is neither an object nor an array
|
||||||
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
|
@throw out_of_range.415 if @a j is binary with a subtype that does not fit
|
||||||
into a byte, before anything is written
|
into a byte, before anything is written
|
||||||
@throw type_error.316 if @a j is a string that is not valid UTF-8, before
|
|
||||||
anything is written
|
|
||||||
*/
|
*/
|
||||||
std::size_t calc_bson_value_size(const BasicJsonType& j)
|
static std::size_t calc_bson_value_size(const BasicJsonType& j)
|
||||||
{
|
{
|
||||||
switch (j.type())
|
switch (j.type())
|
||||||
{
|
{
|
||||||
@@ -1193,7 +1101,7 @@ class binary_writer
|
|||||||
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
return calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
|
||||||
|
|
||||||
case value_t::string:
|
case value_t::string:
|
||||||
return calc_bson_string_size(*j.m_data.m_value.string, j);
|
return calc_bson_string_size(*j.m_data.m_value.string);
|
||||||
|
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
return 0ul;
|
return 0ul;
|
||||||
@@ -1306,10 +1214,8 @@ class binary_writer
|
|||||||
written
|
written
|
||||||
@throw out_of_range.415 if a binary value's subtype does not fit into a
|
@throw out_of_range.415 if a binary value's subtype does not fit into a
|
||||||
byte, before anything is written
|
byte, before anything is written
|
||||||
@throw type_error.316 if a string value or a key is not valid UTF-8,
|
|
||||||
before anything is written
|
|
||||||
*/
|
*/
|
||||||
std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
static std::size_t calc_bson_sizes(const BasicJsonType& document, std::vector<std::size_t>& nested_sizes)
|
||||||
{
|
{
|
||||||
// the object or array whose entries are being sized, and the ones it
|
// the object or array whose entries are being sized, and the ones it
|
||||||
// is in; nothing is allocated unless the document nests
|
// is in; nothing is allocated unless the document nests
|
||||||
@@ -2186,7 +2092,7 @@ class binary_writer
|
|||||||
*/
|
*/
|
||||||
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
void write_bon8_string(const string_t& s, bool& string_open, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
check_utf8(s, context);
|
check_bon8_utf8(s, context);
|
||||||
|
|
||||||
// a string that follows another string terminates it
|
// a string that follows another string terminates it
|
||||||
if (string_open)
|
if (string_open)
|
||||||
@@ -2216,7 +2122,7 @@ class binary_writer
|
|||||||
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
@throw type_error.316 if @a s is not valid UTF-8; the message names the
|
||||||
first byte of the first invalid or incomplete sequence
|
first byte of the first invalid or incomplete sequence
|
||||||
*/
|
*/
|
||||||
static void check_utf8(const string_t& s, const BasicJsonType& context)
|
static void check_bon8_utf8(const string_t& s, const BasicJsonType& context)
|
||||||
{
|
{
|
||||||
static_cast<void>(context); // only used when exceptions are enabled
|
static_cast<void>(context); // only used when exceptions are enabled
|
||||||
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
const auto* data = reinterpret_cast<const unsigned char*>(s.data());
|
||||||
@@ -2227,57 +2133,6 @@ class binary_writer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief return @a s as it should be written, honoring @ref error_handler
|
|
||||||
|
|
||||||
Used by @ref write_cbor, @ref write_msgpack, @ref write_ubjson (and so
|
|
||||||
@ref write_bjdata), and the BSON writing functions for string values and
|
|
||||||
object keys; never by @ref write_bon8, which always validates, since UTF-8
|
|
||||||
lead bytes are structural there.
|
|
||||||
|
|
||||||
- @ref error_handler_t::keep: @a s is returned unchanged, without even
|
|
||||||
checking it (the behavior of release 3.12.0 and earlier).
|
|
||||||
- @ref error_handler_t::strict: @ref check_utf8 is called, which throws
|
|
||||||
type_error.316 if @a s is not valid UTF-8.
|
|
||||||
- @ref error_handler_t::replace / @ref error_handler_t::ignore: @a s is
|
|
||||||
sanitized into @a storage with exactly the rules @ref
|
|
||||||
serializer::dump_escaped_impl uses, so that parsing what @ref
|
|
||||||
basic_json::dump produces for the same string and the same handler
|
|
||||||
yields the same result.
|
|
||||||
|
|
||||||
Well-formed input is never copied: this returns a reference to @a s
|
|
||||||
itself in every case but a sanitized `replace`/`ignore` one, so @a
|
|
||||||
storage must outlive the returned reference only then.
|
|
||||||
|
|
||||||
@param[in] s the string (value or object key) to write
|
|
||||||
@param[in] context the value @a s belongs to (for diagnostics)
|
|
||||||
@param[out] storage backing storage for a sanitized copy
|
|
||||||
|
|
||||||
@return a reference to @a s, or to @a storage once it holds a sanitized copy
|
|
||||||
*/
|
|
||||||
const string_t& sanitize_utf8_for_write(const string_t& s, const BasicJsonType& context, string_t& storage) const
|
|
||||||
{
|
|
||||||
switch (error_handler)
|
|
||||||
{
|
|
||||||
case error_handler_t::keep:
|
|
||||||
return s;
|
|
||||||
|
|
||||||
case error_handler_t::strict:
|
|
||||||
check_utf8(s, context);
|
|
||||||
return s;
|
|
||||||
|
|
||||||
case error_handler_t::replace:
|
|
||||||
case error_handler_t::ignore:
|
|
||||||
default:
|
|
||||||
if (is_valid_utf8(s))
|
|
||||||
{
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
storage = sanitize_utf8(s, error_handler);
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief write an integer in the shortest encoding
|
@brief write an integer in the shortest encoding
|
||||||
|
|
||||||
@@ -2606,10 +2461,6 @@ class binary_writer
|
|||||||
|
|
||||||
/// the output
|
/// the output
|
||||||
OutputSinkType oa;
|
OutputSinkType oa;
|
||||||
|
|
||||||
/// how to treat a string value or object key that is not valid UTF-8
|
|
||||||
/// (CBOR, MessagePack, UBJSON, BJData, and BSON; not BON8)
|
|
||||||
const error_handler_t error_handler = binary_writer_default_error_handler();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
|
|
||||||
/// how to treat decoding errors
|
|
||||||
///
|
|
||||||
/// @ref basic_json::dump uses this to decide what to do with ill-formed
|
|
||||||
/// UTF-8 while escaping a string, and the binary writers (@ref
|
|
||||||
/// basic_json::to_cbor, @ref basic_json::to_ubjson, @ref
|
|
||||||
/// basic_json::to_bjdata, @ref basic_json::to_bson) use it the same way for
|
|
||||||
/// string values and object keys. The binary readers (@ref
|
|
||||||
/// basic_json::from_cbor, @ref basic_json::from_msgpack, @ref
|
|
||||||
/// basic_json::from_ubjson, @ref basic_json::from_bjdata, @ref
|
|
||||||
/// basic_json::from_bson) use it to decide whether to check text strings
|
|
||||||
/// and object keys for well-formed UTF-8 at all, since none of those
|
|
||||||
/// formats requires a decoder to do so.
|
|
||||||
enum class error_handler_t
|
|
||||||
{
|
|
||||||
strict, ///< throw a type_error/parse_error exception in case of invalid UTF-8
|
|
||||||
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
|
||||||
ignore, ///< ignore invalid UTF-8 sequences
|
|
||||||
keep ///< keep invalid UTF-8 sequences unchanged
|
|
||||||
};
|
|
||||||
|
|
||||||
/// the default error handler of the CBOR, UBJSON, BJData, and BSON writers:
|
|
||||||
/// error_handler_t::strict if JSON_STRICT_BINARY_UTF8 is enabled, otherwise
|
|
||||||
/// error_handler_t::keep (the behavior before version 3.13.0)
|
|
||||||
constexpr error_handler_t binary_writer_default_error_handler() noexcept
|
|
||||||
{
|
|
||||||
#if JSON_STRICT_BINARY_UTF8
|
|
||||||
return error_handler_t::strict;
|
|
||||||
#else
|
|
||||||
return error_handler_t::keep;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
|
||||||
@@ -27,7 +27,6 @@
|
|||||||
#include <nlohmann/detail/input/string_scan.hpp>
|
#include <nlohmann/detail/input/string_scan.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||||
#include <nlohmann/detail/output/error_handler.hpp>
|
|
||||||
#include <nlohmann/detail/output/output_adapters.hpp>
|
#include <nlohmann/detail/output/output_adapters.hpp>
|
||||||
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
#include <nlohmann/detail/recursion_depth_limit.hpp>
|
||||||
#include <nlohmann/detail/string_concat.hpp>
|
#include <nlohmann/detail/string_concat.hpp>
|
||||||
@@ -42,6 +41,14 @@ namespace detail
|
|||||||
// serialization //
|
// serialization //
|
||||||
///////////////////
|
///////////////////
|
||||||
|
|
||||||
|
/// how to treat decoding errors
|
||||||
|
enum class error_handler_t
|
||||||
|
{
|
||||||
|
strict, ///< throw a type_error exception in case of invalid UTF-8
|
||||||
|
replace, ///< replace invalid UTF-8 sequences with U+FFFD
|
||||||
|
ignore ///< ignore invalid UTF-8 sequences
|
||||||
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
class serializer
|
class serializer
|
||||||
{
|
{
|
||||||
@@ -832,16 +839,6 @@ class serializer
|
|||||||
// EnsureAscii parameter is used, non-ASCII characters
|
// EnsureAscii parameter is used, non-ASCII characters
|
||||||
if ((codepoint <= 0x1F) || (EnsureAscii && (codepoint >= 0x7F)))
|
if ((codepoint <= 0x1F) || (EnsureAscii && (codepoint >= 0x7F)))
|
||||||
{
|
{
|
||||||
if (EnsureAscii && error_handler == error_handler_t::keep)
|
|
||||||
{
|
|
||||||
// this character was buffered as raw bytes
|
|
||||||
// below in case it turned out to be part of
|
|
||||||
// an ill-formed sequence (which is kept as
|
|
||||||
// is); now that it decoded to a well-formed
|
|
||||||
// code point, undo that and \u-escape it
|
|
||||||
// like any other character instead
|
|
||||||
bytes = bytes_after_last_accept;
|
|
||||||
}
|
|
||||||
if (codepoint <= 0xFFFF)
|
if (codepoint <= 0xFFFF)
|
||||||
{
|
{
|
||||||
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
write_u_escape(bytes, static_cast<std::uint16_t>(codepoint));
|
||||||
@@ -940,44 +937,6 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::keep:
|
|
||||||
{
|
|
||||||
// the bytes of this (now abandoned) ill-formed
|
|
||||||
// sequence seen so far are already buffered below
|
|
||||||
// and are kept unchanged in the output
|
|
||||||
if (undumped_chars > 0)
|
|
||||||
{
|
|
||||||
// the byte that ended the sequence may be OK
|
|
||||||
// for itself (e.g., a quote that must still be
|
|
||||||
// escaped, or the lead byte of a well-formed
|
|
||||||
// code point), so read it again
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// a byte that cannot start a sequence (e.g.,
|
|
||||||
// 0xFF or a stray continuation byte) is kept
|
|
||||||
// as well
|
|
||||||
string_buffer[bytes++] = s[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// write buffer and reset index; there must be 13 bytes
|
|
||||||
// left, as this is the maximal number of bytes to be
|
|
||||||
// written ("\uxxxx\uxxxx\0") for one code point
|
|
||||||
if (string_buffer.size() - bytes < 13)
|
|
||||||
{
|
|
||||||
put_buffer(string_buffer, bytes);
|
|
||||||
bytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_after_last_accept = bytes;
|
|
||||||
undumped_chars = 0;
|
|
||||||
|
|
||||||
// continue processing the string
|
|
||||||
state = UTF8_ACCEPT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
@@ -986,12 +945,9 @@ class serializer
|
|||||||
|
|
||||||
default: // decode found yet incomplete multibyte code point
|
default: // decode found yet incomplete multibyte code point
|
||||||
{
|
{
|
||||||
if (!EnsureAscii || error_handler == error_handler_t::keep)
|
if (!EnsureAscii)
|
||||||
{
|
{
|
||||||
// code point will not be escaped (or will be kept as
|
// code point will not be escaped - copy byte to buffer
|
||||||
// is if it turns out to be ill-formed) - copy byte to
|
|
||||||
// buffer; dropped again above if it decodes to a
|
|
||||||
// well-formed code point that needs \u-escaping
|
|
||||||
string_buffer[bytes++] = s[i];
|
string_buffer[bytes++] = s[i];
|
||||||
}
|
}
|
||||||
++undumped_chars;
|
++undumped_chars;
|
||||||
@@ -1042,14 +998,6 @@ class serializer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case error_handler_t::keep:
|
|
||||||
{
|
|
||||||
// write the ill-formed trailing bytes as is; they were
|
|
||||||
// buffered above regardless of EnsureAscii
|
|
||||||
put_buffer(string_buffer, bytes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // LCOV_EXCL_LINE
|
default: // LCOV_EXCL_LINE
|
||||||
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include <nlohmann/detail/abi_macros.hpp>
|
#include <nlohmann/detail/abi_macros.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
#include <nlohmann/detail/output/error_handler.hpp>
|
|
||||||
|
|
||||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
namespace detail
|
namespace detail
|
||||||
@@ -118,14 +117,13 @@ This is a single-byte step of a "shift-based" UTF-8 decoder originally
|
|||||||
written by Björn Hoehrmann. See
|
written by Björn Hoehrmann. See
|
||||||
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||||
|
|
||||||
The library checks UTF-8 well-formedness (RFC 3629, section 4) in three
|
The library checks UTF-8 well-formedness (RFC 3629, section 4) in four
|
||||||
places, which differ in speed, diagnostics, and how they read the input:
|
places, which differ in speed, diagnostics, and how they read the input:
|
||||||
|
|
||||||
- decode() below: the serializer, to escape and, in strict mode, reject
|
- decode() and @ref is_valid_utf8 below: the serializer (to escape and, in
|
||||||
ill-formed UTF-8 when dumping a string. The CBOR, MessagePack, BSON,
|
strict mode, reject ill-formed UTF-8 when dumping a string) and the CBOR,
|
||||||
UBJSON and BJData readers do not use it: none of those specs requires a
|
MessagePack, BSON, UBJSON and BJData readers (to reject ill-formed UTF-8 in
|
||||||
decoder to reject ill-formed UTF-8 in text strings, so the readers keep
|
text strings at decode time).
|
||||||
the bytes as is and leave the check to dump() and the binary writers.
|
|
||||||
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
- the per-lead-byte switch in lexer::scan_string(): JSON text, with a
|
||||||
diagnostic for each kind of error.
|
diagnostic for each kind of error.
|
||||||
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
- validate_one_utf8() and valid_utf8_prefix() in string_scan.hpp: the lexer's
|
||||||
@@ -181,19 +179,19 @@ inline std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief check a string for well-formed UTF-8 (RFC 3629, section 4)
|
@brief check whether a string consists solely of valid UTF-8
|
||||||
|
|
||||||
Used by the binary readers (CBOR, MessagePack, UBJSON, BJData, BSON) when an
|
Used by the CBOR/MessagePack/BSON/UBJSON binary readers to reject text
|
||||||
@ref error_handler_t other than `keep` is requested for a text string value
|
strings that are not valid UTF-8 at decode time (RFC 8949 §3.1 and the
|
||||||
or object key: none of those formats requires a decoder to reject ill-formed
|
MessagePack/BSON specifications all require text strings to be UTF-8), so
|
||||||
UTF-8 on its own, so the check is opt-in there, unlike the JSON lexer and the
|
that malformed input is caught immediately instead of only surfacing later
|
||||||
serializer's @ref decode -based escaping, which always run it.
|
as a type_error.316 when the resulting value is dumped.
|
||||||
|
|
||||||
@param[in] s the string to check
|
@param[in] s the string to check
|
||||||
@param[in] first the index to start checking at
|
@param[in] first index of the first byte to check; the bytes before it are
|
||||||
@return whether `s.substr(first)` is well-formed UTF-8
|
assumed to have been validated already and to end on a
|
||||||
|
code point boundary
|
||||||
@sa @ref decode
|
@return whether @a s (from index @a first on) is valid UTF-8
|
||||||
*/
|
*/
|
||||||
template<typename StringType>
|
template<typename StringType>
|
||||||
inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noexcept
|
inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noexcept
|
||||||
@@ -213,101 +211,5 @@ inline bool is_valid_utf8(const StringType& s, const std::size_t first = 0) noex
|
|||||||
return state == UTF8_ACCEPT;
|
return state == UTF8_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief sanitize a string with ill-formed UTF-8 for @ref error_handler_t::replace or @ref error_handler_t::ignore
|
|
||||||
|
|
||||||
Replaces every maximal ill-formed subsequence with U+FFFD (`replace`) or
|
|
||||||
drops it (`ignore`), using exactly the same boundaries @ref
|
|
||||||
serializer::dump_escaped_impl uses while escaping a string: a byte that does
|
|
||||||
not extend the sequence started by the previous byte(s) is reread as the
|
|
||||||
start of a new one, instead of being swallowed along with them.
|
|
||||||
|
|
||||||
@pre @a error_handler is @ref error_handler_t::replace or @ref error_handler_t::ignore
|
|
||||||
@note Well-formed input is copied through unchanged, including bytes (e.g.
|
|
||||||
control characters or quotes) that @ref serializer::dump_escaped_impl
|
|
||||||
would itself escape; this function only concerns itself with
|
|
||||||
well-formedness, not with producing valid JSON text.
|
|
||||||
|
|
||||||
@param[in] s the string to sanitize
|
|
||||||
@param[in] error_handler @ref error_handler_t::replace or @ref error_handler_t::ignore
|
|
||||||
|
|
||||||
@return @a s with every ill-formed subsequence replaced or removed
|
|
||||||
|
|
||||||
@sa @ref decode
|
|
||||||
*/
|
|
||||||
template<typename StringType>
|
|
||||||
inline StringType sanitize_utf8(const StringType& s, const error_handler_t error_handler)
|
|
||||||
{
|
|
||||||
JSON_ASSERT(error_handler == error_handler_t::replace || error_handler == error_handler_t::ignore);
|
|
||||||
|
|
||||||
StringType result;
|
|
||||||
result.reserve(s.size());
|
|
||||||
|
|
||||||
std::uint32_t codepoint = 0;
|
|
||||||
std::uint8_t state = UTF8_ACCEPT;
|
|
||||||
// length of result after the last accepted code point
|
|
||||||
std::size_t result_len_after_last_accept = 0;
|
|
||||||
// whether bytes of an as yet unresolved sequence were already appended
|
|
||||||
bool pending = false;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < s.size(); ++i)
|
|
||||||
{
|
|
||||||
switch (decode(state, codepoint, static_cast<std::uint8_t>(s[i])))
|
|
||||||
{
|
|
||||||
case UTF8_ACCEPT: // decode found a well-formed code point
|
|
||||||
{
|
|
||||||
result.push_back(s[i]);
|
|
||||||
result_len_after_last_accept = result.size();
|
|
||||||
pending = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case UTF8_REJECT: // decode found an ill-formed byte
|
|
||||||
{
|
|
||||||
// in case we saw this byte for the first time, read it again,
|
|
||||||
// because it may be fine for itself, just not for the
|
|
||||||
// sequence that came before it
|
|
||||||
if (pending)
|
|
||||||
{
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// drop the bytes of the ill-formed sequence buffered below
|
|
||||||
result.resize(result_len_after_last_accept);
|
|
||||||
|
|
||||||
if (error_handler == error_handler_t::replace)
|
|
||||||
{
|
|
||||||
result.append("\xEF\xBF\xBD");
|
|
||||||
result_len_after_last_accept = result.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
pending = false;
|
|
||||||
state = UTF8_ACCEPT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: // decode found yet incomplete multibyte code point
|
|
||||||
{
|
|
||||||
result.push_back(s[i]);
|
|
||||||
pending = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the string ended with an incomplete sequence
|
|
||||||
if (state != UTF8_ACCEPT)
|
|
||||||
{
|
|
||||||
result.resize(result_len_after_last_accept);
|
|
||||||
|
|
||||||
if (error_handler == error_handler_t::replace)
|
|
||||||
{
|
|
||||||
result.append("\xEF\xBF\xBD");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|||||||
+52
-78
@@ -201,10 +201,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
// used by the vector-returning to_* overloads
|
// used by the vector-returning to_* overloads
|
||||||
template<typename CharType> using vector_binary_writer =
|
template<typename CharType> using vector_binary_writer =
|
||||||
::nlohmann::detail::binary_writer<basic_json, CharType, ::nlohmann::detail::output_vector_sink<CharType>>;
|
::nlohmann::detail::binary_writer<basic_json, CharType, ::nlohmann::detail::output_vector_sink<CharType>>;
|
||||||
template<typename CharType> static vector_binary_writer<CharType> vector_writer(
|
template<typename CharType> static vector_binary_writer<CharType> vector_writer(std::vector<CharType>& v)
|
||||||
std::vector<CharType>& v, const ::nlohmann::detail::error_handler_t error_handler = ::nlohmann::detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
return vector_binary_writer<CharType>(::nlohmann::detail::output_vector_sink<CharType>(v), error_handler);
|
return vector_binary_writer<CharType>(::nlohmann::detail::output_vector_sink<CharType>(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
@@ -5446,87 +5445,78 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
public:
|
public:
|
||||||
/// @brief create a CBOR serialization of a given JSON value
|
/// @brief create a CBOR serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
|
||||||
static std::vector<std::uint8_t> to_cbor(const basic_json& j,
|
static std::vector<std::uint8_t> to_cbor(const basic_json& j)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> result;
|
std::vector<std::uint8_t> result;
|
||||||
result.reserve(detail::binary_reserve_hint(j));
|
result.reserve(detail::binary_reserve_hint(j));
|
||||||
vector_writer(result, error_handler).write_cbor(j);
|
vector_writer(result).write_cbor(j);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a CBOR serialization of a given JSON value
|
/// @brief create a CBOR serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
|
||||||
static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<std::uint8_t>(o, error_handler).write_cbor(j);
|
binary_writer<std::uint8_t>(o).write_cbor(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a CBOR serialization of a given JSON value
|
/// @brief create a CBOR serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
|
||||||
static void to_cbor(const basic_json& j, detail::output_adapter<char> o,
|
static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<char>(o, error_handler).write_cbor(j);
|
binary_writer<char>(o).write_cbor(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a MessagePack serialization of a given JSON value
|
/// @brief create a MessagePack serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
|
||||||
static std::vector<std::uint8_t> to_msgpack(const basic_json& j,
|
static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> result;
|
std::vector<std::uint8_t> result;
|
||||||
result.reserve(detail::binary_reserve_hint(j));
|
result.reserve(detail::binary_reserve_hint(j));
|
||||||
vector_writer(result, error_handler).write_msgpack(j);
|
vector_writer(result).write_msgpack(j);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a MessagePack serialization of a given JSON value
|
/// @brief create a MessagePack serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
|
||||||
static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
binary_writer<std::uint8_t>(o, error_handler).write_msgpack(j);
|
binary_writer<std::uint8_t>(o).write_msgpack(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a MessagePack serialization of a given JSON value
|
/// @brief create a MessagePack serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
|
||||||
static void to_msgpack(const basic_json& j, detail::output_adapter<char> o,
|
static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
binary_writer<char>(o, error_handler).write_msgpack(j);
|
binary_writer<char>(o).write_msgpack(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a UBJSON serialization of a given JSON value
|
/// @brief create a UBJSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
|
||||||
static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
|
static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
|
||||||
const bool use_size = false,
|
const bool use_size = false,
|
||||||
const bool use_type = false,
|
const bool use_type = false)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> result;
|
std::vector<std::uint8_t> result;
|
||||||
result.reserve(detail::binary_reserve_hint(j));
|
result.reserve(detail::binary_reserve_hint(j));
|
||||||
vector_writer(result, error_handler).write_ubjson(j, use_size, use_type);
|
vector_writer(result).write_ubjson(j, use_size, use_type);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a UBJSON serialization of a given JSON value
|
/// @brief create a UBJSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
|
||||||
static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<std::uint8_t>(o, error_handler).write_ubjson(j, use_size, use_type);
|
binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a UBJSON serialization of a given JSON value
|
/// @brief create a UBJSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
|
||||||
static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
|
static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<char>(o, error_handler).write_ubjson(j, use_size, use_type);
|
binary_writer<char>(o).write_ubjson(j, use_size, use_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BJData serialization of a given JSON value
|
/// @brief create a BJData serialization of a given JSON value
|
||||||
@@ -5534,12 +5524,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
|
static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
|
||||||
const bool use_size = false,
|
const bool use_size = false,
|
||||||
const bool use_type = false,
|
const bool use_type = false,
|
||||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
const bjdata_version_t version = bjdata_version_t::draft2)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> result;
|
std::vector<std::uint8_t> result;
|
||||||
result.reserve(detail::binary_reserve_hint(j));
|
result.reserve(detail::binary_reserve_hint(j));
|
||||||
vector_writer(result, error_handler).write_ubjson(j, use_size, use_type, true, true, version);
|
vector_writer(result).write_ubjson(j, use_size, use_type, true, true, version);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5547,47 +5536,42 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
|
||||||
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false,
|
||||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
const bjdata_version_t version = bjdata_version_t::draft2)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<std::uint8_t>(o, error_handler).write_ubjson(j, use_size, use_type, true, true, version);
|
binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BJData serialization of a given JSON value
|
/// @brief create a BJData serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
|
||||||
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
|
static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
|
||||||
const bool use_size = false, const bool use_type = false,
|
const bool use_size = false, const bool use_type = false,
|
||||||
const bjdata_version_t version = bjdata_version_t::draft2,
|
const bjdata_version_t version = bjdata_version_t::draft2)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<char>(o, error_handler).write_ubjson(j, use_size, use_type, true, true, version);
|
binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BSON serialization of a given JSON value
|
/// @brief create a BSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||||
static std::vector<std::uint8_t> to_bson(const basic_json& j,
|
static std::vector<std::uint8_t> to_bson(const basic_json& j)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
std::vector<std::uint8_t> result;
|
std::vector<std::uint8_t> result;
|
||||||
result.reserve(detail::binary_reserve_hint(j));
|
result.reserve(detail::binary_reserve_hint(j));
|
||||||
vector_writer(result, error_handler).write_bson(j);
|
vector_writer(result).write_bson(j);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BSON serialization of a given JSON value
|
/// @brief create a BSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||||
static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
|
static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<std::uint8_t>(o, error_handler).write_bson(j);
|
binary_writer<std::uint8_t>(o).write_bson(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BSON serialization of a given JSON value
|
/// @brief create a BSON serialization of a given JSON value
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/to_bson/
|
||||||
static void to_bson(const basic_json& j, detail::output_adapter<char> o,
|
static void to_bson(const basic_json& j, detail::output_adapter<char> o)
|
||||||
const error_handler_t error_handler = detail::binary_writer_default_error_handler())
|
|
||||||
{
|
{
|
||||||
binary_writer<char>(o, error_handler).write_bson(j);
|
binary_writer<char>(o).write_bson(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a BON8 serialization of a given JSON value
|
/// @brief create a BON8 serialization of a given JSON value
|
||||||
@@ -5621,13 +5605,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json from_cbor(InputType&& i,
|
static basic_json from_cbor(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error,
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor, error_handler).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5642,13 +5625,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error,
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor, error_handler).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(&sdp, strict, tag_handler)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5690,13 +5672,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_msgpack(InputType&& i,
|
static basic_json from_msgpack(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5710,13 +5691,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5756,13 +5736,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_ubjson(InputType&& i,
|
static basic_json from_ubjson(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5776,13 +5755,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5822,13 +5800,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bjdata(InputType&& i,
|
static basic_json from_bjdata(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5842,13 +5819,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5898,13 +5874,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bson(InputType&& i,
|
static basic_json from_bson(InputType&& i,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
auto ia = detail::input_adapter(std::forward<InputType>(i));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
@@ -5918,13 +5893,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true)
|
||||||
const error_handler_t error_handler = error_handler_t::keep)
|
|
||||||
{
|
{
|
||||||
basic_json result;
|
basic_json result;
|
||||||
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
auto ia = detail::input_adapter(std::move(first), std::move(last));
|
||||||
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
|
||||||
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson, error_handler).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
if (!binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(&sdp, strict)) // cppcheck-suppress[accessMoved]
|
||||||
{
|
{
|
||||||
result = value_t::discarded;
|
result = value_t::discarded;
|
||||||
}
|
}
|
||||||
|
|||||||
+99
-9
@@ -2,24 +2,114 @@ project('nlohmann_json',
|
|||||||
'cpp',
|
'cpp',
|
||||||
version : '3.12.0',
|
version : '3.12.0',
|
||||||
license : 'MIT',
|
license : 'MIT',
|
||||||
|
meson_version : '>= 0.64',
|
||||||
|
default_options: ['cpp_std=c++11'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if get_option('MultipleHeaders')
|
||||||
|
incdir = 'include'
|
||||||
|
else
|
||||||
|
incdir = 'single_include'
|
||||||
|
endif
|
||||||
|
|
||||||
|
# The same compile definitions as the CMake target (see target_compile_definitions
|
||||||
|
# in CMakeLists.txt): only an option that differs from its default adds one.
|
||||||
|
json_defines = []
|
||||||
|
if not get_option('GlobalUDLs')
|
||||||
|
json_defines += 'JSON_USE_GLOBAL_UDLS=0'
|
||||||
|
endif
|
||||||
|
if not get_option('ImplicitConversions')
|
||||||
|
json_defines += 'JSON_USE_IMPLICIT_CONVERSIONS=0'
|
||||||
|
endif
|
||||||
|
if get_option('DisableEnumSerialization')
|
||||||
|
json_defines += 'JSON_DISABLE_ENUM_SERIALIZATION=1'
|
||||||
|
endif
|
||||||
|
if get_option('DisableTupleReferenceConversion')
|
||||||
|
json_defines += 'JSON_DISABLE_TUPLE_REFERENCE_CONVERSION=1'
|
||||||
|
endif
|
||||||
|
if get_option('Diagnostics')
|
||||||
|
json_defines += 'JSON_DIAGNOSTICS=1'
|
||||||
|
endif
|
||||||
|
if get_option('Diagnostic_Positions')
|
||||||
|
json_defines += 'JSON_DIAGNOSTIC_POSITIONS=1'
|
||||||
|
endif
|
||||||
|
if get_option('LegacyDiscardedValueComparison')
|
||||||
|
json_defines += 'JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON=1'
|
||||||
|
endif
|
||||||
|
if get_option('StrictNulHandling')
|
||||||
|
json_defines += 'JSON_STRICT_NUL_HANDLING=1'
|
||||||
|
endif
|
||||||
|
|
||||||
|
cpp_args = []
|
||||||
|
foreach define : json_defines
|
||||||
|
cpp_args += '-D' + define
|
||||||
|
endforeach
|
||||||
|
|
||||||
nlohmann_json_dep = declare_dependency(
|
nlohmann_json_dep = declare_dependency(
|
||||||
include_directories: include_directories('single_include')
|
compile_args: cpp_args,
|
||||||
|
include_directories: include_directories(incdir)
|
||||||
)
|
)
|
||||||
|
meson.override_dependency('nlohmann_json', nlohmann_json_dep)
|
||||||
|
|
||||||
|
# The multi-header version under the name earlier versions of this file used
|
||||||
nlohmann_json_multiple_headers = declare_dependency(
|
nlohmann_json_multiple_headers = declare_dependency(
|
||||||
|
compile_args: cpp_args,
|
||||||
include_directories: include_directories('include')
|
include_directories: include_directories('include')
|
||||||
)
|
)
|
||||||
|
|
||||||
if not meson.is_subproject()
|
if not meson.is_subproject()
|
||||||
install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann')
|
install_subdir(
|
||||||
install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann')
|
incdir / 'nlohmann',
|
||||||
install_headers('single_include/nlohmann/json_literals.hpp', subdir: 'nlohmann')
|
install_dir: get_option('includedir'),
|
||||||
|
install_tag: 'devel',
|
||||||
|
)
|
||||||
|
|
||||||
pkgc = import('pkgconfig')
|
pkgc = import('pkgconfig')
|
||||||
pkgc.generate(name: 'nlohmann_json',
|
pkgc.generate(name: 'nlohmann_json',
|
||||||
version: meson.project_version(),
|
version: meson.project_version(),
|
||||||
description: 'JSON for Modern C++'
|
description: 'JSON for Modern C++',
|
||||||
)
|
extra_cflags: cpp_args,
|
||||||
|
install_dir: get_option('datadir') / 'pkgconfig',
|
||||||
|
)
|
||||||
|
|
||||||
|
# CMake package config files, so that find_package(nlohmann_json) works. The
|
||||||
|
# include directory is given relative to the config files, so that the
|
||||||
|
# installation can be relocated. This is not possible if includedir or datadir
|
||||||
|
# is an absolute path outside the prefix (e.g., with the separate outputs of
|
||||||
|
# Nix); then the absolute include directory is used, as CMake does.
|
||||||
|
fs = import('fs')
|
||||||
|
cmake_install_dir = get_option('datadir') / 'cmake' / meson.project_name()
|
||||||
|
cmake_to_prefix = []
|
||||||
|
if fs.is_absolute(get_option('includedir')) or fs.is_absolute(cmake_install_dir)
|
||||||
|
cmake_include_dir = (get_option('prefix') / get_option('includedir')).replace('\\', '/')
|
||||||
|
else
|
||||||
|
foreach component : cmake_install_dir.split('/')
|
||||||
|
cmake_to_prefix += '..'
|
||||||
|
endforeach
|
||||||
|
cmake_include_dir = '${_IMPORT_PREFIX}/' + get_option('includedir')
|
||||||
|
endif
|
||||||
|
|
||||||
|
cmake_conf = configuration_data()
|
||||||
|
cmake_conf.set('PROJECT_NAME', meson.project_name())
|
||||||
|
cmake_conf.set('PROJECT_VERSION', meson.project_version())
|
||||||
|
cmake_conf.set('PROJECT_VERSION_MAJOR', meson.project_version().split('.')[0])
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_TARGET_NAME', meson.project_name())
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_TARGETS_EXPORT_NAME', meson.project_name() + 'Targets')
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_INCLUDE_DIR', cmake_include_dir)
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_CONFIG_TO_PREFIX', '/'.join(cmake_to_prefix))
|
||||||
|
cmake_conf.set('NLOHMANN_JSON_COMPILE_DEFINITIONS', ';'.join(json_defines))
|
||||||
|
|
||||||
|
foreach cmake_file : [
|
||||||
|
['cmake/config.cmake.in', 'nlohmann_jsonConfig.cmake'],
|
||||||
|
['cmake/nlohmann_jsonConfigVersion.cmake.in', 'nlohmann_jsonConfigVersion.cmake'],
|
||||||
|
['cmake/nlohmann_jsonTargets.cmake.in', 'nlohmann_jsonTargets.cmake'],
|
||||||
|
]
|
||||||
|
configure_file(
|
||||||
|
input: cmake_file[0],
|
||||||
|
output: cmake_file[1],
|
||||||
|
configuration: cmake_conf,
|
||||||
|
format: 'cmake@',
|
||||||
|
install_dir: cmake_install_dir,
|
||||||
|
)
|
||||||
|
endforeach
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
option(
|
||||||
|
'MultipleHeaders',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Use non-amalgamated version of the library',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'GlobalUDLs',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true,
|
||||||
|
description: 'Place user-defined string literals in the global namespace',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'ImplicitConversions',
|
||||||
|
type: 'boolean',
|
||||||
|
value: true,
|
||||||
|
description: 'Enable implicit conversions',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'DisableEnumSerialization',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Disable default integer enum serialization',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'DisableTupleReferenceConversion',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Disable conversion from a one-element tuple of a JSON reference',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'Diagnostics',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Use extended diagnostic messages',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'Diagnostic_Positions',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Enable diagnostic positions',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'LegacyDiscardedValueComparison',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Enable legacy discarded value comparison',
|
||||||
|
)
|
||||||
|
option(
|
||||||
|
'StrictNulHandling',
|
||||||
|
type: 'boolean',
|
||||||
|
value: false,
|
||||||
|
description: 'Enable strict NUL-byte handling',
|
||||||
|
)
|
||||||
+151
-578
File diff suppressed because it is too large
Load Diff
@@ -63,10 +63,6 @@
|
|||||||
#define JSON_STRICT_NUL_HANDLING 0
|
#define JSON_STRICT_NUL_HANDLING 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef JSON_STRICT_BINARY_UTF8
|
|
||||||
#define JSON_STRICT_BINARY_UTF8 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
#else
|
#else
|
||||||
@@ -103,20 +99,14 @@
|
|||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
#define NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_STRICT_BINARY_UTF8
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8 _sbu8
|
|
||||||
#else
|
|
||||||
#define NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Construct the namespace ABI tags component
|
// Construct the namespace ABI tags component
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g) json_abi ## a ## b ## c ## d ## e ## f ## g
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f) json_abi ## a ## b ## c ## d ## e ## f
|
||||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f, g) \
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c, d, e, f) \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f, g)
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c, d, e, f)
|
||||||
|
|
||||||
#define NLOHMANN_JSON_ABI_TAGS \
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
@@ -125,8 +115,7 @@
|
|||||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
NLOHMANN_JSON_ABI_TAG_BRACE_INIT_COPY_SEMANTICS, \
|
||||||
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
NLOHMANN_JSON_ABI_TAG_PRECISE_STREAM_POSITION, \
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING, \
|
NLOHMANN_JSON_ABI_TAG_STRICT_NUL_HANDLING)
|
||||||
NLOHMANN_JSON_ABI_TAG_STRICT_BINARY_UTF8)
|
|
||||||
|
|
||||||
// Construct the namespace version component
|
// Construct the namespace version component
|
||||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
|||||||
@@ -44,10 +44,6 @@ TEST_CASE("default namespace")
|
|||||||
expected += "_snul";
|
expected += "_snul";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_STRICT_BINARY_UTF8
|
|
||||||
expected += "_sbu8";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
expected += "_v" STRINGIZE(NLOHMANN_JSON_VERSION_MAJOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_MINOR);
|
||||||
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
expected += "_" STRINGIZE(NLOHMANN_JSON_VERSION_PATCH) "::basic_json";
|
||||||
|
|||||||
@@ -45,10 +45,6 @@ TEST_CASE("default namespace without version component")
|
|||||||
expected += "_snul";
|
expected += "_snul";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JSON_STRICT_BINARY_UTF8
|
|
||||||
expected += "_sbu8";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
expected += "::basic_json";
|
expected += "::basic_json";
|
||||||
|
|
||||||
// fallback for Clang
|
// fallback for Clang
|
||||||
|
|||||||
@@ -1,362 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
struct ill_formed_case
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
std::string bytes;
|
|
||||||
};
|
|
||||||
|
|
||||||
// RFC 3629 ill-formed sequences used throughout this file, plus one
|
|
||||||
// well-formed sequence for contrast
|
|
||||||
const std::vector<ill_formed_case> ill_formed_cases =
|
|
||||||
{
|
|
||||||
{"overlong", "\xC0\xAE"},
|
|
||||||
{"lone_0xFF", "\xFF"},
|
|
||||||
{"truncated", "\xE2\x82"},
|
|
||||||
{"surrogate", "\xED\xA0\x80"},
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::string valid_sequence = "\xC3\xA9"; // U+00E9, "é"
|
|
||||||
|
|
||||||
using eh = json::error_handler_t;
|
|
||||||
const std::vector<eh> all_handlers = {eh::strict, eh::replace, eh::ignore, eh::keep};
|
|
||||||
|
|
||||||
// what dump()+parse() produces for a sanitizing error_handler; this is the
|
|
||||||
// ground truth every binary writer/reader is checked against
|
|
||||||
std::string dump_and_parse(const std::string& raw, eh error_handler)
|
|
||||||
{
|
|
||||||
return json::parse(json(raw).dump(-1, ' ', false, error_handler)).get<std::string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|
||||||
{
|
|
||||||
SECTION("writers: string value")
|
|
||||||
{
|
|
||||||
for (const auto& c : ill_formed_cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.name);
|
|
||||||
const json jval = c.bytes;
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(json::to_cbor(jval, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_msgpack(jval, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_ubjson(jval, false, false, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::strict), json::type_error&);
|
|
||||||
{
|
|
||||||
json jobj;
|
|
||||||
jobj["k"] = jval;
|
|
||||||
CHECK_THROWS_AS(json::to_bson(jobj, eh::strict), json::type_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto h :
|
|
||||||
{
|
|
||||||
eh::replace, eh::ignore
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(static_cast<int>(h));
|
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, h)).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval, h)).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, h)).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, h)).get<std::string>() == expected);
|
|
||||||
{
|
|
||||||
json jobj;
|
|
||||||
jobj["k"] = jval;
|
|
||||||
const auto bytes = json::to_bson(jobj, h);
|
|
||||||
CHECK(json::from_bson(bytes)["k"].get<std::string>() == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep: the writer passes the ill-formed bytes through unchanged,
|
|
||||||
// exactly as every binary writer did before this parameter existed
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, eh::keep)).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval, eh::keep)).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, eh::keep)).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::keep)).get<std::string>() == c.bytes);
|
|
||||||
{
|
|
||||||
json jobj;
|
|
||||||
jobj["k"] = jval;
|
|
||||||
const auto bytes = json::to_bson(jobj, eh::keep);
|
|
||||||
CHECK(json::from_bson(bytes)["k"].get<std::string>() == c.bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("writers: object key")
|
|
||||||
{
|
|
||||||
for (const auto& c : ill_formed_cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.name);
|
|
||||||
json jobj;
|
|
||||||
jobj[c.bytes] = 1;
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(json::to_cbor(jobj, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_msgpack(jobj, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_ubjson(jobj, false, false, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_bjdata(jobj, false, false, json::bjdata_version_t::draft2, eh::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_bson(jobj, eh::strict), json::type_error&);
|
|
||||||
|
|
||||||
for (const auto h :
|
|
||||||
{
|
|
||||||
eh::replace, eh::ignore
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(static_cast<int>(h));
|
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jobj, h)).begin().key() == expected);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jobj, h)).begin().key() == expected);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(jobj, false, false, h)).begin().key() == expected);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(jobj, false, false, json::bjdata_version_t::draft2, h)).begin().key() == expected);
|
|
||||||
CHECK(json::from_bson(json::to_bson(jobj, h)).begin().key() == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep: object keys round-trip unchanged too
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jobj, eh::keep)).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jobj, eh::keep)).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(jobj, false, false, eh::keep)).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(jobj, false, false, json::bjdata_version_t::draft2, eh::keep)).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_bson(json::to_bson(jobj, eh::keep)).begin().key() == c.bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("readers: string value")
|
|
||||||
{
|
|
||||||
for (const auto& c : ill_formed_cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.name);
|
|
||||||
|
|
||||||
// bytes produced the lenient (keep) way, as any binary reader
|
|
||||||
// accepted them before this parameter existed
|
|
||||||
const auto cbor_bytes = json::to_cbor(json(c.bytes), eh::keep);
|
|
||||||
const auto msgpack_bytes = json::to_msgpack(json(c.bytes)); // to_msgpack has no error_handler; always pass-through
|
|
||||||
const auto ubjson_bytes = json::to_ubjson(json(c.bytes), false, false, eh::keep);
|
|
||||||
const auto bjdata_bytes = json::to_bjdata(json(c.bytes), false, false, json::bjdata_version_t::draft2, eh::keep);
|
|
||||||
const auto bson_bytes = [&c]
|
|
||||||
{
|
|
||||||
json jobj;
|
|
||||||
jobj["k"] = c.bytes;
|
|
||||||
return json::to_bson(jobj, eh::keep);
|
|
||||||
}();
|
|
||||||
|
|
||||||
// keep (the default): bytes are kept unchanged
|
|
||||||
CHECK(json::from_cbor(cbor_bytes).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_msgpack(msgpack_bytes).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_ubjson(ubjson_bytes).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_bjdata(bjdata_bytes).get<std::string>() == c.bytes);
|
|
||||||
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == c.bytes);
|
|
||||||
|
|
||||||
// strict: parse_error.113, discarded (not thrown) when allow_exceptions is false
|
|
||||||
CHECK_THROWS_AS(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict), json::parse_error&);
|
|
||||||
CHECK(json::from_cbor(cbor_bytes, true, false, json::cbor_tag_handler_t::error, eh::strict).is_discarded());
|
|
||||||
CHECK_THROWS_AS(json::from_msgpack(msgpack_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK(json::from_msgpack(msgpack_bytes, true, false, eh::strict).is_discarded());
|
|
||||||
CHECK_THROWS_AS(json::from_ubjson(ubjson_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK(json::from_ubjson(ubjson_bytes, true, false, eh::strict).is_discarded());
|
|
||||||
CHECK_THROWS_AS(json::from_bjdata(bjdata_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK(json::from_bjdata(bjdata_bytes, true, false, eh::strict).is_discarded());
|
|
||||||
CHECK_THROWS_AS(json::from_bson(bson_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK(json::from_bson(bson_bytes, true, false, eh::strict).is_discarded());
|
|
||||||
|
|
||||||
// replace / ignore: match what dump() would have sanitized the same bytes to
|
|
||||||
for (const auto h :
|
|
||||||
{
|
|
||||||
eh::replace, eh::ignore
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(static_cast<int>(h));
|
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_msgpack(msgpack_bytes, true, true, h).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_ubjson(ubjson_bytes, true, true, h).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_bjdata(bjdata_bytes, true, true, h).get<std::string>() == expected);
|
|
||||||
CHECK(json::from_bson(bson_bytes, true, true, h)["k"].get<std::string>() == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("readers: object key")
|
|
||||||
{
|
|
||||||
for (const auto& c : ill_formed_cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.name);
|
|
||||||
|
|
||||||
json jobj;
|
|
||||||
jobj[c.bytes] = 1;
|
|
||||||
const auto cbor_bytes = json::to_cbor(jobj, eh::keep);
|
|
||||||
const auto msgpack_bytes = json::to_msgpack(jobj);
|
|
||||||
const auto ubjson_bytes = json::to_ubjson(jobj, false, false, eh::keep);
|
|
||||||
const auto bjdata_bytes = json::to_bjdata(jobj, false, false, json::bjdata_version_t::draft2, eh::keep);
|
|
||||||
const auto bson_bytes = json::to_bson(jobj, eh::keep);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(cbor_bytes).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_msgpack(msgpack_bytes).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_ubjson(ubjson_bytes).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_bjdata(bjdata_bytes).begin().key() == c.bytes);
|
|
||||||
CHECK(json::from_bson(bson_bytes).begin().key() == c.bytes);
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict), json::parse_error&);
|
|
||||||
CHECK_THROWS_AS(json::from_msgpack(msgpack_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK_THROWS_AS(json::from_ubjson(ubjson_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK_THROWS_AS(json::from_bjdata(bjdata_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
CHECK_THROWS_AS(json::from_bson(bson_bytes, true, true, eh::strict), json::parse_error&);
|
|
||||||
|
|
||||||
for (const auto h :
|
|
||||||
{
|
|
||||||
eh::replace, eh::ignore
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(static_cast<int>(h));
|
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).begin().key() == expected);
|
|
||||||
CHECK(json::from_msgpack(msgpack_bytes, true, true, h).begin().key() == expected);
|
|
||||||
CHECK(json::from_ubjson(ubjson_bytes, true, true, h).begin().key() == expected);
|
|
||||||
CHECK(json::from_bjdata(bjdata_bytes, true, true, h).begin().key() == expected);
|
|
||||||
CHECK(json::from_bson(bson_bytes, true, true, h).begin().key() == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("well-formed UTF-8 is unaffected by error_handler")
|
|
||||||
{
|
|
||||||
const json jval = valid_sequence;
|
|
||||||
json jobj;
|
|
||||||
jobj[valid_sequence] = valid_sequence;
|
|
||||||
|
|
||||||
for (const auto h : all_handlers)
|
|
||||||
{
|
|
||||||
CAPTURE(static_cast<int>(h));
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, h)).get<std::string>() == valid_sequence);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval, h)).get<std::string>() == valid_sequence);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, h)).get<std::string>() == valid_sequence);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, h)).get<std::string>() == valid_sequence);
|
|
||||||
CHECK(json::from_bson(json::to_bson(jobj, h)).begin().key() == valid_sequence);
|
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, eh::keep), true, true, json::cbor_tag_handler_t::error, h).get<std::string>() == valid_sequence);
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval), true, true, h).get<std::string>() == valid_sequence);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("dump() with error_handler_t::keep writes raw bytes as is")
|
|
||||||
{
|
|
||||||
for (const auto& c : ill_formed_cases)
|
|
||||||
{
|
|
||||||
CAPTURE(c.name);
|
|
||||||
|
|
||||||
const json jval = c.bytes;
|
|
||||||
const std::string dumped = jval.dump(-1, ' ', false, eh::keep);
|
|
||||||
CHECK(dumped.find(c.bytes) != std::string::npos);
|
|
||||||
|
|
||||||
// even with ensure_ascii, the ill-formed bytes are written as is
|
|
||||||
const std::string dumped_ascii = jval.dump(-1, ' ', true, eh::keep);
|
|
||||||
CHECK(dumped_ascii.find(c.bytes) != std::string::npos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// well-formed characters around an ill-formed sequence are still
|
|
||||||
// escaped as usual under ensure_ascii
|
|
||||||
const json mixed = valid_sequence + ill_formed_cases[1].bytes; // "é" + lone 0xFF
|
|
||||||
const std::string dumped_mixed = mixed.dump(-1, ' ', true, eh::keep);
|
|
||||||
CHECK(dumped_mixed.find("\\u00e9") != std::string::npos);
|
|
||||||
CHECK(dumped_mixed.find(ill_formed_cases[1].bytes) != std::string::npos);
|
|
||||||
|
|
||||||
// the byte that ends an ill-formed sequence is read again, so a quote,
|
|
||||||
// a backslash, or a control character after it is still escaped, and
|
|
||||||
// a well-formed code point after it is escaped under ensure_ascii
|
|
||||||
for (const bool ensure_ascii :
|
|
||||||
{
|
|
||||||
false, true
|
|
||||||
})
|
|
||||||
{
|
|
||||||
CAPTURE(ensure_ascii);
|
|
||||||
CHECK(json("\xC3\"").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\"\"");
|
|
||||||
CHECK(json("\xC3\\").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\\\"");
|
|
||||||
CHECK(json("\xC3\n").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\n\"");
|
|
||||||
CHECK(json("\xE2\x82\"").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xE2\x82\\\"\"");
|
|
||||||
CHECK(json("\xFF\"").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xFF\\\"\"");
|
|
||||||
CHECK(json("a\xE2\x82").dump(-1, ' ', ensure_ascii, eh::keep) == "\"a\xE2\x82\"");
|
|
||||||
}
|
|
||||||
CHECK(json("\xC3\xC3\xA9").dump(-1, ' ', false, eh::keep) == "\"\xC3\xC3\xA9\"");
|
|
||||||
CHECK(json("\xC3\xC3\xA9").dump(-1, ' ', true, eh::keep) == "\"\xC3\\u00e9\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("to_msgpack defaults to keep; to_bon8 is not affected by error_handler")
|
|
||||||
{
|
|
||||||
const json jval = ill_formed_cases[1].bytes; // lone 0xFF
|
|
||||||
|
|
||||||
// to_msgpack's error_handler defaults to keep, as MessagePack's spec
|
|
||||||
// allows any bytes in a str, so the bytes are passed through
|
|
||||||
CHECK(json::to_msgpack(jval) == json::to_msgpack(jval, eh::keep));
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval)).get<std::string>() == ill_formed_cases[1].bytes);
|
|
||||||
|
|
||||||
// the diagnostics context of an ill-formed key is the object
|
|
||||||
json jobj;
|
|
||||||
jobj["\xFF"] = 1;
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_msgpack(jobj, eh::strict), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
|
|
||||||
// to_bon8 has no error_handler parameter; UTF-8 is structural for
|
|
||||||
// BON8, so it always rejects ill-formed input
|
|
||||||
CHECK_THROWS_AS(json::to_bon8(jval), json::type_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("allow_exceptions=false with error_handler_t::strict discards the value")
|
|
||||||
{
|
|
||||||
const auto bytes = json::to_cbor(json(ill_formed_cases[0].bytes), eh::keep);
|
|
||||||
const json result = json::from_cbor(bytes, true, false, json::cbor_tag_handler_t::error, eh::strict);
|
|
||||||
CHECK(result.is_discarded());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("default parameters are unchanged")
|
|
||||||
{
|
|
||||||
const json jval = ill_formed_cases[0].bytes;
|
|
||||||
|
|
||||||
// to_*: the default error_handler is keep, so ill-formed bytes are
|
|
||||||
// written unchanged, exactly as in release 3.12.0 (it is strict only
|
|
||||||
// if JSON_STRICT_BINARY_UTF8 is enabled, see
|
|
||||||
// unit-binary_utf8_strict.cpp)
|
|
||||||
CHECK(json::to_cbor(jval) == json::to_cbor(jval, eh::keep));
|
|
||||||
CHECK(json::to_ubjson(jval) == json::to_ubjson(jval, false, false, eh::keep));
|
|
||||||
CHECK(json::to_bjdata(jval) == json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::keep));
|
|
||||||
{
|
|
||||||
json jobj;
|
|
||||||
jobj["k"] = jval;
|
|
||||||
CHECK(json::to_bson(jobj) == json::to_bson(jobj, eh::keep));
|
|
||||||
}
|
|
||||||
|
|
||||||
// from_*: the default error_handler is keep, so ill-formed bytes are
|
|
||||||
// still accepted unchanged, exactly as in release 3.12.0
|
|
||||||
const auto cbor_bytes = json::to_cbor(jval, eh::keep);
|
|
||||||
CHECK(json::from_cbor(cbor_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
|
||||||
const auto ubjson_bytes = json::to_ubjson(jval, false, false, eh::keep);
|
|
||||||
CHECK(json::from_ubjson(ubjson_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
|
||||||
const auto bjdata_bytes = json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::keep);
|
|
||||||
CHECK(json::from_bjdata(bjdata_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
|
||||||
const auto msgpack_bytes = json::to_msgpack(jval);
|
|
||||||
CHECK(json::from_msgpack(msgpack_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
|
||||||
json bson_obj;
|
|
||||||
bson_obj["k"] = jval;
|
|
||||||
const auto bson_bytes = json::to_bson(bson_obj, eh::keep);
|
|
||||||
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == ill_formed_cases[0].bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
// __ _____ _____ _____
|
|
||||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
||||||
// | | |__ | | | | | | version 3.12.0
|
|
||||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
||||||
//
|
|
||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
|
||||||
|
|
||||||
// The binary writers check strings and object keys for valid UTF-8 only if
|
|
||||||
// JSON_STRICT_BINARY_UTF8 is enabled (planned to be the default in 4.0.0).
|
|
||||||
// Without it, they write the bytes unchanged, as before version 3.13.0; the
|
|
||||||
// tests for that are next to the other tests of each format.
|
|
||||||
#ifdef JSON_STRICT_BINARY_UTF8
|
|
||||||
#undef JSON_STRICT_BINARY_UTF8
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define JSON_STRICT_BINARY_UTF8 1
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
TEST_CASE("JSON_STRICT_BINARY_UTF8 (see #5529, #5651)")
|
|
||||||
{
|
|
||||||
SECTION("CBOR")
|
|
||||||
{
|
|
||||||
// a string value with ill-formed UTF-8 is rejected
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xFF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xC3")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xED\xA0\x80")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(json("\xC0\xAF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is rejected the same way
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
|
|
||||||
// binary values are not text and are unaffected
|
|
||||||
CHECK_NOTHROW(json::to_cbor(json::binary(std::vector<std::uint8_t>({0xFF}))));
|
|
||||||
|
|
||||||
// a value read back from CBOR with ill-formed bytes cannot be written
|
|
||||||
// back either (the reader is lenient regardless of the macro)
|
|
||||||
const json j = json::from_cbor(std::vector<std::uint8_t>({0x62, 0xc0, 0xae}));
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_cbor(j), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("UBJSON")
|
|
||||||
{
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xFF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xC3")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xED\xA0\x80")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_ubjson(json("\xC0\xAF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is rejected the same way
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_ubjson(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("BJData")
|
|
||||||
{
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xFF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xC3")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xED\xA0\x80")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bjdata(json("\xC0\xAF")), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is rejected the same way
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bjdata(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("BSON")
|
|
||||||
{
|
|
||||||
// to_bson() rejects the same kind of ill-formed string value, before
|
|
||||||
// any bytes reach the output adapter (the BSON document length
|
|
||||||
// prefix must be known up front, so nothing is written incrementally)
|
|
||||||
std::vector<std::uint8_t> out{0x42}; // a sentinel byte the writer must not touch
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}, nlohmann::detail::output_adapter<std::uint8_t>(out)), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
CHECK(out == std::vector<std::uint8_t> {0x42});
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC3"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is rejected as well; unlike
|
|
||||||
// the reader (which never validates element names), the writer
|
|
||||||
// checks both string values and object keys
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("an explicit error_handler overrides the default")
|
|
||||||
{
|
|
||||||
// the macro only changes the default of the error_handler parameter
|
|
||||||
CHECK(json::to_cbor(json("\xFF"), json::error_handler_t::keep) == std::vector<std::uint8_t>({0x61, 0xff}));
|
|
||||||
CHECK(json::to_ubjson(json("\xFF"), false, false, json::error_handler_t::keep) == std::vector<std::uint8_t>({'S', 'i', 1, 0xff}));
|
|
||||||
CHECK(json::to_bjdata(json("\xFF"), false, false, json::bjdata_version_t::draft2, json::error_handler_t::keep) == std::vector<std::uint8_t>({'S', 'i', 1, 0xff}));
|
|
||||||
CHECK(json::from_bson(json::to_bson(json{{"s", "\xFF"}}, json::error_handler_t::keep)) == json{{"s", "\xFF"}});
|
|
||||||
CHECK(json::to_cbor(json("\xFF"), json::error_handler_t::replace) == std::vector<std::uint8_t>({0x63, 0xef, 0xbf, 0xbd}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("MessagePack and BON8 are unaffected")
|
|
||||||
{
|
|
||||||
// MessagePack allows any bytes in a str, so to_msgpack() still
|
|
||||||
// defaults to keep (strict only if passed explicitly); BON8 always
|
|
||||||
// checks, because the lead bytes mark where strings end
|
|
||||||
CHECK(json::to_msgpack(json("\xFF")) == std::vector<std::uint8_t>({0xa1, 0xff}));
|
|
||||||
CHECK_THROWS_AS(json::to_msgpack(json("\xFF"), json::error_handler_t::strict), json::type_error&);
|
|
||||||
CHECK_THROWS_AS(json::to_bon8(json("\xFF")), json::type_error&);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3907,43 +3907,6 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
CHECK(json::to_bjdata(j) == v);
|
CHECK(json::to_bjdata(j) == v);
|
||||||
CHECK(json::from_bjdata(v) == j);
|
CHECK(json::from_bjdata(v) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ill-formed UTF-8 (see #5529, #5651)")
|
|
||||||
{
|
|
||||||
// none of the binary format specs requires a decoder to reject
|
|
||||||
// ill-formed UTF-8 in a text string, so a value whose bytes are
|
|
||||||
// not valid UTF-8 (0xC0 0xAE is an overlong encoding of '.')
|
|
||||||
// round-trips byte for byte as a string value; to_bjdata() writes
|
|
||||||
// the bytes unchanged, as before 3.13.0, unless
|
|
||||||
// JSON_STRICT_BINARY_UTF8 is enabled (see
|
|
||||||
// unit-binary_utf8_strict.cpp)
|
|
||||||
const std::vector<uint8_t> v = {'S', 'i', 2, 0xc0, 0xae};
|
|
||||||
json j;
|
|
||||||
CHECK_NOTHROW(j = json::from_bjdata(v));
|
|
||||||
REQUIRE(j.is_string());
|
|
||||||
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
|
||||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(j)) == j);
|
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
|
||||||
const std::vector<uint8_t> v_key = {'{', 'i', 2, 0xc0, 0xae, 'i', 1, '}'};
|
|
||||||
json j_key;
|
|
||||||
CHECK_NOTHROW(j_key = json::from_bjdata(v_key));
|
|
||||||
REQUIRE(j_key.is_object());
|
|
||||||
CHECK(j_key.contains(std::string("\xc0\xae")));
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(j_key)) == j_key);
|
|
||||||
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(json("\xFF"))) == json("\xFF"));
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(json("\xC3"))) == json("\xC3"));
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(json("\xED\xA0\x80"))) == json("\xED\xA0\x80"));
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(json("\xC0\xAF"))) == json("\xC0\xAF"));
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is kept the same way
|
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Array Type")
|
SECTION("Array Type")
|
||||||
|
|||||||
@@ -154,43 +154,6 @@ TEST_CASE("BSON")
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ill-formed UTF-8 (see #5529, #5651)")
|
|
||||||
{
|
|
||||||
// a BSON document {"s": "\xC0\xAE"} (0xC0 0xAE is an overlong
|
|
||||||
// encoding of '.'); the BSON spec does not require a decoder to
|
|
||||||
// reject ill-formed UTF-8 in a string value, so the reader hands the
|
|
||||||
// bytes back unchanged
|
|
||||||
const std::vector<uint8_t> v =
|
|
||||||
{
|
|
||||||
0x0F, 0x00, 0x00, 0x00, // document length
|
|
||||||
0x02, 's', 0x00, // type 0x02 (string), key "s"
|
|
||||||
0x03, 0x00, 0x00, 0x00, // string length (including null)
|
|
||||||
0xc0, 0xae, 0x00, // string content and its null terminator
|
|
||||||
0x00 // document terminator
|
|
||||||
};
|
|
||||||
json j;
|
|
||||||
CHECK_NOTHROW(j = json::from_bson(v));
|
|
||||||
REQUIRE(j.is_object());
|
|
||||||
REQUIRE(j.contains("s"));
|
|
||||||
CHECK(j["s"].get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
|
||||||
// dump() still requires valid UTF-8 and throws for such a value
|
|
||||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
|
||||||
// to_bson() writes the bytes back unchanged, as before 3.13.0,
|
|
||||||
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
|
||||||
CHECK(json::from_bson(json::to_bson(j)) == j);
|
|
||||||
|
|
||||||
CHECK(json::from_bson(json::to_bson(json{{"s", "\xFF"}})) == json{{"s", "\xFF"}});
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK(json::from_bson(json::to_bson(json{{"s", "\xC3"}})) == json{{"s", "\xC3"}});
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK(json::from_bson(json::to_bson(json{{"s", "\xED\xA0\x80"}})) == json{{"s", "\xED\xA0\x80"}});
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK(json::from_bson(json::to_bson(json{{"s", "\xC0\xAF"}})) == json{{"s", "\xC0\xAF"}});
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is kept as well
|
|
||||||
CHECK(json::from_bson(json::to_bson(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("lengths exceeding INT32_MAX cannot be serialized to BSON")
|
SECTION("lengths exceeding INT32_MAX cannot be serialized to BSON")
|
||||||
{
|
{
|
||||||
// out_of_range.412 is thrown from a single shared helper
|
// out_of_range.412 is thrown from a single shared helper
|
||||||
|
|||||||
+18
-67
@@ -1801,41 +1801,19 @@ TEST_CASE("CBOR")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0x7C, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0xA1, 0x7C, 0x01})), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR string: expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x7C", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ill-formed UTF-8 in string (see #5529, #5651)")
|
SECTION("invalid UTF-8 in string (see #5529)")
|
||||||
{
|
{
|
||||||
// RFC 8949 §3.1 leaves it up to the decoder whether to reject
|
|
||||||
// ill-formed UTF-8 in a text string; this library does not, and
|
|
||||||
// hands the original bytes back unchanged, matching the
|
|
||||||
// MessagePack reader and the behavior before #5185/#5531 (not in
|
|
||||||
// any release)
|
|
||||||
|
|
||||||
// a two-character text string (major type 3) whose bytes are not
|
// a two-character text string (major type 3) whose bytes are not
|
||||||
// valid UTF-8 (0xC0 0xAE is an overlong encoding of '.') round-trips
|
// valid UTF-8 (0xC0 0xAE is an overlong encoding of '.') must be
|
||||||
// byte for byte as a string value
|
// rejected at decode time, matching every other kind of
|
||||||
const std::vector<uint8_t> ill_formed_value = {0x62, 0xc0, 0xae};
|
// malformed binary input, rather than only failing later when
|
||||||
json j_value;
|
// the resulting value is dumped
|
||||||
CHECK_NOTHROW(j_value = json::from_cbor(ill_formed_value));
|
json _;
|
||||||
REQUIRE(j_value.is_string());
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x62, 0xc0, 0xae})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||||
CHECK(j_value.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
CHECK(json::from_cbor(std::vector<uint8_t>({0x62, 0xc0, 0xae}), true, false).is_discarded());
|
||||||
// dump() still requires valid UTF-8 and throws for such a value,
|
|
||||||
// unless an error handler that replaces or ignores the bytes is
|
|
||||||
// passed
|
|
||||||
CHECK_THROWS_AS(j_value.dump(), json::type_error&);
|
|
||||||
// to_cbor() writes the bytes back unchanged, as before 3.13.0,
|
|
||||||
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(j_value)) == j_value);
|
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
|
||||||
const std::vector<uint8_t> ill_formed_key = {0xa1, 0x62, 0xc0, 0xae, 0x01};
|
|
||||||
json j_key;
|
|
||||||
CHECK_NOTHROW(j_key = json::from_cbor(ill_formed_key));
|
|
||||||
REQUIRE(j_key.is_object());
|
|
||||||
CHECK(j_key.contains(std::string("\xc0\xae")));
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(j_key)) == j_key);
|
|
||||||
|
|
||||||
// a CBOR byte string (major type 2) with the very same bytes is
|
// a CBOR byte string (major type 2) with the very same bytes is
|
||||||
// NOT text and must still be accepted as-is
|
// NOT text and must still be accepted as-is
|
||||||
json _;
|
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x42, 0xc0, 0xae})));
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x42, 0xc0, 0xae})));
|
||||||
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
||||||
|
|
||||||
@@ -1844,47 +1822,17 @@ TEST_CASE("CBOR")
|
|||||||
CHECK(json::from_cbor(json::to_cbor(j)) == j);
|
CHECK(json::from_cbor(json::to_cbor(j)) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("to_cbor keeps ill-formed UTF-8 (see #5651)")
|
SECTION("invalid UTF-8 in indefinite-length string")
|
||||||
{
|
|
||||||
// to_cbor() writes the bytes unchanged, as before 3.13.0, unless
|
|
||||||
// JSON_STRICT_BINARY_UTF8 is enabled (see
|
|
||||||
// unit-binary_utf8_strict.cpp); from_cbor() reads them back as is
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(json("\xFF"))) == json("\xFF"));
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(json("\xC3"))) == json("\xC3"));
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(json("\xED\xA0\x80"))) == json("\xED\xA0\x80"));
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(json("\xC0\xAF"))) == json("\xC0\xAF"));
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is kept the same way
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
|
||||||
|
|
||||||
// binary values are not text and are unaffected
|
|
||||||
CHECK_NOTHROW(json::to_cbor(json::binary(std::vector<std::uint8_t>({0xFF}))));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("ill-formed UTF-8 in indefinite-length string")
|
|
||||||
{
|
{
|
||||||
json _;
|
json _;
|
||||||
|
|
||||||
// the chunks are concatenated as is, without checking that each
|
// every chunk must be valid UTF-8 on its own (RFC 8949, Section
|
||||||
// chunk is valid UTF-8 on its own (RFC 8949, Section 3.2.3), so
|
// 3.2.3), so a code point split across two chunks is rejected
|
||||||
// a code point split across two chunks yields a valid string
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff})));
|
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0x61, 0xa9, 0xff}), true, false).is_discarded());
|
||||||
CHECK(_ == "\xc3\xa9");
|
|
||||||
CHECK(_.dump() == "\"\xc3\xa9\"");
|
|
||||||
|
|
||||||
// a truncated code point is kept as is
|
// an ill-formed later chunk is rejected after valid ones
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0xff})));
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff})), "[json.exception.parse_error.113] parse error at byte 7: syntax error while parsing CBOR string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||||
CHECK(_ == "\xc3");
|
|
||||||
CHECK_THROWS_AS(_.dump(), json::type_error&);
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(_)) == _);
|
|
||||||
|
|
||||||
// an ill-formed later chunk is kept after valid ones
|
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff})));
|
|
||||||
CHECK(_ == "\xc3\xa9\xc0\xae");
|
|
||||||
CHECK_THROWS_AS(_.dump(), json::type_error&);
|
|
||||||
|
|
||||||
// valid multi-byte chunks are accepted
|
// valid multi-byte chunks are accepted
|
||||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
||||||
@@ -1892,6 +1840,9 @@ TEST_CASE("CBOR")
|
|||||||
|
|
||||||
SECTION("many chunks in indefinite-length string")
|
SECTION("many chunks in indefinite-length string")
|
||||||
{
|
{
|
||||||
|
// only the newly read chunk is validated, not the whole string
|
||||||
|
// collected so far; validating the latter made this input take
|
||||||
|
// quadratic time (about ten seconds for 100000 chunks)
|
||||||
constexpr std::size_t chunks = 100000;
|
constexpr std::size_t chunks = 100000;
|
||||||
std::vector<uint8_t> v{0x7f};
|
std::vector<uint8_t> v{0x7f};
|
||||||
for (std::size_t i = 0; i < chunks; ++i)
|
for (std::size_t i = 0; i < chunks; ++i)
|
||||||
|
|||||||
@@ -1540,39 +1540,19 @@ TEST_CASE("MessagePack")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0x81})), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ill-formed UTF-8 in string (see #5529, #5651)")
|
SECTION("invalid UTF-8 in string (see #5529)")
|
||||||
{
|
{
|
||||||
// the MessagePack specification explicitly allows a str object to
|
|
||||||
// contain a byte sequence that is not valid UTF-8 and expects a
|
|
||||||
// deserializer to hand the original bytes back unchanged; this
|
|
||||||
// library follows that, unlike CBOR/UBJSON/BJData/BSON, whose
|
|
||||||
// specifications require text strings to be valid UTF-8
|
|
||||||
|
|
||||||
// a fixstr of length 2 (0xA0 | 2) whose bytes are not valid UTF-8
|
// a fixstr of length 2 (0xA0 | 2) whose bytes are not valid UTF-8
|
||||||
// (0xC0 0xAE is an overlong encoding of '.') round-trips byte for
|
// (0xC0 0xAE is an overlong encoding of '.') must be rejected at
|
||||||
// byte as a string value
|
// decode time, matching every other kind of malformed binary
|
||||||
const std::vector<uint8_t> ill_formed_value = {0xa2, 0xc0, 0xae};
|
// input, rather than only failing later when the resulting
|
||||||
json j_value;
|
// value is dumped
|
||||||
CHECK_NOTHROW(j_value = json::from_msgpack(ill_formed_value));
|
json _;
|
||||||
REQUIRE(j_value.is_string());
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(std::vector<uint8_t>({0xa2, 0xc0, 0xae})), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing MessagePack string: invalid string: ill-formed UTF-8 byte", json::parse_error&);
|
||||||
CHECK(j_value.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
CHECK(json::from_msgpack(std::vector<uint8_t>({0xa2, 0xc0, 0xae}), true, false).is_discarded());
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(j_value)) == j_value);
|
|
||||||
// dump() still requires valid UTF-8 and throws for such a value,
|
|
||||||
// unless an error handler that replaces or ignores the bytes is
|
|
||||||
// passed
|
|
||||||
CHECK_THROWS_AS(j_value.dump(), json::type_error&);
|
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
|
||||||
const std::vector<uint8_t> ill_formed_key = {0x81, 0xa2, 0xc0, 0xae, 0x01};
|
|
||||||
json j_key;
|
|
||||||
CHECK_NOTHROW(j_key = json::from_msgpack(ill_formed_key));
|
|
||||||
REQUIRE(j_key.is_object());
|
|
||||||
CHECK(j_key.contains(std::string("\xc0\xae")));
|
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(j_key)) == j_key);
|
|
||||||
|
|
||||||
// a MessagePack bin8 blob with the very same bytes is NOT text
|
// a MessagePack bin8 blob with the very same bytes is NOT text
|
||||||
// and must still be accepted as-is
|
// and must still be accepted as-is
|
||||||
json _;
|
|
||||||
CHECK_NOTHROW(_ = json::from_msgpack(std::vector<uint8_t>({0xc4, 0x02, 0xc0, 0xae})));
|
CHECK_NOTHROW(_ = json::from_msgpack(std::vector<uint8_t>({0xc4, 0x02, 0xc0, 0xae})));
|
||||||
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
CHECK(_ == json::binary(std::vector<std::uint8_t>({0xc0, 0xae})));
|
||||||
|
|
||||||
|
|||||||
@@ -2505,43 +2505,6 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
CHECK(json::to_ubjson(j) == v);
|
CHECK(json::to_ubjson(j) == v);
|
||||||
CHECK(json::from_ubjson(v) == j);
|
CHECK(json::from_ubjson(v) == j);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ill-formed UTF-8 (see #5529, #5651)")
|
|
||||||
{
|
|
||||||
// none of the binary format specs requires a decoder to reject
|
|
||||||
// ill-formed UTF-8 in a text string, so a value whose bytes are
|
|
||||||
// not valid UTF-8 (0xC0 0xAE is an overlong encoding of '.')
|
|
||||||
// round-trips byte for byte as a string value; to_ubjson() writes
|
|
||||||
// the bytes unchanged, as before 3.13.0, unless
|
|
||||||
// JSON_STRICT_BINARY_UTF8 is enabled (see
|
|
||||||
// unit-binary_utf8_strict.cpp)
|
|
||||||
const std::vector<uint8_t> v = {'S', 'i', 2, 0xc0, 0xae};
|
|
||||||
json j;
|
|
||||||
CHECK_NOTHROW(j = json::from_ubjson(v));
|
|
||||||
REQUIRE(j.is_string());
|
|
||||||
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
|
||||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(j)) == j);
|
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
|
||||||
const std::vector<uint8_t> v_key = {'{', 'i', 2, 0xc0, 0xae, 'i', 1, '}'};
|
|
||||||
json j_key;
|
|
||||||
CHECK_NOTHROW(j_key = json::from_ubjson(v_key));
|
|
||||||
REQUIRE(j_key.is_object());
|
|
||||||
CHECK(j_key.contains(std::string("\xc0\xae")));
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(j_key)) == j_key);
|
|
||||||
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(json("\xFF"))) == json("\xFF"));
|
|
||||||
// a truncated multi-byte sequence
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(json("\xC3"))) == json("\xC3"));
|
|
||||||
// an encoded surrogate half (U+D800)
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(json("\xED\xA0\x80"))) == json("\xED\xA0\x80"));
|
|
||||||
// an overlong encoding of '.'
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(json("\xC0\xAF"))) == json("\xC0\xAF"));
|
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is kept the same way
|
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(json{{"\xFF", 1}})) == json{{"\xFF", 1}});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Array Type")
|
SECTION("Array Type")
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# check_build_options
|
||||||
|
|
||||||
|
Checks that the Meson build and the pkg-config files offer the same options as the CMake target, so that a new CMake
|
||||||
|
option is not forgotten in one of them.
|
||||||
|
|
||||||
|
The compile definitions of the CMake target (`target_compile_definitions` in [`CMakeLists.txt`](../../CMakeLists.txt))
|
||||||
|
are the reference. When you add an option there, also add it to
|
||||||
|
|
||||||
|
- the pkg-config block in `CMakeLists.txt` (`NLOHMANN_JSON_PKGCONFIG_CFLAGS`),
|
||||||
|
- [`meson_options.txt`](../../meson_options.txt), named without the `JSON_` prefix and with the same default,
|
||||||
|
- [`meson.build`](../../meson.build) (`json_defines`), and
|
||||||
|
- the list of Meson options in
|
||||||
|
[`docs/mkdocs/docs/integration/package_managers.md`](../../docs/mkdocs/docs/integration/package_managers.md).
|
||||||
|
|
||||||
|
Run the check with
|
||||||
|
|
||||||
|
```shell
|
||||||
|
make check_build_options
|
||||||
|
```
|
||||||
|
|
||||||
|
It needs only Python 3 and runs in the `ci_meson_install` CI job.
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Check that the Meson build and the pkg-config files offer the CMake options.
|
||||||
|
|
||||||
|
The compile definitions of the CMake target (target_compile_definitions in
|
||||||
|
CMakeLists.txt) are the reference. For every option used there, the script
|
||||||
|
checks that
|
||||||
|
|
||||||
|
- the CMake pkg-config file adds the same definition under the same condition,
|
||||||
|
- meson_options.txt has a boolean option of the same name without the "JSON_"
|
||||||
|
prefix and with the same default,
|
||||||
|
- meson.build adds the same definition under the same condition, and
|
||||||
|
- the Meson section of the package manager documentation lists the option.
|
||||||
|
|
||||||
|
Meson's MultipleHeaders option selects the include directory and adds no
|
||||||
|
definition; it is the only Meson option without a definition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
|
||||||
|
DOCS = os.path.join('docs', 'mkdocs', 'docs', 'integration', 'package_managers.md')
|
||||||
|
|
||||||
|
# Meson options that add no compile definition, with their default
|
||||||
|
MESON_ONLY = {'MultipleHeaders': 'false'}
|
||||||
|
|
||||||
|
|
||||||
|
def read(root, path):
|
||||||
|
with open(os.path.join(root, path), encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_target_definitions(cmake):
|
||||||
|
"""Return {option: (definition, add_if_on)} from target_compile_definitions."""
|
||||||
|
block = re.search(r'target_compile_definitions\(\s*\$\{NLOHMANN_JSON_TARGET_NAME\}\s*INTERFACE(.*?)\n\)', cmake, re.S)
|
||||||
|
if not block:
|
||||||
|
sys.exit('CMakeLists.txt: target_compile_definitions of the target not found')
|
||||||
|
result = {}
|
||||||
|
for line in block.group(1).split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
m = re.fullmatch(r'\$<\$<NOT:\$<BOOL:\$\{JSON_(\w+)\}>>:(\w+=\w+)>', line)
|
||||||
|
if m:
|
||||||
|
result[m.group(1)] = (m.group(2), False)
|
||||||
|
continue
|
||||||
|
m = re.fullmatch(r'\$<\$<BOOL:\$\{JSON_(\w+)\}>:(\w+=\w+)>', line)
|
||||||
|
if m:
|
||||||
|
result[m.group(1)] = (m.group(2), True)
|
||||||
|
continue
|
||||||
|
sys.exit(f'CMakeLists.txt: unexpected line in target_compile_definitions: {line}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_defaults(cmake):
|
||||||
|
"""Return {option: 'true'/'false'} for the option() calls of the form JSON_<name>."""
|
||||||
|
return {m.group(1): 'true' if m.group(2) == 'ON' else 'false'
|
||||||
|
for m in re.finditer(r'^option\(JSON_(\w+)\s+"[^"]*"\s+(ON|OFF)\)', cmake, re.M)}
|
||||||
|
|
||||||
|
|
||||||
|
def cmake_pkgconfig_definitions(cmake):
|
||||||
|
"""Return {option: (definition, add_if_on)} from the pkg-config block."""
|
||||||
|
return {m.group(2): (m.group(3), m.group(1) is None)
|
||||||
|
for m in re.finditer(r'if \((NOT )?JSON_(\w+)\)\s*\n\s*string\(APPEND NLOHMANN_JSON_PKGCONFIG_CFLAGS " -D(\w+=\w+)"\)', cmake)}
|
||||||
|
|
||||||
|
|
||||||
|
def meson_options(options):
|
||||||
|
"""Return {option: default} for the boolean options in meson_options.txt."""
|
||||||
|
result = {}
|
||||||
|
for block in re.findall(r'option\((.*?)\)', options, re.S):
|
||||||
|
name = re.search(r"'(\w+)'", block).group(1)
|
||||||
|
kind = re.search(r"type\s*:\s*'(\w+)'", block)
|
||||||
|
value = re.search(r'value\s*:\s*(\w+)', block)
|
||||||
|
result[name] = value.group(1) if kind and kind.group(1) == 'boolean' and value else None
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def meson_definitions(meson):
|
||||||
|
"""Return {option: (definition, add_if_on)} from meson.build."""
|
||||||
|
return {m.group(2): (m.group(3), m.group(1) is None)
|
||||||
|
for m in re.finditer(r"if (not )?get_option\('(\w+)'\)\s*\n\s*json_defines \+= '(\w+=\w+)'", meson)}
|
||||||
|
|
||||||
|
|
||||||
|
def describe(definition):
|
||||||
|
name, add_if_on = definition
|
||||||
|
return f'{name} if {"enabled" if add_if_on else "disabled"}'
|
||||||
|
|
||||||
|
|
||||||
|
def compare(errors, where, expected, actual):
|
||||||
|
for option, definition in expected.items():
|
||||||
|
if option not in actual:
|
||||||
|
errors.append(f'{where}: no definition for option {option} (expected {describe(definition)})')
|
||||||
|
elif actual[option] != definition:
|
||||||
|
errors.append(f'{where}: option {option} adds {describe(actual[option])}, expected {describe(definition)}')
|
||||||
|
for option in actual.keys() - expected.keys():
|
||||||
|
errors.append(f'{where}: definition for option {option}, which the CMake target does not have')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__.split('\n')[0])
|
||||||
|
parser.add_argument('root', nargs='?', default=REPO_ROOT, help='repository root (default: %(default)s)')
|
||||||
|
root = parser.parse_args().root
|
||||||
|
|
||||||
|
cmake = read(root, 'CMakeLists.txt')
|
||||||
|
reference = cmake_target_definitions(cmake)
|
||||||
|
defaults = cmake_defaults(cmake)
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
compare(errors, 'CMakeLists.txt (pkg-config)', reference, cmake_pkgconfig_definitions(cmake))
|
||||||
|
compare(errors, 'meson.build', reference, meson_definitions(read(root, 'meson.build')))
|
||||||
|
|
||||||
|
options = meson_options(read(root, 'meson_options.txt'))
|
||||||
|
for option in reference:
|
||||||
|
if option not in defaults:
|
||||||
|
errors.append(f'CMakeLists.txt: no option(JSON_{option} ... ON|OFF)')
|
||||||
|
elif option not in options:
|
||||||
|
errors.append(f'meson_options.txt: option {option} missing')
|
||||||
|
elif options[option] != defaults[option]:
|
||||||
|
errors.append(f'meson_options.txt: option {option} must be boolean with value {defaults[option]} as in CMake')
|
||||||
|
for option, default in MESON_ONLY.items():
|
||||||
|
if options.get(option) != default:
|
||||||
|
errors.append(f'meson_options.txt: option {option} must be boolean with value {default}')
|
||||||
|
for option in options.keys() - reference.keys() - MESON_ONLY.keys():
|
||||||
|
errors.append(f'meson_options.txt: option {option} has no counterpart in the CMake target')
|
||||||
|
|
||||||
|
docs = read(root, DOCS)
|
||||||
|
for option in sorted(set(options) & (reference.keys() | MESON_ONLY.keys())):
|
||||||
|
if f'`{option}`' not in docs:
|
||||||
|
errors.append(f'{DOCS}: Meson option {option} not listed')
|
||||||
|
|
||||||
|
for error in errors:
|
||||||
|
print(error, file=sys.stderr)
|
||||||
|
if errors:
|
||||||
|
print('The Meson build and the pkg-config files must offer the options of the CMake target; see '
|
||||||
|
'tools/check_build_options/README.md.', file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
print(f'OK: {len(reference)} options with compile definitions agree between CMake, pkg-config, and Meson.')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user