Merge branch 'develop' of https://github.com/nlohmann/json into issue3090

This commit is contained in:
Niels Lohmann 2021-11-21 13:40:59 +01:00
commit cf6903ec00
No known key found for this signature in database
GPG Key ID: 7F3CEA63AE251B69
45 changed files with 688 additions and 371 deletions

View File

@ -1,6 +1,9 @@
Checks: '*, Checks: '*,
-altera-id-dependent-backward-branch,
-altera-struct-pack-align, -altera-struct-pack-align,
-altera-unroll-loops,
-android-cloexec-fopen, -android-cloexec-fopen,
-bugprone-easily-swappable-parameters,
-concurrency-mt-unsafe, -concurrency-mt-unsafe,
-cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
@ -11,6 +14,7 @@ Checks: '*,
-cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-virtual-class-destructor,
-fuchsia-default-arguments-calls, -fuchsia-default-arguments-calls,
-fuchsia-default-arguments-declarations, -fuchsia-default-arguments-declarations,
-fuchsia-overloaded-operator, -fuchsia-overloaded-operator,
@ -34,6 +38,7 @@ Checks: '*,
-modernize-use-trailing-return-type, -modernize-use-trailing-return-type,
-readability-function-size, -readability-function-size,
-readability-function-cognitive-complexity, -readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-magic-numbers, -readability-magic-numbers,
-readability-redundant-access-specifiers, -readability-redundant-access-specifiers,
-readability-uppercase-literal-suffix' -readability-uppercase-literal-suffix'

View File

@ -11,7 +11,7 @@ on:
jobs: jobs:
ci_test_clang: ci_test_clang:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: cmake - name: cmake
@ -21,7 +21,7 @@ jobs:
ci_test_gcc: ci_test_gcc:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: cmake - name: cmake
@ -31,7 +31,7 @@ jobs:
ci_static_analysis: ci_static_analysis:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
strategy: strategy:
matrix: matrix:
target: [ci_clang_tidy, ci_cppcheck, ci_test_valgrind, ci_test_clang_sanitizer, ci_test_amalgamation, ci_clang_analyze, ci_cpplint, ci_cmake_flags, ci_single_binaries, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_infer] target: [ci_clang_tidy, ci_cppcheck, ci_test_valgrind, ci_test_clang_sanitizer, ci_test_amalgamation, ci_clang_analyze, ci_cpplint, ci_cmake_flags, ci_single_binaries, ci_reproducible_tests, ci_non_git_tests, ci_offline_testdata, ci_infer]
@ -44,7 +44,7 @@ jobs:
ci_cmake_options: ci_cmake_options:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
strategy: strategy:
matrix: matrix:
target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions] target: [ci_test_diagnostics, ci_test_noexceptions, ci_test_noimplicitconversions]
@ -57,7 +57,7 @@ jobs:
ci_test_coverage: ci_test_coverage:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: cmake - name: cmake
@ -77,10 +77,10 @@ jobs:
ci_test_compilers: ci_test_compilers:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
strategy: strategy:
matrix: matrix:
compiler: [g++-4.8, g++-4.9, g++-5, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11, clang++-12] compiler: [g++-4.8, g++-4.9, g++-5, g++-6, g++-7, g++-8, g++-9, g++-10, clang++-3.5, clang++-3.6, clang++-3.7, clang++-3.8, clang++-3.9, clang++-4.0, clang++-5.0, clang++-6.0, clang++-7, clang++-8, clang++-9, clang++-10, clang++-11, clang++-12, clang++-13]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: cmake - name: cmake
@ -90,7 +90,7 @@ jobs:
ci_test_standards: ci_test_standards:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: ghcr.io/nlohmann/json-ci:v1.0.0 container: ghcr.io/nlohmann/json-ci:v2.0.0
strategy: strategy:
matrix: matrix:
standard: [11, 14, 17, 20] standard: [11, 14, 17, 20]

View File

