mirror of
https://github.com/nlohmann/json.git
synced 2026-08-23 17:43:18 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf14cbdd02 | ||
|
|
3565f40229 | ||
|
|
1c5a953de5 | ||
|
|
a03e65420c | ||
|
|
d6ede37088 | ||
|
|
722c03495f | ||
|
|
c197feff81 | ||
|
|
b2b47c69b1 | ||
|
|
6a406ee141 | ||
|
|
ca76c37650 | ||
|
|
fe2bcc080f |
@@ -38,14 +38,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
languages: c-cpp
|
languages: c-cpp
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
|
|||||||
@@ -43,6 +43,6 @@ jobs:
|
|||||||
output: 'flawfinder_results.sarif'
|
output: 'flawfinder_results.sarif'
|
||||||
|
|
||||||
- name: Upload analysis results to GitHub Security tab
|
- name: Upload analysis results to GitHub Security tab
|
||||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
||||||
|
|||||||
@@ -76,6 +76,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
|
|
||||||
# Upload SARIF file generated in previous step
|
# Upload SARIF file generated in previous step
|
||||||
- name: Upload SARIF file
|
- name: Upload SARIF file
|
||||||
uses: github/codeql-action/upload-sarif@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
uses: github/codeql-action/upload-sarif@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||||
with:
|
with:
|
||||||
sarif_file: semgrep.sarif
|
sarif_file: semgrep.sarif
|
||||||
if: always()
|
if: always()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
|
- uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0
|
||||||
with:
|
with:
|
||||||
stale-issue-label: 'state: stale'
|
stale-issue-label: 'state: stale'
|
||||||
stale-pr-label: 'state: stale'
|
stale-pr-label: 'state: stale'
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ static bool accept(InputType&& i,
|
|||||||
const bool ignore_trailing_commas = false);
|
const bool ignore_trailing_commas = false);
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static bool accept(IteratorType first, IteratorType last,
|
static bool accept(IteratorType first, SentinelType last,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false);
|
const bool ignore_trailing_commas = false);
|
||||||
```
|
```
|
||||||
@@ -17,10 +17,11 @@ static bool accept(IteratorType first, IteratorType last,
|
|||||||
Checks whether the input is valid JSON.
|
Checks whether the input is valid JSON.
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
1. Reads from a compatible input.
|
||||||
2. Reads from a pair of character iterators
|
2. Reads from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||||
|
|
||||||
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
||||||
respectively as UTF-8, UTF-16, and UTF-32.
|
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||||
|
the iterator type with `operator!=`.
|
||||||
|
|
||||||
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
|
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
|
||||||
(i.e., a parse error) nor creates diagnostic information.
|
(i.e., a parse error) nor creates diagnostic information.
|
||||||
@@ -44,6 +45,12 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
|||||||
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
|
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
|
||||||
- a pair of pointers such as `ptr` and `ptr + len`
|
- a pair of pointers such as `ptr` and `ptr + len`
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -61,7 +68,7 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
|||||||
: iterator to the start of the character range
|
: iterator to the start of the character range
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of the character range
|
: iterator to the end of the character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
@@ -112,6 +119,7 @@ A UTF-8 byte order mark is silently ignored.
|
|||||||
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
|
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
|
||||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,38 @@ represent a byte array in modern C++.
|
|||||||
|
|
||||||
The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`.
|
The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`.
|
||||||
|
|
||||||
|
#### Custom BinaryType behavior
|
||||||
|
|
||||||
|
When a custom `BinaryType` is configured (other than the default `#!cpp std::vector<std::uint8_t>`), you can assign
|
||||||
|
values of that type directly to a `basic_json` instance, and they will automatically be recognized as binary values
|
||||||
|
rather than arrays:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using custom_json = nlohmann::basic_json<
|
||||||
|
nlohmann::ordered_map, // ObjectType
|
||||||
|
std::vector, // ArrayType
|
||||||
|
std::string, // StringType
|
||||||
|
bool, // BooleanType
|
||||||
|
std::int64_t, // NumberIntegerType
|
||||||
|
std::uint64_t, // NumberUnsignedType
|
||||||
|
double, // NumberFloatType
|
||||||
|
std::allocator, // AllocatorType
|
||||||
|
nlohmann::adl_serializer,
|
||||||
|
std::vector<std::byte> // Custom BinaryType
|
||||||
|
>;
|
||||||
|
|
||||||
|
std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}};
|
||||||
|
custom_json j = data; // Creates a binary value, not an array
|
||||||
|
assert(j.is_binary());
|
||||||
|
|
||||||
|
// Round-tripping works seamlessly
|
||||||
|
auto extracted = j.get<std::vector<std::byte>>();
|
||||||
|
assert(extracted == data);
|
||||||
|
```
|
||||||
|
|
||||||
|
This automatic type detection is a convenience feature that only applies to custom (non-default) `BinaryType` configurations.
|
||||||
|
The default `nlohmann::json` continues to treat `#!cpp std::vector<std::uint8_t>` as arrays for backward compatibility.
|
||||||
|
|
||||||
#### Storage
|
#### Storage
|
||||||
|
|
||||||
Binary Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of the
|
Binary Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of the
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ static basic_json from_bjdata(InputType&& i,
|
|||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
```
|
```
|
||||||
@@ -16,7 +16,7 @@ static basic_json from_bjdata(IteratorType first, IteratorType last,
|
|||||||
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
|
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
1. Reads from a compatible input.
|
||||||
2. Reads from an iterator range.
|
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md).
|
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md).
|
||||||
|
|
||||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: iterator to the start of the input
|
: iterator to the start of the input
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of the input
|
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
`strict` (in)
|
`strict` (in)
|
||||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||||
@@ -103,3 +109,4 @@ Linear in the size of the input.
|
|||||||
|
|
||||||
- Added in version 3.11.0.
|
- Added in version 3.11.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ static basic_json from_bson(InputType&& i,
|
|||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
```
|
```
|
||||||
@@ -16,7 +16,7 @@ static basic_json from_bson(IteratorType first, IteratorType last,
|
|||||||
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
|
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
1. Reads from a compatible input.
|
||||||
2. Reads from an iterator range.
|
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md).
|
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md).
|
||||||
|
|
||||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: iterator to the start of the input
|
: iterator to the start of the input
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of the input
|
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
`strict` (in)
|
`strict` (in)
|
||||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||||
@@ -103,6 +109,7 @@ Linear in the size of the input.
|
|||||||
|
|
||||||
- Added in version 3.4.0.
|
- Added in version 3.4.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ static basic_json from_cbor(InputType&& i,
|
|||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||||
@@ -19,7 +19,7 @@ static basic_json from_cbor(IteratorType first, IteratorType last,
|
|||||||
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
|
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
1. Reads from a compatible input.
|
||||||
2. Reads from an iterator range.
|
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md).
|
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md).
|
||||||
|
|
||||||
@@ -38,6 +38,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -47,7 +53,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: iterator to the start of the input
|
: iterator to the start of the input
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of the input
|
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
`strict` (in)
|
`strict` (in)
|
||||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||||
@@ -113,6 +119,7 @@ Linear in the size of the input.
|
|||||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||||
- Added `tag_handler` parameter in version 3.9.0.
|
- Added `tag_handler` parameter in version 3.9.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ static basic_json from_msgpack(InputType&& i,
|
|||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
```
|
```
|
||||||
@@ -16,7 +16,7 @@ static basic_json from_msgpack(IteratorType first, IteratorType last,
|
|||||||
Deserializes a given input to a JSON value using the MessagePack serialization format.
|
Deserializes a given input to a JSON value using the MessagePack serialization format.
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
1. Reads from a compatible input.
|
||||||
2. Reads from an iterator range.
|
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md).
|
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md).
|
||||||
|
|
||||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: iterator to the start of the input
|
: iterator to the start of the input
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of the input
|
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
`strict` (in)
|
`strict` (in)
|
||||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||||
@@ -105,6 +111,7 @@ Linear in the size of the input.
|
|||||||
- Changed to consume input adapters, removed `start_index` parameter, and added `strict` parameter in version 3.0.0.
|
- Changed to consume input adapters, removed `start_index` parameter, and added `strict` parameter in version 3.0.0.
|
||||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ static basic_json from_ubjson(InputType&& i,
|
|||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true);
|
const bool allow_exceptions = true);
|
||||||
```
|
```
|
||||||
@@ -16,7 +16,7 @@ static basic_json from_ubjson(IteratorType first, IteratorType last,
|
|||||||
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
|
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
|
||||||
|
|
||||||
1. Reads from a compatible input.
|
1. Reads from a compatible input.
|
||||||
2. Reads from an iterator range.
|
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||||
|
|
||||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md).
|
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md).
|
||||||
|
|
||||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type
|
: a compatible iterator type
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
: iterator to the start of the input
|
: iterator to the start of the input
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of the input
|
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
`strict` (in)
|
`strict` (in)
|
||||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||||
@@ -104,6 +110,7 @@ Linear in the size of the input.
|
|||||||
- Added in version 3.1.0.
|
- Added in version 3.1.0.
|
||||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ static basic_json parse(InputType&& i,
|
|||||||
const bool ignore_trailing_commas = false);
|
const bool ignore_trailing_commas = false);
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
static basic_json parse(IteratorType first, IteratorType last,
|
static basic_json parse(IteratorType first, SentinelType last,
|
||||||
const parser_callback_t cb = nullptr,
|
const parser_callback_t cb = nullptr,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
@@ -19,10 +19,11 @@ 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, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||||
|
|
||||||
The `value_type` of the iterator must be an integral type with size of 1, 2, or 4 bytes, which will be interpreted
|
The `value_type` of the iterator must be an integral type with size of 1, 2, or 4 bytes, which will be interpreted
|
||||||
respectively as UTF-8, UTF-16, and UTF-32.
|
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||||
|
the iterator type with `operator!=`.
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
@@ -43,6 +44,12 @@ static basic_json parse(IteratorType first, IteratorType last,
|
|||||||
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
|
- a pair of `std::string::iterator` or `std::vector<std::uint8_t>::iterator`
|
||||||
- a pair of pointers such as `ptr` and `ptr + len`
|
- a pair of pointers such as `ptr` and `ptr + len`
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`i` (in)
|
`i` (in)
|
||||||
@@ -67,7 +74,7 @@ static basic_json parse(IteratorType first, IteratorType last,
|
|||||||
: iterator to the start of a character range
|
: iterator to the start of a character range
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of a character range
|
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
@@ -238,6 +245,7 @@ Invalid Unicode escapes and unpaired surrogates in the input are reported as
|
|||||||
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
|
- Changed [runtime assertion](../../features/assertions.md) in case of `FILE*` null pointers to exception in version 3.12.0.
|
||||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ static bool sax_parse(InputType&& i,
|
|||||||
const bool ignore_trailing_commas = false);
|
const bool ignore_trailing_commas = false);
|
||||||
|
|
||||||
// (2)
|
// (2)
|
||||||
template<class IteratorType, class SAX>
|
template<class IteratorType, class SAX, class SentinelType = IteratorType>
|
||||||
static bool sax_parse(IteratorType first, IteratorType last,
|
static bool sax_parse(IteratorType first, SentinelType last,
|
||||||
SAX* sax,
|
SAX* sax,
|
||||||
input_format_t format = input_format_t::json,
|
input_format_t format = input_format_t::json,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
@@ -23,10 +23,11 @@ static bool sax_parse(IteratorType first, IteratorType last,
|
|||||||
Read from input and generate SAX events
|
Read from input and generate SAX events
|
||||||
|
|
||||||
1. Read from a compatible input.
|
1. Read from a compatible input.
|
||||||
2. Read from a pair of character iterators
|
2. Read from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||||
|
|
||||||
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
||||||
respectively as UTF-8, UTF-16, and UTF-32.
|
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||||
|
the iterator type with `operator!=`.
|
||||||
|
|
||||||
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
|
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
|
||||||
|
|
||||||
@@ -46,6 +47,12 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
: a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type
|
: a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type
|
||||||
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
|
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
|
||||||
|
|
||||||
|
`SentinelType`
|
||||||
|
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for overload (2), for instance.
|
||||||
|
|
||||||
|
- a custom sentinel type for C++20 ranges
|
||||||
|
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||||
|
|
||||||
`SAX`
|
`SAX`
|
||||||
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
|
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
|
||||||
|
|
||||||
@@ -76,7 +83,7 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
: iterator to the start of a character range
|
: iterator to the start of a character range
|
||||||
|
|
||||||
`last` (in)
|
`last` (in)
|
||||||
: iterator to the end of a character range
|
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||||
|
|
||||||
## Return value
|
## Return value
|
||||||
|
|
||||||
@@ -128,6 +135,7 @@ A UTF-8 byte order mark is silently ignored.
|
|||||||
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
- Ignoring comments via `ignore_comments` added in version 3.9.0.
|
||||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||||
|
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ When the macro is not defined, the library will define it to its default value.
|
|||||||
|
|
||||||
Diagnostic messages can also be controlled with the CMake option
|
Diagnostic messages can also be controlled with the CMake option
|
||||||
[`JSON_Diagnostics`](../../integration/cmake.md#json_diagnostics) (`OFF` by default)
|
[`JSON_Diagnostics`](../../integration/cmake.md#json_diagnostics) (`OFF` by default)
|
||||||
which defines `JSON_DIAGNOSTICS` accordingly.
|
which defines `JSON_DIAGNOSTICS` accordingly. Note this only applies when building the
|
||||||
|
library from source — see the pre-installed-package caveat on that page.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,28 @@ json j = {{"one", 1}, {"two", 2}};
|
|||||||
auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}}
|
auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`#!cpp std::pair` and `#!cpp std::tuple` are also supported, converting positionally to and from a JSON array:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
json j = {1.0, "hello", 42};
|
||||||
|
auto t = j.get<std::tuple<double, std::string, int>>(); // {1.0, "hello", 42}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "Extracting references into a tuple"
|
||||||
|
|
||||||
|
A tuple type may also hold references (e.g. `#!cpp std::tuple<double&, std::string&>`) to avoid copying: `get`
|
||||||
|
then returns a tuple of references pointing directly at the elements stored inside the `basic_json` array,
|
||||||
|
rather than a tuple of copies:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
json j = {1.0, "hello"};
|
||||||
|
auto refs = j.get<std::tuple<double&, std::string&>>();
|
||||||
|
std::get<1>(refs) = "world"; // modifies j[1] in place
|
||||||
|
```
|
||||||
|
|
||||||
|
A referenced type must be one the library actually stores (or an arithmetic type it can convert to/from);
|
||||||
|
otherwise this is a compile error.
|
||||||
|
|
||||||
## Implicit conversions
|
## Implicit conversions
|
||||||
|
|
||||||
By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted:
|
By default, a JSON value implicitly converts to a compatible C++ type, so the explicit `get` call can often be omitted:
|
||||||
@@ -136,6 +158,20 @@ std::vector<int> numbers = {1, 2, 3};
|
|||||||
json j = numbers; // [1,2,3]
|
json j = numbers; // [1,2,3]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! info "Constructing from a C++20 range view"
|
||||||
|
|
||||||
|
A `json` array can also be constructed directly from a C++20 range view (`std::ranges::view`), such as the result
|
||||||
|
of `std::views::filter` or `std::views::transform` -- no intermediate container is needed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::vector<int> nums{1, 2, 37, 42, 21};
|
||||||
|
auto filtered = nums | std::views::filter([](int i) { return i > 10; });
|
||||||
|
json j(filtered); // [37,42,21]
|
||||||
|
```
|
||||||
|
|
||||||
|
This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
|
||||||
|
to incomplete C++20 ranges support there.
|
||||||
|
|
||||||
## Your own types
|
## Your own types
|
||||||
|
|
||||||
The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
|
The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
|
||||||
|
|||||||
@@ -135,6 +135,31 @@ Enable CI build targets. The exact targets are used during the several CI steps
|
|||||||
|
|
||||||
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
|
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
|
||||||
|
|
||||||
|
!!! warning "Does not apply to a pre-installed package"
|
||||||
|
|
||||||
|
This option only takes effect when building nlohmann/json from source as part of your own
|
||||||
|
CMake project (e.g. via [`FetchContent`](#fetchcontent) or [`add_subdirectory`](#external)).
|
||||||
|
It has **no effect** on a package that was already built and installed elsewhere (Homebrew,
|
||||||
|
vcpkg, a system package, etc.) — the resulting compile definition is baked into the exported
|
||||||
|
`nlohmann_jsonTargets.cmake` at install time, and `set(JSON_Diagnostics ON)` before
|
||||||
|
`find_package()` does not change it (verified against the Homebrew-installed package: the
|
||||||
|
exported target still carries a fixed `$<$<BOOL:OFF>:JSON_DIAGNOSTICS=1>`, regardless of any
|
||||||
|
variable set in the consuming project).
|
||||||
|
|
||||||
|
To enable extended diagnostics for a pre-installed package, override the imported target's
|
||||||
|
property directly after `find_package()`:
|
||||||
|
|
||||||
|
```cmake
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
|
||||||
|
INTERFACE_COMPILE_DEFINITIONS "JSON_DIAGNOSTICS=1")
|
||||||
|
```
|
||||||
|
|
||||||
|
This only works cleanly when your project is the sole consumer of that imported target. If
|
||||||
|
nlohmann_json is pulled in from more than one place in your dependency graph with different
|
||||||
|
`JSON_DIAGNOSTICS` values, you may see a `"JSON_DIAGNOSTICS" redefined` compiler error, since
|
||||||
|
conflicting `-D` flags can end up on the same compile command line.
|
||||||
|
|
||||||
### `JSON_Diagnostic_Positions`
|
### `JSON_Diagnostic_Positions`
|
||||||
|
|
||||||
Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default.
|
Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <unordered_map> // unordered_map
|
#include <unordered_map> // unordered_map
|
||||||
#include <utility> // pair, declval
|
#include <utility> // pair, declval
|
||||||
#include <valarray> // valarray
|
#include <valarray> // valarray
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
#include <nlohmann/detail/exceptions.hpp>
|
#include <nlohmann/detail/exceptions.hpp>
|
||||||
#include <nlohmann/detail/macro_scope.hpp>
|
#include <nlohmann/detail/macro_scope.hpp>
|
||||||
@@ -332,6 +333,7 @@ template < typename BasicJsonType, typename ConstructibleArrayType,
|
|||||||
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||||
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||||
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
||||||
|
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||||
!is_basic_json<ConstructibleArrayType>::value,
|
!is_basic_json<ConstructibleArrayType>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
||||||
@@ -377,6 +379,25 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
|
|||||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||||
|
int > = 0 >
|
||||||
|
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
||||||
|
{
|
||||||
|
if (j.is_binary())
|
||||||
|
{
|
||||||
|
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
|
||||||
|
}
|
||||||
|
else if (j.is_array())
|
||||||
|
{
|
||||||
|
from_json_array_impl(j, bin, priority_tag<3> {});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||||
|
|||||||
@@ -377,6 +377,7 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
|||||||
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||||
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
||||||
|
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||||
!is_basic_json<CompatibleArrayType>::value
|
!is_basic_json<CompatibleArrayType>::value
|
||||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||||
@@ -406,6 +407,14 @@ inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bi
|
|||||||
external_constructor<value_t::binary>::construct(j, bin);
|
external_constructor<value_t::binary>::construct(j, bin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||||
|
int > = 0 >
|
||||||
|
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
template<typename BasicJsonType, typename T,
|
||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
|
|||||||
@@ -1846,6 +1846,47 @@ class binary_reader
|
|||||||
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief reject a negative UBJSON/BJData string length
|
||||||
|
|
||||||
|
String and key lengths are written with signed integer markers (i, I, l,
|
||||||
|
L). A negative value is malformed; without this check get_string() would
|
||||||
|
silently treat it as an empty string and leave the following bytes to be
|
||||||
|
misread as the next value. This mirrors the non-negative check the
|
||||||
|
optimized-container count path already performs in get_ubjson_size_value.
|
||||||
|
|
||||||
|
@param[in] len the string length read from the input
|
||||||
|
@return whether the length is valid (non-negative)
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
bool check_ubjson_string_length(const NumberType len)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "string length must not be negative", "string"), nullptr));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create the error message for a missing/invalid length-type marker
|
||||||
|
|
||||||
|
Used both when reading a UBJSON/BJData string and when reading an optimized
|
||||||
|
container size. The accepted markers depend on the input format, and the
|
||||||
|
size variant appends " after '#'" via @a infix.
|
||||||
|
|
||||||
|
@param[in] last_token the offending byte as returned by get_token_string()
|
||||||
|
@param[in] infix extra context inserted after the marker list
|
||||||
|
@return the formatted error message
|
||||||
|
*/
|
||||||
|
std::string unexpected_length_type_message(const std::string& last_token, const char* infix) const
|
||||||
|
{
|
||||||
|
return concat("expected length type specification (",
|
||||||
|
input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L",
|
||||||
|
")", infix, "; last byte: 0x", last_token);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a UBJSON string
|
@brief reads a UBJSON string
|
||||||
|
|
||||||
@@ -1883,25 +1924,25 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -1938,17 +1979,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, ""), "string"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -2227,17 +2259,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, " after '#'"), "size"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ class input_stream_adapter
|
|||||||
|
|
||||||
// General-purpose iterator-based adapter. It might not be as fast as
|
// General-purpose iterator-based adapter. It might not be as fast as
|
||||||
// theoretically possible for some containers, but it is extremely versatile.
|
// theoretically possible for some containers, but it is extremely versatile.
|
||||||
template<typename IteratorType>
|
// SentinelType defaults to IteratorType for backward compatibility, but may
|
||||||
|
// be a different type (e.g., a C++20 sentinel or counted_iterator).
|
||||||
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
class iterator_input_adapter
|
class iterator_input_adapter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -169,9 +171,10 @@ class iterator_input_adapter
|
|||||||
// in wide_string_input_adapter, which does not expose this).
|
// in wide_string_input_adapter, which does not expose this).
|
||||||
static constexpr bool supports_seek =
|
static constexpr bool supports_seek =
|
||||||
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
||||||
|
&& std::is_same<IteratorType, SentinelType>::value
|
||||||
&& sizeof(char_type) == 1;
|
&& sizeof(char_type) == 1;
|
||||||
|
|
||||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
iterator_input_adapter(IteratorType first, SentinelType last)
|
||||||
: begin(first), current(std::move(first)), end(std::move(last))
|
: begin(first), current(std::move(first)), end(std::move(last))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -216,19 +219,30 @@ class iterator_input_adapter
|
|||||||
private:
|
private:
|
||||||
// whether IteratorType refers to a contiguous range and therefore supports
|
// whether IteratorType refers to a contiguous range and therefore supports
|
||||||
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
||||||
// library iterators such as those of std::vector and std::string)
|
// library iterators such as those of std::vector and std::string).
|
||||||
|
// Computing the available element count needs either same-type iterators
|
||||||
|
// (plain std::distance) or, in C++20, a sized sentinel (std::ranges::distance),
|
||||||
|
// e.g. std::counted_iterator paired with std::default_sentinel_t.
|
||||||
static constexpr bool iterator_is_contiguous =
|
static constexpr bool iterator_is_contiguous =
|
||||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
std::contiguous_iterator<IteratorType> ||
|
(std::is_same<IteratorType, SentinelType>::value || std::sized_sentinel_for<SentinelType, IteratorType>)
|
||||||
|
&& (std::contiguous_iterator<IteratorType> || std::is_pointer<IteratorType>::value);
|
||||||
|
#else
|
||||||
|
std::is_same<IteratorType, SentinelType>::value && std::is_pointer<IteratorType>::value;
|
||||||
#endif
|
#endif
|
||||||
std::is_pointer<IteratorType>::value;
|
|
||||||
|
|
||||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
||||||
{
|
{
|
||||||
const std::size_t wanted = count * sizeof(T);
|
const std::size_t wanted = count * sizeof(T);
|
||||||
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
|
// std::ranges::distance also supports sized sentinels of a different
|
||||||
|
// type (e.g. std::counted_iterator + std::default_sentinel_t)
|
||||||
|
const std::size_t available = static_cast<std::size_t>(std::ranges::distance(current, end)) * sizeof(char_type);
|
||||||
|
#else
|
||||||
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
||||||
|
#endif
|
||||||
const std::size_t copied = (std::min)(wanted, available);
|
const std::size_t copied = (std::min)(wanted, available);
|
||||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||||
{
|
{
|
||||||
@@ -267,7 +281,7 @@ class iterator_input_adapter
|
|||||||
|
|
||||||
IteratorType begin;
|
IteratorType begin;
|
||||||
IteratorType current;
|
IteratorType current;
|
||||||
IteratorType end;
|
SentinelType end;
|
||||||
|
|
||||||
template<typename BaseInputAdapter, size_t T>
|
template<typename BaseInputAdapter, size_t T>
|
||||||
friend struct wide_string_input_helper;
|
friend struct wide_string_input_helper;
|
||||||
@@ -381,17 +395,30 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
// A supplementary code point is a high surrogate (0xD800..0xDBFF)
|
||||||
|
// followed by a low surrogate (0xDC00..0xDFFF). A lone low
|
||||||
|
// surrogate, a high surrogate at the end of the input, or a high
|
||||||
|
// surrogate followed by any other unit is malformed UTF-16. In
|
||||||
|
// that case the offending unit is passed through unchanged so the
|
||||||
|
// UTF-8 decoder rejects it, matching how \uXXXX surrogate escapes
|
||||||
|
// are handled in the lexer.
|
||||||
|
bool valid_pair = false;
|
||||||
|
if (wc <= 0xDBFF && JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||||
{
|
{
|
||||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
{
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||||
utf8_bytes_filled = 4;
|
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||||
|
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||||
|
utf8_bytes_filled = 4;
|
||||||
|
valid_pair = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!valid_pair)
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||||
utf8_bytes_filled = 1;
|
utf8_bytes_filled = 1;
|
||||||
@@ -453,19 +480,54 @@ class wide_string_input_adapter
|
|||||||
std::size_t utf8_bytes_filled = 0;
|
std::size_t utf8_bytes_filled = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename IteratorType, typename Enable = void>
|
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
|
||||||
struct iterator_input_adapter_factory
|
struct iterator_input_adapter_factory
|
||||||
{
|
{
|
||||||
using iterator_type = IteratorType;
|
using iterator_type = IteratorType;
|
||||||
|
using sentinel_type = SentinelType;
|
||||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
static adapter_type create(IteratorType first, SentinelType last)
|
||||||
{
|
{
|
||||||
return adapter_type(std::move(first), std::move(last));
|
return adapter_type(std::move(first), std::move(last));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Detection: whether IteratorType and SentinelType can be compared with !=
|
||||||
|
template<typename IteratorType, typename SentinelType, typename = void>
|
||||||
|
struct can_compare_ne_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne_impl < IteratorType, SentinelType,
|
||||||
|
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
// Workaround for reversed operator order
|
||||||
|
template<typename IteratorType, typename SentinelType, typename = void>
|
||||||
|
struct can_compare_ne_reversed : std::false_type {};
|
||||||
|
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne_reversed < IteratorType, SentinelType,
|
||||||
|
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne_either_order : std::integral_constant < bool,
|
||||||
|
can_compare_ne_impl<IteratorType, SentinelType>::value ||
|
||||||
|
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
|
||||||
|
|
||||||
|
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
|
||||||
|
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
|
||||||
|
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
|
||||||
|
// SFINAE the `operator!=` detection above for std::nullptr_t against
|
||||||
|
// container/string types, which would otherwise make such calls ambiguous
|
||||||
|
// with the compatible-input overload.
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne : std::integral_constant < bool,
|
||||||
|
!std::is_same<SentinelType, std::nullptr_t>::value &&
|
||||||
|
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct is_iterator_of_multibyte
|
struct is_iterator_of_multibyte
|
||||||
{
|
{
|
||||||
@@ -476,25 +538,31 @@ struct is_iterator_of_multibyte
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType>
|
||||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||||
{
|
{
|
||||||
using iterator_type = IteratorType;
|
using iterator_type = IteratorType;
|
||||||
|
using sentinel_type = SentinelType;
|
||||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
static adapter_type create(IteratorType first, SentinelType last)
|
||||||
{
|
{
|
||||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// General purpose iterator-based input
|
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||||
template<typename IteratorType>
|
// defaults to IteratorType for the common same-type case, but may differ for
|
||||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||||
|
// be compared with !=.
|
||||||
|
template < typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
typename = typename std::enable_if <
|
||||||
|
can_compare_ne<IteratorType, SentinelType>::value >::type >
|
||||||
|
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
|
||||||
{
|
{
|
||||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
|
||||||
return factory_type::create(first, last);
|
return factory_type::create(first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <tuple> // tuple
|
#include <tuple> // tuple
|
||||||
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
||||||
#include <utility> // declval
|
#include <utility> // declval
|
||||||
|
#include <vector> // vector
|
||||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||||
#include <cstddef> // byte
|
#include <cstddef> // byte
|
||||||
#endif
|
#endif
|
||||||
@@ -632,6 +633,14 @@ template<typename BasicJsonType, typename CompatibleType>
|
|||||||
struct is_compatible_type
|
struct is_compatible_type
|
||||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||||
|
struct is_compatible_binary_type
|
||||||
|
{
|
||||||
|
static constexpr bool value =
|
||||||
|
std::is_same<typename BasicJsonType::binary_t::container_type, CompatibleArrayType>::value &&
|
||||||
|
!std::is_same<typename BasicJsonType::binary_t::container_type, std::vector<std::uint8_t>>::value;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleReferenceType>
|
template<typename BasicJsonType, typename CompatibleReferenceType>
|
||||||
struct is_compatible_reference_type_impl
|
struct is_compatible_reference_type_impl
|
||||||
{
|
{
|
||||||
|
|||||||
+32
-24
@@ -4083,12 +4083,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief deserialize from a pair of character iterators
|
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json parse(IteratorType first,
|
static basic_json parse(IteratorType first,
|
||||||
IteratorType last,
|
SentinelType last,
|
||||||
parser_callback_t cb = nullptr,
|
parser_callback_t cb = nullptr,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
@@ -4122,10 +4123,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief check if the input is valid JSON
|
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
static bool accept(IteratorType first, IteratorType last,
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
|
static bool accept(IteratorType first, SentinelType last,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
@@ -4157,11 +4159,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief generate SAX events
|
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
||||||
template<class IteratorType, class SAX>
|
template<class IteratorType, class SAX, class SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
|
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
|
||||||
input_format_t format = input_format_t::json,
|
input_format_t format = input_format_t::json,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
@@ -4461,11 +4464,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in CBOR format
|
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
@@ -4518,11 +4522,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in MessagePack format
|
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
@@ -4572,11 +4577,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in UBJSON format
|
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
@@ -4626,11 +4632,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BJData format
|
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
@@ -4656,11 +4663,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BSON format
|
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -189,6 +189,7 @@
|
|||||||
#include <unordered_map> // unordered_map
|
#include <unordered_map> // unordered_map
|
||||||
#include <utility> // pair, declval
|
#include <utility> // pair, declval
|
||||||
#include <valarray> // valarray
|
#include <valarray> // valarray
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
// #include <nlohmann/detail/exceptions.hpp>
|
// #include <nlohmann/detail/exceptions.hpp>
|
||||||
// __ _____ _____ _____
|
// __ _____ _____ _____
|
||||||
@@ -3592,6 +3593,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <tuple> // tuple
|
#include <tuple> // tuple
|
||||||
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
||||||
#include <utility> // declval
|
#include <utility> // declval
|
||||||
|
#include <vector> // vector
|
||||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||||
#include <cstddef> // byte
|
#include <cstddef> // byte
|
||||||
#endif
|
#endif
|
||||||
@@ -4394,6 +4396,14 @@ template<typename BasicJsonType, typename CompatibleType>
|
|||||||
struct is_compatible_type
|
struct is_compatible_type
|
||||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||||
|
|
||||||
|
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||||
|
struct is_compatible_binary_type
|
||||||
|
{
|
||||||
|
static constexpr bool value =
|
||||||
|
std::is_same<typename BasicJsonType::binary_t::container_type, CompatibleArrayType>::value &&
|
||||||
|
!std::is_same<typename BasicJsonType::binary_t::container_type, std::vector<std::uint8_t>>::value;
|
||||||
|
};
|
||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleReferenceType>
|
template<typename BasicJsonType, typename CompatibleReferenceType>
|
||||||
struct is_compatible_reference_type_impl
|
struct is_compatible_reference_type_impl
|
||||||
{
|
{
|
||||||
@@ -5537,6 +5547,7 @@ template < typename BasicJsonType, typename ConstructibleArrayType,
|
|||||||
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||||
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||||
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
||||||
|
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||||
!is_basic_json<ConstructibleArrayType>::value,
|
!is_basic_json<ConstructibleArrayType>::value,
|
||||||
int > = 0 >
|
int > = 0 >
|
||||||
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
||||||
@@ -5582,6 +5593,25 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
|
|||||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||||
|
int > = 0 >
|
||||||
|
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
||||||
|
{
|
||||||
|
if (j.is_binary())
|
||||||
|
{
|
||||||
|
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
|
||||||
|
}
|
||||||
|
else if (j.is_array())
|
||||||
|
{
|
||||||
|
from_json_array_impl(j, bin, priority_tag<3> {});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||||
@@ -6478,6 +6508,7 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
|||||||
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||||
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
||||||
|
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||||
!is_basic_json<CompatibleArrayType>::value
|
!is_basic_json<CompatibleArrayType>::value
|
||||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||||
@@ -6507,6 +6538,14 @@ inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bi
|
|||||||
external_constructor<value_t::binary>::construct(j, bin);
|
external_constructor<value_t::binary>::construct(j, bin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||||
|
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||||
|
int > = 0 >
|
||||||
|
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
|
||||||
|
{
|
||||||
|
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
template<typename BasicJsonType, typename T,
|
||||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
||||||
@@ -7103,7 +7142,9 @@ class input_stream_adapter
|
|||||||
|
|
||||||
// General-purpose iterator-based adapter. It might not be as fast as
|
// General-purpose iterator-based adapter. It might not be as fast as
|
||||||
// theoretically possible for some containers, but it is extremely versatile.
|
// theoretically possible for some containers, but it is extremely versatile.
|
||||||
template<typename IteratorType>
|
// SentinelType defaults to IteratorType for backward compatibility, but may
|
||||||
|
// be a different type (e.g., a C++20 sentinel or counted_iterator).
|
||||||
|
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||||
class iterator_input_adapter
|
class iterator_input_adapter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -7117,9 +7158,10 @@ class iterator_input_adapter
|
|||||||
// in wide_string_input_adapter, which does not expose this).
|
// in wide_string_input_adapter, which does not expose this).
|
||||||
static constexpr bool supports_seek =
|
static constexpr bool supports_seek =
|
||||||
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
||||||
|
&& std::is_same<IteratorType, SentinelType>::value
|
||||||
&& sizeof(char_type) == 1;
|
&& sizeof(char_type) == 1;
|
||||||
|
|
||||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
iterator_input_adapter(IteratorType first, SentinelType last)
|
||||||
: begin(first), current(std::move(first)), end(std::move(last))
|
: begin(first), current(std::move(first)), end(std::move(last))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -7164,19 +7206,30 @@ class iterator_input_adapter
|
|||||||
private:
|
private:
|
||||||
// whether IteratorType refers to a contiguous range and therefore supports
|
// whether IteratorType refers to a contiguous range and therefore supports
|
||||||
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
||||||
// library iterators such as those of std::vector and std::string)
|
// library iterators such as those of std::vector and std::string).
|
||||||
|
// Computing the available element count needs either same-type iterators
|
||||||
|
// (plain std::distance) or, in C++20, a sized sentinel (std::ranges::distance),
|
||||||
|
// e.g. std::counted_iterator paired with std::default_sentinel_t.
|
||||||
static constexpr bool iterator_is_contiguous =
|
static constexpr bool iterator_is_contiguous =
|
||||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
std::contiguous_iterator<IteratorType> ||
|
(std::is_same<IteratorType, SentinelType>::value || std::sized_sentinel_for<SentinelType, IteratorType>)
|
||||||
|
&& (std::contiguous_iterator<IteratorType> || std::is_pointer<IteratorType>::value);
|
||||||
|
#else
|
||||||
|
std::is_same<IteratorType, SentinelType>::value && std::is_pointer<IteratorType>::value;
|
||||||
#endif
|
#endif
|
||||||
std::is_pointer<IteratorType>::value;
|
|
||||||
|
|
||||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||||
template<class T>
|
template<class T>
|
||||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
||||||
{
|
{
|
||||||
const std::size_t wanted = count * sizeof(T);
|
const std::size_t wanted = count * sizeof(T);
|
||||||
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
|
// std::ranges::distance also supports sized sentinels of a different
|
||||||
|
// type (e.g. std::counted_iterator + std::default_sentinel_t)
|
||||||
|
const std::size_t available = static_cast<std::size_t>(std::ranges::distance(current, end)) * sizeof(char_type);
|
||||||
|
#else
|
||||||
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
||||||
|
#endif
|
||||||
const std::size_t copied = (std::min)(wanted, available);
|
const std::size_t copied = (std::min)(wanted, available);
|
||||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||||
{
|
{
|
||||||
@@ -7215,7 +7268,7 @@ class iterator_input_adapter
|
|||||||
|
|
||||||
IteratorType begin;
|
IteratorType begin;
|
||||||
IteratorType current;
|
IteratorType current;
|
||||||
IteratorType end;
|
SentinelType end;
|
||||||
|
|
||||||
template<typename BaseInputAdapter, size_t T>
|
template<typename BaseInputAdapter, size_t T>
|
||||||
friend struct wide_string_input_helper;
|
friend struct wide_string_input_helper;
|
||||||
@@ -7329,17 +7382,30 @@ struct wide_string_input_helper<BaseInputAdapter, 2>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(!input.empty()))
|
// A supplementary code point is a high surrogate (0xD800..0xDBFF)
|
||||||
|
// followed by a low surrogate (0xDC00..0xDFFF). A lone low
|
||||||
|
// surrogate, a high surrogate at the end of the input, or a high
|
||||||
|
// surrogate followed by any other unit is malformed UTF-16. In
|
||||||
|
// that case the offending unit is passed through unchanged so the
|
||||||
|
// UTF-8 decoder rejects it, matching how \uXXXX surrogate escapes
|
||||||
|
// are handled in the lexer.
|
||||||
|
bool valid_pair = false;
|
||||||
|
if (wc <= 0xDBFF && JSON_HEDLEY_UNLIKELY(!input.empty()))
|
||||||
{
|
{
|
||||||
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
const auto wc2 = static_cast<unsigned int>(input.get_character());
|
||||||
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
if (0xDC00 <= wc2 && wc2 <= 0xDFFF)
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
{
|
||||||
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
|
||||||
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
|
||||||
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
|
||||||
utf8_bytes_filled = 4;
|
utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
|
||||||
|
utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
|
||||||
|
utf8_bytes_filled = 4;
|
||||||
|
valid_pair = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (!valid_pair)
|
||||||
{
|
{
|
||||||
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
|
||||||
utf8_bytes_filled = 1;
|
utf8_bytes_filled = 1;
|
||||||
@@ -7401,19 +7467,54 @@ class wide_string_input_adapter
|
|||||||
std::size_t utf8_bytes_filled = 0;
|
std::size_t utf8_bytes_filled = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename IteratorType, typename Enable = void>
|
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
|
||||||
struct iterator_input_adapter_factory
|
struct iterator_input_adapter_factory
|
||||||
{
|
{
|
||||||
using iterator_type = IteratorType;
|
using iterator_type = IteratorType;
|
||||||
|
using sentinel_type = SentinelType;
|
||||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
static adapter_type create(IteratorType first, SentinelType last)
|
||||||
{
|
{
|
||||||
return adapter_type(std::move(first), std::move(last));
|
return adapter_type(std::move(first), std::move(last));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Detection: whether IteratorType and SentinelType can be compared with !=
|
||||||
|
template<typename IteratorType, typename SentinelType, typename = void>
|
||||||
|
struct can_compare_ne_impl : std::false_type {};
|
||||||
|
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne_impl < IteratorType, SentinelType,
|
||||||
|
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
// Workaround for reversed operator order
|
||||||
|
template<typename IteratorType, typename SentinelType, typename = void>
|
||||||
|
struct can_compare_ne_reversed : std::false_type {};
|
||||||
|
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne_reversed < IteratorType, SentinelType,
|
||||||
|
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne_either_order : std::integral_constant < bool,
|
||||||
|
can_compare_ne_impl<IteratorType, SentinelType>::value ||
|
||||||
|
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
|
||||||
|
|
||||||
|
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
|
||||||
|
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
|
||||||
|
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
|
||||||
|
// SFINAE the `operator!=` detection above for std::nullptr_t against
|
||||||
|
// container/string types, which would otherwise make such calls ambiguous
|
||||||
|
// with the compatible-input overload.
|
||||||
|
template<typename IteratorType, typename SentinelType>
|
||||||
|
struct can_compare_ne : std::integral_constant < bool,
|
||||||
|
!std::is_same<SentinelType, std::nullptr_t>::value &&
|
||||||
|
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct is_iterator_of_multibyte
|
struct is_iterator_of_multibyte
|
||||||
{
|
{
|
||||||
@@ -7424,25 +7525,31 @@ struct is_iterator_of_multibyte
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType>
|
||||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||||
{
|
{
|
||||||
using iterator_type = IteratorType;
|
using iterator_type = IteratorType;
|
||||||
|
using sentinel_type = SentinelType;
|
||||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
||||||
|
|
||||||
static adapter_type create(IteratorType first, IteratorType last)
|
static adapter_type create(IteratorType first, SentinelType last)
|
||||||
{
|
{
|
||||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// General purpose iterator-based input
|
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||||
template<typename IteratorType>
|
// defaults to IteratorType for the common same-type case, but may differ for
|
||||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||||
|
// be compared with !=.
|
||||||
|
template < typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
typename = typename std::enable_if <
|
||||||
|
can_compare_ne<IteratorType, SentinelType>::value >::type >
|
||||||
|
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
|
||||||
{
|
{
|
||||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
|
||||||
return factory_type::create(first, last);
|
return factory_type::create(first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12288,6 +12395,47 @@ class binary_reader
|
|||||||
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
return get_ubjson_value(get_char ? get_ignore_noop() : current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief reject a negative UBJSON/BJData string length
|
||||||
|
|
||||||
|
String and key lengths are written with signed integer markers (i, I, l,
|
||||||
|
L). A negative value is malformed; without this check get_string() would
|
||||||
|
silently treat it as an empty string and leave the following bytes to be
|
||||||
|
misread as the next value. This mirrors the non-negative check the
|
||||||
|
optimized-container count path already performs in get_ubjson_size_value.
|
||||||
|
|
||||||
|
@param[in] len the string length read from the input
|
||||||
|
@return whether the length is valid (non-negative)
|
||||||
|
*/
|
||||||
|
template<typename NumberType>
|
||||||
|
bool check_ubjson_string_length(const NumberType len)
|
||||||
|
{
|
||||||
|
if (JSON_HEDLEY_UNLIKELY(len < 0))
|
||||||
|
{
|
||||||
|
return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, "string length must not be negative", "string"), nullptr));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create the error message for a missing/invalid length-type marker
|
||||||
|
|
||||||
|
Used both when reading a UBJSON/BJData string and when reading an optimized
|
||||||
|
container size. The accepted markers depend on the input format, and the
|
||||||
|
size variant appends " after '#'" via @a infix.
|
||||||
|
|
||||||
|
@param[in] last_token the offending byte as returned by get_token_string()
|
||||||
|
@param[in] infix extra context inserted after the marker list
|
||||||
|
@return the formatted error message
|
||||||
|
*/
|
||||||
|
std::string unexpected_length_type_message(const std::string& last_token, const char* infix) const
|
||||||
|
{
|
||||||
|
return concat("expected length type specification (",
|
||||||
|
input_format == input_format_t::bjdata ? "U, i, u, I, m, l, M, L" : "U, i, I, l, L",
|
||||||
|
")", infix, "; last byte: 0x", last_token);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief reads a UBJSON string
|
@brief reads a UBJSON string
|
||||||
|
|
||||||
@@ -12325,25 +12473,25 @@ class binary_reader
|
|||||||
case 'i':
|
case 'i':
|
||||||
{
|
{
|
||||||
std::int8_t len{};
|
std::int8_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'I':
|
case 'I':
|
||||||
{
|
{
|
||||||
std::int16_t len{};
|
std::int16_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
{
|
{
|
||||||
std::int32_t len{};
|
std::int32_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
{
|
{
|
||||||
std::int64_t len{};
|
std::int64_t len{};
|
||||||
return get_number(input_format, len) && get_string(input_format, len, result);
|
return get_number(input_format, len) && check_ubjson_string_length(len) && get_string(input_format, len, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
@@ -12380,17 +12528,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, ""), "string"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -12669,17 +12808,8 @@ class binary_reader
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto last_token = get_token_string();
|
auto last_token = get_token_string();
|
||||||
std::string message;
|
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
|
||||||
|
exception_message(input_format, unexpected_length_type_message(last_token, " after '#'"), "size"), nullptr));
|
||||||
if (input_format != input_format_t::bjdata)
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
|
|
||||||
}
|
|
||||||
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@@ -25045,12 +25175,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief deserialize from a pair of character iterators
|
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json parse(IteratorType first,
|
static basic_json parse(IteratorType first,
|
||||||
IteratorType last,
|
SentinelType last,
|
||||||
parser_callback_t cb = nullptr,
|
parser_callback_t cb = nullptr,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
@@ -25084,10 +25215,11 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments, ignore_trailing_commas).accept(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief check if the input is valid JSON
|
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
static bool accept(IteratorType first, IteratorType last,
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
|
static bool accept(IteratorType first, SentinelType last,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
const bool ignore_trailing_commas = false)
|
const bool ignore_trailing_commas = false)
|
||||||
{
|
{
|
||||||
@@ -25119,11 +25251,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
: detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief generate SAX events
|
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
||||||
template<class IteratorType, class SAX>
|
template<class IteratorType, class SAX, class SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_NON_NULL(3)
|
JSON_HEDLEY_NON_NULL(3)
|
||||||
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
|
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
|
||||||
input_format_t format = input_format_t::json,
|
input_format_t format = input_format_t::json,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool ignore_comments = false,
|
const bool ignore_comments = false,
|
||||||
@@ -25423,11 +25556,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in CBOR format
|
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true,
|
const bool allow_exceptions = true,
|
||||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||||
@@ -25480,11 +25614,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in MessagePack format
|
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
@@ -25534,11 +25669,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in UBJSON format
|
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
@@ -25588,11 +25724,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BJData format
|
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
@@ -25618,11 +25755,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
return res ? result : basic_json(value_t::discarded);
|
return res ? result : basic_json(value_t::discarded);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create a JSON value from an input in BSON format
|
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||||
template<typename IteratorType>
|
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||||
|
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||||
const bool strict = true,
|
const bool strict = true,
|
||||||
const bool allow_exceptions = true)
|
const bool allow_exceptions = true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,4 +30,17 @@ inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
|
|||||||
return byte_vector;
|
return byte_vector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sentinel for istreambuf_iterator; compares != true until EOF is reached
|
||||||
|
// lets tests read a file directly via the new iterator+sentinel overloads
|
||||||
|
// instead of buffering the whole file into a vector first.
|
||||||
|
// Only the iterator-first direction (it != sentinel) is ever evaluated by
|
||||||
|
// the library's parse loop, so no reversed-order overload is needed.
|
||||||
|
struct istreambuf_sentinel
|
||||||
|
{
|
||||||
|
friend bool operator!=(const std::istreambuf_iterator<char>& it, const istreambuf_sentinel& /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return it != std::istreambuf_iterator<char>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|||||||
@@ -2721,6 +2721,19 @@ TEST_CASE("BJData")
|
|||||||
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing BJData string: expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x31", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("negative length")
|
||||||
|
{
|
||||||
|
json _;
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vi = {'S', 'i', 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vi), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing BJData string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_bjdata(vi, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vl = {'S', 'l', 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_bjdata(vl), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing BJData string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_bjdata(vl, true, false).is_discarded());
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("parse bjdata markers in ubjson")
|
SECTION("parse bjdata markers in ubjson")
|
||||||
{
|
{
|
||||||
// create a single-character string for all number types
|
// create a single-character string for all number types
|
||||||
@@ -3699,6 +3712,15 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse BJData directly from a file using iterator and sentinel")
|
||||||
|
{
|
||||||
|
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.bjdata";
|
||||||
|
std::ifstream file(filename, std::ios::binary);
|
||||||
|
const std::istreambuf_iterator<char> first(file);
|
||||||
|
const json parsed = json::from_bjdata(first, utils::istreambuf_sentinel{});
|
||||||
|
CHECK((parsed.is_object() || parsed.is_array()));
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
TEST_CASE("all BJData first bytes")
|
TEST_CASE("all BJData first bytes")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1208,6 +1208,19 @@ TEST_CASE("BSON numerical data")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse BSON directly from a file using iterator and sentinel")
|
||||||
|
{
|
||||||
|
std::string const filename = TEST_DATA_DIRECTORY "/json.org/1.json";
|
||||||
|
|
||||||
|
std::ifstream f_json(filename);
|
||||||
|
const json expected = json::parse(f_json);
|
||||||
|
|
||||||
|
std::ifstream file(filename + ".bson", std::ios::binary);
|
||||||
|
const std::istreambuf_iterator<char> first(file);
|
||||||
|
const json parsed = json::from_bson(first, utils::istreambuf_sentinel{});
|
||||||
|
CHECK(parsed == expected);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("BSON roundtrips" * doctest::skip())
|
TEST_CASE("BSON roundtrips" * doctest::skip())
|
||||||
{
|
{
|
||||||
SECTION("reference files")
|
SECTION("reference files")
|
||||||
|
|||||||
@@ -1921,6 +1921,15 @@ TEST_CASE("single CBOR roundtrip")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse CBOR directly from a file using iterator and sentinel")
|
||||||
|
{
|
||||||
|
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.cbor";
|
||||||
|
std::ifstream file(filename, std::ios::binary);
|
||||||
|
const std::istreambuf_iterator<char> first(file);
|
||||||
|
const json parsed = json::from_cbor(first, utils::istreambuf_sentinel{});
|
||||||
|
CHECK((parsed.is_object() || parsed.is_array()));
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
TEST_CASE("CBOR regressions")
|
TEST_CASE("CBOR regressions")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1782,6 +1782,21 @@ TEST_CASE("std::optional")
|
|||||||
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
||||||
CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
|
CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
|
||||||
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
|
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
|
||||||
|
|
||||||
|
// Assignment goes through the same overload resolution as direct
|
||||||
|
// construction, so it throws for the same reason. This relies on
|
||||||
|
// basic_json's implicit conversion operator, so it only applies
|
||||||
|
// when JSON_USE_IMPLICIT_CONVERSIONS is enabled (the default).
|
||||||
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
||||||
|
std::optional<std::string> opt_assign;
|
||||||
|
CHECK_THROWS_WITH_AS(opt_assign = j_null,
|
||||||
|
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// get_to() is the correct way to obtain std::nullopt from a JSON null.
|
||||||
|
std::optional<std::string> opt_get_to = "placeholder";
|
||||||
|
j_null.get_to(opt_get_to);
|
||||||
|
CHECK(opt_get_to == std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("string")
|
SECTION("string")
|
||||||
|
|||||||
@@ -1641,6 +1641,15 @@ TEST_CASE("single MessagePack roundtrip")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse MessagePack directly from a file using iterator and sentinel")
|
||||||
|
{
|
||||||
|
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.msgpack";
|
||||||
|
std::ifstream file(filename, std::ios::binary);
|
||||||
|
const std::istreambuf_iterator<char> first(file);
|
||||||
|
const json parsed = json::from_msgpack(first, utils::istreambuf_sentinel{});
|
||||||
|
CHECK((parsed.is_object() || parsed.is_array()));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("MessagePack roundtrips" * doctest::skip())
|
TEST_CASE("MessagePack roundtrips" * doctest::skip())
|
||||||
{
|
{
|
||||||
SECTION("input from msgpack-python")
|
SECTION("input from msgpack-python")
|
||||||
|
|||||||
@@ -1136,6 +1136,40 @@ TEST_CASE("regression tests 2")
|
|||||||
CHECK((decoded == json_4804::array()));
|
CHECK((decoded == json_4804::array()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("discussion #4209 - custom BinaryType direct assignment and round-tripping")
|
||||||
|
{
|
||||||
|
// Test that assigning a custom BinaryType directly creates a binary value, not an array
|
||||||
|
const std::vector<std::byte> original{std::byte{1}, std::byte{2}, std::byte{3}};
|
||||||
|
const json_4804 j = original;
|
||||||
|
CHECK(j.is_binary());
|
||||||
|
CHECK(!j.is_array());
|
||||||
|
|
||||||
|
// Test round-tripping: extracting the binary value back as the custom container type
|
||||||
|
const auto extracted = j.get<std::vector<std::byte>>();
|
||||||
|
CHECK(extracted == original);
|
||||||
|
|
||||||
|
// Test that the default json alias behavior is unchanged: std::vector<uint8_t> -> array
|
||||||
|
const json default_json = std::vector<std::uint8_t> {1, 2, 3};
|
||||||
|
CHECK(default_json.is_array());
|
||||||
|
CHECK(!default_json.is_binary());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("discussion #4209 - custom BinaryType extraction from parsed array")
|
||||||
|
{
|
||||||
|
// Test that extracting a custom BinaryType from a parsed JSON array still works
|
||||||
|
// (not just from a binary-typed node)
|
||||||
|
const auto j = json_4804::parse("[1,2,3]");
|
||||||
|
CHECK(j.is_array());
|
||||||
|
CHECK(!j.is_binary());
|
||||||
|
|
||||||
|
// Extracting as custom BinaryType should work from arrays
|
||||||
|
const auto extracted = j.get<std::vector<std::byte>>();
|
||||||
|
CHECK(extracted.size() == 3);
|
||||||
|
CHECK(extracted[0] == std::byte{1});
|
||||||
|
CHECK(extracted[1] == std::byte{2});
|
||||||
|
CHECK(extracted[2] == std::byte{3});
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit")
|
SECTION("issue #5046 - implicit conversion of return json to std::optional no longer implicit")
|
||||||
{
|
{
|
||||||
const json jval{};
|
const json jval{};
|
||||||
|
|||||||
@@ -1862,6 +1862,31 @@ TEST_CASE("UBJSON")
|
|||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&);
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(v), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing UBJSON string: expected length type specification (U, i, I, l, L); last byte: 0x31", json::parse_error&);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("negative length")
|
||||||
|
{
|
||||||
|
json _;
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vi = {'S', 'i', 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vi), "[json.exception.parse_error.113] parse error at byte 3: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vi, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vI = {'S', 'I', 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vI), "[json.exception.parse_error.113] parse error at byte 4: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vI, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vl = {'S', 'l', 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vl), "[json.exception.parse_error.113] parse error at byte 6: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vl, true, false).is_discarded());
|
||||||
|
|
||||||
|
std::vector<uint8_t> const vL = {'S', 'L', 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::from_ubjson(vL), "[json.exception.parse_error.113] parse error at byte 10: syntax error while parsing UBJSON string: string length must not be negative", json::parse_error&);
|
||||||
|
CHECK(json::from_ubjson(vL, true, false).is_discarded());
|
||||||
|
|
||||||
|
// a length of zero remains valid and yields an empty string
|
||||||
|
std::vector<uint8_t> const v0 = {'S', 'i', 0};
|
||||||
|
CHECK(json::from_ubjson(v0) == json(""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("array")
|
SECTION("array")
|
||||||
@@ -2393,6 +2418,15 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Parse UBJSON directly from a file using iterator and sentinel")
|
||||||
|
{
|
||||||
|
std::string const filename = TEST_DATA_DIRECTORY "/json_testsuite/sample.json.ubjson";
|
||||||
|
std::ifstream file(filename, std::ios::binary);
|
||||||
|
const std::istreambuf_iterator<char> first(file);
|
||||||
|
const json parsed = json::from_ubjson(first, utils::istreambuf_sentinel{});
|
||||||
|
CHECK((parsed.is_object() || parsed.is_array()));
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
TEST_CASE("all UBJSON first bytes")
|
TEST_CASE("all UBJSON first bytes")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,6 +6,13 @@
|
|||||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// cmake/test.cmake selects the C++ standard versions with which to build a
|
||||||
|
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
|
||||||
|
// When using macros that are only defined for particular versions of the standard
|
||||||
|
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
|
||||||
|
// version macro in a comment close by, like this:
|
||||||
|
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
@@ -13,6 +20,10 @@ using nlohmann::json;
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
|
#include <iterator>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
TEST_CASE("Use arbitrary stdlib container")
|
TEST_CASE("Use arbitrary stdlib container")
|
||||||
@@ -168,4 +179,55 @@ TEST_CASE("Custom iterator")
|
|||||||
CHECK(as_json.at(3) == 4);
|
CHECK(as_json.at(3) == 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom sentinel type for testing heterogeneous iterator+sentinel support
|
||||||
|
struct CustomSentinel
|
||||||
|
{
|
||||||
|
const char* end_ptr;
|
||||||
|
|
||||||
|
// only the iterator-first direction (it != sentinel) is ever evaluated by
|
||||||
|
// the library's parse loop; a reversed-order overload would go unused and
|
||||||
|
// trip -Wunneeded-internal-declaration under -Weverything
|
||||||
|
friend bool operator!=(const char* it, const CustomSentinel& sentinel)
|
||||||
|
{
|
||||||
|
return it != sentinel.end_ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("Parse with heterogeneous iterator and sentinel types")
|
||||||
|
{
|
||||||
|
const std::string json_str = R"({"key":"value"})";
|
||||||
|
const char* end_ptr = json_str.data() + json_str.size();
|
||||||
|
|
||||||
|
// Parse using pointer and sentinel (different types)
|
||||||
|
json j = json::parse(json_str.data(), CustomSentinel{end_ptr});
|
||||||
|
CHECK(j["key"] == "value");
|
||||||
|
|
||||||
|
// Accept using pointer and sentinel
|
||||||
|
CHECK(json::accept(json_str.data(), CustomSentinel{end_ptr}));
|
||||||
|
|
||||||
|
// Test that the same-type case still works
|
||||||
|
std::string raw_data = R"([1,2,3])";
|
||||||
|
std::list<char> data(raw_data.begin(), raw_data.end());
|
||||||
|
json j2 = json::parse(data.begin(), data.end());
|
||||||
|
CHECK(j2.at(0) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||||
|
// JSON_HAS_CPP_20 (do not remove; see note at top of file)
|
||||||
|
TEST_CASE("Parse with std::counted_iterator and std::default_sentinel_t")
|
||||||
|
{
|
||||||
|
using iterator_type = std::string::const_iterator;
|
||||||
|
const std::string json_str = R"({"key":"value","array":[1,2,3]})";
|
||||||
|
const auto len = static_cast<std::iter_difference_t<iterator_type>>(json_str.size());
|
||||||
|
|
||||||
|
const std::counted_iterator<iterator_type> first(json_str.begin(), len);
|
||||||
|
const json j = json::parse(first, std::default_sentinel);
|
||||||
|
CHECK(j["key"] == "value");
|
||||||
|
CHECK(j["array"].size() == 3);
|
||||||
|
|
||||||
|
const std::counted_iterator<iterator_type> first2(json_str.begin(), len);
|
||||||
|
CHECK(json::accept(first2, std::default_sentinel));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -53,6 +53,27 @@ TEST_CASE("wide strings")
|
|||||||
std::wstring const w = L"\"\xDBFF";
|
std::wstring const w = L"\"\xDBFF";
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||||
|
|
||||||
|
// the exact message depends on the width of wchar_t: a 16-bit
|
||||||
|
// wchar_t passes the lone surrogate to the UTF-8 decoder unchanged
|
||||||
|
// (rejected as a single ill-formed byte at column 2), while a
|
||||||
|
// 32-bit wchar_t first encodes it as an ill-formed three-byte
|
||||||
|
// sequence (rejected one byte later, at column 3)
|
||||||
|
const char* const error_low_surrogate = sizeof(wchar_t) == 2
|
||||||
|
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
||||||
|
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xB0'";
|
||||||
|
const char* const error_high_surrogate = sizeof(wchar_t) == 2
|
||||||
|
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
||||||
|
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xA0'";
|
||||||
|
|
||||||
|
// a lone low surrogate cannot start a pair
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xDC00), L'"'}), error_low_surrogate, json::parse_error&);
|
||||||
|
// a high surrogate followed by a non-low-surrogate unit is invalid
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xD800), L'a', L'"'}), error_high_surrogate, json::parse_error&);
|
||||||
|
// a lone low surrogate must not swallow the following unit: pairing
|
||||||
|
// it with any second unit would produce valid UTF-8, so the error
|
||||||
|
// has to report an ill-formed byte at the surrogate's own position
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::wstring{L'"', static_cast<wchar_t>(0xDC00), L'a', L'"'}), error_low_surrogate, json::parse_error&);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +89,22 @@ TEST_CASE("wide strings")
|
|||||||
|
|
||||||
SECTION("invalid std::u16string")
|
SECTION("invalid std::u16string")
|
||||||
{
|
{
|
||||||
if (wstring_is_utf16())
|
if (u16string_is_utf16())
|
||||||
{
|
{
|
||||||
std::u16string const w = u"\"\xDBFF";
|
std::u16string const w = u"\"\xDBFF";
|
||||||
json _;
|
json _;
|
||||||
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
CHECK_THROWS_AS(_ = json::parse(w), json::parse_error&);
|
||||||
|
|
||||||
|
// a lone low surrogate cannot start a pair
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xDC00, u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
||||||
|
// a high surrogate followed by a non-low-surrogate unit is invalid
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xD800, u'a', u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
||||||
|
// a lone low surrogate must not swallow the following unit: pairing
|
||||||
|
// it with any second unit would produce valid UTF-8, so the error
|
||||||
|
// has to report an ill-formed byte at the surrogate's own position
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json::parse(std::u16string{u'"', 0xDC00, u'a', u'"'}), "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'", json::parse_error&);
|
||||||
|
// a valid surrogate pair is still decoded (U+1F600)
|
||||||
|
CHECK(json::parse(std::u16string{u'"', 0xD83D, 0xDE00, u'"'}).get<std::string>() == "\xF0\x9F\x98\x80");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user