mirror of
https://github.com/nlohmann/json.git
synced 2026-07-28 05:14:54 +00:00
Compare commits
12
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ce130af4e | ||
|
|
5b5ba1ac35 | ||
|
|
85839d7408 | ||
|
|
6fb162a394 | ||
|
|
0bc01a066c | ||
|
|
722c03495f | ||
|
|
c197feff81 | ||
|
|
b2b47c69b1 | ||
|
|
6a406ee141 | ||
|
|
ca76c37650 | ||
|
|
fe2bcc080f | ||
|
|
c60217e801 |
@@ -21,6 +21,7 @@ cc_library(
|
||||
"include/nlohmann/adl_serializer.hpp",
|
||||
"include/nlohmann/byte_container_with_subtype.hpp",
|
||||
"include/nlohmann/detail/abi_macros.hpp",
|
||||
"include/nlohmann/detail/conversions/from_chars.hpp",
|
||||
"include/nlohmann/detail/conversions/from_json.hpp",
|
||||
"include/nlohmann/detail/conversions/to_chars.hpp",
|
||||
"include/nlohmann/detail/conversions/to_json.hpp",
|
||||
|
||||
@@ -8,8 +8,8 @@ static bool accept(InputType&& i,
|
||||
const bool ignore_trailing_commas = false);
|
||||
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static bool accept(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static bool accept(IteratorType first, SentinelType last,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false);
|
||||
```
|
||||
@@ -17,10 +17,11 @@ static bool accept(IteratorType first, IteratorType last,
|
||||
Checks whether the input is valid JSON.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from a pair of character iterators
|
||||
2. Reads from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||
|
||||
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
||||
respectively as UTF-8, UTF-16, and UTF-32.
|
||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||
the iterator type with `operator!=`.
|
||||
|
||||
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
|
||||
(i.e., a parse error) nor creates diagnostic information.
|
||||
@@ -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 pointers such as `ptr` and `ptr + len`
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -61,7 +68,7 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
||||
: iterator to the start of the character range
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the character range
|
||||
: iterator to the end of the character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
## Return value
|
||||
|
||||
@@ -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.
|
||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -51,6 +51,38 @@ represent a byte array in modern C++.
|
||||
|
||||
The default values for `BinaryType` is `#!cpp std::vector<std::uint8_t>`.
|
||||
|
||||
#### Custom BinaryType behavior
|
||||
|
||||
When a custom `BinaryType` is configured (other than the default `#!cpp std::vector<std::uint8_t>`), you can assign
|
||||
values of that type directly to a `basic_json` instance, and they will automatically be recognized as binary values
|
||||
rather than arrays:
|
||||
|
||||
```cpp
|
||||
using custom_json = nlohmann::basic_json<
|
||||
nlohmann::ordered_map, // ObjectType
|
||||
std::vector, // ArrayType
|
||||
std::string, // StringType
|
||||
bool, // BooleanType
|
||||
std::int64_t, // NumberIntegerType
|
||||
std::uint64_t, // NumberUnsignedType
|
||||
double, // NumberFloatType
|
||||
std::allocator, // AllocatorType
|
||||
nlohmann::adl_serializer,
|
||||
std::vector<std::byte> // Custom BinaryType
|
||||
>;
|
||||
|
||||
std::vector<std::byte> data{std::byte{1}, std::byte{2}, std::byte{3}};
|
||||
custom_json j = data; // Creates a binary value, not an array
|
||||
assert(j.is_binary());
|
||||
|
||||
// Round-tripping works seamlessly
|
||||
auto extracted = j.get<std::vector<std::byte>>();
|
||||
assert(extracted == data);
|
||||
```
|
||||
|
||||
This automatic type detection is a convenience feature that only applies to custom (non-default) `BinaryType` configurations.
|
||||
The default `nlohmann::json` continues to treat `#!cpp std::vector<std::uint8_t>` as arrays for backward compatibility.
|
||||
|
||||
#### Storage
|
||||
|
||||
Binary Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of the
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_bjdata(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the BJData (Binary JData) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bjdata.md).
|
||||
|
||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -103,3 +109,4 @@ Linear in the size of the input.
|
||||
|
||||
- Added in version 3.11.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_bson(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the BSON (Binary JSON) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/bson.md).
|
||||
|
||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -103,6 +109,7 @@ Linear in the size of the input.
|
||||
|
||||
- Added in version 3.4.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ static basic_json from_cbor(InputType&& i,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error);
|
||||
@@ -19,7 +19,7 @@ static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the CBOR (Concise Binary Object Representation) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/cbor.md).
|
||||
|
||||
@@ -38,6 +38,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -47,7 +53,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -113,6 +119,7 @@ Linear in the size of the input.
|
||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||
- Added `tag_handler` parameter in version 3.9.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_msgpack(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the MessagePack serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/messagepack.md).
|
||||
|
||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -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.
|
||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ static basic_json from_ubjson(InputType&& i,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true);
|
||||
```
|
||||
@@ -16,7 +16,7 @@ static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
Deserializes a given input to a JSON value using the UBJSON (Universal Binary JSON) serialization format.
|
||||
|
||||
1. Reads from a compatible input.
|
||||
2. Reads from an iterator range.
|
||||
2. Reads from an iterator range, or an iterator and a sentinel of a different type (C++20 ranges support).
|
||||
|
||||
The exact mapping and its limitations are described on a [dedicated page](../../features/binary_formats/ubjson.md).
|
||||
|
||||
@@ -35,6 +35,12 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
`IteratorType`
|
||||
: a compatible iterator type
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -44,7 +50,7 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
||||
: iterator to the start of the input
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of the input
|
||||
: iterator to the end of the input, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
`strict` (in)
|
||||
: whether to expect the input to be consumed until EOF (`#!cpp true` by default)
|
||||
@@ -104,6 +110,7 @@ Linear in the size of the input.
|
||||
- Added in version 3.1.0.
|
||||
- Added `allow_exceptions` parameter in version 3.2.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ static basic_json parse(InputType&& i,
|
||||
const bool ignore_trailing_commas = false);
|
||||
|
||||
// (2)
|
||||
template<typename IteratorType>
|
||||
static basic_json parse(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
static basic_json parse(IteratorType first, SentinelType last,
|
||||
const parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -19,10 +19,11 @@ static basic_json parse(IteratorType first, IteratorType last,
|
||||
```
|
||||
|
||||
1. Deserialize from a compatible input.
|
||||
2. Deserialize from a pair of character iterators
|
||||
2. Deserialize from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||
|
||||
The `value_type` of the iterator must be an integral type with size of 1, 2, or 4 bytes, which will be interpreted
|
||||
respectively as UTF-8, UTF-16, and UTF-32.
|
||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||
the iterator type with `operator!=`.
|
||||
|
||||
## Template parameters
|
||||
|
||||
@@ -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 pointers such as `ptr` and `ptr + len`
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
## Parameters
|
||||
|
||||
`i` (in)
|
||||
@@ -67,7 +74,7 @@ static basic_json parse(IteratorType first, IteratorType last,
|
||||
: iterator to the start of a character range
|
||||
|
||||
`last` (in)
|
||||
: iterator to the end of a character range
|
||||
: iterator to the end of a character range, or a sentinel value that compares equal to the end iterator with `operator!=`
|
||||
|
||||
## Return value
|
||||
|
||||
@@ -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.
|
||||
- Added `ignore_trailing_commas` in version 3.13.0.
|
||||
- Extended container support (1) to include types with lvalue-only ADL `begin`/`end` (matching `std::begin`/`std::end` semantics) in version 3.13.0.
|
||||
- Extended overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! warning "Deprecation"
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ static bool sax_parse(InputType&& i,
|
||||
const bool ignore_trailing_commas = false);
|
||||
|
||||
// (2)
|
||||
template<class IteratorType, class SAX>
|
||||
static bool sax_parse(IteratorType first, IteratorType last,
|
||||
template<class IteratorType, class SAX, class SentinelType = IteratorType>
|
||||
static bool sax_parse(IteratorType first, SentinelType last,
|
||||
SAX* sax,
|
||||
input_format_t format = input_format_t::json,
|
||||
const bool strict = true,
|
||||
@@ -23,10 +23,11 @@ static bool sax_parse(IteratorType first, IteratorType last,
|
||||
Read from input and generate SAX events
|
||||
|
||||
1. Read from a compatible input.
|
||||
2. Read from a pair of character iterators
|
||||
2. Read from a pair of character iterators, or an iterator and a sentinel of a different type (C++20 ranges support)
|
||||
|
||||
The value_type of the iterator must be an integral type with a size of 1, 2, or 4 bytes, which will be interpreted
|
||||
respectively as UTF-8, UTF-16, and UTF-32.
|
||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
||||
the iterator type with `operator!=`.
|
||||
|
||||
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
|
||||
|
||||
@@ -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
|
||||
with a size of 1, 2, or 4 bytes (interpreted respectively as UTF-8, UTF-16, and UTF-32)
|
||||
|
||||
`SentinelType`
|
||||
: defaults to `IteratorType`; may be a different type comparable to `IteratorType` via `operator!=`, for overload (2), for instance.
|
||||
|
||||
- a custom sentinel type for C++20 ranges
|
||||
- `std::default_sentinel_t`, when `IteratorType` is `std::counted_iterator`
|
||||
|
||||
`SAX`
|
||||
: a class fulfilling the SAX event listener interface; see [`json_sax`](../json_sax/index.md)
|
||||
|
||||
@@ -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
|
||||
|
||||
`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
|
||||
|
||||
@@ -128,6 +135,7 @@ A UTF-8 byte order mark is silently ignored.
|
||||
- Ignoring comments via `ignore_comments` added in version 3.9.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 overload (2) to accept heterogeneous iterator+sentinel pairs (C++20 ranges support) in version 3.13.0.
|
||||
|
||||
!!! 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
|
||||
[`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
|
||||
|
||||
|
||||
@@ -47,6 +47,28 @@ json j = {{"one", 1}, {"two", 2}};
|
||||
auto m = j.get<std::map<std::string, int>>(); // {{"one", 1}, {"two", 2}}
|
||||
```
|
||||
|
||||
`#!cpp std::pair` and `#!cpp std::tuple` are also supported, converting positionally to and from a JSON array:
|
||||
|
||||
```cpp
|
||||
json j = {1.0, "hello", 42};
|
||||
auto t = j.get<std::tuple<double, std::string, int>>(); // {1.0, "hello", 42}
|
||||
```
|
||||
|
||||
!!! info "Extracting references into a tuple"
|
||||
|
||||
A tuple type may also hold references (e.g. `#!cpp std::tuple<double&, std::string&>`) to avoid copying: `get`
|
||||
then returns a tuple of references pointing directly at the elements stored inside the `basic_json` array,
|
||||
rather than a tuple of copies:
|
||||
|
||||
```cpp
|
||||
json j = {1.0, "hello"};
|
||||
auto refs = j.get<std::tuple<double&, std::string&>>();
|
||||
std::get<1>(refs) = "world"; // modifies j[1] in place
|
||||
```
|
||||
|
||||
A referenced 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
|
||||
|
||||
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]
|
||||
```
|
||||
|
||||
!!! info "Constructing from a C++20 range view"
|
||||
|
||||
A `json` array can also be constructed directly from a C++20 range view (`std::ranges::view`), such as the result
|
||||
of `std::views::filter` or `std::views::transform` -- no intermediate container is needed:
|
||||
|
||||
```cpp
|
||||
std::vector<int> nums{1, 2, 37, 42, 21};
|
||||
auto filtered = nums | std::views::filter([](int i) { return i > 10; });
|
||||
json j(filtered); // [37,42,21]
|
||||
```
|
||||
|
||||
This requires [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) to be enabled and is unavailable on MinGW due
|
||||
to incomplete C++20 ranges support there.
|
||||
|
||||
## Your own types
|
||||
|
||||
The conversions above are built in for standard types. To make the same syntax work for **your own** types, provide
|
||||
|
||||
@@ -135,6 +135,31 @@ Enable CI build targets. The exact targets are used during the several CI steps
|
||||
|
||||
Enable [extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) by defining macro [`JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md). This option is `OFF` by default.
|
||||
|
||||
!!! warning "Does not apply to a pre-installed package"
|
||||
|
||||
This option only takes effect when building nlohmann/json from source as part of your own
|
||||
CMake project (e.g. via [`FetchContent`](#fetchcontent) or [`add_subdirectory`](#external)).
|
||||
It has **no effect** on a package that was already built and installed elsewhere (Homebrew,
|
||||
vcpkg, a system package, etc.) — the resulting compile definition is baked into the exported
|
||||
`nlohmann_jsonTargets.cmake` at install time, and `set(JSON_Diagnostics ON)` before
|
||||
`find_package()` does not change it (verified against the Homebrew-installed package: the
|
||||
exported target still carries a fixed `$<$<BOOL:OFF>:JSON_DIAGNOSTICS=1>`, regardless of any
|
||||
variable set in the consuming project).
|
||||
|
||||
To enable extended diagnostics for a pre-installed package, override the imported target's
|
||||
property directly after `find_package()`:
|
||||
|
||||
```cmake
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
set_target_properties(nlohmann_json::nlohmann_json PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "JSON_DIAGNOSTICS=1")
|
||||
```
|
||||
|
||||
This only works cleanly when your project is the sole consumer of that imported target. If
|
||||
nlohmann_json is pulled in from more than one place in your dependency graph with different
|
||||
`JSON_DIAGNOSTICS` values, you may see a `"JSON_DIAGNOSTICS" redefined` compiler error, since
|
||||
conflicting `-D` flags can end up on the same compile command line.
|
||||
|
||||
### `JSON_Diagnostic_Positions`
|
||||
|
||||
Enable position diagnostics by defining macro [`JSON_DIAGNOSTIC_POSITIONS`](../api/macros/json_diagnostic_positions.md). This option is `OFF` by default.
|
||||
|
||||
@@ -0,0 +1,636 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
|
||||
#if JSON_HAS_FROM_CHARS
|
||||
#include <algorithm> // find, find_if
|
||||
#include <charconv> // from_chars, from_chars_result
|
||||
#include <cmath> // isnan
|
||||
#else
|
||||
#include <cctype> // isspace
|
||||
#include <cerrno> // ERANGE
|
||||
#include <clocale> // localeconv, newlocale, freelocale
|
||||
#include <cstdlib> // strto*
|
||||
#include <memory> // unique_ptr
|
||||
#include <type_traits> // enable_if, is_unsigned, remove_pointer
|
||||
#include <utility> // declval
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<xlocale.h>)
|
||||
#include <xlocale.h> // strto*_l, isspace_l
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#include <limits> // numeric_limits<T>::[has_]infinity, quiet_NaN
|
||||
#include <system_error> // errc
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
{
|
||||
|
||||
constexpr char get_locale_independent_decimal_point() noexcept
|
||||
{
|
||||
return '.';
|
||||
}
|
||||
|
||||
#if JSON_HAS_FROM_CHARS
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// Delegate to std::from_chars if supported //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*!
|
||||
@brief Number parsing implementation details
|
||||
|
||||
A traits class template that allows users of nlohmann::detail::from_chars to
|
||||
query details of the used implementation.
|
||||
|
||||
@tparam T numeric type to parse, matching the `value` argument of from_chars
|
||||
*/
|
||||
template <typename T>
|
||||
struct from_chars_traits
|
||||
{
|
||||
/// whether the from_chars in unaffected by the current global C locale
|
||||
static constexpr bool is_locale_independent = true;
|
||||
|
||||
/// getter for the character used as decimal point by from_chars
|
||||
static constexpr char(*get_decimal_point)() = &get_locale_independent_decimal_point;
|
||||
};
|
||||
|
||||
using std::from_chars_result;
|
||||
|
||||
/*!
|
||||
@brief Parse integer/floating-point numbers
|
||||
|
||||
Parses the string [first, last) into the numeric type T.
|
||||
|
||||
This implementation merely delegates to `std::from_chars` with one noteworthy
|
||||
difference: Different C++ standard library implementations do not agree on
|
||||
whether `value` should be set in the out-of-range case for floating-point
|
||||
numbers. Presently, libstdc++ leaves `value` unchanged whereas libc++ and MS STL
|
||||
set `value` to +/-0 or +/-inf which allows its users to distinguish between the
|
||||
various cases of over- and underflow. The C++ proposal [P4168][1] details this
|
||||
discrepancy and advocates to standardize the latter behavior.
|
||||
|
||||
Since we need to be able to distinguish absolute underflow (+/-0) from absolute
|
||||
overflow (+/-inf), this implementation "corrects" the out-of-range behavior
|
||||
accordingly by estimating the effective exponent through manual string parsing.
|
||||
|
||||
[1]: https://isocpp.org/files/papers/P4168R0.html
|
||||
|
||||
@tparam T numeric type to parse
|
||||
@param[in] first points to the beginning of the parsed string (inclusive)
|
||||
@param[in] end points to the end of the parsed string (exclusive)
|
||||
@param[out] value references the variable to set the parsed number
|
||||
@return structure consisting of `ptr` and `ec` such that:
|
||||
- If the string starting at `first` matches a number, `ptr` points to
|
||||
the address immediately after the last character that matches.
|
||||
* If the matched number can be represented by `T`, `value` is set and
|
||||
`ec` is value-initialized.
|
||||
* If the matched number cannot be represented by `T`, `ec` is set to
|
||||
`std::errc::result_out_of_range` and depending on the type of `T`:
|
||||
+ `value` is left unchanged for integral `T`,
|
||||
+ `value` is set to +/-0 or +/-inf for floating-point `T`.
|
||||
- If the string starting at `first` doesn't match a number, `ptr`
|
||||
points to `first`, `ec` is `std::errc::invalid_argument`, and `value`
|
||||
is left unchanged.
|
||||
*/
|
||||
template <typename T>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
inline from_chars_result from_chars(const char* first, const char* last, T& value)
|
||||
{
|
||||
// No implementation of std::from_chars will set its value argument to NaN,
|
||||
// so by using NaN as the initial value, we can tell if it changed.
|
||||
T inner_value = std::numeric_limits<T>::quiet_NaN();
|
||||
auto res = std::from_chars(first, last, inner_value);
|
||||
|
||||
if constexpr (std::numeric_limits<T>::has_infinity)
|
||||
{
|
||||
if (res.ec == std::errc::result_out_of_range)
|
||||
{
|
||||
if (std::isnan(inner_value)) // inner_value was not set
|
||||
{
|
||||
// [first, res.ptr) matches a valid floating-point number that's
|
||||
// out-of-range and our std::from_chars did not set `value`, so we
|
||||
// need to distinguish the type of under-/overflow ourselves.
|
||||
const char* mantissa_begin = *first == '-' ? first + 1 : first;
|
||||
const char* mantissa_end = std::find_if(mantissa_begin, res.ptr, [](char c)
|
||||
{
|
||||
return c == 'e' || c == 'E';
|
||||
});
|
||||
const char* decimal_point = std::find(mantissa_begin, mantissa_end, '.');
|
||||
const char* significant_digit = std::find_if(mantissa_begin, mantissa_end, [](char d)
|
||||
{
|
||||
return d >= '1' && d <= '9';
|
||||
});
|
||||
|
||||
// Position of the significant digit relative to the decimal gives
|
||||
// the order of magnitude of the mantissa.
|
||||
auto effective_exponent = static_cast<int>(decimal_point - significant_digit);
|
||||
|
||||
// If the number includes an explicit exponent, add that to the the
|
||||
// total exponent.
|
||||
if (mantissa_end != res.ptr)
|
||||
{
|
||||
const char* exponent_begin = *(mantissa_end + 1) == '+'
|
||||
? mantissa_end + 2 // skip the exponent's + sign
|
||||
: mantissa_end + 1;
|
||||
int exponent = 0;
|
||||
auto exp_res = std::from_chars(exponent_begin, res.ptr, exponent);
|
||||
JSON_ASSERT(exp_res.ptr == res.ptr);
|
||||
|
||||
if (exp_res.ec == std::errc::result_out_of_range)
|
||||
{
|
||||
// NOTE: Parentheses around function names mitigate min/max macro collision on Windows
|
||||
effective_exponent = *exponent_begin == '-'
|
||||
? (std::numeric_limits<int>::min)()
|
||||
: (std::numeric_limits<int>::max)();
|
||||
}
|
||||
else
|
||||
{
|
||||
JSON_ASSERT(exp_res.ec == std::errc{});
|
||||
effective_exponent += exponent;
|
||||
}
|
||||
}
|
||||
|
||||
// Set `value` to the sign-correct non-finite value based on the
|
||||
// effective exponent.
|
||||
value = (*first == '-' ? -1 : 1) * (effective_exponent < 0
|
||||
? T{}
|
||||
: std::numeric_limits<T>::infinity());
|
||||
}
|
||||
else
|
||||
{
|
||||
value = inner_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res.ec == std::errc{})
|
||||
{
|
||||
value = inner_value;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// Fallback implementation using strto*[_l] //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
/*!
|
||||
@brief Extended locale functions on Windows
|
||||
|
||||
A trait object which provides wrappers for isspace and the strto* family of
|
||||
functions, based on the platform's support for extended locale APIs.
|
||||
|
||||
This is the Windows implementation. The primary class template definition
|
||||
falls back to the standard locale-dependent functions, which is used on
|
||||
MinGW, whose C runtime only provides an incomplete subset of the
|
||||
`_<funcname>_l` family (e.g., missing `_strtof_l`/`_strtold_l` depending on
|
||||
the toolchain version). A specialization for genuine MSVC (including
|
||||
clang-cl, which mimics the MSVC ABI and CRT) is used instead, as the full
|
||||
`_<funcname>_l` family has reliably been available there since at least
|
||||
Visual Studio 2005.
|
||||
*/
|
||||
template <typename = const char*, typename = float>
|
||||
struct extended_locale_traits
|
||||
{
|
||||
static constexpr bool is_locale_independent = false;
|
||||
|
||||
JSON_HEDLEY_PURE
|
||||
static char get_decimal_point() noexcept
|
||||
{
|
||||
const auto* loc = localeconv();
|
||||
JSON_ASSERT(loc != nullptr);
|
||||
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
||||
}
|
||||
|
||||
static int isspace(int c) noexcept
|
||||
{
|
||||
return std::isspace(c);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static long long strtoll(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return std::strtoll(str, endptr, 10);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return std::strtoull(str, endptr, 10);
|
||||
}
|
||||
|
||||
static constexpr float(&strtof)(const char*, char**) = std::strtof;
|
||||
static constexpr double(&strtod)(const char*, char**) = std::strtod;
|
||||
static constexpr long double(&strtold)(const char*, char**) = std::strtold;
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
/*!
|
||||
@brief Get a pointer to a globally shared instance of the "C" locale on Windows
|
||||
|
||||
This function uses _create_locale/_free_locale to allocate a "C" locale instance
|
||||
which can be passed to extended locale APIs. This instance is created on first
|
||||
use and has static storage duration, such that it can be used for the duration
|
||||
of the program.
|
||||
*/
|
||||
inline _locale_t get_c_locale_t() noexcept
|
||||
{
|
||||
struct LocaleTDeleter
|
||||
{
|
||||
void operator()(_locale_t loc) noexcept
|
||||
{
|
||||
::_free_locale(loc);
|
||||
}
|
||||
};
|
||||
using LocaleUPtr = std::unique_ptr<std::remove_pointer<_locale_t>::type, LocaleTDeleter>;
|
||||
static const LocaleUPtr c_locale = LocaleUPtr{::_create_locale(LC_ALL, "C"), LocaleTDeleter{}};
|
||||
return c_locale.get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct extended_locale_traits<T, float>
|
||||
{
|
||||
static constexpr bool is_locale_independent = true;
|
||||
static constexpr char(&get_decimal_point)() = get_locale_independent_decimal_point;
|
||||
|
||||
static int isspace(int c) noexcept
|
||||
{
|
||||
return _isspace_l(c, get_c_locale_t());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static long long strtoll(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return _strtoll_l(str, endptr, 10, get_c_locale_t());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return _strtoull_l(str, endptr, 10, get_c_locale_t());
|
||||
}
|
||||
|
||||
static float strtof(T str, char** str_end)
|
||||
{
|
||||
return _strtof_l(str, str_end, get_c_locale_t());
|
||||
}
|
||||
|
||||
static double strtod(T str, char** str_end)
|
||||
{
|
||||
return _strtod_l(str, str_end, get_c_locale_t());
|
||||
}
|
||||
|
||||
static long double strtold(T str, char** str_end)
|
||||
{
|
||||
return _strtold_l(str, str_end, get_c_locale_t());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
/*!
|
||||
@brief Get a pointer to a globally shared instance of the "C" locale on POSIX
|
||||
|
||||
This function uses newlocale/freelocale to allocate a "C" locale instance
|
||||
which can be passed to extended locale APIs. This instance is created on first
|
||||
use and has static storage duration, such that it can be used for the duration
|
||||
of the program.
|
||||
|
||||
newlocale/freelocale themselves are in POSIX and are available independent of
|
||||
the platform's support for extended locale APIs.
|
||||
*/
|
||||
inline locale_t get_c_locale_t() noexcept
|
||||
{
|
||||
struct LocaleTDeleter
|
||||
{
|
||||
void operator()(locale_t loc) noexcept
|
||||
{
|
||||
::freelocale(loc);
|
||||
}
|
||||
};
|
||||
using LocaleUPtr = std::unique_ptr<std::remove_pointer<locale_t>::type, LocaleTDeleter>;
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
#endif
|
||||
static const LocaleUPtr c_locale = LocaleUPtr {::newlocale(LC_ALL_MASK, "C", nullptr), LocaleTDeleter{}};
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
return c_locale.get();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief Extended locale functions on POSIX
|
||||
|
||||
A trait object which provides wrappers for isspace and the strto* family of
|
||||
functions, based on the platform's support for extended locale APIs.
|
||||
|
||||
This is the POSIX implementation where extended locale support might not be
|
||||
available. The primary class template definition falls back to the standard
|
||||
locale-dependent functions. A partial specialization uses SFINAE to detect the
|
||||
availability of `strtof_l` (as a proxy for the whole `<funcname>_l` family) and
|
||||
provides access to locale-independent functions.
|
||||
*/
|
||||
template <typename = const char*, typename = float>
|
||||
struct extended_locale_traits
|
||||
{
|
||||
static constexpr bool is_locale_independent = false;
|
||||
|
||||
/*!
|
||||
@brief Query the decimal point used by the current C locale
|
||||
|
||||
Note that calling this function while switching the global C locale from
|
||||
another thread is undefined behavior.
|
||||
*/
|
||||
JSON_HEDLEY_PURE
|
||||
static char get_decimal_point() noexcept
|
||||
{
|
||||
const auto* loc = localeconv();
|
||||
JSON_ASSERT(loc != nullptr);
|
||||
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
||||
}
|
||||
|
||||
static int isspace(int c) noexcept
|
||||
{
|
||||
return std::isspace(c);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static long long strtoll(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return std::strtoll(str, endptr, 10);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return std::strtoull(str, endptr, 10);
|
||||
}
|
||||
|
||||
static constexpr float(&strtof)(const char*, char**) = std::strtof;
|
||||
static constexpr double(&strtod)(const char*, char**) = std::strtod;
|
||||
static constexpr long double(&strtold)(const char*, char**) = std::strtold;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct extended_locale_traits<T, decltype(strtof_l(
|
||||
std::declval<T>(),
|
||||
std::declval<char**>(),
|
||||
std::declval<locale_t>()))>
|
||||
{
|
||||
static constexpr bool is_locale_independent = true;
|
||||
static constexpr char(&get_decimal_point)() = get_locale_independent_decimal_point;
|
||||
|
||||
static int isspace(int c) noexcept
|
||||
{
|
||||
return isspace_l(c, get_c_locale_t());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static long long strtoll(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return ::strtoll_l(str, endptr, 10, get_c_locale_t());
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static unsigned long long strtoull(const char* str, char** endptr) noexcept
|
||||
{
|
||||
return ::strtoull_l(str, endptr, 10, get_c_locale_t());
|
||||
}
|
||||
|
||||
static float strtof(T str, char** str_end)
|
||||
{
|
||||
return ::strtof_l(str, str_end, get_c_locale_t());
|
||||
}
|
||||
|
||||
static double strtod(T str, char** str_end)
|
||||
{
|
||||
return ::strtod_l(str, str_end, get_c_locale_t());
|
||||
}
|
||||
|
||||
static long double strtold(T str, char** str_end)
|
||||
{
|
||||
return ::strtold_l(str, str_end, get_c_locale_t());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*!
|
||||
@brief Number parsing implementation details
|
||||
|
||||
A traits class template that allows users of nlohmann::detail::from_chars to
|
||||
query details of the used implementation.
|
||||
|
||||
Each specialization provides the following static members:
|
||||
- is_locale_independent: whether the from_chars in unaffected by the current
|
||||
global C locale
|
||||
- get_decimal_point: getter for the character used as decimal point by from_chars
|
||||
- strto: dispatches to the C standard library strto* function for type T, or to
|
||||
the corresponding extended locale function, if available.
|
||||
|
||||
@tparam T numeric type to parse, matching the `value` argument of from_chars
|
||||
*/
|
||||
template <typename T>
|
||||
struct from_chars_traits;
|
||||
|
||||
template <>
|
||||
struct from_chars_traits<long long> // NOLINT(runtime/int)
|
||||
{
|
||||
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||
|
||||
static char get_decimal_point()
|
||||
{
|
||||
return extended_locale_traits<>::get_decimal_point();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static constexpr long long(*strto)(const char*, char**) = &extended_locale_traits<>::strtoll;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct from_chars_traits<unsigned long long> // NOLINT(runtime/int)
|
||||
{
|
||||
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||
|
||||
static char get_decimal_point()
|
||||
{
|
||||
return extended_locale_traits<>::get_decimal_point();
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
static constexpr unsigned long long(*strto)(const char*, char**) = &extended_locale_traits<>::strtoull;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct from_chars_traits<float>
|
||||
{
|
||||
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||
|
||||
static char get_decimal_point()
|
||||
{
|
||||
return extended_locale_traits<>::get_decimal_point();
|
||||
}
|
||||
|
||||
static constexpr float(*strto)(const char*, char**) = &extended_locale_traits<>::strtof;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct from_chars_traits<double>
|
||||
{
|
||||
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||
|
||||
static char get_decimal_point()
|
||||
{
|
||||
return extended_locale_traits<>::get_decimal_point();
|
||||
}
|
||||
|
||||
static constexpr double(*strto)(const char*, char**) = &extended_locale_traits<>::strtod;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct from_chars_traits<long double>
|
||||
{
|
||||
static constexpr bool is_locale_independent = extended_locale_traits<>::is_locale_independent;
|
||||
|
||||
static char get_decimal_point()
|
||||
{
|
||||
return extended_locale_traits<>::get_decimal_point();
|
||||
}
|
||||
|
||||
static constexpr long double(*strto)(const char*, char**) = &extended_locale_traits<>::strtold;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr typename std::enable_if<std::numeric_limits<T>::has_infinity, bool>::type is_out_of_range_value(T value) noexcept
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
return value == T {} || value == std::numeric_limits<T>::infinity() || value == -std::numeric_limits<T>::infinity();
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr typename std::enable_if < !std::numeric_limits<T>::has_infinity, bool >::type is_out_of_range_value(T value) noexcept
|
||||
{
|
||||
// NOTE: Parentheses around function names mitigate min/max macro collision on Windows
|
||||
return value == (std::numeric_limits<T>::max)() || value == (std::numeric_limits<T>::min)();
|
||||
}
|
||||
|
||||
struct from_chars_result
|
||||
{
|
||||
const char* ptr;
|
||||
std::errc ec;
|
||||
};
|
||||
|
||||
/*!
|
||||
@brief Parse integer/floating-point numbers
|
||||
|
||||
Parses the string [first, last) into the numeric type T.
|
||||
|
||||
This implementation uses the C standard library functions strto*, or their
|
||||
extended locale counterparts strto*_l, to emulate the behavior of
|
||||
`std::from_chars` on platforms where it is not (fully) supported.
|
||||
|
||||
Regarding the under-/overflow behavior, this implementation adopts the behavior
|
||||
proposed by [P4168][1] and implemented by libc++ and MS STL of setting `value`
|
||||
to the corresponding non-finite floating-point value in case of an out-of-range
|
||||
result.
|
||||
|
||||
[1]: https://isocpp.org/files/papers/P4168R0.html
|
||||
|
||||
@pre Unlike std::from_chars, this implementation requires the string to be
|
||||
null-terminated, such that `last` dereferences into a NUL byte.
|
||||
|
||||
@tparam T numeric type to parse
|
||||
@param[in] first points to the beginning of the parsed string (inclusive)
|
||||
@param[in] end points to the end of the parsed string (exclusive)
|
||||
@param[out] value references the variable to set the parsed number
|
||||
@return structure consisting of `ptr` and `ec` such that:
|
||||
- If the string starting at `first` matches a number, `ptr` points to
|
||||
the address immediately after the last character that matches.
|
||||
* If the matched number can be represented by `T`, `value` is set and
|
||||
`ec` is value-initialized.
|
||||
* If the matched number cannot be represented by `T`, `ec` is set to
|
||||
`std::errc::result_out_of_range` and depending on the type of `T`:
|
||||
+ `value` is left unchanged for integral `T`,
|
||||
+ `value` is set to +/-0 or +/-inf for floating-point `T`.
|
||||
- If the string starting at `first` doesn't match a number, `ptr`
|
||||
points to `first`, `ec` is `std::errc::invalid_argument`, and `value`
|
||||
is left unchanged.
|
||||
*/
|
||||
template <typename T>
|
||||
JSON_HEDLEY_NON_NULL(1, 2)
|
||||
inline from_chars_result from_chars(const char* first, const char* last, T& value)
|
||||
{
|
||||
JSON_ASSERT(*last == '\0');
|
||||
|
||||
// Unlike strto*, from_chars does not accept leading whitespace or + signs
|
||||
if (first == last || *first == '+'
|
||||
|| (std::is_unsigned<T>::value && *first == '-')
|
||||
|| extended_locale_traits<>::isspace(*first) != 0)
|
||||
{
|
||||
return {first, std::errc::invalid_argument};
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
char* ptr = nullptr; // NOLINT(misc-const-correctness)
|
||||
T result = from_chars_traits<T>::strto(first, &ptr);
|
||||
|
||||
if (ptr == first)
|
||||
{
|
||||
return {ptr, std::errc::invalid_argument};
|
||||
}
|
||||
|
||||
// Upon under-/overflow, strto* returns a marginal value and sets errno.
|
||||
// Note that it is NOT sufficient to just check errno: strto* only clears
|
||||
// errno if the parsed string actually parses into 0/[U]LLONG_MIN/-_MAX
|
||||
// and this result was returned without indicating an under-/overflow.
|
||||
// Otherwise, errno may not be relied upon to indicate the *absence* of
|
||||
// an out-of-range error.
|
||||
if (is_out_of_range_value(result) && errno == ERANGE)
|
||||
{
|
||||
if (std::numeric_limits<T>::has_infinity)
|
||||
{
|
||||
// ONLY for floating-point types, set `value` to the same non-finite
|
||||
// result returned by strto* to allow users to distinguish between
|
||||
// different types of under-/overflow.
|
||||
value = result;
|
||||
}
|
||||
return {ptr, std::errc::result_out_of_range};
|
||||
}
|
||||
|
||||
value = result;
|
||||
return {ptr, std::errc{}};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <unordered_map> // unordered_map
|
||||
#include <utility> // pair, declval
|
||||
#include <valarray> // valarray
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/exceptions.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
@@ -332,6 +333,7 @@ template < typename BasicJsonType, typename ConstructibleArrayType,
|
||||
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
|
||||
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
|
||||
!is_basic_json<ConstructibleArrayType>::value,
|
||||
int > = 0 >
|
||||
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
||||
@@ -377,6 +379,25 @@ inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t&
|
||||
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||
int > = 0 >
|
||||
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
|
||||
{
|
||||
if (j.is_binary())
|
||||
{
|
||||
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
|
||||
}
|
||||
else if (j.is_array())
|
||||
{
|
||||
from_json_array_impl(j, bin, priority_tag<3> {});
|
||||
}
|
||||
else
|
||||
{
|
||||
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename ConstructibleObjectType,
|
||||
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
||||
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
||||
|
||||
@@ -177,8 +177,11 @@ struct external_constructor<value_t::array>
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
|
||||
int > = 0 >
|
||||
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
, int > = 0 >
|
||||
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
{
|
||||
using std::begin;
|
||||
@@ -218,6 +221,25 @@ struct external_constructor<value_t::array>
|
||||
j.set_parents();
|
||||
j.assert_invariant();
|
||||
}
|
||||
|
||||
// std::ranges does not work properly on MinGW due to incomplete C++20 support
|
||||
// see https://github.com/nlohmann/json/issues/4916
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
||||
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
||||
{
|
||||
j.m_data.m_value.destroy(j.m_data.m_type);
|
||||
j.m_data.m_type = value_t::array;
|
||||
j.m_data.m_value = value_t::array;
|
||||
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
||||
{
|
||||
j.m_data.m_value.array->push_back(x);
|
||||
j.set_parent(j.m_data.m_value.array->back());
|
||||
}
|
||||
j.assert_invariant();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template<>
|
||||
@@ -355,19 +377,44 @@ template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
||||
!is_basic_json<CompatibleArrayType>::value,
|
||||
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&&
|
||||
!is_basic_json<CompatibleArrayType>::value
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
,
|
||||
int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, arr);
|
||||
}
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
template < typename BasicJsonType, typename T,
|
||||
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
|
||||
&& !is_compatible_string_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
||||
&& !is_compatible_object_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
||||
&& !is_basic_json<std::remove_cvref_t<T>>::value, int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, T && arr)
|
||||
{
|
||||
external_constructor<value_t::array>::construct(j, std::forward<T>(arr));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType>
|
||||
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
|
||||
{
|
||||
external_constructor<value_t::binary>::construct(j, bin);
|
||||
}
|
||||
|
||||
template < typename BasicJsonType, typename CompatibleArrayType,
|
||||
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
||||
int > = 0 >
|
||||
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
|
||||
{
|
||||
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
|
||||
}
|
||||
|
||||
template<typename BasicJsonType, typename T,
|
||||
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
||||
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
||||
|
||||
@@ -155,7 +155,9 @@ class input_stream_adapter
|
||||
|
||||
// General-purpose iterator-based adapter. It might not be as fast as
|
||||
// theoretically possible for some containers, but it is extremely versatile.
|
||||
template<typename IteratorType>
|
||||
// SentinelType defaults to IteratorType for backward compatibility, but may
|
||||
// be a different type (e.g., a C++20 sentinel or counted_iterator).
|
||||
template<typename IteratorType, typename SentinelType = IteratorType>
|
||||
class iterator_input_adapter
|
||||
{
|
||||
public:
|
||||
@@ -169,9 +171,10 @@ class iterator_input_adapter
|
||||
// in wide_string_input_adapter, which does not expose this).
|
||||
static constexpr bool supports_seek =
|
||||
std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
|
||||
&& std::is_same<IteratorType, SentinelType>::value
|
||||
&& sizeof(char_type) == 1;
|
||||
|
||||
iterator_input_adapter(IteratorType first, IteratorType last)
|
||||
iterator_input_adapter(IteratorType first, SentinelType last)
|
||||
: begin(first), current(std::move(first)), end(std::move(last))
|
||||
{}
|
||||
|
||||
@@ -216,19 +219,30 @@ class iterator_input_adapter
|
||||
private:
|
||||
// whether IteratorType refers to a contiguous range and therefore supports
|
||||
// a std::memcpy fast path (pointers always do; in C++20 we can also detect
|
||||
// library iterators such as those of std::vector and std::string)
|
||||
// library iterators such as those of std::vector and std::string).
|
||||
// Computing the available element count needs either same-type iterators
|
||||
// (plain std::distance) or, in C++20, a sized sentinel (std::ranges::distance),
|
||||
// e.g. std::counted_iterator paired with std::default_sentinel_t.
|
||||
static constexpr bool iterator_is_contiguous =
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
std::contiguous_iterator<IteratorType> ||
|
||||
(std::is_same<IteratorType, SentinelType>::value || std::sized_sentinel_for<SentinelType, IteratorType>)
|
||||
&& (std::contiguous_iterator<IteratorType> || std::is_pointer<IteratorType>::value);
|
||||
#else
|
||||
std::is_same<IteratorType, SentinelType>::value && std::is_pointer<IteratorType>::value;
|
||||
#endif
|
||||
std::is_pointer<IteratorType>::value;
|
||||
|
||||
// contiguous fast path: bulk copy the remaining range with std::memcpy
|
||||
template<class T>
|
||||
std::size_t get_elements_impl(T* dest, std::size_t count, std::true_type /*contiguous*/)
|
||||
{
|
||||
const std::size_t wanted = count * sizeof(T);
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
// std::ranges::distance also supports sized sentinels of a different
|
||||
// type (e.g. std::counted_iterator + std::default_sentinel_t)
|
||||
const std::size_t available = static_cast<std::size_t>(std::ranges::distance(current, end)) * sizeof(char_type);
|
||||
#else
|
||||
const std::size_t available = static_cast<std::size_t>(std::distance(current, end)) * sizeof(char_type);
|
||||
#endif
|
||||
const std::size_t copied = (std::min)(wanted, available);
|
||||
if (JSON_HEDLEY_LIKELY(copied != 0))
|
||||
{
|
||||
@@ -267,7 +281,7 @@ class iterator_input_adapter
|
||||
|
||||
IteratorType begin;
|
||||
IteratorType current;
|
||||
IteratorType end;
|
||||
SentinelType end;
|
||||
|
||||
template<typename BaseInputAdapter, size_t T>
|
||||
friend struct wide_string_input_helper;
|
||||
@@ -453,19 +467,54 @@ class wide_string_input_adapter
|
||||
std::size_t utf8_bytes_filled = 0;
|
||||
};
|
||||
|
||||
template<typename IteratorType, typename Enable = void>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType, typename Enable = void>
|
||||
struct iterator_input_adapter_factory
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = SentinelType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
static adapter_type create(IteratorType first, SentinelType last)
|
||||
{
|
||||
return adapter_type(std::move(first), std::move(last));
|
||||
}
|
||||
};
|
||||
|
||||
// Detection: whether IteratorType and SentinelType can be compared with !=
|
||||
template<typename IteratorType, typename SentinelType, typename = void>
|
||||
struct can_compare_ne_impl : std::false_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_impl < IteratorType, SentinelType,
|
||||
void_t < decltype(std::declval<IteratorType>() != std::declval<SentinelType>()) >>
|
||||
: std::true_type {};
|
||||
|
||||
// Workaround for reversed operator order
|
||||
template<typename IteratorType, typename SentinelType, typename = void>
|
||||
struct can_compare_ne_reversed : std::false_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_reversed < IteratorType, SentinelType,
|
||||
void_t < decltype(std::declval<SentinelType>() != std::declval<IteratorType>()) >>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne_either_order : std::integral_constant < bool,
|
||||
can_compare_ne_impl<IteratorType, SentinelType>::value ||
|
||||
can_compare_ne_reversed<IteratorType, SentinelType>::value > {};
|
||||
|
||||
// std::nullptr_t is excluded explicitly: a literal `nullptr` passed as a
|
||||
// trailing default argument (e.g. parse(s, nullptr, ...)) must never be
|
||||
// mistaken for a sentinel, and some compilers (e.g. GCC 4.8) unreliably
|
||||
// SFINAE the `operator!=` detection above for std::nullptr_t against
|
||||
// container/string types, which would otherwise make such calls ambiguous
|
||||
// with the compatible-input overload.
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct can_compare_ne : std::integral_constant < bool,
|
||||
!std::is_same<SentinelType, std::nullptr_t>::value &&
|
||||
can_compare_ne_either_order<IteratorType, SentinelType>::value > {};
|
||||
|
||||
template<typename T>
|
||||
struct is_iterator_of_multibyte
|
||||
{
|
||||
@@ -476,25 +525,31 @@ struct is_iterator_of_multibyte
|
||||
};
|
||||
};
|
||||
|
||||
template<typename IteratorType>
|
||||
struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
template<typename IteratorType, typename SentinelType>
|
||||
struct iterator_input_adapter_factory<IteratorType, SentinelType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
|
||||
{
|
||||
using iterator_type = IteratorType;
|
||||
using sentinel_type = SentinelType;
|
||||
using char_type = typename std::iterator_traits<iterator_type>::value_type;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type>;
|
||||
using base_adapter_type = iterator_input_adapter<iterator_type, sentinel_type>;
|
||||
using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
|
||||
|
||||
static adapter_type create(IteratorType first, IteratorType last)
|
||||
static adapter_type create(IteratorType first, SentinelType last)
|
||||
{
|
||||
return adapter_type(base_adapter_type(std::move(first), std::move(last)));
|
||||
}
|
||||
};
|
||||
|
||||
// General purpose iterator-based input
|
||||
template<typename IteratorType>
|
||||
typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
|
||||
// General purpose iterator-based input (iterator+sentinel pair; SentinelType
|
||||
// defaults to IteratorType for the common same-type case, but may differ for
|
||||
// C++20 ranges-style iterator+sentinel pairs). Only enable for types that can
|
||||
// be compared with !=.
|
||||
template < typename IteratorType, typename SentinelType = IteratorType,
|
||||
typename = typename std::enable_if <
|
||||
can_compare_ne<IteratorType, SentinelType>::value >::type >
|
||||
typename iterator_input_adapter_factory<IteratorType, SentinelType>::adapter_type input_adapter(IteratorType first, SentinelType last)
|
||||
{
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType>;
|
||||
using factory_type = iterator_input_adapter_factory<IteratorType, SentinelType>;
|
||||
return factory_type::create(first, last);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <array> // array
|
||||
#include <clocale> // localeconv
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdio> // snprintf
|
||||
#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
|
||||
#include <initializer_list> // initializer_list
|
||||
#include <string> // char_traits, string
|
||||
#include <system_error> // errc
|
||||
#include <utility> // move
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/detail/conversions/from_chars.hpp>
|
||||
#include <nlohmann/detail/input/input_adapters.hpp>
|
||||
#include <nlohmann/detail/input/position_t.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
@@ -152,7 +152,7 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
|
||||
: ia(std::move(adapter))
|
||||
, ignore_comments(ignore_comments_)
|
||||
, decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
|
||||
, decimal_point_char(static_cast<char_int_type>(from_chars_traits<number_float_t>::get_decimal_point()))
|
||||
{}
|
||||
|
||||
// deleted because of pointer members
|
||||
@@ -163,19 +163,6 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
~lexer() = default;
|
||||
|
||||
private:
|
||||
/////////////////////
|
||||
// locales
|
||||
/////////////////////
|
||||
|
||||
/// return the locale-dependent decimal point
|
||||
JSON_HEDLEY_PURE
|
||||
static char get_decimal_point() noexcept
|
||||
{
|
||||
const auto* loc = localeconv();
|
||||
JSON_ASSERT(loc != nullptr);
|
||||
return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// scan functions
|
||||
/////////////////////
|
||||
@@ -941,24 +928,6 @@ class lexer : public lexer_base<BasicJsonType>
|
||||
}
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
static void strtof(float& f, const char* str, char** endptr) noexcept
|
||||
{
|
||||
f = std::strtof(str, endptr);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
static void strtof(double& f, const char* str, char** endptr) noexcept
|
||||
{
|
||||
f = std::strtod(str, endptr);
|
||||
}
|
||||
|
||||
JSON_HEDLEY_NON_NULL(2)
|
||||
static void strtof(long double& f, const char* str, char** endptr) noexcept
|
||||
{
|
||||
f = std::strtold(str, endptr);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief scan a number literal
|
||||
|
||||
@@ -1279,19 +1248,18 @@ scan_number_done:
|
||||
// we are done scanning a number)
|
||||
unget();
|
||||
|
||||
char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
errno = 0;
|
||||
|
||||
// try to parse integers first and fall back to floats
|
||||
if (number_type == token_type::value_unsigned)
|
||||
{
|
||||
const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
|
||||
unsigned long long x{}; // NOLINT(runtime/int)
|
||||
const auto res = ::nlohmann::detail::from_chars(token_buffer.data(), token_buffer.data() + token_buffer.size(), x);
|
||||
|
||||
// we checked the number format before
|
||||
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
||||
JSON_ASSERT(res.ptr == token_buffer.data() + token_buffer.size());
|
||||
|
||||
if (errno != ERANGE)
|
||||
if (res.ec != std::errc::result_out_of_range)
|
||||
{
|
||||
JSON_ASSERT(res.ec == std::errc{});
|
||||
value_unsigned = static_cast<number_unsigned_t>(x);
|
||||
if (value_unsigned == x)
|
||||
{
|
||||
@@ -1301,13 +1269,15 @@ scan_number_done:
|
||||
}
|
||||
else if (number_type == token_type::value_integer)
|
||||
{
|
||||
const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
|
||||
long long x{}; // NOLINT(runtime/int)
|
||||
const auto res = ::nlohmann::detail::from_chars(token_buffer.data(), token_buffer.data() + token_buffer.size(), x);
|
||||
|
||||
// we checked the number format before
|
||||
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
||||
JSON_ASSERT(res.ptr == token_buffer.data() + token_buffer.size());
|
||||
|
||||
if (errno != ERANGE)
|
||||
if (res.ec != std::errc::result_out_of_range)
|
||||
{
|
||||
JSON_ASSERT(res.ec == std::errc{});
|
||||
value_integer = static_cast<number_integer_t>(x);
|
||||
if (value_integer == x)
|
||||
{
|
||||
@@ -1318,10 +1288,11 @@ scan_number_done:
|
||||
|
||||
// this code is reached if we parse a floating-point number or if an
|
||||
// integer conversion above failed
|
||||
strtof(value_float, token_buffer.data(), &endptr);
|
||||
const auto res = ::nlohmann::detail::from_chars(token_buffer.data(), token_buffer.data() + token_buffer.size(), value_float);
|
||||
|
||||
// we checked the number format before
|
||||
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
|
||||
JSON_ASSERT(res.ptr == token_buffer.data() + token_buffer.size());
|
||||
JSON_ASSERT(res.ec != std::errc::invalid_argument);
|
||||
|
||||
return token_type::value_float;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,20 @@
|
||||
#define JSON_HAS_FILESYSTEM 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_FROM_CHARS
|
||||
#if defined(JSON_HAS_CPP_17) && defined(__cpp_lib_to_chars)
|
||||
// The std::from_chars<float> implementation in libstdc++ 11 gets some of the corner cases wrong;
|
||||
// starting with libstdc++ 12, these are fixed by switching the implementation to fast_float.
|
||||
#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE < 12
|
||||
#define JSON_HAS_FROM_CHARS 0
|
||||
#else
|
||||
#define JSON_HAS_FROM_CHARS 1
|
||||
#endif
|
||||
#else
|
||||
#define JSON_HAS_FROM_CHARS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef JSON_HAS_THREE_WAY_COMPARISON
|
||||
#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
|
||||
&& defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#undef JSON_HAS_CPP_26
|
||||
#undef JSON_HAS_FILESYSTEM
|
||||
#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||
#undef JSON_HAS_FROM_CHARS
|
||||
#undef JSON_HAS_THREE_WAY_COMPARISON
|
||||
#undef JSON_HAS_RANGES
|
||||
#undef JSON_HAS_STD_FORMAT
|
||||
|
||||
@@ -13,11 +13,15 @@
|
||||
#include <tuple> // tuple
|
||||
#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
|
||||
#include <utility> // declval
|
||||
#include <vector> // vector
|
||||
#if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L
|
||||
#include <cstddef> // byte
|
||||
#endif
|
||||
#include <nlohmann/detail/iterators/iterator_traits.hpp>
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
#include <optional> // optional
|
||||
#endif
|
||||
#include <nlohmann/detail/meta/call_std/begin.hpp>
|
||||
#include <nlohmann/detail/meta/call_std/end.hpp>
|
||||
#include <nlohmann/detail/meta/cpp_future.hpp>
|
||||
@@ -450,6 +454,51 @@ struct is_constructible_string_type
|
||||
value_type_t, laundered_type >>::value;
|
||||
};
|
||||
|
||||
// Forward declarations: iteration_proxy.hpp includes this file, so we cannot
|
||||
// include it here.
|
||||
template<typename IteratorType> class iteration_proxy;
|
||||
template<typename IteratorType> class iteration_proxy_value;
|
||||
|
||||
// Identifies nlohmann's internal iteration-proxy types. These must be excluded
|
||||
// before evaluating any std::ranges concept to avoid circular constraints.
|
||||
template<typename T> struct is_iteration_proxy_type : std::false_type {};
|
||||
template<typename T> struct is_iteration_proxy_type<iteration_proxy<T>> : std::true_type {};
|
||||
template<typename T> struct is_iteration_proxy_type<iteration_proxy_value<T>> : std::true_type {};
|
||||
|
||||
// In C++26, std::optional satisfies std::ranges::view; exclude it so the
|
||||
// range-view overload does not hijack the optional serializer.
|
||||
#ifdef JSON_HAS_CPP_17
|
||||
template<typename T> struct is_range_view_optional_type : std::false_type {};
|
||||
template<typename T> struct is_range_view_optional_type<std::optional<T>> : std::true_type {};
|
||||
#else
|
||||
template<typename T> struct is_range_view_optional_type : std::false_type {};
|
||||
#endif
|
||||
|
||||
// std::ranges does not work properly on MinGW due to incomplete C++20 support
|
||||
// see https://github.com/nlohmann/json/issues/4916
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
|
||||
// SafeToCheck guards against types that trigger circular constraints when
|
||||
// std::ranges::view<T> is evaluated on GCC 12 / libstdc++ 12:
|
||||
// - iteration_proxy / iteration_proxy_value directly
|
||||
// - views wrapping the above (e.g. owning_view<iteration_proxy<...>>)
|
||||
// - views wrapping basic_json (e.g. ref_view<json>) — same circularity
|
||||
// via json's constructors → is_compatible_array_type → here
|
||||
// nlohmann's plain range_value_t (iterator_traits-based) is safe to call
|
||||
// before any std::ranges concept is touched, so we use it for the checks.
|
||||
template < typename T, bool SafeToCheck =
|
||||
!is_iteration_proxy_type<T>::value &&
|
||||
!is_iteration_proxy_type<detected_t<range_value_t, T>>::value &&
|
||||
!is_basic_json<detected_t<range_value_t, T>>::value &&
|
||||
!is_range_view_optional_type<T>::value >
|
||||
struct is_compatible_range_view : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_compatible_range_view<T, true>
|
||||
: std::bool_constant<std::ranges::view<T>> {};
|
||||
|
||||
#endif
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
|
||||
struct is_compatible_array_type_impl : std::false_type {};
|
||||
|
||||
@@ -461,13 +510,38 @@ struct is_compatible_array_type_impl <
|
||||
is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
|
||||
// special case for types like std::filesystem::path whose iterator's value_type are themselves
|
||||
// c.f. https://github.com/nlohmann/json/pull/3073
|
||||
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
|
||||
!std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value
|
||||
// When range-view support is enabled, std::ranges::view types (e.g. std::string_view,
|
||||
// filter_view) can match BOTH this iterator-based specialization AND the view-based one
|
||||
// below, causing ambiguity. Exclude views here so the two specializations are mutually
|
||||
// exclusive: this one handles plain iterable containers, the other handles views.
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
&& !is_compatible_range_view<CompatibleArrayType>::value
|
||||
#endif
|
||||
>>
|
||||
{
|
||||
static constexpr bool value =
|
||||
is_constructible<BasicJsonType,
|
||||
range_value_t<CompatibleArrayType>>::value;
|
||||
};
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type_impl <
|
||||
BasicJsonType, CompatibleArrayType,
|
||||
enable_if_t < is_compatible_range_view<CompatibleArrayType>::value
|
||||
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, char>::value
|
||||
&& !std::is_same<detected_t<range_value_t, CompatibleArrayType>, wchar_t>::value >>
|
||||
{
|
||||
// CompatibleArrayType is a std::ranges::view here, so std::ranges::range_value_t
|
||||
// is safe and correctly handles C++20 iterators that may lack classic iterator_traits.
|
||||
static constexpr bool value =
|
||||
is_constructible<BasicJsonType,
|
||||
std::ranges::range_value_t<CompatibleArrayType>>::value;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_array_type
|
||||
: is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
|
||||
@@ -559,6 +633,14 @@ template<typename BasicJsonType, typename CompatibleType>
|
||||
struct is_compatible_type
|
||||
: is_compatible_type_impl<BasicJsonType, CompatibleType> {};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleArrayType>
|
||||
struct is_compatible_binary_type
|
||||
{
|
||||
static constexpr bool value =
|
||||
std::is_same<typename BasicJsonType::binary_t::container_type, CompatibleArrayType>::value &&
|
||||
!std::is_same<typename BasicJsonType::binary_t::container_type, std::vector<std::uint8_t>>::value;
|
||||
};
|
||||
|
||||
template<typename BasicJsonType, typename CompatibleReferenceType>
|
||||
struct is_compatible_reference_type_impl
|
||||
{
|
||||
|
||||
+32
-24
@@ -4083,12 +4083,13 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return result;
|
||||
}
|
||||
|
||||
/// @brief deserialize from a pair of character iterators
|
||||
/// @brief deserialize from a pair of character iterators (or an iterator+sentinel pair, C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/parse/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json parse(IteratorType first,
|
||||
IteratorType last,
|
||||
SentinelType last,
|
||||
parser_callback_t cb = nullptr,
|
||||
const bool allow_exceptions = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// @brief check if the input is valid JSON
|
||||
/// @brief check if the input is valid JSON (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/accept/
|
||||
template<typename IteratorType>
|
||||
static bool accept(IteratorType first, IteratorType last,
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
static bool accept(IteratorType first, SentinelType last,
|
||||
const bool ignore_comments = false,
|
||||
const bool ignore_trailing_commas = false)
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// @brief generate SAX events
|
||||
/// @brief generate SAX events (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
|
||||
template<class IteratorType, class SAX>
|
||||
template<class IteratorType, class SAX, class SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_NON_NULL(3)
|
||||
static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
|
||||
static bool sax_parse(IteratorType first, SentinelType last, SAX* sax,
|
||||
input_format_t format = input_format_t::json,
|
||||
const bool strict = true,
|
||||
const bool ignore_comments = false,
|
||||
@@ -4461,11 +4464,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in CBOR format
|
||||
/// @brief create a JSON value from an input in CBOR format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_cbor(IteratorType first, IteratorType last,
|
||||
static basic_json from_cbor(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true,
|
||||
const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
|
||||
@@ -4518,11 +4522,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in MessagePack format
|
||||
/// @brief create a JSON value from an input in MessagePack format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_msgpack(IteratorType first, IteratorType last,
|
||||
static basic_json from_msgpack(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -4572,11 +4577,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in UBJSON format
|
||||
/// @brief create a JSON value from an input in UBJSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_ubjson(IteratorType first, IteratorType last,
|
||||
static basic_json from_ubjson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -4626,11 +4632,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BJData format
|
||||
/// @brief create a JSON value from an input in BJData format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_bjdata(IteratorType first, IteratorType last,
|
||||
static basic_json from_bjdata(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
@@ -4656,11 +4663,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
return res ? result : basic_json(value_t::discarded);
|
||||
}
|
||||
|
||||
/// @brief create a JSON value from an input in BSON format
|
||||
/// @brief create a JSON value from an input in BSON format (iterator pair, or iterator+sentinel pair for C++20 ranges support)
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/from_bson/
|
||||
template<typename IteratorType>
|
||||
template<typename IteratorType, typename SentinelType = IteratorType,
|
||||
detail::enable_if_t<detail::can_compare_ne<IteratorType, SentinelType>::value, int> = 0>
|
||||
JSON_HEDLEY_WARN_UNUSED_RESULT
|
||||
static basic_json from_bson(IteratorType first, IteratorType last,
|
||||
static basic_json from_bson(IteratorType first, SentinelType last,
|
||||
const bool strict = true,
|
||||
const bool allow_exceptions = true)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -121,6 +121,10 @@ json_test_set_test_options(test-disabled_exceptions
|
||||
# raise timeout of expensive Unicode test
|
||||
json_test_set_test_options(test-unicode4 TEST_PROPERTIES TIMEOUT 3000)
|
||||
|
||||
# link pthreads to tests that need it
|
||||
find_package(Threads REQUIRED)
|
||||
json_test_set_test_options(test-regression2 LINK_LIBRARIES Threads::Threads)
|
||||
|
||||
#############################################################################
|
||||
# add unit tests
|
||||
#############################################################################
|
||||
|
||||
@@ -30,4 +30,17 @@ inline std::vector<std::uint8_t> read_binary_file(const std::string& filename)
|
||||
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
|
||||
|
||||
@@ -3699,6 +3699,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)
|
||||
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())
|
||||
{
|
||||
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)
|
||||
TEST_CASE("CBOR regressions")
|
||||
{
|
||||
|
||||
@@ -55,6 +55,8 @@ TEST_CASE("lexer class")
|
||||
|
||||
SECTION("numbers")
|
||||
{
|
||||
// Number parsing implementation uses std::from_chars where available,
|
||||
// so run this test suite with JSON_HAS_CPP_17 as well.
|
||||
CHECK((scan_string("0") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("1") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("2") == json::lexer::token_type::value_unsigned));
|
||||
@@ -65,13 +67,20 @@ TEST_CASE("lexer class")
|
||||
CHECK((scan_string("7") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("8") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("9") == json::lexer::token_type::value_unsigned));
|
||||
CHECK((scan_string("18446744073709551615") == json::lexer::token_type::value_unsigned));
|
||||
|
||||
CHECK((scan_string("-0") == json::lexer::token_type::value_integer));
|
||||
CHECK((scan_string("-1") == json::lexer::token_type::value_integer));
|
||||
CHECK((scan_string("-9223372036854775808") == json::lexer::token_type::value_integer));
|
||||
|
||||
CHECK((scan_string("1.1") == json::lexer::token_type::value_float));
|
||||
CHECK((scan_string("-1.1") == json::lexer::token_type::value_float));
|
||||
CHECK((scan_string("1E10") == json::lexer::token_type::value_float));
|
||||
|
||||
// out-of-range integers/floats are treated as value_float tokens
|
||||
CHECK((scan_string("18446744073709551616") == json::lexer::token_type::value_float));
|
||||
CHECK((scan_string("-9223372036854775809") == json::lexer::token_type::value_float));
|
||||
CHECK((scan_string("1E400") == json::lexer::token_type::value_float));
|
||||
}
|
||||
|
||||
SECTION("whitespace")
|
||||
|
||||
@@ -1782,6 +1782,21 @@ TEST_CASE("std::optional")
|
||||
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
||||
CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
|
||||
"[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")
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "doctest_compatibility.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
// nlohmann::details::from_chars is a wrapper around std::from_chars when it is
|
||||
// available (__cpp_lib_to_chars is defined), otherwise our fallback
|
||||
// implementation kicks in.
|
||||
//
|
||||
// Due to the incomplete implementation of this C++17 standard library feature
|
||||
// (for example, lack of support for long double even in current libc++),
|
||||
// JSON_HAS_CPP_17 does not guarantee that std::from_chars is available.
|
||||
// However, the reverse holds: If the test is compiled in C++11 mode, the
|
||||
// fallback implementation will be used for sure.
|
||||
// By mentioning the JSON_HAS_CPP_17 macro in this here comment, the test will
|
||||
// be compiled both using our fallback and, at least on some platforms,
|
||||
// the C++17 standard library version, as a way of ensuring that the
|
||||
// expectations formulated in this test align with `std::from_chars`.
|
||||
|
||||
namespace
|
||||
{
|
||||
struct init_val_t {};
|
||||
constexpr init_val_t init_val{};
|
||||
|
||||
template <typename T>
|
||||
void check_result(std::string& str, T& value, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||
{
|
||||
// On platforms where neither `std::from_chars`, nor extended locale support (`strtof_l`) are
|
||||
// available, the `strtof`-based implementation is in fact locale-dependent and might use a
|
||||
// different decimal separator, so we need to adapt the test expectations accordingly.
|
||||
std::replace(str.begin(), str.end(), '.', nlohmann::detail::from_chars_traits<T>::get_decimal_point());
|
||||
|
||||
auto res = nlohmann::detail::from_chars(str.data(), str.data() + str.size(), value);
|
||||
CHECK_MESSAGE(res.ec == expected_ec, "Error code mismatch while parsing: \"", str,
|
||||
"\": Actual: ", make_error_code(res.ec).message(),
|
||||
"; expected: ", make_error_code(expected_ec).message());
|
||||
CHECK_MESSAGE(res.ptr - str.data() == expected_ptr_offset, "Ptr offset mismatch while parsing: \"", str, "\"");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, void>::type
|
||||
check(std::string str, T expected_value, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||
{
|
||||
T value = 42;
|
||||
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||
CHECK_MESSAGE(value == expected_value, "while parsing: \"", str, "\"");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_floating_point<T>::value, void>::type
|
||||
check(std::string str, T expected_value, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||
{
|
||||
{
|
||||
T value = 42;
|
||||
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||
CHECK_MESSAGE(value == expected_value, "while parsing: \"", str, "\"");
|
||||
CHECK_MESSAGE(std::signbit(value) == std::signbit(expected_value), "while parsing: \"", str, "\"");
|
||||
}
|
||||
{
|
||||
T value = std::numeric_limits<T>::quiet_NaN();
|
||||
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||
CHECK_MESSAGE(value == expected_value, "while parsing: \"", str, "\"");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, void>::type
|
||||
check(std::string str, init_val_t /*unused*/, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||
{
|
||||
T const init_value = 42;
|
||||
T value = init_value;
|
||||
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||
CHECK_MESSAGE(value == init_value, "while parsing: \"", str, "\"");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_floating_point<T>::value, void>::type
|
||||
check(std::string str, init_val_t /*unused*/, std::ptrdiff_t expected_ptr_offset, std::errc expected_ec)
|
||||
{
|
||||
{
|
||||
T const init_value = 42;
|
||||
T value = init_value;
|
||||
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||
CHECK_MESSAGE(value == init_value, "while parsing: \"", str, "\"");
|
||||
}
|
||||
{
|
||||
T value = std::numeric_limits<T>::quiet_NaN();
|
||||
check_result(str, value, expected_ptr_offset, expected_ec);
|
||||
CHECK_MESSAGE(doctest::IsNaN<T>(value), "while parsing: \"", str, "\"");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("integral results consistent with std::from_chars")
|
||||
{
|
||||
SECTION("unsigned long long")
|
||||
{
|
||||
check<unsigned long long>("", init_val, 0, std::errc::invalid_argument);
|
||||
check<unsigned long long>("0", 0ULL, 1, std::errc{});
|
||||
check<unsigned long long>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||
check<unsigned long long>("123 ", 123ULL, 3, std::errc{});
|
||||
check<unsigned long long>("+123", init_val, 0, std::errc::invalid_argument);
|
||||
check<unsigned long long>("-123", init_val, 0, std::errc::invalid_argument);
|
||||
check<unsigned long long>("123", 123ULL, 3, std::errc{});
|
||||
check<unsigned long long>("123e10", 123ULL, 3, std::errc{});
|
||||
check<unsigned long long>("18446744073709551615", 18446744073709551615ULL, 20, std::errc{});
|
||||
check<unsigned long long>("18446744073709551616", init_val, 20, std::errc::result_out_of_range);
|
||||
}
|
||||
|
||||
SECTION("long long")
|
||||
{
|
||||
check<long long>("", init_val, 0, std::errc::invalid_argument);
|
||||
check<long long>("0", 0LL, 1, std::errc{});
|
||||
check<long long>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||
check<long long>("123 ", 123LL, 3, std::errc{});
|
||||
check<long long>("+123", init_val, 0, std::errc::invalid_argument);
|
||||
check<long long>("-123", -123LL, 4, std::errc{});
|
||||
check<long long>("123", 123LL, 3, std::errc{});
|
||||
check<long long>("123e10", 123LL, 3, std::errc{});
|
||||
check<long long>("9223372036854775807", 9223372036854775807LL, 19, std::errc{});
|
||||
check<long long>("9223372036854775808", init_val, 19, std::errc::result_out_of_range);
|
||||
check<long long>("-9223372036854775808", -9223372036854775807LL - 1, 20, std::errc{});
|
||||
check<long long>("-9223372036854775809", init_val, 20, std::errc::result_out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("floating point results consistent with std::from_chars")
|
||||
{
|
||||
INFO("Using decimal point '",
|
||||
std::string{1, nlohmann::detail::from_chars_traits<float>::get_decimal_point()},
|
||||
"' to match our possibly-locale-dependent from_chars fallback implementation");
|
||||
|
||||
SECTION("single precision")
|
||||
{
|
||||
check<float>("", init_val, 0, std::errc::invalid_argument);
|
||||
check<float>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||
check<float>("123 ", 123.0f, 3, std::errc{});
|
||||
check<float>("+123", init_val, 0, std::errc::invalid_argument);
|
||||
check<float>("-123", -123.0f, 4, std::errc{});
|
||||
check<float>("123", 123.0f, 3, std::errc{});
|
||||
check<float>("123e10", 123e10f, 6, std::errc{});
|
||||
check<float>("123e+10", 123e10f, 7, std::errc{});
|
||||
check<float>("123e-10", 123e-10f, 7, std::errc{});
|
||||
check<float>("123.456", 123.456f, 7, std::errc{});
|
||||
check<float>("123;456", 123.0f, 3, std::errc{});
|
||||
check<float>("123.456 ", 123.456f, 7, std::errc{});
|
||||
check<float>("123;456 ", 123.0f, 3, std::errc{});
|
||||
check<float>("123456789.123456789", 123456789.123456789f, 19, std::errc{});
|
||||
check<float>("1e40", std::numeric_limits<float>::infinity(), 4, std::errc::result_out_of_range);
|
||||
check<float>("-1e40", -std::numeric_limits<float>::infinity(), 5, std::errc::result_out_of_range);
|
||||
check<float>("2e308", std::numeric_limits<float>::infinity(), 5, std::errc::result_out_of_range);
|
||||
check<float>("-2e308", -std::numeric_limits<float>::infinity(), 6, std::errc::result_out_of_range);
|
||||
check<float>("123.456e-789", 0.0f, 12, std::errc::result_out_of_range);
|
||||
check<float>("-123.456e-789", -0.0f, 13, std::errc::result_out_of_range);
|
||||
check<float>("1e-45", 1e-45f, 5, std::errc{});
|
||||
check<float>("1e-46", 0.0f, 5, std::errc::result_out_of_range);
|
||||
check<float>("1E-45", 1e-45f, 5, std::errc{});
|
||||
check<float>("1E-46", 0.0f, 5, std::errc::result_out_of_range);
|
||||
check<float>("10e-46", 1e-45f, 6, std::errc{});
|
||||
check<float>("0.1e-45", 0.0f, 7, std::errc::result_out_of_range);
|
||||
check<float>("100000000000000000000000000000000000000", 1e38f, 39, std::errc{});
|
||||
check<float>("100000000000000000000000000000000000000.0", 1e38f, 41, std::errc{});
|
||||
check<float>("1000000000000000000000000000000000000000e-1", 1e38f, 43, std::errc{});
|
||||
check<float>("0.0000000000000000000000000000000000000000000001e84", 1e38f, 51, std::errc{});
|
||||
check<float>("0.000000000000000000000000000000000000000000001", 1e-45f, 47, std::errc{});
|
||||
check<float>("00.000000000000000000000000000000000000000000001", 1e-45f, 48, std::errc{});
|
||||
check<float>("0.0000000000000000000000000000000000000000000001e1", 1e-45f, 50, std::errc{});
|
||||
check<float>("1000000000000000000000000000000000000000e-84", 1e-45f, 44, std::errc{});
|
||||
check<float>("1000000000000000000000000000000000000000", std::numeric_limits<float>::infinity(), 40, std::errc::result_out_of_range);
|
||||
check<float>("1000000000000000000000000000000000000000.0", std::numeric_limits<float>::infinity(), 42, std::errc::result_out_of_range);
|
||||
check<float>("100000000000000000000000000000000000000e1", std::numeric_limits<float>::infinity(), 41, std::errc::result_out_of_range);
|
||||
check<float>("0.0000000000000000000000000000000000000000000001e85", std::numeric_limits<float>::infinity(), 51, std::errc::result_out_of_range);
|
||||
check<float>("1000000000000000000000000000000000000000e-85", 0.0f, 44, std::errc::result_out_of_range);
|
||||
check<float>("0.0000000000000000000000000000000000000000000001", 0.0f, 48, std::errc::result_out_of_range);
|
||||
check<float>("0.000000000000000000000000000000000000000000001e-1", 0.0f, 50, std::errc::result_out_of_range);
|
||||
check<float>("1e-99999999999999999999", 0.0f, 23, std::errc::result_out_of_range);
|
||||
check<float>("1e+99999999999999999999", std::numeric_limits<float>::infinity(), 23, std::errc::result_out_of_range);
|
||||
check<float>("-1e-99999999999999999999", -0.0f, 24, std::errc::result_out_of_range);
|
||||
check<float>("-1e+99999999999999999999", -std::numeric_limits<float>::infinity(), 24, std::errc::result_out_of_range);
|
||||
}
|
||||
|
||||
SECTION("double precision")
|
||||
{
|
||||
check<double>("", init_val, 0, std::errc::invalid_argument);
|
||||
check<double>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||
check<double>("123 ", 123.0, 3, std::errc{});
|
||||
check<double>("+123", init_val, 0, std::errc::invalid_argument);
|
||||
check<double>("-123", -123.0, 4, std::errc{});
|
||||
check<double>("123", 123.0, 3, std::errc{});
|
||||
check<double>("123e10", 123e10, 6, std::errc{});
|
||||
check<double>("123e+10", 123e10, 7, std::errc{});
|
||||
check<double>("123e-10", 123e-10, 7, std::errc{});
|
||||
check<double>("123.456", 123.456, 7, std::errc{});
|
||||
check<double>("123;456", 123.0, 3, std::errc{});
|
||||
check<double>("123.456 ", 123.456, 7, std::errc{});
|
||||
check<double>("123;456 ", 123.0, 3, std::errc{});
|
||||
check<double>("123456789.123456789", 123456789.123456789, 19, std::errc{});
|
||||
check<double>("1e40", 1e40, 4, std::errc{});
|
||||
check<double>("-1e40", -1e40, 5, std::errc{});
|
||||
check<double>("2e308", std::numeric_limits<double>::infinity(), 5, std::errc::result_out_of_range);
|
||||
check<double>("-2e308", -std::numeric_limits<double>::infinity(), 6, std::errc::result_out_of_range);
|
||||
check<double>("2e-324", 0.0, 6, std::errc::result_out_of_range);
|
||||
check<double>("-2e-324", -0.0, 7, std::errc::result_out_of_range);
|
||||
check<double>("123.456e-789", 0.0, 12, std::errc::result_out_of_range);
|
||||
check<double>("-123.456e-789", -0.0, 13, std::errc::result_out_of_range);
|
||||
check<double>("1e-99999999999999999999", 0.0, 23, std::errc::result_out_of_range);
|
||||
check<double>("1e+99999999999999999999", std::numeric_limits<double>::infinity(), 23, std::errc::result_out_of_range);
|
||||
check<double>("-1e-99999999999999999999", -0.0, 24, std::errc::result_out_of_range);
|
||||
check<double>("-1e+99999999999999999999", -std::numeric_limits<double>::infinity(), 24, std::errc::result_out_of_range);
|
||||
}
|
||||
|
||||
SECTION("long double precision")
|
||||
{
|
||||
check<long double>("", init_val, 0, std::errc::invalid_argument);
|
||||
check<long double>(" 123", init_val, 0, std::errc::invalid_argument);
|
||||
check<long double>("123 ", 123.0L, 3, std::errc{});
|
||||
check<long double>("+123", init_val, 0, std::errc::invalid_argument);
|
||||
check<long double>("-123", -123.0L, 4, std::errc{});
|
||||
check<long double>("123", 123.0L, 3, std::errc{});
|
||||
check<long double>("123e10", 123e10L, 6, std::errc{});
|
||||
check<long double>("123e+10", 123e10L, 7, std::errc{});
|
||||
check<long double>("123e-10", 123e-10L, 7, std::errc{});
|
||||
check<long double>("123.456", 123.456L, 7, std::errc{});
|
||||
check<long double>("123;456", 123.0L, 3, std::errc{});
|
||||
check<long double>("123.456 ", 123.456L, 7, std::errc{});
|
||||
check<long double>("123;456 ", 123.0L, 3, std::errc{});
|
||||
check<long double>("123456789.123456789", 123456789.123456789L, 19, std::errc{});
|
||||
check<long double>("1e40", 1e40L, 4, std::errc{});
|
||||
check<long double>("-1e40", -1e40L, 5, std::errc{});
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4127)
|
||||
#endif
|
||||
if (sizeof(long double) > 8)
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
{
|
||||
// Right-hand-side is calculated to avoid warning about literal
|
||||
// exceeding range on platforms where this branch is NOT taken.
|
||||
check<long double>("2e308", 1e308L * 2, 5, std::errc{});
|
||||
check<long double>("-2e308", -1e308L * 2, 6, std::errc{});
|
||||
check<long double>("2e-324", 8e-324L / 4, 6, std::errc{});
|
||||
check<long double>("-2e-324", -8e-324L / 4, 7, std::errc{});
|
||||
}
|
||||
else
|
||||
{
|
||||
check<long double>("2e308", std::numeric_limits<long double>::infinity(), 5, std::errc::result_out_of_range);
|
||||
check<long double>("-2e308", -std::numeric_limits<long double>::infinity(), 6, std::errc::result_out_of_range);
|
||||
check<long double>("2e-324", 0.0L, 6, std::errc::result_out_of_range);
|
||||
check<long double>("-2e-324", -0.0L, 7, std::errc::result_out_of_range);
|
||||
}
|
||||
check<long double>("1e5000", std::numeric_limits<long double>::infinity(), 6, std::errc::result_out_of_range);
|
||||
check<long double>("-1e5000", -std::numeric_limits<long double>::infinity(), 7, std::errc::result_out_of_range);
|
||||
check<long double>("123.456e-7890", 0.0L, 13, std::errc::result_out_of_range);
|
||||
check<long double>("-123.456e-7890", -0.0L, 14, std::errc::result_out_of_range);
|
||||
check<long double>("1e-99999999999999999999", 0.0L, 23, std::errc::result_out_of_range);
|
||||
check<long double>("1e+99999999999999999999", std::numeric_limits<long double>::infinity(), 23, std::errc::result_out_of_range);
|
||||
check<long double>("-1e-99999999999999999999", -0.0L, 24, std::errc::result_out_of_range);
|
||||
check<long double>("-1e+99999999999999999999", -std::numeric_limits<long double>::infinity(), 24, std::errc::result_out_of_range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
SECTION("input from msgpack-python")
|
||||
|
||||
@@ -26,8 +26,11 @@ using ordered_json = nlohmann::ordered_json;
|
||||
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <clocale>
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -1136,6 +1139,40 @@ TEST_CASE("regression tests 2")
|
||||
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")
|
||||
{
|
||||
const json jval{};
|
||||
@@ -1165,6 +1202,84 @@ TEST_CASE("regression tests 2")
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
SECTION("issue #4916 - constructing array from C++20 ranges view does not work")
|
||||
{
|
||||
std::vector<int> nums{1, 2, 37, 42, 21};
|
||||
auto filteredNums = nums | std::views::filter([](int i)
|
||||
{
|
||||
return i > 10;
|
||||
});
|
||||
json const j(filteredNums);
|
||||
CHECK(j.type() == json::value_t::array);
|
||||
CHECK(j == json({37, 42, 21}));
|
||||
}
|
||||
#endif
|
||||
|
||||
// owning_view is not available in libstdc++ < 12
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__) && !(defined(__GLIBCXX__) && _GLIBCXX_RELEASE < 12)
|
||||
SECTION("issue #4916 - constructing array from prvalue C++20 ranges view (owning_view)")
|
||||
{
|
||||
json const j(std::vector<int> {1, 2, 37, 42, 21} | std::views::filter([](int i)
|
||||
{
|
||||
return i > 10;
|
||||
}));
|
||||
CHECK(j.type() == json::value_t::array);
|
||||
CHECK(j == json({37, 42, 21}));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JSON_HAS_RANGES && !defined(__MINGW32__)
|
||||
SECTION("issue #4916 - constructing array from C++20 transform view (prvalue elements)")
|
||||
{
|
||||
std::vector<int> nums{1, 2, 3};
|
||||
auto t = nums | std::views::transform([](int i) noexcept
|
||||
{
|
||||
return i * 2;
|
||||
});
|
||||
json const j(t);
|
||||
CHECK(j.type() == json::value_t::array);
|
||||
CHECK(j == json({2, 4, 6}));
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("issue #5198 - TOCTOU race between lexer construction and locale changes causes float truncation")
|
||||
{
|
||||
std::atomic<bool> stop_requested{};
|
||||
std::thread switcher([&stop_requested]
|
||||
{
|
||||
bool german = true;
|
||||
const std::string initial_locale = std::setlocale(LC_NUMERIC, nullptr);
|
||||
|
||||
while (!stop_requested)
|
||||
{
|
||||
const bool setlocale_res = std::setlocale(LC_NUMERIC, german ? "de_DE.UTF-8" : "C");
|
||||
WARN_MESSAGE(setlocale_res,
|
||||
"Setting locale failed, probably because de_DE.UTF-8 is not installed. "
|
||||
"This test was skipped as it would be inconclusive.");
|
||||
if (!setlocale_res)
|
||||
{
|
||||
stop_requested = true;
|
||||
}
|
||||
german = !german;
|
||||
}
|
||||
|
||||
(void)std::setlocale(LC_NUMERIC, initial_locale.c_str()); // restore original locale
|
||||
});
|
||||
|
||||
for (std::size_t i = 0; i < 10000 && !stop_requested; ++i)
|
||||
{
|
||||
const json j = json::parse("{\"val\": 99.123456789}");
|
||||
const double parsed = j.value("val", 0.0);
|
||||
if (!CHECK(parsed == 99.123456789))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stop_requested = true;
|
||||
switcher.join();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE_TEMPLATE("issue #4798 - nlohmann::json::to_msgpack() encode float NaN as double", T, double, float) // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization)
|
||||
|
||||
@@ -2393,6 +2393,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)
|
||||
TEST_CASE("all UBJSON first bytes")
|
||||
{
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// 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 <nlohmann/json.hpp>
|
||||
@@ -13,6 +20,10 @@ using nlohmann::json;
|
||||
|
||||
#include <list>
|
||||
|
||||
#if defined(__cpp_lib_concepts) && defined(JSON_HAS_CPP_20)
|
||||
#include <iterator>
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Use arbitrary stdlib container")
|
||||
@@ -168,4 +179,55 @@ TEST_CASE("Custom iterator")
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user