@ -22,7 +22,7 @@
- [Design goals](#design-goals) - [Design goals](#design-goals)
- [Sponsors](#sponsors) - [Sponsors](#sponsors)
- [Support](#support) ([documentation](https://json.nlohmann.me), [FAQ](http://127.0.0.1:8000/home/faq/), [discussions](https://github.com/nlohmann/json/discussions), [API](https://json.nlohmann.me/api/basic_json/), [bug issues](https://github.com/nlohmann/json/issues)) - [Support](#support) ([documentation](https://json.nlohmann.me), [FAQ](https://json.nlohmann.me/home/faq/), [discussions](https://github.com/nlohmann/json/discussions), [API](https://json.nlohmann.me/api/basic_json/), [bug issues](https://github.com/nlohmann/json/issues))
- [Examples](#examples) - [Examples](#examples)
- [JSON as first-class data type](#json-as-first-class-data-type) - [JSON as first-class data type](#json-as-first-class-data-type)
- [Serialization / Deserialization](#serialization--deserialization) - [Serialization / Deserialization](#serialization--deserialization)
@ -1083,36 +1083,38 @@ The following compilers are currently used in continuous integration at [Travis]
| Apple Clang 12.0.0 (clang-1200.0.32.27); Xcode 12.2 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.27); Xcode 12.2 | macOS 10.15.7 | GitHub Actions |
| Apple Clang 12.0.0 (clang-1200.0.32.28); Xcode 12.3 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.28); Xcode 12.3 | macOS 10.15.7 | GitHub Actions |
| Apple Clang 12.0.0 (clang-1200.0.32.29); Xcode 12.4 | macOS 10.15.7 | GitHub Actions | | Apple Clang 12.0.0 (clang-1200.0.32.29); Xcode 12.4 | macOS 10.15.7 | GitHub Actions |
| GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 4.8.5 (Ubuntu 4.8.5-4ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 4.9.3 (Ubuntu 4.9.3-13ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 4.9.3 (Ubuntu 4.9.3-13ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 5.4.0 (Ubuntu 5.4.0-6ubuntu1~16.04.12) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 6.4.0 (Ubuntu 6.4.0-17ubuntu1) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 6.5.0 (Ubuntu 6.5.0-2ubuntu1~14.04.1) | Ubuntu 14.04.5 LTS | Travis | | GCC 6.5.0 (Ubuntu 6.5.0-2ubuntu1~14.04.1) | Ubuntu 14.04.5 LTS | Travis |
| GCC 7.5.0 (Ubuntu 7.5.0-6ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 7.5.0 (Ubuntu 7.5.0-6ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions | | GCC 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions |
| GCC 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions | | GCC 8.1.0 (i686-posix-dwarf-rev0, Built by MinGW-W64 project) | Windows-10.0.17763 | GitHub Actions |
| GCC 8.4.0 (Ubuntu 8.4.0-3ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 8.4.0 (Ubuntu 8.4.0-3ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 11.0.1 20210321 (experimental) | Ubuntu 20.04.2 LTS | GitHub Actions | | GCC 11.0.1 20210321 (experimental) | Ubuntu 20.04.3 LTS | GitHub Actions |
| GCC 11.1.0 | Ubuntu (aarch64) | Drone CI | | GCC 11.1.0 | Ubuntu (aarch64) | Drone CI |
| Clang 3.5.2 (3.5.2-3ubuntu1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.5.2 (3.5.2-3ubuntu1) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 3.6.2 (3.6.2-3ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.6.2 (3.6.2-3ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 3.7.1 (3.7.1-2ubuntu2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.7.1 (3.7.1-2ubuntu2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 3.8.0 (3.8.0-2ubuntu4) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.8.0 (3.8.0-2ubuntu4) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 3.9.1 (3.9.1-4ubuntu3\~16.04.2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 3.9.1 (3.9.1-4ubuntu3\~16.04.2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 4.0.0 (4.0.0-1ubuntu1\~16.04.2) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 4.0.0 (4.0.0-1ubuntu1\~16.04.2) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 5.0.0 (5.0.0-3\~16.04.1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 5.0.0 (5.0.0-3\~16.04.1) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 6.0.1 (6.0.1-14) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 6.0.1 (6.0.1-14) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 7.0.1 (7.0.1-12) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 7.0.1 (7.0.1-12) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 8.0.1 (8.0.1-9) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 8.0.1 (8.0.1-9) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 9.0.1 (9.0.1-12) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 9.0.1 (9.0.1-12) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 10.0.0 (10.0.0-4ubuntu1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 10.0.0 (10.0.0-4ubuntu1) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 10.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 10.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions |
| Clang 11.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 with GNU-like command-line | Windows-10.0.17763 | GitHub Actions |
| Clang 11.0.0 with MSVC-like command-line | Windows-10.0.17763 | GitHub Actions | | Clang 11.0.0 with MSVC-like command-line | Windows-10.0.17763 | GitHub Actions |
| Clang 11.0.0 (11.0.0-2~ubuntu20.04.1) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 11.0.0 (11.0.0-2~ubuntu20.04.1) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 12.0.0 (12.0.0-3ubuntu1~20.04.3) | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 12.0.0 (12.0.0-3ubuntu1~20.04.3) | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 13.0.0 (13.0.0-++20210828094952+9c49fee5e7ac-1exp120210828075752.71 | Ubuntu 20.04.2 LTS | GitHub Actions | | Clang 13.0.1 (13.0.1-++20211015123032+cf15ccdeb6d5-1~exp1~20211015003613.5 | Ubuntu 20.04.3 LTS | GitHub Actions |
| Clang 14.0.0 (14.0.0-++20211015062452+81e9c90686f7-1~exp1~20211015063048.20 | Ubuntu 20.04.3 LTS | GitHub Actions |
| Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor | | Visual Studio 14 2015 MSVC 19.0.24241.7 (Build Engine version 14.0.25420.1) | Windows-6.3.9600 | AppVeyor |
| Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor | | Visual Studio 15 2017 MSVC 19.16.27035.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | AppVeyor |
| Visual Studio 15 2017 MSVC 19.16.27045.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | GitHub Actions | | Visual Studio 15 2017 MSVC 19.16.27045.0 (Build Engine version 15.9.21+g9802d43bc3 for .NET Framework) | Windows-10.0.14393 | GitHub Actions |

View File

@ -13,12 +13,12 @@ execute_process(COMMAND ${ASTYLE_TOOL} --version OUTPUT_VARIABLE ASTYLE_TOOL_VER
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" ASTYLE_TOOL_VERSION "${ASTYLE_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" ASTYLE_TOOL_VERSION "${ASTYLE_TOOL_VERSION}")
message(STATUS "🔖 Artistic Style ${ASTYLE_TOOL_VERSION} (${ASTYLE_TOOL})") message(STATUS "🔖 Artistic Style ${ASTYLE_TOOL_VERSION} (${ASTYLE_TOOL})")
find_program(CLANG_TOOL NAMES clang++-HEAD clang++-13 clang++-12 clang++-11 clang++) find_program(CLANG_TOOL NAMES clang++-HEAD clang++-14 clang++-13 clang++-12 clang++-11 clang++)
execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION) execute_process(COMMAND ${CLANG_TOOL} --version OUTPUT_VARIABLE CLANG_TOOL_VERSION ERROR_VARIABLE CLANG_TOOL_VERSION)
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TOOL_VERSION "${CLANG_TOOL_VERSION}")
message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})") message(STATUS "🔖 Clang ${CLANG_TOOL_VERSION} (${CLANG_TOOL})")
find_program(CLANG_TIDY_TOOL NAMES clang-tidy-12 clang-tidy-11 clang-tidy) find_program(CLANG_TIDY_TOOL NAMES clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11 clang-tidy)
execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION) execute_process(COMMAND ${CLANG_TIDY_TOOL} --version OUTPUT_VARIABLE CLANG_TIDY_TOOL_VERSION ERROR_VARIABLE CLANG_TIDY_TOOL_VERSION)
string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}") string(REGEX MATCH "[0-9]+(\\.[0-9]+)+" CLANG_TIDY_TOOL_VERSION "${CLANG_TIDY_TOOL_VERSION}")
message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})") message(STATUS "🔖 Clang-Tidy ${CLANG_TIDY_TOOL_VERSION} (${CLANG_TIDY_TOOL})")
@ -79,7 +79,7 @@ message(STATUS "🔖 Valgrind ${VALGRIND_TOOL_VERSION} (${VALGRIND_TOOL})")
find_program(GENHTML_TOOL NAMES genhtml) find_program(GENHTML_TOOL NAMES genhtml)
find_program(PLOG_CONVERTER_TOOL NAMES plog-converter) find_program(PLOG_CONVERTER_TOOL NAMES plog-converter)
find_program(PVS_STUDIO_ANALYZER_TOOL NAMES pvs-studio-analyzer) find_program(PVS_STUDIO_ANALYZER_TOOL NAMES pvs-studio-analyzer)
find_program(SCAN_BUILD_TOOL NAMES scan-build-12 scan-build-11 scan-build) find_program(SCAN_BUILD_TOOL NAMES scan-build-14 scan-build-13 scan-build-12 scan-build-11 scan-build)
# the individual source files # the individual source files
file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp) file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/include/nlohmann/*.hpp)
@ -811,7 +811,7 @@ add_custom_target(ci_cmake_flags
# Use more installed compilers. # Use more installed compilers.
############################################################################### ###############################################################################
foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-7 g++-8 g++-9 g++-10 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12) foreach(COMPILER g++-4.8 g++-4.9 g++-5 g++-6 g++-7 g++-8 g++-9 g++-10 clang++-3.5 clang++-3.6 clang++-3.7 clang++-3.8 clang++-3.9 clang++-4.0 clang++-5.0 clang++-6.0 clang++-7 clang++-8 clang++-9 clang++-10 clang++-11 clang++-12 clang++-13)
find_program(COMPILER_TOOL NAMES ${COMPILER}) find_program(COMPILER_TOOL NAMES ${COMPILER})
if (COMPILER_TOOL) if (COMPILER_TOOL)
add_custom_target(ci_test_compiler_${COMPILER} add_custom_target(ci_test_compiler_${COMPILER}

View File

@ -0,0 +1,15 @@
#include <iostream>
#include <iomanip>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text given an input with other values
std::vector<std::uint8_t> input = {'[', '1', ',', '2', ',', '3', ']', 'o', 't', 'h', 'e', 'r'};
// parse and serialize JSON
json j_complete = json::parse(input.begin(), input.begin() + 7);
std::cout << std::setw(4) << j_complete << "\n\n";
}

View File

@ -0,0 +1 @@
<a target="_blank" href="https://wandbox.org/permlink/hUJYo1HWmfTBLMGn"><b>online</b></a>

View File

@ -0,0 +1,6 @@
[
1,
2,
3
]

View File

@ -0,0 +1,15 @@
#include <iostream>
#include <iomanip>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
// a JSON text given as string that is not null-terminated
const char* ptr = "[1,2,3]another value";
// parse and serialize JSON
json j_complete = json::parse(ptr, ptr + 7);
std::cout << std::setw(4) << j_complete << "\n\n";
}

View File

@ -0,0 +1 @@
<a target="_blank" href="https://wandbox.org/permlink/AWbpa8e1xRV3y4MM"><b>online</b></a>

View File

@ -0,0 +1,6 @@
[
1,
2,
3
]

View File

@ -7,12 +7,17 @@ using json = nlohmann::json;
int main() int main()
{ {
// create two JSON objects // create two JSON objects
json o1 = R"( {"color": "red", "price": 17.99} )"_json; json o1 = R"( {"color": "red", "price": 17.99, "names": {"de": "Flugzeug"}} )"_json;
json o2 = R"( {"color": "blue", "speed": 100} )"_json; json o2 = R"( {"color": "blue", "speed": 100, "names": {"en": "plane"}} )"_json;
json o3 = o1;
// add all keys from o2 to o1 (updating "color") // add all keys from o2 to o1 (updating "color", replacing "names")
o1.update(o2); o1.update(o2);
// output updated object o1 // add all keys from o2 to o1 (updating "color", merging "names")
o3.update(o2, true);
// output updated object o1 and o3
std::cout << std::setw(2) << o1 << '\n'; std::cout << std::setw(2) << o1 << '\n';
std::cout << std::setw(2) << o3 << '\n';
} }

View File

@ -1,5 +1,17 @@
{ {
"color": "blue", "color": "blue",
"names": {
"en": "plane"
},
"price": 17.99,
"speed": 100
}
{
"color": "blue",
"names": {
"de": "Flugzeug",
"en": "plane"
},
"price": 17.99, "price": 17.99,
"speed": 100 "speed": 100
} }

View File

@ -7,12 +7,17 @@ using json = nlohmann::json;
int main() int main()
{ {
// create two JSON objects // create two JSON objects
json o1 = R"( {"color": "red", "price": 17.99} )"_json; json o1 = R"( {"color": "red", "price": 17.99, "names": {"de": "Flugzeug"}} )"_json;
json o2 = R"( {"color": "blue", "speed": 100} )"_json; json o2 = R"( {"color": "blue", "speed": 100, "names": {"en": "plane"}} )"_json;
json o3 = o1;
// add all keys from o2 to o1 (updating "color") // add all keys from o2 to o1 (updating "color", replacing "names")
o1.update(o2.begin(), o2.end()); o1.update(o2.begin(), o2.end());
// output updated object o1 // add all keys from o2 to o1 (updating "color", merging "names")
o3.update(o2.begin(), o2.end(), true);
// output updated object o1 and o3
std::cout << std::setw(2) << o1 << '\n'; std::cout << std::setw(2) << o1 << '\n';
std::cout << std::setw(2) << o3 << '\n';
} }

View File

@ -1,5 +1,17 @@
{ {
"color": "blue", "color": "blue",
"names": {
"en": "plane"
},
"price": 17.99,
"speed": 100
}
{
"color": "blue",
"names": {
"de": "Flugzeug",
"en": "plane"
},
"price": 17.99, "price": 17.99,
"speed": 100 "speed": 100
} }

View File

@ -55,7 +55,7 @@ type `#!cpp binary_t*` must be dereferenced.
- MessagePack - MessagePack
- If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1, - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, or 16 elements, the fixext family (fixext1,
fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is fixext2, fixext4, fixext8) is used. For other sizes, the ext family (ext8, ext16, ext32) is used. The subtype is
then added as singed 8-bit integer. then added as signed 8-bit integer.
- If no subtype is given, the bin family (bin8, bin16, bin32) is used. - If no subtype is given, the bin family (bin8, bin16, bin32) is used.
- BSON - BSON

View File

@ -68,7 +68,7 @@ void erase(const size_type idx);
- Throws [`invalid_iterator.205`](../../home/exceptions.md#jsonexceptioninvalid_iterator205) if called on a - Throws [`invalid_iterator.205`](../../home/exceptions.md#jsonexceptioninvalid_iterator205) if called on a
primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of primitive type with invalid iterator (i.e., any iterator which is not `begin()`); example: `"iterator out of
range"` range"`
2. The function can throw thw following exceptions: 2. The function can throw the following exceptions:
- Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) if called on a `null` value; - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) if called on a `null` value;
example: `"cannot use erase() with null"` example: `"cannot use erase() with null"`
- Throws [`invalid_iterator.203`](../../home/exceptions.md#jsonexceptioninvalid_iterator203) if called on iterators - Throws [`invalid_iterator.203`](../../home/exceptions.md#jsonexceptioninvalid_iterator203) if called on iterators
@ -76,10 +76,10 @@ void erase(const size_type idx);
- Throws [`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) if called on a - Throws [`invalid_iterator.204`](../../home/exceptions.md#jsonexceptioninvalid_iterator204) if called on a
primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out primitive type with invalid iterators (i.e., if `first != begin()` and `last != end()`); example: `"iterators out
of range"` of range"`
3. The function can throw thw following exceptions: 3. The function can throw the following exceptions:
- Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than
JSON object; example: `"cannot use erase() with null"` JSON object; example: `"cannot use erase() with null"`
4. The function can throw thw following exceptions: 4. The function can throw the following exceptions:
- Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than - Throws [`type_error.307`](../../home/exceptions.md#jsonexceptiontype_error307) when called on a type other than
JSON object; example: `"cannot use erase() with null"` JSON object; example: `"cannot use erase() with null"`
- Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) when `idx >= size()`; example: - Throws [`out_of_range.401`](../../home/exceptions.md#jsonexceptionout_of_range401) when `idx >= size()`; example:

View File

@ -59,12 +59,12 @@ void insert(const_iterator first, const_iterator last);
arrays; example: `"cannot use insert() with string"` arrays; example: `"cannot use insert() with string"`
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an - Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an
iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"`
2. The function can throw thw following exceptions: 2. The function can throw the following exceptions:
- Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than - Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than
arrays; example: `"cannot use insert() with string"` arrays; example: `"cannot use insert() with string"`
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an - Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an
iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"`
3. The function can throw thw following exceptions: 3. The function can throw the following exceptions:
- Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than - Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than
arrays; example: `"cannot use insert() with string"` arrays; example: `"cannot use insert() with string"`
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an - Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an
@ -73,12 +73,12 @@ void insert(const_iterator first, const_iterator last);
do not belong to the same JSON value; example: `"iterators do not fit"` do not belong to the same JSON value; example: `"iterators do not fit"`
- Throws [`invalid_iterator.211`](../../home/exceptions.md#jsonexceptioninvalid_iterator211) if `first` or `last` - Throws [`invalid_iterator.211`](../../home/exceptions.md#jsonexceptioninvalid_iterator211) if `first` or `last`
are iterators into container for which insert is called; example: `"passed iterators may not belong to container"` are iterators into container for which insert is called; example: `"passed iterators may not belong to container"`
4. The function can throw thw following exceptions: 4. The function can throw the following exceptions:
- Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than - Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than
arrays; example: `"cannot use insert() with string"` arrays; example: `"cannot use insert() with string"`
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an - Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an
iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"` iterator which does not belong to the current JSON value; example: `"iterator does not fit current value"`
5. The function can throw thw following exceptions: 5. The function can throw the following exceptions:
- Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than - Throws [`type_error.309`](../../home/exceptions.md#jsonexceptiontype_error309) if called on JSON values other than
objects; example: `"cannot use insert() with string"` objects; example: `"cannot use insert() with string"`
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an - Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an

View File

@ -65,7 +65,7 @@ When iterating over an array, `key()` will return the index of the element as st
!!! warning !!! warning
Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exeeds the iteration. See Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exceeds the iteration. See
<https://github.com/nlohmann/json/issues/2040> for more information. <https://github.com/nlohmann/json/issues/2040> for more information.
## Example ## Example

View File

@ -33,7 +33,7 @@ const_reference operator[](const json_pointer& ptr) const;
: index of the element to access : index of the element to access
`key` (in) `key` (in)
: object key of the elements to remove : object key of the element to access
`ptr` (in) `ptr` (in)
: JSON pointer to the desired element : JSON pointer to the desired element
@ -48,10 +48,10 @@ const_reference operator[](const json_pointer& ptr) const;
1. The function can throw the following exceptions: 1. The function can throw the following exceptions:
- Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an array - Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an array
or null; in that cases, using the `[]` operator with an index makes no sense. or null; in that case, using the `[]` operator with an index makes no sense.
2. The function can throw the following exceptions: 2. The function can throw the following exceptions:
- Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an array - Throws [`type_error.305`](../../home/exceptions.md#jsonexceptiontype_error305) if the JSON value is not an object
or null; in that cases, using the `[]` operator with an index makes no sense. or null; in that case, using the `[]` operator with a key makes no sense.
3. The function can throw the following exceptions: 3. The function can throw the following exceptions:
- Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed - Throws [`parse_error.106`](../../home/exceptions.md#jsonexceptionparse_error106) if an array index in the passed
JSON pointer `ptr` begins with '0'. JSON pointer `ptr` begins with '0'.

View File

@ -19,7 +19,7 @@ static basic_json parse(IteratorType first, IteratorType last,
1. Deserialize from a compatible input. 1. Deserialize from a compatible input.
2. Deserialize from a pair of character iterators 2. Deserialize from a pair of character iterators
The value_type of the iterator must be a integral type with size of 1, 2 or 4 bytes, which will be interpreted The `value_type` of the iterator must be an integral type with size of 1, 2 or 4 bytes, which will be interpreted
respectively as UTF-8, UTF-16 and UTF-32. respectively as UTF-8, UTF-16 and UTF-32.
## Template parameters ## Template parameters
@ -34,7 +34,10 @@ static basic_json parse(IteratorType first, IteratorType last,
- an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators. - an object `obj` for which `begin(obj)` and `end(obj)` produces a valid pair of iterators.
`IteratorType` `IteratorType`
: a compatible iterator type : a compatible iterator type, for instance.
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
- a pair of pointers such as `ptr` and `ptr + len`
## Parameters ## Parameters
@ -135,6 +138,34 @@ super-linear complexity.
--8<-- "examples/parse__contiguouscontainer__parser_callback_t.output" --8<-- "examples/parse__contiguouscontainer__parser_callback_t.output"
``` ```
??? example "Parsing from a non null-terminated string"
The example below demonstrates the `parse()` function reading from a string that is not null-terminated.
```cpp
--8<-- "examples/parse__pointers.cpp"
```
Output:
```json
--8<-- "examples/parse__pointers.output"
```
??? example "Parsing from an iterator pair"
The example below demonstrates the `parse()` function reading from an iterator pair.
```cpp
--8<-- "examples/parse__iterator_pair.cpp"
```
Output:
```json
--8<-- "examples/parse__iterator_pair.output"
```
??? example "Effect of `allow_exceptions` parameter" ??? example "Effect of `allow_exceptions` parameter"
The example below demonstrates the effect of the `allow_exceptions` parameter in the ´parse()` function. The example below demonstrates the effect of the `allow_exceptions` parameter in the ´parse()` function.

View File

@ -2,14 +2,17 @@
```cpp ```cpp
// (1) // (1)
void update(const_reference j); void update(const_reference j, bool merge_objects = false);
// (2) // (2)
void update(const_iterator first, const_iterator last); void update(const_iterator first, const_iterator last, bool merge_objects = false);
``` ```
1. Inserts all values from JSON object `j` and overwrites existing keys. 1. Inserts all values from JSON object `j`.
2. Inserts all values from from range `[first, last)` and overwrites existing keys. 2. Inserts all values from from range `[first, last)`
When `merge_objects` is `#!c false` (default), existing keys are overwritten. When `merge_objects` is `#!c true`,
recursively merges objects with common keys.
The function is motivated by Python's [dict.update](https://docs.python.org/3.6/library/stdtypes.html#dict.update) The function is motivated by Python's [dict.update](https://docs.python.org/3.6/library/stdtypes.html#dict.update)
function. function.
@ -19,6 +22,10 @@ function.
`j` (in) `j` (in)
: JSON object to read values from : JSON object to read values from
`merge_objects` (in)
: when `#!c true`, existing keys are not overwritten, but contents of objects are merged recursively (default:
`#!c false`)
`first` (in) `first` (in)
: begin of the range of elements to insert : begin of the range of elements to insert
@ -30,7 +37,7 @@ function.
1. The function can throw the following exceptions: 1. The function can throw the following exceptions:
- Throws [`type_error.312`](../../home/exceptions.md#jsonexceptiontype_error312) if called on JSON values other than - Throws [`type_error.312`](../../home/exceptions.md#jsonexceptiontype_error312) if called on JSON values other than
objects; example: `"cannot use update() with string"` objects; example: `"cannot use update() with string"`
2. The function can throw thw following exceptions: 2. The function can throw the following exceptions:
- Throws [`type_error.312`](../../home/exceptions.md#jsonexceptiontype_error312) if called on JSON values other than - Throws [`type_error.312`](../../home/exceptions.md#jsonexceptiontype_error312) if called on JSON values other than
objects; example: `"cannot use update() with string"` objects; example: `"cannot use update() with string"`
- Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an - Throws [`invalid_iterator.202`](../../home/exceptions.md#jsonexceptioninvalid_iterator202) if called on an
@ -73,6 +80,63 @@ function.
--8<-- "examples/update__range.output" --8<-- "examples/update__range.output"
``` ```
??? example
One common usecase for this function is the handling of user settings. Assume your application can configured in
some aspects:
```json
{
"color": "red",
"active": true,
"name": {"de": "Maus", "en": "mouse"}
}
```
The user may override the default settings selectively:
```json
{
"color": "blue",
"name": {"es": "ratón"},
}
```
Then `update` manages the merging of default settings and user settings:
```cpp
auto user_settings = json::parse("config.json");
auto effective_settings = get_default_settings();
effective_settings.update(user_settings);
```
Now `effective_settings` contains the default settings, but those keys set by the user are overwritten:
```json
{
"color": "blue",
"active": true,
"name": {"es": "ratón"}
}
```
Note existing keys were just overwritten. To merge objects, `merge_objects` setting should be set to `#!c true`:
```cpp
auto user_settings = json::parse("config.json");
auto effective_settings = get_default_settings();
effective_settings.update(user_settings, true);
```
```json
{
"color": "blue",
"active": true,
"name": {"de": "Maus", "en": "mouse", "es": "ratón"}
}
```
## Version history ## Version history
- Added in version 3.0.0. - Added in version 3.0.0.
- Added `merge_objects` parameter in 3.10.4.

View File

@ -69,12 +69,12 @@ changes to any JSON value.
## Exceptions ## Exceptions
1. The function can throw thw following exceptions: 1. The function can throw the following exceptions:
- Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match - Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match
the type of the value at `key` the type of the value at `key`
- Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object; - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object;
in that case, using `value()` with a key makes no sense. in that case, using `value()` with a key makes no sense.
2. The function can throw thw following exceptions: 2. The function can throw the following exceptions:
- Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match - Throws [`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) if `default_value` does not match
the type of the value at `ptr` the type of the value at `ptr`
- Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object; - Throws [`type_error.306`](../../home/exceptions.md#jsonexceptiontype_error306) if the JSON value is not an object;

View File

@ -198,7 +198,7 @@ JSON does not have a binary type, and this library does not introduce a new type
### MessagePack ### MessagePack
[MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and ext32. The subtype is then added as singed 8-bit integer. [MessagePack](binary_formats/messagepack.md) supports binary values and subtypes. If a subtype is given, the ext family is used. The library will choose the smallest representation among fixext1, fixext2, fixext4, fixext8, ext8, ext16, and ext32. The subtype is then added as signed 8-bit integer.
If no subtype is given, the bin family (bin8, bin16, bin32) is used. If no subtype is given, the bin family (bin8, bin16, bin32) is used.
@ -282,7 +282,7 @@ If no subtype is given, the bin family (bin8, bin16, bin32) is used.
0x23 0x69 0x01 // '#' i 1 number of object elements 0x23 0x69 0x01 // '#' i 1 number of object elements
0x69 0x06 // i 6 (length of the key) 0x69 0x06 // i 6 (length of the key)
0x62 0x69 0x6E 0x61 0x72 0x79 // "binary" 0x62 0x69 0x6E 0x61 0x72 0x79 // "binary"
0x24 0x55 // '$' 'U' type of the array elements: unsinged integers 0x24 0x55 // '$' 'U' type of the array elements: unsigned integers
0x23 0x69 0x04 // '#' i 4 number of array elements 0x23 0x69 0x04 // '#' i 4 number of array elements
0xCA 0xFE 0xBA 0xBE // content 0xCA 0xFE 0xBA 0xBE // content
``` ```

View File

@ -25,7 +25,7 @@ However, you can pass set parameter `ignore_comments` to `#!c true` in the parse
} }
``` ```
When calling `parse` without additional argument, a parse error exception is thrown. If `skip_comments` is set to `#! true`, the comments are skipped during parsing: When calling `parse` without additional argument, a parse error exception is thrown. If `ignore_comments` is set to `#! true`, the comments are ignored during parsing:
```cpp ```cpp
#include <iostream> #include <iostream>
@ -55,7 +55,7 @@ However, you can pass set parameter `ignore_comments` to `#!c true` in the parse
json j = json::parse(s, json j = json::parse(s,
/* callback */ nullptr, /* callback */ nullptr,
/* allow exceptions */ true, /* allow exceptions */ true,
/* skip_comments */ true); /* ignore_comments */ true);
std::cout << j.dump(2) << '\n'; std::cout << j.dump(2) << '\n';
} }
``` ```
@ -80,4 +80,4 @@ However, you can pass set parameter `ignore_comments` to `#!c true` in the parse
"Neptune" "Neptune"
] ]
} }
``` ```

View File

@ -100,7 +100,7 @@ for (auto& [key, val] : j_object.items())
!!! warning !!! warning
Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exeeds the iteration. See <https://github.com/nlohmann/json/issues/2040> for more information. Using `items()` on temporary objects is dangerous. Make sure the object's lifetime exceeds the iteration. See <https://github.com/nlohmann/json/issues/2040> for more information.
### Reverse iteration order ### Reverse iteration order

View File

@ -128,7 +128,7 @@ That is, `-0` is stored as a signed integer, but the serialization does not repr
- The serialization can be in scientific notation even if the input is not: `#!c 0.0000972439793401814` will be - The serialization can be in scientific notation even if the input is not: `#!c 0.0000972439793401814` will be
serialized as `#!c 9.72439793401814e-05`. The reverse can also be true: `#!c 12345E-5` will be serialized as serialized as `#!c 9.72439793401814e-05`. The reverse can also be true: `#!c 12345E-5` will be serialized as
`#!c 0.12345`. `#!c 0.12345`.
- Conversions from `#!c float` to `#!c double` can also introduce rouding errors: - Conversions from `#!c float` to `#!c double` can also introduce rounding errors:
```cpp ```cpp
float f = 0.3; float f = 0.3;
json j = f; json j = f;

View File

@ -177,7 +177,7 @@ This error indicates a syntax error while deserializing a JSON text. The error m
!!! tip !!! tip
- Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully openened. - Make sure the input is correctly read. Try to write the input to standard output to check if, for instance, the input file was successfully opened.
- Paste the input to a JSON validator like <http://jsonlint.com> or a tool like [jq](https://stedolan.github.io/jq/). - Paste the input to a JSON validator like <http://jsonlint.com> or a tool like [jq](https://stedolan.github.io/jq/).
### json.exception.parse_error.102 ### json.exception.parse_error.102

View File

@ -468,7 +468,7 @@ This release further fixes several bugs in the library. All changes are backward
- allow compare user-defined string types (#1130) - allow compare user-defined string types (#1130)
- better support for algorithms using iterators from `items()` (#1045, #1134) - better support for algorithms using iterators from `items()` (#1045, #1134)
- added parameter to avoid compilation error with MSVC 2015 debug builds (#1114) - added parameter to avoid compilation error with MSVC 2015 debug builds (#1114)
- re-added accidentially skipped unit tests (#1176) - re-added accidentally skipped unit tests (#1176)
- fixed MSVC issue with `std::swap` (#1168) - fixed MSVC issue with `std::swap` (#1168)
### :zap: Improvements ### :zap: Improvements
@ -1008,7 +1008,7 @@ This release fixes a few bugs in the JSON parser found in the [Parsing JSON is a
This release fixes the semantics of `operator[]` for JSON Pointers (see below). This fix is backwards compatible. This release fixes the semantics of `operator[]` for JSON Pointers (see below). This fix is backwards compatible.
### Changes ### Changes
- **`operator[]` for JSON Pointers** now behaves like the other versions of `operator[]` and transforms `null` values into objects or arrays if required. This allows to created nested structues like `j["/foo/bar/2"] = 17` (yielding `{"foo": "bar": [null, null, 17]}`) without problems. - **`operator[]` for JSON Pointers** now behaves like the other versions of `operator[]` and transforms `null` values into objects or arrays if required. This allows to created nested structures like `j["/foo/bar/2"] = 17` (yielding `{"foo": "bar": [null, null, 17]}`) without problems.
- overworked a helper SFINAE function - overworked a helper SFINAE function
- fixed some documentation issues - fixed some documentation issues
- fixed the CMake files to allow to run the test suite outside the main project directory - fixed the CMake files to allow to run the test suite outside the main project directory
@ -1093,7 +1093,7 @@ This release combines a lot of small fixes and improvements. The release is back
### Changes ### Changes
- The **parser** has been overworked, and a lot of small issues have been fixed: - The **parser** has been overworked, and a lot of small issues have been fixed:
- Improved parser performance by avoiding recursion and using move semantics for the return value. - Improved parser performance by avoiding recursion and using move semantics for the return value.
- Unescaped control charaters `\x10`-`\x1f` are not accepted any more. - Unescaped control characters `\x10`-`\x1f` are not accepted any more.
- Fixed a bug in the parser when reading from an input stream. - Fixed a bug in the parser when reading from an input stream.
- Improved test case coverage for UTF-8 parsing: now, all valid Unicode code points are tested both escaped and unescaped. - Improved test case coverage for UTF-8 parsing: now, all valid Unicode code points are tested both escaped and unescaped.
- The precision of output streams is now preserved by the parser. - The precision of output streams is now preserved by the parser.

View File

@ -60,7 +60,7 @@ class exception : public std::exception
protected: protected:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
static std::string name(const std::string& ename, int id_) static std::string name(const std::string& ename, int id_)
{ {
@ -198,7 +198,7 @@ class parse_error : public exception
{ {
std::string w = exception::name("parse_error", id_) + "parse error" + std::string w = exception::name("parse_error", id_) + "parse error" +
position_string(pos) + ": " + exception::diagnostics(context) + what_arg; position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, pos.chars_read_total, w.c_str()); return {id_, pos.chars_read_total, w.c_str()};
} }
template<typename BasicJsonType> template<typename BasicJsonType>
@ -207,7 +207,7 @@ class parse_error : public exception
std::string w = exception::name("parse_error", id_) + "parse error" + std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
": " + exception::diagnostics(context) + what_arg; ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, byte_, w.c_str()); return {id_, byte_, w.c_str()};
} }
/*! /*!
@ -276,7 +276,7 @@ class invalid_iterator : public exception
static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
return invalid_iterator(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -331,7 +331,7 @@ class type_error : public exception
static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
return type_error(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -379,7 +379,7 @@ class out_of_range : public exception
static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
return out_of_range(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -418,7 +418,7 @@ class other_error : public exception
static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
return other_error(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:

View File

@ -42,7 +42,7 @@ enum class cbor_tag_handler_t
@note from https://stackoverflow.com/a/1001328/266378 @note from https://stackoverflow.com/a/1001328/266378
*/ */
static inline bool little_endianess(int num = 1) noexcept static inline bool little_endianness(int num = 1) noexcept
{ {
return *reinterpret_cast<char*>(&num) == 1; return *reinterpret_cast<char*>(&num) == 1;
} }
@ -2341,7 +2341,7 @@ class binary_reader
@return whether conversion completed @return whether conversion completed
@note This function needs to respect the system's endianess, because @note This function needs to respect the system's endianness, because
bytes in CBOR, MessagePack, and UBJSON are stored in network order bytes in CBOR, MessagePack, and UBJSON are stored in network order
(big endian) and therefore need reordering on little endian systems. (big endian) and therefore need reordering on little endian systems.
*/ */
@ -2514,8 +2514,8 @@ class binary_reader
/// the number of characters read /// the number of characters read
std::size_t chars_read = 0; std::size_t chars_read = 0;
/// whether we can assume little endianess /// whether we can assume little endianness
const bool is_little_endian = little_endianess(); const bool is_little_endian = little_endianness();
/// the SAX parser /// the SAX parser
json_sax_t* sax = nullptr; json_sax_t* sax = nullptr;

View File

@ -450,7 +450,7 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) /
} }
// This class only handles inputs of input_buffer_adapter type. // This class only handles inputs of input_buffer_adapter type.
// It's required so that expressions like {ptr, len} can be implicitely casted // It's required so that expressions like {ptr, len} can be implicitly casted
// to the correct adapter. // to the correct adapter.
class span_input_adapter class span_input_adapter
{ {

View File

@ -36,7 +36,7 @@ This class implements a both iterators (iterator and const_iterator) for the
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
*/ */
template<typename BasicJsonType> template<typename BasicJsonType>
class iter_impl class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
{ {
/// the iterator with BasicJsonType of different const-ness /// the iterator with BasicJsonType of different const-ness
using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>; using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;

View File

@ -1535,7 +1535,7 @@ class binary_writer
@tparam OutputIsLittleEndian Set to true if output data is @tparam OutputIsLittleEndian Set to true if output data is
required to be little endian required to be little endian
@note This function needs to respect the system's endianess, because bytes @note This function needs to respect the system's endianness, because bytes
in CBOR, MessagePack, and UBJSON are stored in network order (big in CBOR, MessagePack, and UBJSON are stored in network order (big
endian) and therefore need reordering on little endian systems. endian) and therefore need reordering on little endian systems.
*/ */
@ -1625,8 +1625,8 @@ class binary_writer
} }
private: private:
/// whether we can assume little endianess /// whether we can assume little endianness
const bool is_little_endian = little_endianess(); const bool is_little_endian = little_endianness();
/// the output /// the output
output_adapter_t<CharType> oa = nullptr; output_adapter_t<CharType> oa = nullptr;

View File

@ -9,6 +9,8 @@
#include <cstdio> // snprintf #include <cstdio> // snprintf
#include <limits> // numeric_limits #include <limits> // numeric_limits
#include <string> // string, char_traits #include <string> // string, char_traits
#include <iomanip> // setfill, setw
#include <sstream> // stringstream
#include <type_traits> // is_same #include <type_traits> // is_same
#include <utility> // move #include <utility> // move
@ -499,10 +501,9 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(9, '\0'); std::stringstream ss;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0);
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte); JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType()));
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
@ -594,10 +595,9 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(9, '\0'); std::stringstream ss;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast<std::uint8_t>(s.back()) | 0);
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back())); JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
} }
case error_handler_t::ignore: case error_handler_t::ignore:

View File

@ -908,7 +908,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
- If a subtype is given and the binary array contains exactly 1, 2, 4, 8, - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,
or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)
is used. For other sizes, the ext family (ext8, ext16, ext32) is used. is used. For other sizes, the ext family (ext8, ext16, ext32) is used.
The subtype is then added as singed 8-bit integer. The subtype is then added as signed 8-bit integer.
- If no subtype is given, the bin family (bin8, bin16, bin32) is used. - If no subtype is given, the bin family (bin8, bin16, bin32) is used.
- BSON - BSON
- If a subtype is given, it is used and added as unsigned 8-bit integer. - If a subtype is given, it is used and added as unsigned 8-bit integer.
@ -1073,64 +1073,34 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
/// constructor for strings /// constructor for strings
json_value(const string_t& value) json_value(const string_t& value) : string(create<string_t>(value)) {}
{
string = create<string_t>(value);
}
/// constructor for rvalue strings /// constructor for rvalue strings
json_value(string_t&& value) json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
{
string = create<string_t>(std::move(value));
}
/// constructor for objects /// constructor for objects
json_value(const object_t& value) json_value(const object_t& value) : object(create<object_t>(value)) {}
{
object = create<object_t>(value);
}
/// constructor for rvalue objects /// constructor for rvalue objects
json_value(object_t&& value) json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
{
object = create<object_t>(std::move(value));
}
/// constructor for arrays /// constructor for arrays
json_value(const array_t& value) json_value(const array_t& value) : array(create<array_t>(value)) {}
{
array = create<array_t>(value);
}
/// constructor for rvalue arrays /// constructor for rvalue arrays
json_value(array_t&& value) json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
{
array = create<array_t>(std::move(value));
}
/// constructor for binary arrays /// constructor for binary arrays
json_value(const typename binary_t::container_type& value) json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
{
binary = create<binary_t>(value);
}
/// constructor for rvalue binary arrays /// constructor for rvalue binary arrays
json_value(typename binary_t::container_type&& value) json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
{
binary = create<binary_t>(std::move(value));
}
/// constructor for binary arrays (internal type) /// constructor for binary arrays (internal type)
json_value(const binary_t& value) json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
{
binary = create<binary_t>(value);
}
/// constructor for rvalue binary arrays (internal type) /// constructor for rvalue binary arrays (internal type)
json_value(binary_t&& value) json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
{
binary = create<binary_t>(std::move(value));
}
void destroy(value_t t) void destroy(value_t t)
{ {
@ -5051,7 +5021,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
`key()` returns an empty string. `key()` returns an empty string.
@warning Using `items()` on temporary objects is dangerous. Make sure the @warning Using `items()` on temporary objects is dangerous. Make sure the
object's lifetime exeeds the iteration. See object's lifetime exceeds the iteration. See
<https://github.com/nlohmann/json/issues/2040> for more <https://github.com/nlohmann/json/issues/2040> for more
information. information.
@ -5984,6 +5954,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
Inserts all values from JSON object @a j and overwrites existing keys. Inserts all values from JSON object @a j and overwrites existing keys.
@param[in] j JSON object to read values from @param[in] j JSON object to read values from
@param[in] merge_objects when true, existing keys are not overwritten, but
contents of objects are merged recursively
(default: false)
@throw type_error.312 if called on JSON values other than objects; example: @throw type_error.312 if called on JSON values other than objects; example:
`"cannot use update() with string"` `"cannot use update() with string"`
@ -5995,34 +5968,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
@since version 3.0.0 @since version 3.0.0, `merge_objects` parameter added in 3.10.4.
*/ */
void update(const_reference j) void update(const_reference j, bool merge_objects = false)
{ {
// implicitly convert null value to an empty object update(j.begin(), j.end(), merge_objects);
if (is_null())
{
m_type = value_t::object;
m_value.object = create<object_t>();
assert_invariant();
}
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
}
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this));
}
for (auto it = j.cbegin(); it != j.cend(); ++it)
{
m_value.object->operator[](it.key()) = it.value();
#if JSON_DIAGNOSTICS
m_value.object->operator[](it.key()).m_parent = this;
#endif
}
} }
/*! /*!
@ -6033,12 +5983,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@param[in] first begin of the range of elements to insert @param[in] first begin of the range of elements to insert
@param[in] last end of the range of elements to insert @param[in] last end of the range of elements to insert
@param[in] merge_objects when true, existing keys are not overwritten, but
contents of objects are merged recursively
(default: false)
@throw type_error.312 if called on JSON values other than objects; example: @throw type_error.312 if called on JSON values other than objects; example:
`"cannot use update() with string"` `"cannot use update() with string"`
@throw invalid_iterator.202 if iterator @a first or @a last does does not @throw type_error.312 if iterator @a first or @a last does does not
point to an object; example: `"iterators first and last must point to point to an object; example: `"cannot use update() with string"`
objects"`
@throw invalid_iterator.210 if @a first and @a last do not belong to the @throw invalid_iterator.210 if @a first and @a last do not belong to the
same JSON value; example: `"iterators do not fit"` same JSON value; example: `"iterators do not fit"`
@ -6049,9 +6001,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
@since version 3.0.0 @since version 3.0.0, `merge_objects` parameter added in 3.10.4.
*/ */
void update(const_iterator first, const_iterator last) void update(const_iterator first, const_iterator last, bool merge_objects = false)
{ {
// implicitly convert null value to an empty object // implicitly convert null value to an empty object
if (is_null()) if (is_null())
@ -6073,14 +6025,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// passed iterators must belong to objects // passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|| !last.m_object->is_object()))
{ {
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object));
} }
for (auto it = first; it != last; ++it) for (auto it = first; it != last; ++it)
{ {
if (merge_objects && it.value().is_object())
{
auto it2 = m_value.object->find(it.key());
if (it2 != m_value.object->end())
{
it2->second.update(it.value(), true);
continue;
}
}
m_value.object->operator[](it.key()) = it.value(); m_value.object->operator[](it.key()) = it.value();
#if JSON_DIAGNOSTICS #if JSON_DIAGNOSTICS
m_value.object->operator[](it.key()).m_parent = this; m_value.object->operator[](it.key()).m_parent = this;
@ -8976,20 +8936,19 @@ std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
// nonmember support // // nonmember support //
/////////////////////// ///////////////////////
// specialization of std::swap, and std::hash namespace std // NOLINT(cert-dcl58-cpp)
namespace std
{ {
/// hash value for JSON objects /// hash value for JSON objects
template<> NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct hash<nlohmann::json> struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL>
{ {
/*! /*!
@brief return a hash value for a JSON object @brief return a hash value for a JSON object
@since version 1.0.0 @since version 1.0.0, extended for arbitrary basic_json types in 3.10.5.
*/ */
std::size_t operator()(const nlohmann::json& j) const std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
{ {
return nlohmann::detail::hash(j); return nlohmann::detail::hash(j);
} }
@ -8999,7 +8958,7 @@ struct hash<nlohmann::json>
/// @note: do not remove the space after '<', /// @note: do not remove the space after '<',
/// see https://github.com/nlohmann/json/pull/679 /// see https://github.com/nlohmann/json/pull/679
template<> template<>
struct less<::nlohmann::detail::value_t> struct less< ::nlohmann::detail::value_t>
{ {
/*! /*!
@brief compare two value_t enum values @brief compare two value_t enum values
@ -9018,13 +8977,12 @@ struct less<::nlohmann::detail::value_t>
/*! /*!
@brief exchanges the values of two JSON objects @brief exchanges the values of two JSON objects
@since version 1.0.0 @since version 1.0.0, extended for arbitrary basic_json types in 3.10.5.
*/ */
template<> NLOHMANN_BASIC_JSON_TPL_DECLARATION
inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name)
is_nothrow_move_constructible<nlohmann::json>::value&& // NOLINT(misc-redundant-expression) is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression)
is_nothrow_move_assignable<nlohmann::json>::value is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
)
{ {
j1.swap(j2); j1.swap(j2);
} }

View File

@ -107,16 +107,55 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
iterator erase(iterator pos) iterator erase(iterator pos)
{ {
auto it = pos; return erase(pos, std::next(pos));
}
// Since we cannot move const Keys, re-construct them in place iterator erase(iterator first, iterator last)
for (auto next = it; ++next != this->end(); ++it) {
const auto elements_affected = std::distance(first, last);
const auto offset = std::distance(Container::begin(), first);
// This is the start situation. We need to delete elements_affected
// elements (3 in this example: e, f, g), and need to return an
// iterator past the last deleted element (h in this example).
// Note that offset is the distance from the start of the vector
// to first. We will need this later.
// [ a, b, c, d, e, f, g, h, i, j ]
// ^ ^
// first last
// Since we cannot move const Keys, we re-construct them in place.
// We start at first and re-construct (viz. copy) the elements from
// the back of the vector. Example for first iteration:
// ,--------.
// v | destroy e and re-construct with h
// [ a, b, c, d, e, f, g, h, i, j ]
// ^ ^
// it it + elements_affected
for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
{ {
it->~value_type(); // Destroy but keep allocation it->~value_type(); // destroy but keep allocation
new (&*it) value_type{std::move(*next)}; new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
} }
Container::pop_back();
return pos; // [ a, b, c, d, h, i, j, h, i, j ]
// ^ ^
// first last
// remove the unneeded elements at the end of the vector
Container::resize(this->size() - static_cast<size_type>(elements_affected));
// [ a, b, c, d, h, i, j ]
// ^ ^
// first last
// first is now pointing past the last deleted element, but we cannot
// use this iterator, because it may have been invalidated by the
// resize call. Instead, we can return begin() + offset.
return Container::begin() + offset;
} }
size_type count(const Key& key) const size_type count(const Key& key) const

View File

@ -2836,7 +2836,7 @@ class exception : public std::exception
protected: protected:
JSON_HEDLEY_NON_NULL(3) JSON_HEDLEY_NON_NULL(3)
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
static std::string name(const std::string& ename, int id_) static std::string name(const std::string& ename, int id_)
{ {
@ -2974,7 +2974,7 @@ class parse_error : public exception
{ {
std::string w = exception::name("parse_error", id_) + "parse error" + std::string w = exception::name("parse_error", id_) + "parse error" +
position_string(pos) + ": " + exception::diagnostics(context) + what_arg; position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, pos.chars_read_total, w.c_str()); return {id_, pos.chars_read_total, w.c_str()};
} }
template<typename BasicJsonType> template<typename BasicJsonType>
@ -2983,7 +2983,7 @@ class parse_error : public exception
std::string w = exception::name("parse_error", id_) + "parse error" + std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
": " + exception::diagnostics(context) + what_arg; ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, byte_, w.c_str()); return {id_, byte_, w.c_str()};
} }
/*! /*!
@ -3052,7 +3052,7 @@ class invalid_iterator : public exception
static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
return invalid_iterator(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -3107,7 +3107,7 @@ class type_error : public exception
static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
return type_error(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -3155,7 +3155,7 @@ class out_of_range : public exception
static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
return out_of_range(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -3194,7 +3194,7 @@ class other_error : public exception
static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{ {
std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
return other_error(id_, w.c_str()); return {id_, w.c_str()};
} }
private: private:
@ -5955,7 +5955,7 @@ auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) /
} }
// This class only handles inputs of input_buffer_adapter type. // This class only handles inputs of input_buffer_adapter type.
// It's required so that expressions like {ptr, len} can be implicitely casted // It's required so that expressions like {ptr, len} can be implicitly casted
// to the correct adapter. // to the correct adapter.
class span_input_adapter class span_input_adapter
{ {
@ -8510,7 +8510,7 @@ enum class cbor_tag_handler_t
@note from https://stackoverflow.com/a/1001328/266378 @note from https://stackoverflow.com/a/1001328/266378
*/ */
static inline bool little_endianess(int num = 1) noexcept static inline bool little_endianness(int num = 1) noexcept
{ {
return *reinterpret_cast<char*>(&num) == 1; return *reinterpret_cast<char*>(&num) == 1;
} }
@ -10809,7 +10809,7 @@ class binary_reader
@return whether conversion completed @return whether conversion completed
@note This function needs to respect the system's endianess, because @note This function needs to respect the system's endianness, because
bytes in CBOR, MessagePack, and UBJSON are stored in network order bytes in CBOR, MessagePack, and UBJSON are stored in network order
(big endian) and therefore need reordering on little endian systems. (big endian) and therefore need reordering on little endian systems.
*/ */
@ -10982,8 +10982,8 @@ class binary_reader
/// the number of characters read /// the number of characters read
std::size_t chars_read = 0; std::size_t chars_read = 0;
/// whether we can assume little endianess /// whether we can assume little endianness
const bool is_little_endian = little_endianess(); const bool is_little_endian = little_endianness();
/// the SAX parser /// the SAX parser
json_sax_t* sax = nullptr; json_sax_t* sax = nullptr;
@ -11701,7 +11701,7 @@ This class implements a both iterators (iterator and const_iterator) for the
iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
*/ */
template<typename BasicJsonType> template<typename BasicJsonType>
class iter_impl class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
{ {
/// the iterator with BasicJsonType of different const-ness /// the iterator with BasicJsonType of different const-ness
using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>; using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
@ -15282,7 +15282,7 @@ class binary_writer
@tparam OutputIsLittleEndian Set to true if output data is @tparam OutputIsLittleEndian Set to true if output data is
required to be little endian required to be little endian
@note This function needs to respect the system's endianess, because bytes @note This function needs to respect the system's endianness, because bytes
in CBOR, MessagePack, and UBJSON are stored in network order (big in CBOR, MessagePack, and UBJSON are stored in network order (big
endian) and therefore need reordering on little endian systems. endian) and therefore need reordering on little endian systems.
*/ */
@ -15372,8 +15372,8 @@ class binary_writer
} }
private: private:
/// whether we can assume little endianess /// whether we can assume little endianness
const bool is_little_endian = little_endianess(); const bool is_little_endian = little_endianness();
/// the output /// the output
output_adapter_t<CharType> oa = nullptr; output_adapter_t<CharType> oa = nullptr;
@ -15395,6 +15395,8 @@ class binary_writer
#include <cstdio> // snprintf #include <cstdio> // snprintf
#include <limits> // numeric_limits #include <limits> // numeric_limits
#include <string> // string, char_traits #include <string> // string, char_traits
#include <iomanip> // setfill, setw
#include <sstream> // stringstream
#include <type_traits> // is_same #include <type_traits> // is_same
#include <utility> // move #include <utility> // move
@ -17003,10 +17005,9 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(9, '\0'); std::stringstream ss;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0);
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte); JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType()));
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
@ -17098,10 +17099,9 @@ class serializer
{ {
case error_handler_t::strict: case error_handler_t::strict:
{ {
std::string sn(9, '\0'); std::stringstream ss;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast<std::uint8_t>(s.back()) | 0);
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back())); JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
} }
case error_handler_t::ignore: case error_handler_t::ignore:
@ -17574,16 +17574,55 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
iterator erase(iterator pos) iterator erase(iterator pos)
{ {
auto it = pos; return erase(pos, std::next(pos));
}
// Since we cannot move const Keys, re-construct them in place iterator erase(iterator first, iterator last)
for (auto next = it; ++next != this->end(); ++it) {
const auto elements_affected = std::distance(first, last);
const auto offset = std::distance(Container::begin(), first);
// This is the start situation. We need to delete elements_affected
// elements (3 in this example: e, f, g), and need to return an
// iterator past the last deleted element (h in this example).
// Note that offset is the distance from the start of the vector
// to first. We will need this later.
// [ a, b, c, d, e, f, g, h, i, j ]
// ^ ^
// first last
// Since we cannot move const Keys, we re-construct them in place.
// We start at first and re-construct (viz. copy) the elements from
// the back of the vector. Example for first iteration:
// ,--------.
// v | destroy e and re-construct with h
// [ a, b, c, d, e, f, g, h, i, j ]
// ^ ^
// it it + elements_affected
for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
{ {
it->~value_type(); // Destroy but keep allocation it->~value_type(); // destroy but keep allocation
new (&*it) value_type{std::move(*next)}; new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
} }
Container::pop_back();
return pos; // [ a, b, c, d, h, i, j, h, i, j ]
// ^ ^
// first last
// remove the unneeded elements at the end of the vector
Container::resize(this->size() - static_cast<size_type>(elements_affected));
// [ a, b, c, d, h, i, j ]
// ^ ^
// first last
// first is now pointing past the last deleted element, but we cannot
// use this iterator, because it may have been invalidated by the
// resize call. Instead, we can return begin() + offset.
return Container::begin() + offset;
} }
size_type count(const Key& key) const size_type count(const Key& key) const
@ -18489,7 +18528,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
- If a subtype is given and the binary array contains exactly 1, 2, 4, 8, - If a subtype is given and the binary array contains exactly 1, 2, 4, 8,
or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8)
is used. For other sizes, the ext family (ext8, ext16, ext32) is used. is used. For other sizes, the ext family (ext8, ext16, ext32) is used.
The subtype is then added as singed 8-bit integer. The subtype is then added as signed 8-bit integer.
- If no subtype is given, the bin family (bin8, bin16, bin32) is used. - If no subtype is given, the bin family (bin8, bin16, bin32) is used.
- BSON - BSON
- If a subtype is given, it is used and added as unsigned 8-bit integer. - If a subtype is given, it is used and added as unsigned 8-bit integer.
@ -18654,64 +18693,34 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
/// constructor for strings /// constructor for strings
json_value(const string_t& value) json_value(const string_t& value) : string(create<string_t>(value)) {}
{
string = create<string_t>(value);
}
/// constructor for rvalue strings /// constructor for rvalue strings
json_value(string_t&& value) json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
{
string = create<string_t>(std::move(value));
}
/// constructor for objects /// constructor for objects
json_value(const object_t& value) json_value(const object_t& value) : object(create<object_t>(value)) {}
{
object = create<object_t>(value);
}
/// constructor for rvalue objects /// constructor for rvalue objects
json_value(object_t&& value) json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
{
object = create<object_t>(std::move(value));
}
/// constructor for arrays /// constructor for arrays
json_value(const array_t& value) json_value(const array_t& value) : array(create<array_t>(value)) {}
{
array = create<array_t>(value);
}
/// constructor for rvalue arrays /// constructor for rvalue arrays
json_value(array_t&& value) json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
{
array = create<array_t>(std::move(value));
}
/// constructor for binary arrays /// constructor for binary arrays
json_value(const typename binary_t::container_type& value) json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
{
binary = create<binary_t>(value);
}
/// constructor for rvalue binary arrays /// constructor for rvalue binary arrays
json_value(typename binary_t::container_type&& value) json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
{
binary = create<binary_t>(std::move(value));
}
/// constructor for binary arrays (internal type) /// constructor for binary arrays (internal type)
json_value(const binary_t& value) json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
{
binary = create<binary_t>(value);
}
/// constructor for rvalue binary arrays (internal type) /// constructor for rvalue binary arrays (internal type)
json_value(binary_t&& value) json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
{
binary = create<binary_t>(std::move(value));
}
void destroy(value_t t) void destroy(value_t t)
{ {
@ -22632,7 +22641,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
`key()` returns an empty string. `key()` returns an empty string.
@warning Using `items()` on temporary objects is dangerous. Make sure the @warning Using `items()` on temporary objects is dangerous. Make sure the
object's lifetime exeeds the iteration. See object's lifetime exceeds the iteration. See
<https://github.com/nlohmann/json/issues/2040> for more <https://github.com/nlohmann/json/issues/2040> for more
information. information.
@ -23565,6 +23574,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
Inserts all values from JSON object @a j and overwrites existing keys. Inserts all values from JSON object @a j and overwrites existing keys.
@param[in] j JSON object to read values from @param[in] j JSON object to read values from
@param[in] merge_objects when true, existing keys are not overwritten, but
contents of objects are merged recursively
(default: false)
@throw type_error.312 if called on JSON values other than objects; example: @throw type_error.312 if called on JSON values other than objects; example:
`"cannot use update() with string"` `"cannot use update() with string"`
@ -23576,34 +23588,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
@since version 3.0.0 @since version 3.0.0, `merge_objects` parameter added in 3.10.4.
*/ */
void update(const_reference j) void update(const_reference j, bool merge_objects = false)
{ {
// implicitly convert null value to an empty object update(j.begin(), j.end(), merge_objects);
if (is_null())
{
m_type = value_t::object;
m_value.object = create<object_t>();
assert_invariant();
}
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
}
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this));
}
for (auto it = j.cbegin(); it != j.cend(); ++it)
{
m_value.object->operator[](it.key()) = it.value();
#if JSON_DIAGNOSTICS
m_value.object->operator[](it.key()).m_parent = this;
#endif
}
} }
/*! /*!
@ -23614,12 +23603,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@param[in] first begin of the range of elements to insert @param[in] first begin of the range of elements to insert
@param[in] last end of the range of elements to insert @param[in] last end of the range of elements to insert
@param[in] merge_objects when true, existing keys are not overwritten, but
contents of objects are merged recursively
(default: false)
@throw type_error.312 if called on JSON values other than objects; example: @throw type_error.312 if called on JSON values other than objects; example:
`"cannot use update() with string"` `"cannot use update() with string"`
@throw invalid_iterator.202 if iterator @a first or @a last does does not @throw type_error.312 if iterator @a first or @a last does does not
point to an object; example: `"iterators first and last must point to point to an object; example: `"cannot use update() with string"`
objects"`
@throw invalid_iterator.210 if @a first and @a last do not belong to the @throw invalid_iterator.210 if @a first and @a last do not belong to the
same JSON value; example: `"iterators do not fit"` same JSON value; example: `"iterators do not fit"`
@ -23630,9 +23621,9 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
@sa https://docs.python.org/3.6/library/stdtypes.html#dict.update @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update
@since version 3.0.0 @since version 3.0.0, `merge_objects` parameter added in 3.10.4.
*/ */
void update(const_iterator first, const_iterator last) void update(const_iterator first, const_iterator last, bool merge_objects = false)
{ {
// implicitly convert null value to an empty object // implicitly convert null value to an empty object
if (is_null()) if (is_null())
@ -23654,14 +23645,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
// passed iterators must belong to objects // passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
|| !last.m_object->is_object()))
{ {
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object));
} }
for (auto it = first; it != last; ++it) for (auto it = first; it != last; ++it)
{ {
if (merge_objects && it.value().is_object())
{
auto it2 = m_value.object->find(it.key());
if (it2 != m_value.object->end())
{
it2->second.update(it.value(), true);
continue;
}
}
m_value.object->operator[](it.key()) = it.value(); m_value.object->operator[](it.key()) = it.value();
#if JSON_DIAGNOSTICS #if JSON_DIAGNOSTICS
m_value.object->operator[](it.key()).m_parent = this; m_value.object->operator[](it.key()).m_parent = this;
@ -26557,20 +26556,19 @@ std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
// nonmember support // // nonmember support //
/////////////////////// ///////////////////////
// specialization of std::swap, and std::hash namespace std // NOLINT(cert-dcl58-cpp)
namespace std
{ {
/// hash value for JSON objects /// hash value for JSON objects
template<> NLOHMANN_BASIC_JSON_TPL_DECLARATION
struct hash<nlohmann::json> struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL>
{ {
/*! /*!
@brief return a hash value for a JSON object @brief return a hash value for a JSON object
@since version 1.0.0 @since version 1.0.0, extended for arbitrary basic_json types in 3.10.5.
*/ */
std::size_t operator()(const nlohmann::json& j) const std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
{ {
return nlohmann::detail::hash(j); return nlohmann::detail::hash(j);
} }
@ -26580,7 +26578,7 @@ struct hash<nlohmann::json>
/// @note: do not remove the space after '<', /// @note: do not remove the space after '<',
/// see https://github.com/nlohmann/json/pull/679 /// see https://github.com/nlohmann/json/pull/679
template<> template<>
struct less<::nlohmann::detail::value_t> struct less< ::nlohmann::detail::value_t>
{ {
/*! /*!
@brief compare two value_t enum values @brief compare two value_t enum values
@ -26599,13 +26597,12 @@ struct less<::nlohmann::detail::value_t>
/*! /*!
@brief exchanges the values of two JSON objects @brief exchanges the values of two JSON objects
@since version 1.0.0 @since version 1.0.0, extended for arbitrary basic_json types in 3.10.5.
*/ */
template<> NLOHMANN_BASIC_JSON_TPL_DECLARATION
inline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name)
is_nothrow_move_constructible<nlohmann::json>::value&& // NOLINT(misc-redundant-expression) is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression)
is_nothrow_move_assignable<nlohmann::json>::value is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
)
{ {
j1.swap(j2); j1.swap(j2);
} }

View File

@ -97,6 +97,18 @@ TEST_CASE("Better diagnostics")
CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error); CHECK_THROWS_WITH_AS(_ = json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error);
} }
SECTION("Wrong type in update()")
{
json j = {{"foo", "bar"}};
json k = {{"bla", 1}};
CHECK_THROWS_WITH_AS(j.update(k["bla"].begin(), k["bla"].end()), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
CHECK_THROWS_WITH_AS(j.update(k["bla"]), "[json.exception.type_error.312] (/bla) cannot use update() with number", json::type_error);
}
}
TEST_CASE("Regression tests for extended diagnostics")
{
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448") SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
{ {
CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error); CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
@ -110,7 +122,7 @@ TEST_CASE("Better diagnostics")
CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error); CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
} }
SECTION("Regression test for https://github.com/nlohmann/json/issues/2838") SECTION("Regression test for issue #2838 - Assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
{ {
// void push_back(basic_json&& val) // void push_back(basic_json&& val)
{ {
@ -235,7 +247,7 @@ TEST_CASE("Better diagnostics")
} }
} }
SECTION("Regression test for https://github.com/nlohmann/json/issues/3032") SECTION("Regression test for issue #3032 - Yet another assertion failure when inserting into arrays with JSON_DIAGNOSTICS set")
{ {
// reference operator[](size_type idx) // reference operator[](size_type idx)
{ {

View File

@ -31,10 +31,11 @@ SOFTWARE.
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using json = nlohmann::json; using json = nlohmann::json;
using ordered_json = nlohmann::ordered_json;
#include <set> #include <set>
TEST_CASE("hash") TEST_CASE("hash<nlohmann::json>")
{ {
// Collect hashes for different JSON values and make sure that they are distinct // Collect hashes for different JSON values and make sure that they are distinct
// We cannot compare against fixed values, because the implementation of // We cannot compare against fixed values, because the implementation of
@ -82,3 +83,52 @@ TEST_CASE("hash")
CHECK(hashes.size() == 21); CHECK(hashes.size() == 21);
} }
TEST_CASE("hash<nlohmann::ordered_json>")
{
// Collect hashes for different JSON values and make sure that they are distinct
// We cannot compare against fixed values, because the implementation of
// std::hash may differ between compilers.
std::set<std::size_t> hashes;
// null
hashes.insert(std::hash<ordered_json> {}(ordered_json(nullptr)));
// boolean
hashes.insert(std::hash<ordered_json> {}(ordered_json(true)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(false)));
// string
hashes.insert(std::hash<ordered_json> {}(ordered_json("")));
hashes.insert(std::hash<ordered_json> {}(ordered_json("foo")));
// number
hashes.insert(std::hash<ordered_json> {}(ordered_json(0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(unsigned(0))));
hashes.insert(std::hash<ordered_json> {}(ordered_json(-1)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(0.0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json(42.23)));
// array
hashes.insert(std::hash<ordered_json> {}(ordered_json::array()));
hashes.insert(std::hash<ordered_json> {}(ordered_json::array({1, 2, 3})));
// object
hashes.insert(std::hash<ordered_json> {}(ordered_json::object()));
hashes.insert(std::hash<ordered_json> {}(ordered_json::object({{"foo", "bar"}})));
// binary
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({})));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({}, 0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({}, 42)));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3})));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3}, 0)));
hashes.insert(std::hash<ordered_json> {}(ordered_json::binary({1, 2, 3}, 42)));
// discarded
hashes.insert(std::hash<ordered_json> {}(ordered_json(ordered_json::value_t::discarded)));
CHECK(hashes.size() == 21);
}

View File

@ -648,7 +648,7 @@ TEST_CASE("JSON patch")
CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json); CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json);
// create a diff from two JSONs // create a diff from two JSONs
json p2 = json::diff(target, source); json p2 = json::diff(target, source); // NOLINT(readability-suspicious-call-argument)
// p2 = [{"op": "delete", "path": "/GB"}] // p2 = [{"op": "delete", "path": "/GB"}]
CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json); CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json);
} }

View File

@ -40,7 +40,7 @@ TEST_CASE("tests on very large JSONs")
{ {
const auto depth = 5000000; const auto depth = 5000000;
std::string s(2 * depth, '['); std::string s(static_cast<std::size_t>(2 * depth), '[');
std::fill(s.begin() + depth, s.end(), ']'); std::fill(s.begin() + depth, s.end(), ']');
json _; json _;

View File

@ -796,64 +796,89 @@ TEST_CASE("modifiers")
SECTION("update()") SECTION("update()")
{ {
json j_object1 = {{"one", "eins"}, {"two", "zwei"}}; SECTION("non-recursive (default)")
json j_object2 = {{"three", "drei"}, {"two", "zwo"}};
json j_array = {1, 2, 3, 4};
SECTION("const reference")
{ {
SECTION("proper usage") json j_object1 = {{"one", "eins"}, {"two", "zwei"}};
{ json j_object2 = {{"three", "drei"}, {"two", "zwo"}};
j_object1.update(j_object2); json j_array = {1, 2, 3, 4};
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
json j_null; SECTION("const reference")
j_null.update(j_object2); {
CHECK(j_null == j_object2); SECTION("proper usage")
{
j_object1.update(j_object2);
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
json j_null;
j_null.update(j_object2);
CHECK(j_null == j_object2);
}
SECTION("wrong types")
{
CHECK_THROWS_AS(j_array.update(j_object1), json::type_error&);
CHECK_THROWS_WITH(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array");
CHECK_THROWS_AS(j_object1.update(j_array), json::type_error&);
CHECK_THROWS_WITH(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array");
}
} }
SECTION("wrong types") SECTION("iterator range")
{ {
CHECK_THROWS_AS(j_array.update(j_object1), json::type_error&); SECTION("proper usage")
CHECK_THROWS_WITH(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array"); {
j_object1.update(j_object2.begin(), j_object2.end());
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}}));
CHECK_THROWS_AS(j_object1.update(j_array), json::type_error&); json j_null;
CHECK_THROWS_WITH(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array"); j_null.update(j_object2.begin(), j_object2.end());
CHECK(j_null == j_object2);
}
SECTION("empty range")
{
j_object1.update(j_object2.begin(), j_object2.begin());
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}}));
}
SECTION("invalid iterators")
{
json j_other_array2 = {"first", "second"};
CHECK_THROWS_AS(j_array.update(j_object2.begin(), j_object2.end()), json::type_error&);
CHECK_THROWS_AS(j_object1.update(j_object1.begin(), j_object2.end()), json::invalid_iterator&);
CHECK_THROWS_AS(j_object1.update(j_array.begin(), j_array.end()), json::type_error&);
CHECK_THROWS_WITH(j_array.update(j_object2.begin(), j_object2.end()),
"[json.exception.type_error.312] cannot use update() with array");
CHECK_THROWS_WITH(j_object1.update(j_object1.begin(), j_object2.end()),
"[json.exception.invalid_iterator.210] iterators do not fit");
CHECK_THROWS_WITH(j_object1.update(j_array.begin(), j_array.end()),
"[json.exception.type_error.312] cannot use update() with array");
}
} }
} }
SECTION("iterator range") SECTION("recursive")
{ {
SECTION("proper usage") SECTION("const reference")
{ {
j_object1.update(j_object2.begin(), j_object2.end()); SECTION("extend object")
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}})); {
json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
json j2 = {{"string", "t"}, {"numbers", {{"two", 2}}}};
j1.update(j2, true);
CHECK(j1 == json({{"string", "t"}, {"numbers", {{"one", 1}, {"two", 2}}}}));
}
json j_null; SECTION("replace object")
j_null.update(j_object2.begin(), j_object2.end()); {
CHECK(j_null == j_object2); json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}};
} json j2 = {{"string", "t"}, {"numbers", 1}};
j1.update(j2, true);
SECTION("empty range") CHECK(j1 == json({{"string", "t"}, {"numbers", 1}}));
{ }
j_object1.update(j_object2.begin(), j_object2.begin());
CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}}));
}
SECTION("invalid iterators")
{
json j_other_array2 = {"first", "second"};
CHECK_THROWS_AS(j_array.update(j_object2.begin(), j_object2.end()), json::type_error&);
CHECK_THROWS_AS(j_object1.update(j_object1.begin(), j_object2.end()), json::invalid_iterator&);
CHECK_THROWS_AS(j_object1.update(j_array.begin(), j_array.end()), json::invalid_iterator&);
CHECK_THROWS_WITH(j_array.update(j_object2.begin(), j_object2.end()),
"[json.exception.type_error.312] cannot use update() with array");
CHECK_THROWS_WITH(j_object1.update(j_object1.begin(), j_object2.end()),
"[json.exception.invalid_iterator.210] iterators do not fit");
CHECK_THROWS_WITH(j_object1.update(j_array.begin(), j_array.end()),
"[json.exception.invalid_iterator.202] iterators first and last must point to objects");
} }
} }
} }

View File

@ -349,7 +349,7 @@ TEST_CASE("MessagePack")
{ {
for (uint64_t i : for (uint64_t i :
{ {
4294967296lu, 9223372036854775807lu 4294967296LU, 9223372036854775807LU
}) })
{ {
CAPTURE(i) CAPTURE(i)
@ -719,7 +719,7 @@ TEST_CASE("MessagePack")
{ {
for (uint64_t i : for (uint64_t i :
{ {
4294967296lu, 18446744073709551615lu 4294967296LU, 18446744073709551615LU
}) })
{ {
CAPTURE(i) CAPTURE(i)

View File

@ -210,6 +210,45 @@ TEST_CASE("ordered_map")
++it2; ++it2;
CHECK(it2 == om.end()); CHECK(it2 == om.end());
} }
SECTION("with iterator pair")
{
SECTION("range in the middle")
{
// need more elements
om["vier"] = "four";
om["fünf"] = "five";
// delete "zwei" and "drei"
auto it = om.erase(om.begin() + 1, om.begin() + 3);
CHECK(it->first == "vier");
CHECK(om.size() == 3);
}
SECTION("range at the beginning")
{
// need more elements
om["vier"] = "four";
om["fünf"] = "five";
// delete "eins" and "zwei"
auto it = om.erase(om.begin(), om.begin() + 2);
CHECK(it->first == "drei");
CHECK(om.size() == 3);
}
SECTION("range at the end")
{
// need more elements
om["vier"] = "four";
om["fünf"] = "five";
// delete "vier" and "fünf"
auto it = om.erase(om.begin() + 3, om.end());
CHECK(it == om.end());
CHECK(om.size() == 3);
}
}
} }
SECTION("count") SECTION("count")

View File

@ -602,7 +602,7 @@ TEST_CASE("regression tests 2")
auto val2 = j.value("y", defval); auto val2 = j.value("y", defval);
} }
SECTION("issue #2293 - eof doesnt cause parsing to stop") SECTION("issue #2293 - eof doesn't cause parsing to stop")
{ {
std::vector<uint8_t> data = std::vector<uint8_t> data =
{ {
@ -818,6 +818,23 @@ TEST_CASE("regression tests 2")
std::vector<FooBar> foo; std::vector<FooBar> foo;
j.get_to(foo); j.get_to(foo);
} }
SECTION("issue #3108 - ordered_json doesn't support range based erase")
{
ordered_json j = {1, 2, 2, 4};
auto last = std::unique(j.begin(), j.end());
j.erase(last, j.end());
CHECK(j.dump() == "[1,2,4]");
j.erase(std::remove_if(j.begin(), j.end(), [](const ordered_json & val)
{
return val == 2;
}), j.end());
CHECK(j.dump() == "[1,4]");
}
} }
DOCTEST_CLANG_SUPPRESS_WARNING_POP DOCTEST_CLANG_SUPPRESS_WARNING_POP