mirror of
https://github.com/nlohmann/json.git
synced 2026-10-22 14:08:13 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a4c1a2d42 |
No files matched your search
@@ -23,6 +23,9 @@ Checks whether the input is valid JSON.
|
|||||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
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 iterator type with `operator!=`.
|
||||||
|
|
||||||
|
Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its
|
||||||
|
low surrogate, and a code point above `U+10FFFF` all make this function return `false`.
|
||||||
|
|
||||||
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
|
Unlike the [`parse()`](parse.md) function, this function neither throws an exception in case of invalid JSON input
|
||||||
(i.e., a parse error) nor creates diagnostic information.
|
(i.e., a parse error) nor creates diagnostic information.
|
||||||
|
|
||||||
@@ -35,9 +38,12 @@ Unlike the [`parse()`](parse.md) function, this function neither throws an excep
|
|||||||
- a `#!c FILE` pointer (throws if null)
|
- a `#!c FILE` pointer (throws if null)
|
||||||
- a C-style array of characters
|
- a C-style array of characters
|
||||||
- a pointer to a null-terminated string of single byte characters (throws if null)
|
- a pointer to a null-terminated string of single byte characters (throws if null)
|
||||||
- a `std::string`
|
- a `std::string` or a `std::string_view` (C++17)
|
||||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||||
|
- wide characters (`wchar_t`, `char16_t`, `char32_t`), which are converted to UTF-8: a wide string object
|
||||||
|
(`std::wstring`, `std::u16string`, `std::u32string`) or an array (e.g. a wide string literal); a pointer to a
|
||||||
|
null-terminated wide string (e.g. `std::u16string::c_str()`) is not supported
|
||||||
|
|
||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type, for instance.
|
: a compatible iterator type, for instance.
|
||||||
@@ -125,6 +131,21 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
--8<-- "examples/accept__iterator_pair.output"
|
--8<-- "examples/accept__iterator_pair.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? example "Example: (3) reading from UTF-16 strings"
|
||||||
|
|
||||||
|
The encoding is deduced from the character type of the input. String literals like `u"..."`, `U"..."`, and
|
||||||
|
`L"..."` work as well.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/accept__wide_string.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/accept__wide_string.output"
|
||||||
|
```
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [parse](parse.md) - deserialize from a compatible input
|
- [parse](parse.md) - deserialize from a compatible input
|
||||||
@@ -151,6 +172,9 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)).
|
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)).
|
||||||
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)).
|
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)).
|
||||||
|
- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or
|
||||||
|
a code point above `U+10FFFF`) is rejected in version 3.13.0; before that, some such sequences were accepted or
|
||||||
|
ended the input silently.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ are collected in [Template Parameter Requirements](../../features/types/template
|
|||||||
- [**json**](../json.md) - default specialization
|
- [**json**](../json.md) - default specialization
|
||||||
- [**ordered_json**](../ordered_json.md) - a specialization that maintains the insertion order of object keys
|
- [**ordered_json**](../ordered_json.md) - a specialization that maintains the insertion order of object keys
|
||||||
|
|
||||||
|
## Documents and views
|
||||||
|
|
||||||
|
Besides `basic_json`, the library offers a second way to hold a parsed JSON text: a
|
||||||
|
[`basic_json_document`](../basic_json_document/index.md), which owns or borrows the text and holds a flat index of the
|
||||||
|
values in it, and a [`basic_json_view`](../basic_json_view/index.md), a read-only handle to one value of such a document.
|
||||||
|
Both are defined in the header `<nlohmann/json_view.hpp>`; see
|
||||||
|
[Zero-copy JSON views](../../features/json_view.md) for the concept and the trade-offs.
|
||||||
|
|
||||||
|
- [**json_document**](../json_document.md) / [**ordered_json_document**](../ordered_json_document.md) - read-only
|
||||||
|
documents of the `basic_json` specialization they belong to
|
||||||
|
- [**json_editable_document**](../json_editable_document.md) /
|
||||||
|
[**ordered_json_editable_document**](../ordered_json_editable_document.md) - documents that can be modified in place
|
||||||
|
- [**json_view**](../json_view.md) / [**ordered_json_view**](../ordered_json_view.md) - views of a document
|
||||||
|
- [**json_editable_view**](../json_editable_view.md) /
|
||||||
|
[**ordered_json_editable_view**](../ordered_json_editable_view.md) - views of an editable document
|
||||||
|
|
||||||
## Iterator invalidation
|
## Iterator invalidation
|
||||||
|
|
||||||
All operations that add values to an **array** ([`push_back`](push_back.md) , [`operator+=`](operator+=.md),
|
All operations that add values to an **array** ([`push_back`](push_back.md) , [`operator+=`](operator+=.md),
|
||||||
@@ -343,6 +359,9 @@ Access to the JSON value
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc8259)
|
- [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc8259)
|
||||||
|
- [basic_json_document](../basic_json_document/index.md) / [basic_json_view](../basic_json_view/index.md) - parse a JSON
|
||||||
|
text into a flat index and read it through a view
|
||||||
|
- [Zero-copy JSON views](../../features/json_view.md) - when to use a document and a view instead of a `basic_json`
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ static basic_json parse(IteratorType first, SentinelType last,
|
|||||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
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 iterator type with `operator!=`.
|
||||||
|
|
||||||
|
Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its
|
||||||
|
low surrogate, and a code point above `U+10FFFF` all yield a
|
||||||
|
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101).
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
`InputType`
|
`InputType`
|
||||||
@@ -34,9 +38,12 @@ static basic_json parse(IteratorType first, SentinelType last,
|
|||||||
- a `FILE` pointer (throws if null)
|
- a `FILE` pointer (throws if null)
|
||||||
- a C-style array of characters
|
- a C-style array of characters
|
||||||
- a pointer to a null-terminated string of single byte characters (throws if null)
|
- a pointer to a null-terminated string of single byte characters (throws if null)
|
||||||
- a `std::string`
|
- a `std::string` or a `std::string_view` (C++17)
|
||||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||||
|
- wide characters (`wchar_t`, `char16_t`, `char32_t`), which are converted to UTF-8: a wide string object
|
||||||
|
(`std::wstring`, `std::u16string`, `std::u32string`) or an array (e.g. a wide string literal); a pointer to a
|
||||||
|
null-terminated wide string (e.g. `std::u16string::c_str()`) is not supported
|
||||||
|
|
||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type, for instance.
|
: a compatible iterator type, for instance.
|
||||||
@@ -112,6 +119,13 @@ By default, a `'\0'` (NUL) byte anywhere in the input is treated as end of input
|
|||||||
outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the
|
outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-bytes-in-the-input) for details and the
|
||||||
[`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead.
|
[`JSON_STRICT_NUL_HANDLING`](../macros/json_strict_nul_handling.md) macro to opt into rejecting it instead.
|
||||||
|
|
||||||
|
!!! tip "Parsing into a document instead of a value"
|
||||||
|
|
||||||
|
[`basic_json_document::parse`](../basic_json_document/parse.md) accepts the same inputs and the same options, but
|
||||||
|
keeps the result as a flat index of the parsed values instead of a tree of `basic_json` values. It is faster and uses
|
||||||
|
less memory when only parts of the document are read, and it also accepts a pointer to a null-terminated wide string,
|
||||||
|
which `parse()` does not. See [Zero-copy JSON views](../../features/json_view.md).
|
||||||
|
|
||||||
!!! tip "Performance"
|
!!! tip "Performance"
|
||||||
|
|
||||||
Contiguous inputs (strings, string views, vectors, arrays, pointer ranges) are the fastest to parse. A
|
Contiguous inputs (strings, string views, vectors, arrays, pointer ranges) are the fastest to parse. A
|
||||||
@@ -250,6 +264,22 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
--8<-- "examples/trailing_commas.output"
|
--8<-- "examples/trailing_commas.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? example "Example: parse from UTF-16, UTF-32, and wide strings"
|
||||||
|
|
||||||
|
The encoding is deduced from the character type of the input; `std::u16string` and `std::u32string` are interpreted
|
||||||
|
as UTF-16 and UTF-32, and `std::wstring` uses the platform's `wchar_t` width. String literals like `u"..."`,
|
||||||
|
`U"..."`, and `L"..."` work as well, as do iterators over wide characters.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/parse__wide_string.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/parse__wide_string.output"
|
||||||
|
```
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [accept](accept.md) - check if the input is valid JSON
|
- [accept](accept.md) - check if the input is valid JSON
|
||||||
@@ -280,6 +310,9 @@ outside of a string, invalid) byte; see the [FAQ entry](../../home/faq.md#nul-by
|
|||||||
time).
|
time).
|
||||||
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) restores `std::fgetc`).
|
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md) restores `std::fgetc`).
|
||||||
|
- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or
|
||||||
|
a code point above `U+10FFFF`) is rejected with `parse_error.101` in version 3.13.0; before that, some such
|
||||||
|
sequences were accepted or ended the input silently.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ Read from input and generate SAX events
|
|||||||
respectively as UTF-8, UTF-16, and UTF-32. If `SentinelType` differs from `IteratorType`, it must be comparable to
|
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 iterator type with `operator!=`.
|
||||||
|
|
||||||
|
Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its
|
||||||
|
low surrogate, and a code point above `U+10FFFF` all yield a
|
||||||
|
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101).
|
||||||
|
|
||||||
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
|
The SAX event lister must follow the interface of [`json_sax`](../json_sax/index.md).
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
@@ -42,8 +46,12 @@ The SAX event lister must follow the interface of [`json_sax`](../json_sax/index
|
|||||||
- a `FILE` pointer
|
- a `FILE` pointer
|
||||||
- a C-style array of characters
|
- a C-style array of characters
|
||||||
- a pointer to a null-terminated string of single byte characters
|
- a pointer to a null-terminated string of single byte characters
|
||||||
|
- a `std::string` or a `std::string_view` (C++17)
|
||||||
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
- a container `obj` for which `begin(obj)` and `end(obj)` produce a valid pair of iterators
|
||||||
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
(as found via ADL or member functions, with semantics compatible to `std::begin` and `std::end`)
|
||||||
|
- wide characters (`wchar_t`, `char16_t`, `char32_t`), which are converted to UTF-8: a wide string object
|
||||||
|
(`std::wstring`, `std::u16string`, `std::u32string`) or an array (e.g. a wide string literal); a pointer to a
|
||||||
|
null-terminated wide string (e.g. `std::u16string::c_str()`) is not supported
|
||||||
|
|
||||||
`IteratorType`
|
`IteratorType`
|
||||||
: a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type
|
: a compatible iterator type for overload (2); a pair of character iterators whose `value_type` is an integral type
|
||||||
@@ -160,6 +168,9 @@ use the same file with stdio functions, but it must not wait for another thread
|
|||||||
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)).
|
([`JSON_DISABLE_STREAM_BULK_SCAN`](../macros/json_disable_stream_bulk_scan.md)).
|
||||||
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
- Parsing from a `FILE*` locks the file once instead of once per character in version 3.13.0
|
||||||
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)).
|
([`JSON_DISABLE_UNLOCKED_FILE_IO`](../macros/json_disable_unlocked_file_io.md)).
|
||||||
|
- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or
|
||||||
|
a code point above `U+10FFFF`) is rejected with `parse_error.101` in version 3.13.0; before that, some such
|
||||||
|
sequences were accepted or ended the input silently.
|
||||||
|
|
||||||
!!! warning "Deprecation"
|
!!! warning "Deprecation"
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,12 @@ points apply to every edit:
|
|||||||
[`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is
|
[`source_offset()`](../basic_json_view/source_offset.md) of a value introduced by an edit is
|
||||||
`#!cpp static_cast<std::size_t>(-1)`, the same value it reports for a decoded string.
|
`#!cpp static_cast<std::size_t>(-1)`, the same value it reports for a decoded string.
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [basic_json_view](../basic_json_view/index.md) - the read-only handle to a value of a document
|
||||||
|
- [json_document](../json_document.md) - the document type of the default specialization
|
||||||
|
- [Zero-copy JSON views](../../features/json_view.md) - the concept behind documents and views
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
- Added in version 3.13.0.
|
||||||
@@ -21,17 +21,18 @@ static basic_json_document parse(IteratorType first, IteratorType last,
|
|||||||
|
|
||||||
Both overloads accept the same JSON text as [`BasicJsonType::parse()`](../basic_json/parse.md), with the same
|
Both overloads accept the same JSON text as [`BasicJsonType::parse()`](../basic_json/parse.md), with the same
|
||||||
`ignore_comments`/`ignore_trailing_commas` options, but build a [`basic_json_document`](index.md) (a flat index into
|
`ignore_comments`/`ignore_trailing_commas` options, but build a [`basic_json_document`](index.md) (a flat index into
|
||||||
the input) instead of a tree of `BasicJsonType` values. They take the same inputs as `BasicJsonType::parse()`. Byte
|
the input) instead of a tree of `BasicJsonType` values. They take the same inputs as `BasicJsonType::parse()`, plus a
|
||||||
inputs that stay alive can be borrowed; all others (streams, `#!cpp FILE*`, wide characters, non-contiguous
|
pointer to a null-terminated wide string, which `BasicJsonType::parse()` does not accept. Byte inputs that stay alive
|
||||||
iterators) are read into a copy that the document owns -- see the Notes. Use [`parse_copy()`](parse_copy.md) to get an
|
can be borrowed; all others (streams, `#!cpp FILE*`, wide characters, non-contiguous iterators) are read into a copy
|
||||||
owned copy of a borrowable input as well.
|
that the document owns -- see the Notes. Use [`parse_copy()`](parse_copy.md) to get an owned copy of a borrowable input
|
||||||
|
as well.
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
`InputType`
|
`InputType`
|
||||||
: A compatible input, for instance:
|
: A compatible input, for instance:
|
||||||
|
|
||||||
- a `#!cpp std::string`, `#!cpp std::string_view`, or a C-style array of single-byte characters
|
- a `#!cpp std::string`, a `#!cpp std::string_view` (C++17), or a C-style array of single-byte characters
|
||||||
- a pointer to a null-terminated string of single-byte characters (`#!cpp char`, `#!cpp signed char`,
|
- a pointer to a null-terminated string of single-byte characters (`#!cpp char`, `#!cpp signed char`,
|
||||||
`#!cpp unsigned char`, `#!cpp std::uint8_t`)
|
`#!cpp unsigned char`, `#!cpp std::uint8_t`)
|
||||||
- a container for which `#!cpp obj.data()` and `#!cpp obj.size()` give contiguous single-byte access, e.g.
|
- a container for which `#!cpp obj.data()` and `#!cpp obj.size()` give contiguous single-byte access, e.g.
|
||||||
@@ -47,6 +48,10 @@ owned copy of a borrowable input as well.
|
|||||||
`#!cpp std::string::iterator`; the iterators of single-byte characters are borrowed or read like the byte inputs
|
`#!cpp std::string::iterator`; the iterators of single-byte characters are borrowed or read like the byte inputs
|
||||||
above, those of wide characters are converted to UTF-8
|
above, those of wide characters are converted to UTF-8
|
||||||
|
|
||||||
|
Ill-formed UTF-16 and UTF-32 sequences are rejected: a lone surrogate, a high surrogate that is not followed by its low
|
||||||
|
surrogate, and a code point above `U+10FFFF` all yield a
|
||||||
|
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101).
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
`input` (in)
|
`input` (in)
|
||||||
@@ -155,3 +160,5 @@ buffer of a given length, pass a pair of pointers: `#!cpp parse(ptr, ptr + len)`
|
|||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
- Added in version 3.13.0.
|
||||||
|
- Ill-formed UTF-16 and UTF-32 input (a lone surrogate, a high surrogate that is not followed by its low surrogate, or
|
||||||
|
a code point above `U+10FFFF`) is rejected with `parse_error.101`.
|
||||||
@@ -143,6 +143,12 @@ still refers to it -- including ones taken before the change -- reads the new va
|
|||||||
|
|
||||||
- [**source_offset**](source_offset.md) - byte offset of this value in the document's source text
|
- [**source_offset**](source_offset.md) - byte offset of this value in the document's source text
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [basic_json_document](../basic_json_document/index.md) - the document a view refers into
|
||||||
|
- [materialize](materialize.md) - build the `BasicJsonType` value of a subtree
|
||||||
|
- [Zero-copy JSON views](../../features/json_view.md) - the concept behind documents and views
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
- Added in version 3.13.0.
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// a valid JSON text, given as UTF-16
|
||||||
|
const std::u16string valid_text = u"{\"name\":\"\u00e9\"}";
|
||||||
|
|
||||||
|
// an invalid JSON text, given as UTF-16
|
||||||
|
const std::u16string invalid_text = u"{\"name\":\"\u00e9\",}";
|
||||||
|
|
||||||
|
std::cout << std::boolalpha
|
||||||
|
<< json::accept(valid_text) << ' '
|
||||||
|
<< json::accept(invalid_text) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
true false
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// the same JSON text, given in different encodings
|
||||||
|
const std::string utf8 = "{\"name\":\"\u00e9\"}";
|
||||||
|
const std::u16string utf16 = u"{\"name\":\"\u00e9\"}";
|
||||||
|
const std::u32string utf32 = U"{\"name\":\"\u00e9\"}";
|
||||||
|
const std::wstring wide = L"{\"name\":\"\u00e9\"}";
|
||||||
|
|
||||||
|
// the encoding is deduced from the character type of the input, and all
|
||||||
|
// of them yield the same value
|
||||||
|
std::cout << json::parse(utf8) << '\n'
|
||||||
|
<< json::parse(utf16) << '\n'
|
||||||
|
<< json::parse(utf32) << '\n'
|
||||||
|
<< json::parse(wide) << '\n';
|
||||||
|
|
||||||
|
// a wide string literal works as well: u"..." is UTF-16, U"..." is UTF-32
|
||||||
|
std::cout << json::parse(u"[1, 2, 3]") << '\n';
|
||||||
|
|
||||||
|
// iterators over wide characters work as well
|
||||||
|
std::cout << json::parse(utf16.begin(), utf16.end()) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{"name":"é"}
|
||||||
|
{"name":"é"}
|
||||||
|
{"name":"é"}
|
||||||
|
{"name":"é"}
|
||||||
|
[1,2,3]
|
||||||
|
{"name":"é"}
|
||||||
@@ -38,6 +38,16 @@ which avoids repeating it:
|
|||||||
--8<-- "examples/get_to.output"
|
--8<-- "examples/get_to.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! info "Getting values out of a document or a view"
|
||||||
|
|
||||||
|
A [view](../api/basic_json_view/index.md) offers the same `get<T>()` conversion, but reads it straight from the
|
||||||
|
document's index, without building a `basic_json` value first. Two conversions never copy at all:
|
||||||
|
[`get_string()`](../api/basic_json_view/get_string.md) returns a string as a view into the source text, and
|
||||||
|
[`number_token()`](../api/basic_json_view/number_token.md) returns a number exactly as it was written. Every other
|
||||||
|
type is obtained from a subtree that is built on demand with
|
||||||
|
[`materialize()`](../api/basic_json_view/materialize.md). See
|
||||||
|
[Zero-copy JSON views](json_view.md#getting-values-out-without-copying).
|
||||||
|
|
||||||
The library already knows how to convert to and from the scalar types and the STL containers (such as
|
The library already knows how to convert to and from the scalar types and the STL containers (such as
|
||||||
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
|
`#!cpp std::vector`, `#!cpp std::map`, `#!cpp std::array`, `#!cpp std::optional`, and many more). Converting a JSON
|
||||||
object back to a `#!cpp std::map` or a JSON array back to a `#!cpp std::vector` therefore works without any extra code:
|
object back to a `#!cpp std::map` or a JSON array back to a `#!cpp std::vector` therefore works without any extra code:
|
||||||
@@ -234,3 +244,4 @@ The conversions above are built in for standard types. To make the same syntax w
|
|||||||
- [`get_ref`](../api/basic_json/get_ref.md) / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
|
- [`get_ref`](../api/basic_json/get_ref.md) / [`get_ptr`](../api/basic_json/get_ptr.md) - access the stored value without copying
|
||||||
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
|
- [Arbitrary types conversions](arbitrary_types.md) - support your own types
|
||||||
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
|
- [`JSON_USE_IMPLICIT_CONVERSIONS`](../api/macros/json_use_implicit_conversions.md) - toggle implicit conversions
|
||||||
|
- [Zero-copy JSON views](json_view.md) - get values out of a document or a view
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
# Creating JSON values
|
# Creating JSON values
|
||||||
|
|
||||||
There are several ways to create a JSON value in memory. This page gives an overview; to read a value from JSON text
|
There are several ways to create a JSON value in memory. This page gives an overview; to read a value from JSON text
|
||||||
instead, see [parsing](parsing/index.md).
|
instead, see [parsing](parsing/index.md). A JSON text can also be parsed into a
|
||||||
|
[document](../api/basic_json_document/index.md) and read through a [view](../api/basic_json_view/index.md) instead of
|
||||||
|
into a value; see [Zero-copy JSON views](json_view.md).
|
||||||
|
|
||||||
## From C++ values
|
## From C++ values
|
||||||
|
|
||||||
@@ -101,3 +103,4 @@ string `#!json "42"`.
|
|||||||
- [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal
|
- [`operator""_json`](../api/operator_literal_json.md) - the `_json` literal
|
||||||
- [Converting values](conversions.md) - which C++ types can be used
|
- [Converting values](conversions.md) - which C++ types can be used
|
||||||
- [Parsing](parsing/index.md) - create a value from JSON text
|
- [Parsing](parsing/index.md) - create a value from JSON text
|
||||||
|
- [Zero-copy JSON views](json_view.md) - create a document and a view from JSON text instead of a value
|
||||||
@@ -287,6 +287,23 @@ external dependency and is not defined by default.
|
|||||||
|
|
||||||
See [full documentation of `JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md).
|
See [full documentation of `JSON_USE_SIMDUTF`](../api/macros/json_use_simdutf.md).
|
||||||
|
|
||||||
|
## `JSON_VIEW_NO_SIMD`
|
||||||
|
|
||||||
|
When defined, the parser of [`basic_json_document`](../api/basic_json_document/index.md)
|
||||||
|
(`<nlohmann/json_view.hpp>`) uses only portable C++ to scan strings, instead of vector instructions (SSE2 on x86-64,
|
||||||
|
NEON on AArch64). The same input is accepted or rejected either way; only the speed differs. It is not defined by
|
||||||
|
default.
|
||||||
|
|
||||||
|
See [full documentation of `JSON_VIEW_NO_SIMD`](../api/macros/json_view_no_simd.md).
|
||||||
|
|
||||||
|
## `JSON_VIEW_USE_SSSE3`
|
||||||
|
|
||||||
|
When defined on x86-64, the parser of [`basic_json_document`](../api/basic_json_document/index.md)
|
||||||
|
(`<nlohmann/json_view.hpp>`) validates non-ASCII text in strings with SSSE3 without asking the CPU first. It is not
|
||||||
|
defined by default.
|
||||||
|
|
||||||
|
See [full documentation of `JSON_VIEW_USE_SSSE3`](../api/macros/json_view_use_ssse3.md).
|
||||||
|
|
||||||
## `NLOHMANN_DEFINE_TYPE_*(...)`, `NLOHMANN_DEFINE_DERIVED_TYPE_*(...)`
|
## `NLOHMANN_DEFINE_TYPE_*(...)`, `NLOHMANN_DEFINE_DERIVED_TYPE_*(...)`
|
||||||
|
|
||||||
The library defines 12 macros to simplify the serialization/deserialization of types. See the page on
|
The library defines 12 macros to simplify the serialization/deserialization of types. See the page on
|
||||||
|
|||||||
@@ -85,6 +85,16 @@ json a = {1, 2, 3, 4};
|
|||||||
a.erase(1); // [1,3,4] (erase by index)
|
a.erase(1); // [1,3,4] (erase by index)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Editing a document
|
||||||
|
|
||||||
|
The operations above work on a `basic_json` value. A JSON text that was parsed into a
|
||||||
|
[document](json_view.md#editing-a-document) can also be changed in place, without building a `basic_json` tree for the
|
||||||
|
parts that are not touched: [`set`](../api/basic_json_document/set.md) replaces a value or an object member,
|
||||||
|
[`push_back`](../api/basic_json_document/push_back.md) appends to an array,
|
||||||
|
[`insert`](../api/basic_json_document/insert.md) inserts into an array, and
|
||||||
|
[`erase`](../api/basic_json_document/erase.md) removes a member or an element. These functions are available on the
|
||||||
|
editable document types, such as [`json_editable_document`](../api/json_editable_document.md).
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
|
- [`push_back`](../api/basic_json/push_back.md) / [`emplace_back`](../api/basic_json/emplace_back.md) - append to an array
|
||||||
@@ -93,3 +103,4 @@ a.erase(1); // [1,3,4] (erase by index)
|
|||||||
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch
|
- [`merge_patch`](../api/basic_json/merge_patch.md) - apply an RFC 7396 merge patch
|
||||||
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
|
- [`erase`](../api/basic_json/erase.md) / [`clear`](../api/basic_json/clear.md) - remove elements
|
||||||
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
|
- [JSON Patch and Diff](json_patch.md) and [JSON Merge Patch](merge_patch.md) - structured modifications
|
||||||
|
- [Editing a document](json_view.md#editing-a-document) - change a parsed document in place
|
||||||
@@ -18,6 +18,7 @@ flowchart LR
|
|||||||
The [`parse`](../../api/basic_json/parse.md) function reads a JSON value from an input. The input can be
|
The [`parse`](../../api/basic_json/parse.md) function reads a JSON value from an input. The input can be
|
||||||
|
|
||||||
- a string (`#!cpp std::string`, C string, or string literal),
|
- a string (`#!cpp std::string`, C string, or string literal),
|
||||||
|
- a wide string (`#!cpp std::u16string`, `#!cpp std::u32string`, or `#!cpp std::wstring`, or a wide string literal),
|
||||||
- a `#!cpp std::istream` (e.g., an `#!cpp std::ifstream` reading from a file),
|
- a `#!cpp std::istream` (e.g., an `#!cpp std::ifstream` reading from a file),
|
||||||
- a `#!cpp FILE*` pointer,
|
- a `#!cpp FILE*` pointer,
|
||||||
- a pair of iterators over a contiguous range (e.g., a `#!cpp std::vector<std::uint8_t>`), or
|
- a pair of iterators over a contiguous range (e.g., a `#!cpp std::vector<std::uint8_t>`), or
|
||||||
@@ -32,12 +33,18 @@ std::ifstream f("example.json");
|
|||||||
json data = json::parse(f);
|
json data = json::parse(f);
|
||||||
```
|
```
|
||||||
|
|
||||||
The input must be encoded in UTF-8; other encodings are not supported. A single input may contain only one JSON value.
|
The input must be encoded in UTF-8; other encodings are not supported. The exception is wide-character input, which
|
||||||
|
is read as UTF-16 or UTF-32 -- depending on the character type -- and converted to UTF-8; see the
|
||||||
|
[`parse`](../../api/basic_json/parse.md) page for an example. A single input may contain only one JSON value.
|
||||||
Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format.
|
Inputs consisting of multiple values separated by newlines are handled by the [JSON Lines](json_lines.md) format.
|
||||||
|
|
||||||
By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse`
|
By default, the library rejects comments and trailing commas. Both can be enabled with parameters of the `parse`
|
||||||
function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md).
|
function — see [comments](../comments.md) and [trailing commas](../trailing_commas.md).
|
||||||
|
|
||||||
|
[`basic_json_document::parse`](../../api/basic_json_document/parse.md) accepts the same inputs with the same options,
|
||||||
|
but keeps the result in a document rather than in a `basic_json` value; see
|
||||||
|
[Documents and views](#documents-and-views) below.
|
||||||
|
|
||||||
## Strictness and trailing data
|
## Strictness and trailing data
|
||||||
|
|
||||||
[`parse`](../../api/basic_json/parse.md) reads a single JSON value and requires the whole input to be consumed: any
|
[`parse`](../../api/basic_json/parse.md) reads a single JSON value and requires the whole input to be consumed: any
|
||||||
@@ -98,6 +105,24 @@ The library offers two parsing models:
|
|||||||
You can influence a DOM parse without switching to the SAX interface by passing a
|
You can influence a DOM parse without switching to the SAX interface by passing a
|
||||||
[parser callback](parser_callbacks.md), which is called during parsing and can, for example, discard parts of the input.
|
[parser callback](parser_callbacks.md), which is called during parsing and can, for example, discard parts of the input.
|
||||||
|
|
||||||
|
## Documents and views
|
||||||
|
|
||||||
|
A third model sits between DOM and SAX: [`basic_json_document::parse`](../../api/basic_json_document/parse.md) parses
|
||||||
|
the input into a document that keeps the text and a flat index of the values in it, and
|
||||||
|
[`basic_json_view`](../../api/basic_json_view/index.md) gives read-only access to the values through that index.
|
||||||
|
Strings and numbers stay in the source text and are only decoded when they are read, which is faster and uses less
|
||||||
|
memory than building a tree of `basic_json` values when only parts of the document are needed. A document can also be
|
||||||
|
modified in place, and it can be saved and loaded again without parsing it.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
json_document doc = json_document::parse(R"({"happy": true, "pi": 3.141})");
|
||||||
|
std::cout << doc.root()["happy"].get<bool>() << '\n';
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Zero-copy JSON views](../json_view.md) for the concept and the trade-offs.
|
||||||
|
|
||||||
## Exceptions
|
## Exceptions
|
||||||
|
|
||||||
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
|
When the input is not valid JSON, the `parse` function throws an exception by default. If exceptions are undesired or
|
||||||
@@ -108,6 +133,7 @@ options.
|
|||||||
## See also
|
## See also
|
||||||
|
|
||||||
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
|
- [`parse`](../../api/basic_json/parse.md) - deserialize from a compatible input
|
||||||
|
- [`basic_json_document::parse`](../../api/basic_json_document/parse.md) - deserialize into a document instead of a value
|
||||||
- [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON
|
- [`accept`](../../api/basic_json/accept.md) - check if the input is valid JSON
|
||||||
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
|
- [`sax_parse`](../../api/basic_json/sax_parse.md) - generate SAX events
|
||||||
- [JSON Lines](json_lines.md) - parse newline-delimited JSON
|
- [JSON Lines](json_lines.md) - parse newline-delimited JSON
|
||||||
@@ -115,3 +141,4 @@ options.
|
|||||||
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
- [SAX interface](sax_interface.md) - implement a custom SAX handler
|
||||||
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
- [parsing and exceptions](parse_exceptions.md) - control error handling
|
||||||
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
|
- [parsing untrusted input](untrusted_input.md) - what to consider when parsing input from untrusted sources
|
||||||
|
- [Zero-copy JSON views](../json_view.md) - parse into a document and read it through a view
|
||||||
@@ -81,6 +81,9 @@ Invalid UTF-8 is rejected while parsing, not just while serializing:
|
|||||||
|
|
||||||
- In JSON text, an ill-formed UTF-8 byte in a string is a
|
- In JSON text, an ill-formed UTF-8 byte in a string is a
|
||||||
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) ("invalid string: ill-formed UTF-8 byte").
|
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) ("invalid string: ill-formed UTF-8 byte").
|
||||||
|
- In UTF-16 or UTF-32 input, an ill-formed sequence is a
|
||||||
|
[`parse_error.101`](../../home/exceptions.md#jsonexceptionparse_error101) as well: a lone surrogate, a high surrogate
|
||||||
|
that is not followed by its low surrogate, and a code point above `U+10FFFF` are all rejected.
|
||||||
- In a binary format, a string that is not valid UTF-8 is a
|
- In a binary format, a string that is not valid UTF-8 is a
|
||||||
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
|
[`parse_error.113`](../../home/exceptions.md#jsonexceptionparse_error113).
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Debugging
|
# Debugging
|
||||||
|
|
||||||
This page collects the library's built-in debugger integrations and other debugging-related features. They are
|
This page collects the library's built-in debugger integrations and other debugging-related features. They are not
|
||||||
not linked from a single place elsewhere in the docs, so are collected here.
|
covered by any single page elsewhere in the docs, so they are collected here.
|
||||||
|
|
||||||
## Visual Studio (natvis)
|
## Visual Studio (natvis)
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ As this global context comes at the price of storing one additional pointer per
|
|||||||
|
|
||||||
Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type.
|
Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type.
|
||||||
|
|
||||||
See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for more information.
|
See [documentation of `JSON_DIAGNOSTICS`](../api/macros/json_diagnostics.md) for more information, and
|
||||||
|
[Debugging](debugging.md) for the debugger integrations of the library.
|
||||||
|
|
||||||
## Parse errors
|
## Parse errors
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,9 @@ rejected by default, and the library never adds them when serializing.
|
|||||||
|
|
||||||
The library supports **Unicode input** as follows:
|
The library supports **Unicode input** as follows:
|
||||||
|
|
||||||
- Only **UTF-8** encoded input is supported, which is the default encoding for JSON, according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1).
|
- **UTF-8** encoded input is supported, which is the default encoding for JSON, according to [RFC 8259](https://tools.ietf.org/html/rfc8259.html#section-8.1).
|
||||||
- `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively. These encodings are not supported when reading from files or other input containers.
|
- `std::u16string` and `std::u32string` can be parsed, assuming UTF-16 and UTF-32 encoding, respectively; `std::wstring` works as well and is interpreted as UTF-16 or UTF-32, depending on the width of the platform's `wchar_t`. These encodings cannot be read from a `FILE*` or an `std::istream`, which carry bytes only.
|
||||||
|
- Ill-formed UTF-16 or UTF-32 input is rejected: a lone surrogate, a high surrogate that is not followed by its low surrogate, and a code point above `U+10FFFF` all yield a parse error. The same holds for iterators over 2- or 4-byte characters.
|
||||||
- Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors.
|
- Other encodings such as Latin-1 or ISO 8859-1 are **not** supported and will yield parse or serialization errors.
|
||||||
- The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1).
|
- The library will not replace [Unicode noncharacters](http://www.unicode.org/faq/private_use.html#nonchar1).
|
||||||
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
- Invalid surrogates (e.g., incomplete pairs such as `\uDEAD`) will yield parse errors.
|
||||||
@@ -146,7 +147,7 @@ json::parse(std::string("\"") + '\0' + "\""); // throws parse_error.101: control
|
|||||||
|
|
||||||
Why are wide strings (e.g., `std::wstring`) dumped as arrays of numbers?
|
Why are wide strings (e.g., `std::wstring`) dumped as arrays of numbers?
|
||||||
|
|
||||||
As described [above](#parse-errors-reading-non-ascii-characters), the library assumes UTF-8 as encoding. To store a wide string, you need to change the encoding.
|
As described [above](#parse-errors-reading-non-ascii-characters), the library stores its strings as UTF-8. To store a wide string, you need to change the encoding.
|
||||||
|
|
||||||
!!! example
|
!!! example
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ JSON for Modern C++ is a header-only C++11 library that turns JSON into a first-
|
|||||||
magic of modern C++ so that creating, reading, and modifying JSON values feels as natural as it does in languages like
|
magic of modern C++ so that creating, reading, and modifying JSON values feels as natural as it does in languages like
|
||||||
Python. The whole library is available as a single header, `json.hpp`, with no dependencies, no subproject, and no
|
Python. The whole library is available as a single header, `json.hpp`, with no dependencies, no subproject, and no
|
||||||
complex build system to set up; a companion header, `json_fwd.hpp`, provides forward declarations to keep compile times
|
complex build system to set up; a companion header, `json_fwd.hpp`, provides forward declarations to keep compile times
|
||||||
down. See [header-only integration](integration/index.md) for details. It is heavily unit-tested with 100% code
|
down, `<nlohmann/json_literals.hpp>` holds the user-defined string literals on their own, and
|
||||||
|
`<nlohmann/json_view.hpp>` adds zero-copy views and editable documents. See
|
||||||
|
[header-only integration](integration/index.md) for details. It is heavily unit-tested with 100% code
|
||||||
coverage, checked with Valgrind and the Clang Sanitizers for memory leaks, and continuously fuzz-tested by Google
|
coverage, checked with Valgrind and the Clang Sanitizers for memory leaks, and continuously fuzz-tested by Google
|
||||||
OSS-Fuzz.
|
OSS-Fuzz.
|
||||||
|
|
||||||
@@ -58,7 +60,8 @@ Conda, and more), and pkg-config.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Creating, parsing, accessing, and serializing JSON values, JSON Pointer/Patch, binary formats, and more.
|
Creating, parsing, accessing, and serializing JSON values, JSON Pointer/Patch, binary formats, zero-copy views,
|
||||||
|
and more.
|
||||||
|
|
||||||
[:octicons-arrow-right-24: Features](features/index.md)
|
[:octicons-arrow-right-24: Features](features/index.md)
|
||||||
|
|
||||||
@@ -74,7 +77,8 @@ Conda, and more), and pkg-config.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
The complete reference for `basic_json` and its member functions, types, and related classes.
|
The complete reference for `basic_json` and its member functions, types, and related classes, as well as for
|
||||||
|
`json_document` and `json_view`.
|
||||||
|
|
||||||
[:octicons-arrow-right-24: API documentation](api/basic_json/index.md)
|
[:octicons-arrow-right-24: API documentation](api/basic_json/index.md)
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint64_t
|
#include <cstdint> // uint64_t
|
||||||
#include <cstdlib> // strtoull
|
#include <cstdlib> // strtoull
|
||||||
#include <cstring> // memchr, memcmp, memcpy
|
#include <cstring> // memcmp, memcpy
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <iosfwd> // ostream
|
#include <iosfwd> // ostream
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
@@ -303,12 +303,6 @@ class json_pointer
|
|||||||
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
|
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
|
||||||
};
|
};
|
||||||
|
|
||||||
/// whether a reference token is "-" (a plain comparison with a string literal is not always inlined)
|
|
||||||
static bool is_dash(const string_t& s) noexcept
|
|
||||||
{
|
|
||||||
return s.size() == 1 && s[0] == '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] s reference token to be converted into an array index
|
@param[in] s reference token to be converted into an array index
|
||||||
@param[out] idx the integer representation of @a s if @ref array_index_status::ok
|
@param[out] idx the integer representation of @a s if @ref array_index_status::ok
|
||||||
@@ -334,7 +328,7 @@ class json_pointer
|
|||||||
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
|
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
|
||||||
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
|
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
|
||||||
// token are left to the conversion below and are reported as unresolved
|
// token are left to the conversion below and are reported as unresolved
|
||||||
if (JSON_HEDLEY_UNLIKELY(!s.empty() && !is_dash(s) && !(s[0] >= '0' && s[0] <= '9')))
|
if (JSON_HEDLEY_UNLIKELY(!s.empty() && s != "-" && !(s[0] >= '0' && s[0] <= '9')))
|
||||||
{
|
{
|
||||||
return array_index_status::not_a_number;
|
return array_index_status::not_a_number;
|
||||||
}
|
}
|
||||||
@@ -481,40 +475,9 @@ class json_pointer
|
|||||||
return pos == size;
|
return pos == size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reserve room for @a n elements of an array type that has reserve()
|
|
||||||
template<typename ArrayType>
|
|
||||||
static auto reserve_elements(ArrayType& array, const std::size_t n, detail::priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(array.reserve(n), void())
|
|
||||||
{
|
|
||||||
array.reserve(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ArrayType>
|
|
||||||
static void reserve_elements(ArrayType& /*array*/, const std::size_t /*n*/, detail::priority_tag<0> /*unused*/)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @brief object member access with a key that is only copied if it is inserted
|
/// @brief object member access with a key that is only copied if it is inserted
|
||||||
///
|
|
||||||
/// Objects that can find the position of a key and insert there
|
|
||||||
/// (std::map) search once, others look the key up and add it if it is missing.
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static auto object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<1> /*unused*/)
|
static BasicJsonType& object_member(BasicJsonType& object, const string_t& key)
|
||||||
-> decltype(object.m_data.m_value.object->emplace_hint(object.m_data.m_value.object->lower_bound(key), key, nullptr), std::declval<BasicJsonType&>())
|
|
||||||
{
|
|
||||||
auto& members = *object.m_data.m_value.object;
|
|
||||||
auto it = members.lower_bound(key);
|
|
||||||
if (it != members.end() && !members.key_comp()(key, it->first))
|
|
||||||
{
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto old_capacity = object.container_capacity();
|
|
||||||
it = members.emplace_hint(it, key, nullptr);
|
|
||||||
return object.set_parent_after_insert(it->second, old_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static BasicJsonType& object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<0> /*unused*/)
|
|
||||||
{
|
{
|
||||||
auto& members = *object.m_data.m_value.object;
|
auto& members = *object.m_data.m_value.object;
|
||||||
const auto it = members.find(key);
|
const auto it = members.find(key);
|
||||||
@@ -525,12 +488,6 @@ class json_pointer
|
|||||||
return object.operator[](key);
|
return object.operator[](key);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static BasicJsonType& object_member(BasicJsonType& object, const string_t& key)
|
|
||||||
{
|
|
||||||
return object_member_impl(object, key, detail::priority_tag<1> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
json_pointer top() const
|
json_pointer top() const
|
||||||
{
|
{
|
||||||
@@ -763,103 +720,48 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the values along the pointer last resolved by @ref resolve_plain
|
|
||||||
|
|
||||||
A patch from @ref basic_json::diff lists its operations in document order,
|
|
||||||
so consecutive pointers share most of their reference tokens; the values
|
|
||||||
along the previous pointer are kept to resume from the common prefix.
|
|
||||||
`steps[k]` is the value after the first `k` reference tokens together with
|
|
||||||
the length of the prefix of `*path` that holds them. A value stays valid as
|
|
||||||
long as no operation changes its container: callers keep only values up to
|
|
||||||
the parent of the pointer they modify, or call `reset()`.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
struct plain_steps
|
|
||||||
{
|
|
||||||
const string_t* path = nullptr;
|
|
||||||
std::vector<std::pair<std::size_t, BasicJsonType*>> steps{};
|
|
||||||
|
|
||||||
void reset() noexcept
|
|
||||||
{
|
|
||||||
path = nullptr;
|
|
||||||
steps.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief resolve a plain JSON pointer without building its reference tokens
|
@brief resolve a plain JSON pointer without building its reference tokens
|
||||||
|
|
||||||
@param[in] root the value the pointer starts at
|
@param[in] ptr the value the pointer starts at
|
||||||
@param[in] path a JSON pointer string
|
@param[in] path a JSON pointer string
|
||||||
@param[in] length the number of leading bytes of @a path to resolve; 0 or
|
@param[in] length the number of leading bytes of @a path to resolve; 0 or
|
||||||
the position of a slash
|
the position of a slash
|
||||||
@param[in,out] token a string to hold the reference tokens, to reuse its storage
|
@param[in,out] token a string to hold the reference tokens, to reuse its storage
|
||||||
@param[in,out] cache the values along the previously resolved pointer;
|
|
||||||
@a path must outlive the next call
|
|
||||||
|
|
||||||
@return the pointed to value, or `nullptr` if the pointer does not denote
|
@return the pointed to value, or `nullptr` if the pointer does not denote
|
||||||
an existing value, or does so in a way that is not plain: an array
|
an existing value, or does so in a way that is not plain: a
|
||||||
index that is malformed or out of range, or a "-", or a reference
|
reference token with an escape, an array index that is malformed or
|
||||||
token with an escape. No exception is
|
out of range, or a "-". No exception is thrown for these; callers
|
||||||
thrown for these; callers use @ref json_pointer instead, which
|
use @ref json_pointer instead, which handles them.
|
||||||
handles them.
|
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static BasicJsonType* resolve_plain(BasicJsonType* root, const string_t& path, const std::size_t length, string_t& token, plain_steps<BasicJsonType>& cache)
|
static BasicJsonType* resolve_plain(BasicJsonType* ptr, const string_t& path, const std::size_t length, string_t& token)
|
||||||
{
|
{
|
||||||
auto& steps = cache.steps;
|
for (std::size_t pos = 0; pos < length;)
|
||||||
|
|
||||||
// resume from the deepest step that is also a prefix of this path
|
|
||||||
std::size_t depth = 1;
|
|
||||||
if (cache.path != nullptr && !steps.empty() && steps[0].second == root)
|
|
||||||
{
|
|
||||||
const string_t& previous = *cache.path;
|
|
||||||
for (depth = steps.size(); depth > 1; --depth)
|
|
||||||
{
|
|
||||||
// a step ends at a token boundary of the previous path
|
|
||||||
const std::size_t prefix = steps[depth - 1].first;
|
|
||||||
if (prefix <= length && (prefix == length || path[prefix] == '/') && std::memcmp(path.data(), previous.data(), prefix) == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps.resize(depth);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
steps.assign(1, std::make_pair(std::size_t{0}, root));
|
|
||||||
}
|
|
||||||
cache.path = &path;
|
|
||||||
|
|
||||||
BasicJsonType* ptr = steps[depth - 1].second;
|
|
||||||
for (std::size_t pos = steps[depth - 1].first; pos < length;)
|
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(path[pos] != '/'))
|
if (JSON_HEDLEY_UNLIKELY(path[pos] != '/'))
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the reference token between the slash at pos and the next one,
|
// the reference token between the slash at pos and the next one
|
||||||
// which must not have an escape
|
std::size_t end = path.find_first_of('/', pos + 1);
|
||||||
const char* const begin = path.data() + pos + 1;
|
if (end == string_t::npos || end > length)
|
||||||
const char* const limit = path.data() + length;
|
|
||||||
const char* end = begin;
|
|
||||||
while (end < limit && *end != '/')
|
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(*end == '~'))
|
end = length;
|
||||||
{
|
}
|
||||||
return nullptr;
|
token.clear();
|
||||||
}
|
token.append(path.data() + pos + 1, end - pos - 1);
|
||||||
++end;
|
if (JSON_HEDLEY_UNLIKELY(token.find_first_of('~') != string_t::npos))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ptr->type())
|
switch (ptr->type())
|
||||||
{
|
{
|
||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
token.clear();
|
|
||||||
token.append(begin, static_cast<std::size_t>(end - begin));
|
|
||||||
const auto it = ptr->m_data.m_value.object->find(token);
|
const auto it = ptr->m_data.m_value.object->find(token);
|
||||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||||
{
|
{
|
||||||
@@ -871,24 +773,8 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
// an index of up to 18 digits without a leading zero fits into size_type
|
typename BasicJsonType::size_type idx{};
|
||||||
// (9 digits for a 32-bit size_type); everything else is left to json_pointer
|
if (JSON_HEDLEY_UNLIKELY(parse_array_index<BasicJsonType>(token, idx) != array_index_status::ok || idx >= ptr->m_data.m_value.array->size()))
|
||||||
constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9;
|
|
||||||
const std::size_t digits = static_cast<std::size_t>(end - begin);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0')))
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
typename BasicJsonType::size_type idx = 0;
|
|
||||||
for (const char* c = begin; c != end; ++c)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9'))
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
idx = static_cast<typename BasicJsonType::size_type>((idx * 10) + static_cast<typename BasicJsonType::size_type>(*c - '0'));
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -907,8 +793,7 @@ class json_pointer
|
|||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
pos = static_cast<std::size_t>(end - path.data());
|
pos = end;
|
||||||
steps.emplace_back(pos, ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
@@ -1022,7 +907,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
||||||
@@ -1093,7 +978,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" cannot be used for const access
|
// "-" cannot be used for const access
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
||||||
@@ -1155,7 +1040,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -1232,7 +1117,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
return false;
|
return false;
|
||||||
@@ -1391,52 +1276,6 @@ class json_pointer
|
|||||||
obj[key] = std::forward<ValueType>(v);
|
obj[key] = std::forward<ValueType>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the number of entries that flattening @a value adds: its primitive
|
|
||||||
/// values and empty containers
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static std::size_t count_flattened(const BasicJsonType& value)
|
|
||||||
{
|
|
||||||
std::size_t count = 0;
|
|
||||||
std::vector<const BasicJsonType*> pending(1, &value);
|
|
||||||
while (!pending.empty())
|
|
||||||
{
|
|
||||||
const BasicJsonType* const v = pending.back();
|
|
||||||
pending.pop_back();
|
|
||||||
if (v->is_array() && !v->m_data.m_value.array->empty())
|
|
||||||
{
|
|
||||||
for (const auto& element : *v->m_data.m_value.array)
|
|
||||||
{
|
|
||||||
pending.push_back(&element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (v->is_object() && !v->m_data.m_value.object->empty())
|
|
||||||
{
|
|
||||||
for (const auto& member : *v->m_data.m_value.object)
|
|
||||||
{
|
|
||||||
pending.push_back(&member.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve the storage for @a value of objects that have a reserve(), so
|
|
||||||
// that it does not have to be moved while it grows
|
|
||||||
template<typename ObjectType, typename BasicJsonType>
|
|
||||||
static auto reserve_flattened(ObjectType& obj, const BasicJsonType& value, detail::priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(obj.reserve(std::size_t {}), void())
|
|
||||||
{
|
|
||||||
obj.reserve(count_flattened(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ObjectType, typename BasicJsonType>
|
|
||||||
static void reserve_flattened(ObjectType& /*obj*/, const BasicJsonType& /*value*/, detail::priority_tag<0> /*unused*/)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// append the decimal representation of @a i to @a out
|
/// append the decimal representation of @a i to @a out
|
||||||
static void append_index(string_t& out, std::size_t i)
|
static void append_index(string_t& out, std::size_t i)
|
||||||
{
|
{
|
||||||
@@ -1546,7 +1385,6 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reserve_flattened(object, value, detail::priority_tag<1> {});
|
|
||||||
enter(value);
|
enter(value);
|
||||||
while (!stack.empty())
|
while (!stack.empty())
|
||||||
{
|
{
|
||||||
@@ -1554,9 +1392,8 @@ class json_pointer
|
|||||||
// frame, which would invalidate a reference to it
|
// frame, which would invalidate a reference to it
|
||||||
const BasicJsonType* const container = stack.back().container;
|
const BasicJsonType* const container = stack.back().container;
|
||||||
|
|
||||||
// the path of the previous child is replaced by the path of the
|
// drop the path of the previous child
|
||||||
// next one; for an array element in one resize
|
path.resize(stack.back().path_length);
|
||||||
const std::size_t path_length = stack.back().path_length;
|
|
||||||
|
|
||||||
if (container->is_array())
|
if (container->is_array())
|
||||||
{
|
{
|
||||||
@@ -1570,20 +1407,8 @@ class json_pointer
|
|||||||
|
|
||||||
// iterate array and use index as a reference string
|
// iterate array and use index as a reference string
|
||||||
++stack.back().index;
|
++stack.back().index;
|
||||||
std::array<char, 24> digits{};
|
path.push_back('/');
|
||||||
std::size_t pos = digits.size();
|
append_index(path, i);
|
||||||
std::size_t rest = i;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
digits[--pos] = static_cast<char>('0' + rest % 10);
|
|
||||||
rest /= 10;
|
|
||||||
}
|
|
||||||
while (rest != 0);
|
|
||||||
const std::size_t count = digits.size() - pos;
|
|
||||||
path.resize(path_length + 1 + count);
|
|
||||||
char* const out = &path[path_length];
|
|
||||||
out[0] = '/';
|
|
||||||
std::memcpy(out + 1, digits.data() + pos, count);
|
|
||||||
enter(array[i]);
|
enter(array[i]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1597,7 +1422,6 @@ class json_pointer
|
|||||||
|
|
||||||
// iterate object and use keys as reference string
|
// iterate object and use keys as reference string
|
||||||
++stack.back().member;
|
++stack.back().member;
|
||||||
path.resize(path_length);
|
|
||||||
path.push_back('/');
|
path.push_back('/');
|
||||||
detail::escape_into(path, it->first);
|
detail::escape_into(path, it->first);
|
||||||
enter(it->second);
|
enter(it->second);
|
||||||
@@ -1693,28 +1517,8 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
end = key.size();
|
end = key.size();
|
||||||
}
|
}
|
||||||
const std::size_t token_size = end - pos - 1;
|
|
||||||
|
|
||||||
// a token that is a plain index of up to 9 digits, which is
|
|
||||||
// the case for almost all array elements, is not copied
|
|
||||||
std::size_t index = 0;
|
|
||||||
bool plain_index = current->type() == detail::value_t::array && token_size != 0 && token_size <= 9 && (token_size == 1 || data[pos + 1] != '0');
|
|
||||||
for (std::size_t k = pos + 1; plain_index && k < end; ++k)
|
|
||||||
{
|
|
||||||
plain_index = data[k] >= '0' && data[k] <= '9';
|
|
||||||
index = (index * 10) + static_cast<std::size_t>(data[k] - '0');
|
|
||||||
}
|
|
||||||
if (plain_index)
|
|
||||||
{
|
|
||||||
current = ¤t->operator[](static_cast<typename BasicJsonType::size_type>(index));
|
|
||||||
values.push_back(current);
|
|
||||||
ends.push_back(end);
|
|
||||||
pos = end;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(data + pos + 1, token_size);
|
token.append(data + pos + 1, end - pos - 1);
|
||||||
if (token.find_first_of('~') != string_t::npos)
|
if (token.find_first_of('~') != string_t::npos)
|
||||||
{
|
{
|
||||||
detail::unescape(token);
|
detail::unescape(token);
|
||||||
@@ -1724,13 +1528,10 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
case detail::value_t::null:
|
case detail::value_t::null:
|
||||||
{
|
{
|
||||||
// a first child 0 makes the value an array without a look-up
|
if (arrays.contains(data, pos))
|
||||||
if ((token_size == 1 && data[pos + 1] == '0') || arrays.contains(data, pos))
|
|
||||||
{
|
{
|
||||||
// some reference token below this position is 0, so the
|
// some reference token below this position is 0, so the
|
||||||
// value is an array
|
// value is an array
|
||||||
*current = BasicJsonType(detail::value_t::array);
|
|
||||||
reserve_elements(*current->m_data.m_value.array, 2, detail::priority_tag<1> {});
|
|
||||||
current = ¤t->operator[](array_index<BasicJsonType>(token));
|
current = ¤t->operator[](array_index<BasicJsonType>(token));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
+32
-157
@@ -6878,72 +6878,6 @@ public:
|
|||||||
/// @name JSON Patch functions
|
/// @name JSON Patch functions
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
private:
|
|
||||||
/// the members of an operation of a JSON patch that are of interest
|
|
||||||
struct patch_members
|
|
||||||
{
|
|
||||||
basic_json* op = nullptr;
|
|
||||||
basic_json* path = nullptr;
|
|
||||||
basic_json* value = nullptr;
|
|
||||||
basic_json* from = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief find the members "op", "path", "value", and "from" of the object @a val in one pass
|
|
||||||
static patch_members find_patch_members(const basic_json& val, std::true_type /*key_type is string_t*/)
|
|
||||||
{
|
|
||||||
patch_members members;
|
|
||||||
for (auto& member : *val.m_data.m_value.object)
|
|
||||||
{
|
|
||||||
const string_t& key = member.first;
|
|
||||||
switch (key.size())
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
if (key == "op")
|
|
||||||
{
|
|
||||||
members.op = &member.second;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
if (key == "path")
|
|
||||||
{
|
|
||||||
members.path = &member.second;
|
|
||||||
}
|
|
||||||
else if (key == "from")
|
|
||||||
{
|
|
||||||
members.from = &member.second;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
if (key == "value")
|
|
||||||
{
|
|
||||||
members.value = &member.second;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief find the members by lookup for key types other than string_t
|
|
||||||
static patch_members find_patch_members(const basic_json& val, std::false_type /*key_type is not string_t*/)
|
|
||||||
{
|
|
||||||
const auto find_member = [&val](const char* name) -> basic_json *
|
|
||||||
{
|
|
||||||
const auto it = val.m_data.m_value.object->find(string_t(name));
|
|
||||||
return it != val.m_data.m_value.object->end() ? &it->second : nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
patch_members members;
|
|
||||||
members.op = find_member("op");
|
|
||||||
members.path = find_member("path");
|
|
||||||
members.value = find_member("value");
|
|
||||||
members.from = find_member("from");
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @brief applies a JSON patch in-place without copying the object
|
/// @brief applies a JSON patch in-place without copying the object
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/patch/
|
/// @sa https://json.nlohmann.me/api/basic_json/patch/
|
||||||
void patch_inplace(const basic_json& json_patch)
|
void patch_inplace(const basic_json& json_patch)
|
||||||
@@ -7117,18 +7051,18 @@ public:
|
|||||||
// a diff creates them: apply the operation and return true; return
|
// a diff creates them: apply the operation and return true; return
|
||||||
// false, with nothing changed, for all others
|
// false, with nothing changed, for all others
|
||||||
string_t token;
|
string_t token;
|
||||||
typename json_pointer::template plain_steps<basic_json> steps;
|
const string_t value_key = "value";
|
||||||
const auto try_plain = [&result, &token, &steps](patch_operations operation, const string_t& path, const patch_members & members, const basic_json & val)
|
const auto try_plain = [&result, &token, &value_key](patch_operations operation, const string_t& path, const basic_json & val)
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty() || path.find_first_of('~') != string_t::npos)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// a missing "value" is reported below
|
// a missing "value" is reported below
|
||||||
const basic_json* const value_ptr = members.value;
|
const auto value_it = val.m_data.m_value.object->find(value_key);
|
||||||
const basic_json& value = value_ptr != nullptr ? *value_ptr : val;
|
const basic_json& value = value_it != val.m_data.m_value.object->end() ? value_it->second : val;
|
||||||
if (operation != patch_operations::remove && value_ptr == nullptr)
|
if (operation != patch_operations::remove && value_it == val.m_data.m_value.object->end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -7137,7 +7071,7 @@ public:
|
|||||||
{
|
{
|
||||||
case patch_operations::replace:
|
case patch_operations::replace:
|
||||||
{
|
{
|
||||||
basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token, steps);
|
basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token);
|
||||||
if (target == nullptr)
|
if (target == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -7148,7 +7082,7 @@ public:
|
|||||||
|
|
||||||
case patch_operations::test:
|
case patch_operations::test:
|
||||||
{
|
{
|
||||||
const basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token, steps);
|
const basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token);
|
||||||
// a failing test is reported below
|
// a failing test is reported below
|
||||||
return target != nullptr && *target == value;
|
return target != nullptr && *target == value;
|
||||||
}
|
}
|
||||||
@@ -7168,17 +7102,13 @@ public:
|
|||||||
}
|
}
|
||||||
--slash;
|
--slash;
|
||||||
|
|
||||||
basic_json* const parent = json_pointer::resolve_plain(&result, path, slash, token, steps);
|
basic_json* const parent = json_pointer::resolve_plain(&result, path, slash, token);
|
||||||
if (parent == nullptr)
|
if (parent == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(path.data() + slash + 1, path.size() - slash - 1);
|
token.append(path.data() + slash + 1, path.size() - slash - 1);
|
||||||
if (token.find_first_of('~') != string_t::npos)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operation == patch_operations::add)
|
if (operation == patch_operations::add)
|
||||||
{
|
{
|
||||||
@@ -7234,32 +7164,31 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// the name of the first member to check
|
|
||||||
const string_t op_member = "op";
|
|
||||||
|
|
||||||
// iterate and apply the operations
|
// iterate and apply the operations
|
||||||
for (const auto& val : json_patch)
|
for (const auto& val : json_patch)
|
||||||
{
|
{
|
||||||
// wrapper to get a value for an operation
|
// wrapper to get a value for an operation
|
||||||
const auto get_value = [&val](basic_json * found,
|
const auto get_value = [&val](const string_t& op,
|
||||||
const string_t& op,
|
const string_t& member,
|
||||||
const char* member,
|
|
||||||
bool string_type) -> basic_json &
|
bool string_type) -> basic_json &
|
||||||
{
|
{
|
||||||
|
// find value
|
||||||
|
auto it = val.m_data.m_value.object->find(member);
|
||||||
|
|
||||||
// check if the desired value is present
|
// check if the desired value is present
|
||||||
if (JSON_HEDLEY_UNLIKELY(found == nullptr))
|
if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
|
||||||
{
|
{
|
||||||
detail::throw_patch_missing_member(op, string_t(member), false, val);
|
detail::throw_patch_missing_member(op, member, false, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the result is of type string
|
// check if the result is of type string
|
||||||
if (JSON_HEDLEY_UNLIKELY(string_type && !found->is_string()))
|
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
|
||||||
{
|
{
|
||||||
detail::throw_patch_missing_member(op, string_t(member), true, val);
|
detail::throw_patch_missing_member(op, member, true, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no error: return value
|
// no error: return value
|
||||||
return *found;
|
return it->second;
|
||||||
};
|
};
|
||||||
|
|
||||||
// type check: every element of the array must be an object
|
// type check: every element of the array must be an object
|
||||||
@@ -7268,30 +7197,26 @@ public:
|
|||||||
detail::throw_patch_not_an_array(val);
|
detail::throw_patch_not_an_array(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch_members members = find_patch_members(val, std::is_same<typename object_t::key_type, string_t> {});
|
|
||||||
|
|
||||||
// collect mandatory members
|
// collect mandatory members
|
||||||
const string_t& op = *get_value(members.op, op_member, "op", true).m_data.m_value.string;
|
const string_t& op = *get_value("op", "op", true).m_data.m_value.string;
|
||||||
const string_t& path = *get_value(members.path, op, "path", true).m_data.m_value.string;
|
const string_t& path = *get_value(op, "path", true).m_data.m_value.string;
|
||||||
const patch_operations operation = get_op(op);
|
const patch_operations operation = get_op(op);
|
||||||
|
|
||||||
// the usual operations on a plain pointer are applied without
|
// the usual operations on a plain pointer are applied without
|
||||||
// building the pointer's reference tokens; anything else (and
|
// building the pointer's reference tokens; anything else (and
|
||||||
// every error) is handled below
|
// every error) is handled below
|
||||||
if (try_plain(operation, path, members, val))
|
if (try_plain(operation, path, val))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the general operations may change any container
|
|
||||||
steps.reset();
|
|
||||||
json_pointer ptr(path);
|
json_pointer ptr(path);
|
||||||
|
|
||||||
switch (operation)
|
switch (operation)
|
||||||
{
|
{
|
||||||
case patch_operations::add:
|
case patch_operations::add:
|
||||||
{
|
{
|
||||||
operation_add(ptr, get_value(members.value, "add", "value", false));
|
operation_add(ptr, get_value("add", "value", false));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7304,13 +7229,13 @@ public:
|
|||||||
case patch_operations::replace:
|
case patch_operations::replace:
|
||||||
{
|
{
|
||||||
// the "path" location must exist - use at()
|
// the "path" location must exist - use at()
|
||||||
result.at(ptr) = get_value(members.value, "replace", "value", false);
|
result.at(ptr) = get_value("replace", "value", false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case patch_operations::move:
|
case patch_operations::move:
|
||||||
{
|
{
|
||||||
const string_t& from_path = *get_value(members.from, "move", "from", true).m_data.m_value.string;
|
const string_t& from_path = *get_value("move", "from", true).m_data.m_value.string;
|
||||||
json_pointer from_ptr(from_path);
|
json_pointer from_ptr(from_path);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
|
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
|
||||||
@@ -7333,7 +7258,7 @@ public:
|
|||||||
|
|
||||||
case patch_operations::copy:
|
case patch_operations::copy:
|
||||||
{
|
{
|
||||||
const string_t& from_path = *get_value(members.from, "copy", "from", true).m_data.m_value.string;
|
const string_t& from_path = *get_value("copy", "from", true).m_data.m_value.string;
|
||||||
const json_pointer from_ptr(from_path);
|
const json_pointer from_ptr(from_path);
|
||||||
|
|
||||||
// The copy is functionally identical to an "add"
|
// The copy is functionally identical to an "add"
|
||||||
@@ -7351,7 +7276,7 @@ public:
|
|||||||
{
|
{
|
||||||
// check if "value" matches the one at "path"
|
// check if "value" matches the one at "path"
|
||||||
// the "path" location must exist - use at()
|
// the "path" location must exist - use at()
|
||||||
success = (result.at(ptr) == get_value(members.value, "test", "value", false));
|
success = (result.at(ptr) == get_value("test", "value", false));
|
||||||
}
|
}
|
||||||
JSON_INTERNAL_CATCH (out_of_range&)
|
JSON_INTERNAL_CATCH (out_of_range&)
|
||||||
{
|
{
|
||||||
@@ -7443,18 +7368,13 @@ public:
|
|||||||
/// @brief append an operation @a op for @a path to @a result, with @a value if it is not null
|
/// @brief append an operation @a op for @a path to @a result, with @a value if it is not null
|
||||||
static void diff_operation(basic_json& result, const char* op, const string_t& path, const basic_json* value)
|
static void diff_operation(basic_json& result, const char* op, const string_t& path, const basic_json* value)
|
||||||
{
|
{
|
||||||
// the keys are distinct and in the order of std::map; the members are
|
|
||||||
// added without looking them up
|
|
||||||
basic_json entry(value_t::object);
|
basic_json entry(value_t::object);
|
||||||
auto& members = *entry.m_data.m_value.object;
|
entry["op"] = op;
|
||||||
typename object_t::iterator hint = members.end();
|
entry["path"] = path;
|
||||||
json_pointer::add_flattened(members, hint, string_t("op"), string_t(op), detail::priority_tag<2> {});
|
|
||||||
json_pointer::add_flattened(members, hint, string_t("path"), path, detail::priority_tag<2> {});
|
|
||||||
if (value != nullptr)
|
if (value != nullptr)
|
||||||
{
|
{
|
||||||
json_pointer::add_flattened(members, hint, string_t("value"), *value, detail::priority_tag<2> {});
|
entry["value"] = *value;
|
||||||
}
|
}
|
||||||
entry.set_parents();
|
|
||||||
result.push_back(std::move(entry));
|
result.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7476,50 +7396,6 @@ public:
|
|||||||
diff_operation(result, "add", path, &value);
|
diff_operation(result, "add", path, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief the values in the target of the members of a source object, for the
|
|
||||||
/// objects of up to 16 members without allocating
|
|
||||||
///
|
|
||||||
/// reserve() must be called with the number of values that are added.
|
|
||||||
class diff_target_values
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
diff_target_values() = default;
|
|
||||||
diff_target_values(const diff_target_values&) = delete;
|
|
||||||
diff_target_values& operator=(const diff_target_values&) = delete;
|
|
||||||
~diff_target_values() = default;
|
|
||||||
|
|
||||||
void reserve(const std::size_t n)
|
|
||||||
{
|
|
||||||
if (n > inline_size)
|
|
||||||
{
|
|
||||||
large.resize(n);
|
|
||||||
items = large.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_back(const basic_json* value)
|
|
||||||
{
|
|
||||||
items[count++] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t size() const noexcept
|
|
||||||
{
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
const basic_json* operator[](const std::size_t i) const noexcept
|
|
||||||
{
|
|
||||||
return items[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr std::size_t inline_size = 16;
|
|
||||||
std::size_t count = 0;
|
|
||||||
const basic_json** items = inline_items;
|
|
||||||
std::vector<const basic_json*> large{}; // NOLINT(readability-redundant-member-init)
|
|
||||||
const basic_json* inline_items[inline_size]; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief append "/" and the decimal representation of @a index to @a path
|
/// @brief append "/" and the decimal representation of @a index to @a path
|
||||||
static void diff_append_index(string_t& path, std::size_t index)
|
static void diff_append_index(string_t& path, std::size_t index)
|
||||||
{
|
{
|
||||||
@@ -7600,9 +7476,8 @@ public:
|
|||||||
operations that remove every member of @a source and add every member of
|
operations that remove every member of @a source and add every member of
|
||||||
@a target to @a result, and returns false.
|
@a target to @a result, and returns false.
|
||||||
*/
|
*/
|
||||||
template<typename TargetValues>
|
|
||||||
static bool diff_object_keys(basic_json& result, const basic_json& source, const basic_json& target,
|
static bool diff_object_keys(basic_json& result, const basic_json& source, const basic_json& target,
|
||||||
const string_t& path, TargetValues& target_values,
|
const string_t& path, std::vector<const basic_json*>& target_values,
|
||||||
std::vector<const_iterator>& added)
|
std::vector<const_iterator>& added)
|
||||||
{
|
{
|
||||||
// first pass: one lookup per source key
|
// first pass: one lookup per source key
|
||||||
@@ -7816,7 +7691,7 @@ public:
|
|||||||
|
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
diff_target_values target_values;
|
std::vector<const basic_json*> target_values;
|
||||||
std::vector<const_iterator> added;
|
std::vector<const_iterator> added;
|
||||||
if (diff_object_keys(result, source, target, path, target_values, added))
|
if (diff_object_keys(result, source, target, path, target_values, added))
|
||||||
{
|
{
|
||||||
|
|||||||
Generated
+65
-389
@@ -22357,7 +22357,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <cstdint> // uint64_t
|
#include <cstdint> // uint64_t
|
||||||
#include <cstdlib> // strtoull
|
#include <cstdlib> // strtoull
|
||||||
#include <cstring> // memchr, memcmp, memcpy
|
#include <cstring> // memcmp, memcpy
|
||||||
#ifndef JSON_NO_IO
|
#ifndef JSON_NO_IO
|
||||||
#include <iosfwd> // ostream
|
#include <iosfwd> // ostream
|
||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
@@ -22653,12 +22653,6 @@ class json_pointer
|
|||||||
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
|
exceeds_size_type ///< @a s converts to an integer that exceeds size_type
|
||||||
};
|
};
|
||||||
|
|
||||||
/// whether a reference token is "-" (a plain comparison with a string literal is not always inlined)
|
|
||||||
static bool is_dash(const string_t& s) noexcept
|
|
||||||
{
|
|
||||||
return s.size() == 1 && s[0] == '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] s reference token to be converted into an array index
|
@param[in] s reference token to be converted into an array index
|
||||||
@param[out] idx the integer representation of @a s if @ref array_index_status::ok
|
@param[out] idx the integer representation of @a s if @ref array_index_status::ok
|
||||||
@@ -22684,7 +22678,7 @@ class json_pointer
|
|||||||
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
|
// error condition (cf. RFC 6901, Sect. 4); this also covers single-
|
||||||
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
|
// character tokens, so "/x" and "/xy" fail alike; "-" and the empty
|
||||||
// token are left to the conversion below and are reported as unresolved
|
// token are left to the conversion below and are reported as unresolved
|
||||||
if (JSON_HEDLEY_UNLIKELY(!s.empty() && !is_dash(s) && !(s[0] >= '0' && s[0] <= '9')))
|
if (JSON_HEDLEY_UNLIKELY(!s.empty() && s != "-" && !(s[0] >= '0' && s[0] <= '9')))
|
||||||
{
|
{
|
||||||
return array_index_status::not_a_number;
|
return array_index_status::not_a_number;
|
||||||
}
|
}
|
||||||
@@ -22831,40 +22825,9 @@ class json_pointer
|
|||||||
return pos == size;
|
return pos == size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reserve room for @a n elements of an array type that has reserve()
|
|
||||||
template<typename ArrayType>
|
|
||||||
static auto reserve_elements(ArrayType& array, const std::size_t n, detail::priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(array.reserve(n), void())
|
|
||||||
{
|
|
||||||
array.reserve(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ArrayType>
|
|
||||||
static void reserve_elements(ArrayType& /*array*/, const std::size_t /*n*/, detail::priority_tag<0> /*unused*/)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// @brief object member access with a key that is only copied if it is inserted
|
/// @brief object member access with a key that is only copied if it is inserted
|
||||||
///
|
|
||||||
/// Objects that can find the position of a key and insert there
|
|
||||||
/// (std::map) search once, others look the key up and add it if it is missing.
|
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static auto object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<1> /*unused*/)
|
static BasicJsonType& object_member(BasicJsonType& object, const string_t& key)
|
||||||
-> decltype(object.m_data.m_value.object->emplace_hint(object.m_data.m_value.object->lower_bound(key), key, nullptr), std::declval<BasicJsonType&>())
|
|
||||||
{
|
|
||||||
auto& members = *object.m_data.m_value.object;
|
|
||||||
auto it = members.lower_bound(key);
|
|
||||||
if (it != members.end() && !members.key_comp()(key, it->first))
|
|
||||||
{
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto old_capacity = object.container_capacity();
|
|
||||||
it = members.emplace_hint(it, key, nullptr);
|
|
||||||
return object.set_parent_after_insert(it->second, old_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static BasicJsonType& object_member_impl(BasicJsonType& object, const string_t& key, detail::priority_tag<0> /*unused*/)
|
|
||||||
{
|
{
|
||||||
auto& members = *object.m_data.m_value.object;
|
auto& members = *object.m_data.m_value.object;
|
||||||
const auto it = members.find(key);
|
const auto it = members.find(key);
|
||||||
@@ -22875,12 +22838,6 @@ class json_pointer
|
|||||||
return object.operator[](key);
|
return object.operator[](key);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static BasicJsonType& object_member(BasicJsonType& object, const string_t& key)
|
|
||||||
{
|
|
||||||
return object_member_impl(object, key, detail::priority_tag<1> {});
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON_PRIVATE_UNLESS_TESTED:
|
JSON_PRIVATE_UNLESS_TESTED:
|
||||||
json_pointer top() const
|
json_pointer top() const
|
||||||
{
|
{
|
||||||
@@ -23113,103 +23070,48 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
@brief the values along the pointer last resolved by @ref resolve_plain
|
|
||||||
|
|
||||||
A patch from @ref basic_json::diff lists its operations in document order,
|
|
||||||
so consecutive pointers share most of their reference tokens; the values
|
|
||||||
along the previous pointer are kept to resume from the common prefix.
|
|
||||||
`steps[k]` is the value after the first `k` reference tokens together with
|
|
||||||
the length of the prefix of `*path` that holds them. A value stays valid as
|
|
||||||
long as no operation changes its container: callers keep only values up to
|
|
||||||
the parent of the pointer they modify, or call `reset()`.
|
|
||||||
*/
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
struct plain_steps
|
|
||||||
{
|
|
||||||
const string_t* path = nullptr;
|
|
||||||
std::vector<std::pair<std::size_t, BasicJsonType*>> steps{};
|
|
||||||
|
|
||||||
void reset() noexcept
|
|
||||||
{
|
|
||||||
path = nullptr;
|
|
||||||
steps.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief resolve a plain JSON pointer without building its reference tokens
|
@brief resolve a plain JSON pointer without building its reference tokens
|
||||||
|
|
||||||
@param[in] root the value the pointer starts at
|
@param[in] ptr the value the pointer starts at
|
||||||
@param[in] path a JSON pointer string
|
@param[in] path a JSON pointer string
|
||||||
@param[in] length the number of leading bytes of @a path to resolve; 0 or
|
@param[in] length the number of leading bytes of @a path to resolve; 0 or
|
||||||
the position of a slash
|
the position of a slash
|
||||||
@param[in,out] token a string to hold the reference tokens, to reuse its storage
|
@param[in,out] token a string to hold the reference tokens, to reuse its storage
|
||||||
@param[in,out] cache the values along the previously resolved pointer;
|
|
||||||
@a path must outlive the next call
|
|
||||||
|
|
||||||
@return the pointed to value, or `nullptr` if the pointer does not denote
|
@return the pointed to value, or `nullptr` if the pointer does not denote
|
||||||
an existing value, or does so in a way that is not plain: an array
|
an existing value, or does so in a way that is not plain: a
|
||||||
index that is malformed or out of range, or a "-", or a reference
|
reference token with an escape, an array index that is malformed or
|
||||||
token with an escape. No exception is
|
out of range, or a "-". No exception is thrown for these; callers
|
||||||
thrown for these; callers use @ref json_pointer instead, which
|
use @ref json_pointer instead, which handles them.
|
||||||
handles them.
|
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static BasicJsonType* resolve_plain(BasicJsonType* root, const string_t& path, const std::size_t length, string_t& token, plain_steps<BasicJsonType>& cache)
|
static BasicJsonType* resolve_plain(BasicJsonType* ptr, const string_t& path, const std::size_t length, string_t& token)
|
||||||
{
|
{
|
||||||
auto& steps = cache.steps;
|
for (std::size_t pos = 0; pos < length;)
|
||||||
|
|
||||||
// resume from the deepest step that is also a prefix of this path
|
|
||||||
std::size_t depth = 1;
|
|
||||||
if (cache.path != nullptr && !steps.empty() && steps[0].second == root)
|
|
||||||
{
|
|
||||||
const string_t& previous = *cache.path;
|
|
||||||
for (depth = steps.size(); depth > 1; --depth)
|
|
||||||
{
|
|
||||||
// a step ends at a token boundary of the previous path
|
|
||||||
const std::size_t prefix = steps[depth - 1].first;
|
|
||||||
if (prefix <= length && (prefix == length || path[prefix] == '/') && std::memcmp(path.data(), previous.data(), prefix) == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
steps.resize(depth);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
steps.assign(1, std::make_pair(std::size_t{0}, root));
|
|
||||||
}
|
|
||||||
cache.path = &path;
|
|
||||||
|
|
||||||
BasicJsonType* ptr = steps[depth - 1].second;
|
|
||||||
for (std::size_t pos = steps[depth - 1].first; pos < length;)
|
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(path[pos] != '/'))
|
if (JSON_HEDLEY_UNLIKELY(path[pos] != '/'))
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the reference token between the slash at pos and the next one,
|
// the reference token between the slash at pos and the next one
|
||||||
// which must not have an escape
|
std::size_t end = path.find_first_of('/', pos + 1);
|
||||||
const char* const begin = path.data() + pos + 1;
|
if (end == string_t::npos || end > length)
|
||||||
const char* const limit = path.data() + length;
|
|
||||||
const char* end = begin;
|
|
||||||
while (end < limit && *end != '/')
|
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(*end == '~'))
|
end = length;
|
||||||
{
|
}
|
||||||
return nullptr;
|
token.clear();
|
||||||
}
|
token.append(path.data() + pos + 1, end - pos - 1);
|
||||||
++end;
|
if (JSON_HEDLEY_UNLIKELY(token.find_first_of('~') != string_t::npos))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ptr->type())
|
switch (ptr->type())
|
||||||
{
|
{
|
||||||
case detail::value_t::object:
|
case detail::value_t::object:
|
||||||
{
|
{
|
||||||
token.clear();
|
|
||||||
token.append(begin, static_cast<std::size_t>(end - begin));
|
|
||||||
const auto it = ptr->m_data.m_value.object->find(token);
|
const auto it = ptr->m_data.m_value.object->find(token);
|
||||||
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
if (JSON_HEDLEY_UNLIKELY(it == ptr->m_data.m_value.object->end()))
|
||||||
{
|
{
|
||||||
@@ -23221,24 +23123,8 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
// an index of up to 18 digits without a leading zero fits into size_type
|
typename BasicJsonType::size_type idx{};
|
||||||
// (9 digits for a 32-bit size_type); everything else is left to json_pointer
|
if (JSON_HEDLEY_UNLIKELY(parse_array_index<BasicJsonType>(token, idx) != array_index_status::ok || idx >= ptr->m_data.m_value.array->size()))
|
||||||
constexpr std::size_t max_digits = sizeof(typename BasicJsonType::size_type) >= 8 ? 18 : 9;
|
|
||||||
const std::size_t digits = static_cast<std::size_t>(end - begin);
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(digits == 0 || digits > max_digits || (digits > 1 && *begin == '0')))
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
typename BasicJsonType::size_type idx = 0;
|
|
||||||
for (const char* c = begin; c != end; ++c)
|
|
||||||
{
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(*c < '0' || *c > '9'))
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
idx = static_cast<typename BasicJsonType::size_type>((idx * 10) + static_cast<typename BasicJsonType::size_type>(*c - '0'));
|
|
||||||
}
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(idx >= ptr->m_data.m_value.array->size()))
|
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -23257,8 +23143,7 @@ class json_pointer
|
|||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
pos = static_cast<std::size_t>(end - path.data());
|
pos = end;
|
||||||
steps.emplace_back(pos, ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
@@ -23372,7 +23257,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat(
|
||||||
@@ -23443,7 +23328,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" cannot be used for const access
|
// "-" cannot be used for const access
|
||||||
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
JSON_THROW(detail::out_of_range::create(detail::exception_id::pointer_past_the_end_index, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
|
||||||
@@ -23505,7 +23390,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -23582,7 +23467,7 @@ class json_pointer
|
|||||||
|
|
||||||
case detail::value_t::array:
|
case detail::value_t::array:
|
||||||
{
|
{
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_dash(reference_token)))
|
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
|
||||||
{
|
{
|
||||||
// "-" always fails the range check
|
// "-" always fails the range check
|
||||||
return false;
|
return false;
|
||||||
@@ -23741,52 +23626,6 @@ class json_pointer
|
|||||||
obj[key] = std::forward<ValueType>(v);
|
obj[key] = std::forward<ValueType>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// the number of entries that flattening @a value adds: its primitive
|
|
||||||
/// values and empty containers
|
|
||||||
template<typename BasicJsonType>
|
|
||||||
static std::size_t count_flattened(const BasicJsonType& value)
|
|
||||||
{
|
|
||||||
std::size_t count = 0;
|
|
||||||
std::vector<const BasicJsonType*> pending(1, &value);
|
|
||||||
while (!pending.empty())
|
|
||||||
{
|
|
||||||
const BasicJsonType* const v = pending.back();
|
|
||||||
pending.pop_back();
|
|
||||||
if (v->is_array() && !v->m_data.m_value.array->empty())
|
|
||||||
{
|
|
||||||
for (const auto& element : *v->m_data.m_value.array)
|
|
||||||
{
|
|
||||||
pending.push_back(&element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (v->is_object() && !v->m_data.m_value.object->empty())
|
|
||||||
{
|
|
||||||
for (const auto& member : *v->m_data.m_value.object)
|
|
||||||
{
|
|
||||||
pending.push_back(&member.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve the storage for @a value of objects that have a reserve(), so
|
|
||||||
// that it does not have to be moved while it grows
|
|
||||||
template<typename ObjectType, typename BasicJsonType>
|
|
||||||
static auto reserve_flattened(ObjectType& obj, const BasicJsonType& value, detail::priority_tag<1> /*unused*/)
|
|
||||||
-> decltype(obj.reserve(std::size_t {}), void())
|
|
||||||
{
|
|
||||||
obj.reserve(count_flattened(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ObjectType, typename BasicJsonType>
|
|
||||||
static void reserve_flattened(ObjectType& /*obj*/, const BasicJsonType& /*value*/, detail::priority_tag<0> /*unused*/)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// append the decimal representation of @a i to @a out
|
/// append the decimal representation of @a i to @a out
|
||||||
static void append_index(string_t& out, std::size_t i)
|
static void append_index(string_t& out, std::size_t i)
|
||||||
{
|
{
|
||||||
@@ -23896,7 +23735,6 @@ class json_pointer
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reserve_flattened(object, value, detail::priority_tag<1> {});
|
|
||||||
enter(value);
|
enter(value);
|
||||||
while (!stack.empty())
|
while (!stack.empty())
|
||||||
{
|
{
|
||||||
@@ -23904,9 +23742,8 @@ class json_pointer
|
|||||||
// frame, which would invalidate a reference to it
|
// frame, which would invalidate a reference to it
|
||||||
const BasicJsonType* const container = stack.back().container;
|
const BasicJsonType* const container = stack.back().container;
|
||||||
|
|
||||||
// the path of the previous child is replaced by the path of the
|
// drop the path of the previous child
|
||||||
// next one; for an array element in one resize
|
path.resize(stack.back().path_length);
|
||||||
const std::size_t path_length = stack.back().path_length;
|
|
||||||
|
|
||||||
if (container->is_array())
|
if (container->is_array())
|
||||||
{
|
{
|
||||||
@@ -23920,20 +23757,8 @@ class json_pointer
|
|||||||
|
|
||||||
// iterate array and use index as a reference string
|
// iterate array and use index as a reference string
|
||||||
++stack.back().index;
|
++stack.back().index;
|
||||||
std::array<char, 24> digits{};
|
path.push_back('/');
|
||||||
std::size_t pos = digits.size();
|
append_index(path, i);
|
||||||
std::size_t rest = i;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
digits[--pos] = static_cast<char>('0' + rest % 10);
|
|
||||||
rest /= 10;
|
|
||||||
}
|
|
||||||
while (rest != 0);
|
|
||||||
const std::size_t count = digits.size() - pos;
|
|
||||||
path.resize(path_length + 1 + count);
|
|
||||||
char* const out = &path[path_length];
|
|
||||||
out[0] = '/';
|
|
||||||
std::memcpy(out + 1, digits.data() + pos, count);
|
|
||||||
enter(array[i]);
|
enter(array[i]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -23947,7 +23772,6 @@ class json_pointer
|
|||||||
|
|
||||||
// iterate object and use keys as reference string
|
// iterate object and use keys as reference string
|
||||||
++stack.back().member;
|
++stack.back().member;
|
||||||
path.resize(path_length);
|
|
||||||
path.push_back('/');
|
path.push_back('/');
|
||||||
detail::escape_into(path, it->first);
|
detail::escape_into(path, it->first);
|
||||||
enter(it->second);
|
enter(it->second);
|
||||||
@@ -24043,28 +23867,8 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
end = key.size();
|
end = key.size();
|
||||||
}
|
}
|
||||||
const std::size_t token_size = end - pos - 1;
|
|
||||||
|
|
||||||
// a token that is a plain index of up to 9 digits, which is
|
|
||||||
// the case for almost all array elements, is not copied
|
|
||||||
std::size_t index = 0;
|
|
||||||
bool plain_index = current->type() == detail::value_t::array && token_size != 0 && token_size <= 9 && (token_size == 1 || data[pos + 1] != '0');
|
|
||||||
for (std::size_t k = pos + 1; plain_index && k < end; ++k)
|
|
||||||
{
|
|
||||||
plain_index = data[k] >= '0' && data[k] <= '9';
|
|
||||||
index = (index * 10) + static_cast<std::size_t>(data[k] - '0');
|
|
||||||
}
|
|
||||||
if (plain_index)
|
|
||||||
{
|
|
||||||
current = ¤t->operator[](static_cast<typename BasicJsonType::size_type>(index));
|
|
||||||
values.push_back(current);
|
|
||||||
ends.push_back(end);
|
|
||||||
pos = end;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(data + pos + 1, token_size);
|
token.append(data + pos + 1, end - pos - 1);
|
||||||
if (token.find_first_of('~') != string_t::npos)
|
if (token.find_first_of('~') != string_t::npos)
|
||||||
{
|
{
|
||||||
detail::unescape(token);
|
detail::unescape(token);
|
||||||
@@ -24074,13 +23878,10 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
case detail::value_t::null:
|
case detail::value_t::null:
|
||||||
{
|
{
|
||||||
// a first child 0 makes the value an array without a look-up
|
if (arrays.contains(data, pos))
|
||||||
if ((token_size == 1 && data[pos + 1] == '0') || arrays.contains(data, pos))
|
|
||||||
{
|
{
|
||||||
// some reference token below this position is 0, so the
|
// some reference token below this position is 0, so the
|
||||||
// value is an array
|
// value is an array
|
||||||
*current = BasicJsonType(detail::value_t::array);
|
|
||||||
reserve_elements(*current->m_data.m_value.array, 2, detail::priority_tag<1> {});
|
|
||||||
current = ¤t->operator[](array_index<BasicJsonType>(token));
|
current = ¤t->operator[](array_index<BasicJsonType>(token));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -39387,72 +39188,6 @@ public:
|
|||||||
/// @name JSON Patch functions
|
/// @name JSON Patch functions
|
||||||
/// @{
|
/// @{
|
||||||
|
|
||||||
private:
|
|
||||||
/// the members of an operation of a JSON patch that are of interest
|
|
||||||
struct patch_members
|
|
||||||
{
|
|
||||||
basic_json* op = nullptr;
|
|
||||||
basic_json* path = nullptr;
|
|
||||||
basic_json* value = nullptr;
|
|
||||||
basic_json* from = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief find the members "op", "path", "value", and "from" of the object @a val in one pass
|
|
||||||
static patch_members find_patch_members(const basic_json& val, std::true_type /*key_type is string_t*/)
|
|
||||||
{
|
|
||||||
patch_members members;
|
|
||||||
for (auto& member : *val.m_data.m_value.object)
|
|
||||||
{
|
|
||||||
const string_t& key = member.first;
|
|
||||||
switch (key.size())
|
|
||||||
{
|
|
||||||
case 2:
|
|
||||||
if (key == "op")
|
|
||||||
{
|
|
||||||
members.op = &member.second;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
if (key == "path")
|
|
||||||
{
|
|
||||||
members.path = &member.second;
|
|
||||||
}
|
|
||||||
else if (key == "from")
|
|
||||||
{
|
|
||||||
members.from = &member.second;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
if (key == "value")
|
|
||||||
{
|
|
||||||
members.value = &member.second;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief find the members by lookup for key types other than string_t
|
|
||||||
static patch_members find_patch_members(const basic_json& val, std::false_type /*key_type is not string_t*/)
|
|
||||||
{
|
|
||||||
const auto find_member = [&val](const char* name) -> basic_json *
|
|
||||||
{
|
|
||||||
const auto it = val.m_data.m_value.object->find(string_t(name));
|
|
||||||
return it != val.m_data.m_value.object->end() ? &it->second : nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
patch_members members;
|
|
||||||
members.op = find_member("op");
|
|
||||||
members.path = find_member("path");
|
|
||||||
members.value = find_member("value");
|
|
||||||
members.from = find_member("from");
|
|
||||||
return members;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @brief applies a JSON patch in-place without copying the object
|
/// @brief applies a JSON patch in-place without copying the object
|
||||||
/// @sa https://json.nlohmann.me/api/basic_json/patch/
|
/// @sa https://json.nlohmann.me/api/basic_json/patch/
|
||||||
void patch_inplace(const basic_json& json_patch)
|
void patch_inplace(const basic_json& json_patch)
|
||||||
@@ -39626,18 +39361,18 @@ public:
|
|||||||
// a diff creates them: apply the operation and return true; return
|
// a diff creates them: apply the operation and return true; return
|
||||||
// false, with nothing changed, for all others
|
// false, with nothing changed, for all others
|
||||||
string_t token;
|
string_t token;
|
||||||
typename json_pointer::template plain_steps<basic_json> steps;
|
const string_t value_key = "value";
|
||||||
const auto try_plain = [&result, &token, &steps](patch_operations operation, const string_t& path, const patch_members & members, const basic_json & val)
|
const auto try_plain = [&result, &token, &value_key](patch_operations operation, const string_t& path, const basic_json & val)
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty() || path.find_first_of('~') != string_t::npos)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// a missing "value" is reported below
|
// a missing "value" is reported below
|
||||||
const basic_json* const value_ptr = members.value;
|
const auto value_it = val.m_data.m_value.object->find(value_key);
|
||||||
const basic_json& value = value_ptr != nullptr ? *value_ptr : val;
|
const basic_json& value = value_it != val.m_data.m_value.object->end() ? value_it->second : val;
|
||||||
if (operation != patch_operations::remove && value_ptr == nullptr)
|
if (operation != patch_operations::remove && value_it == val.m_data.m_value.object->end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -39646,7 +39381,7 @@ public:
|
|||||||
{
|
{
|
||||||
case patch_operations::replace:
|
case patch_operations::replace:
|
||||||
{
|
{
|
||||||
basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token, steps);
|
basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token);
|
||||||
if (target == nullptr)
|
if (target == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -39657,7 +39392,7 @@ public:
|
|||||||
|
|
||||||
case patch_operations::test:
|
case patch_operations::test:
|
||||||
{
|
{
|
||||||
const basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token, steps);
|
const basic_json* const target = json_pointer::resolve_plain(&result, path, path.size(), token);
|
||||||
// a failing test is reported below
|
// a failing test is reported below
|
||||||
return target != nullptr && *target == value;
|
return target != nullptr && *target == value;
|
||||||
}
|
}
|
||||||
@@ -39677,17 +39412,13 @@ public:
|
|||||||
}
|
}
|
||||||
--slash;
|
--slash;
|
||||||
|
|
||||||
basic_json* const parent = json_pointer::resolve_plain(&result, path, slash, token, steps);
|
basic_json* const parent = json_pointer::resolve_plain(&result, path, slash, token);
|
||||||
if (parent == nullptr)
|
if (parent == nullptr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(path.data() + slash + 1, path.size() - slash - 1);
|
token.append(path.data() + slash + 1, path.size() - slash - 1);
|
||||||
if (token.find_first_of('~') != string_t::npos)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (operation == patch_operations::add)
|
if (operation == patch_operations::add)
|
||||||
{
|
{
|
||||||
@@ -39743,32 +39474,31 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// the name of the first member to check
|
|
||||||
const string_t op_member = "op";
|
|
||||||
|
|
||||||
// iterate and apply the operations
|
// iterate and apply the operations
|
||||||
for (const auto& val : json_patch)
|
for (const auto& val : json_patch)
|
||||||
{
|
{
|
||||||
// wrapper to get a value for an operation
|
// wrapper to get a value for an operation
|
||||||
const auto get_value = [&val](basic_json * found,
|
const auto get_value = [&val](const string_t& op,
|
||||||
const string_t& op,
|
const string_t& member,
|
||||||
const char* member,
|
|
||||||
bool string_type) -> basic_json &
|
bool string_type) -> basic_json &
|
||||||
{
|
{
|
||||||
|
// find value
|
||||||
|
auto it = val.m_data.m_value.object->find(member);
|
||||||
|
|
||||||
// check if the desired value is present
|
// check if the desired value is present
|
||||||
if (JSON_HEDLEY_UNLIKELY(found == nullptr))
|
if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
|
||||||
{
|
{
|
||||||
detail::throw_patch_missing_member(op, string_t(member), false, val);
|
detail::throw_patch_missing_member(op, member, false, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the result is of type string
|
// check if the result is of type string
|
||||||
if (JSON_HEDLEY_UNLIKELY(string_type && !found->is_string()))
|
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
|
||||||
{
|
{
|
||||||
detail::throw_patch_missing_member(op, string_t(member), true, val);
|
detail::throw_patch_missing_member(op, member, true, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no error: return value
|
// no error: return value
|
||||||
return *found;
|
return it->second;
|
||||||
};
|
};
|
||||||
|
|
||||||
// type check: every element of the array must be an object
|
// type check: every element of the array must be an object
|
||||||
@@ -39777,30 +39507,26 @@ public:
|
|||||||
detail::throw_patch_not_an_array(val);
|
detail::throw_patch_not_an_array(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch_members members = find_patch_members(val, std::is_same<typename object_t::key_type, string_t> {});
|
|
||||||
|
|
||||||
// collect mandatory members
|
// collect mandatory members
|
||||||
const string_t& op = *get_value(members.op, op_member, "op", true).m_data.m_value.string;
|
const string_t& op = *get_value("op", "op", true).m_data.m_value.string;
|
||||||
const string_t& path = *get_value(members.path, op, "path", true).m_data.m_value.string;
|
const string_t& path = *get_value(op, "path", true).m_data.m_value.string;
|
||||||
const patch_operations operation = get_op(op);
|
const patch_operations operation = get_op(op);
|
||||||
|
|
||||||
// the usual operations on a plain pointer are applied without
|
// the usual operations on a plain pointer are applied without
|
||||||
// building the pointer's reference tokens; anything else (and
|
// building the pointer's reference tokens; anything else (and
|
||||||
// every error) is handled below
|
// every error) is handled below
|
||||||
if (try_plain(operation, path, members, val))
|
if (try_plain(operation, path, val))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the general operations may change any container
|
|
||||||
steps.reset();
|
|
||||||
json_pointer ptr(path);
|
json_pointer ptr(path);
|
||||||
|
|
||||||
switch (operation)
|
switch (operation)
|
||||||
{
|
{
|
||||||
case patch_operations::add:
|
case patch_operations::add:
|
||||||
{
|
{
|
||||||
operation_add(ptr, get_value(members.value, "add", "value", false));
|
operation_add(ptr, get_value("add", "value", false));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39813,13 +39539,13 @@ public:
|
|||||||
case patch_operations::replace:
|
case patch_operations::replace:
|
||||||
{
|
{
|
||||||
// the "path" location must exist - use at()
|
// the "path" location must exist - use at()
|
||||||
result.at(ptr) = get_value(members.value, "replace", "value", false);
|
result.at(ptr) = get_value("replace", "value", false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case patch_operations::move:
|
case patch_operations::move:
|
||||||
{
|
{
|
||||||
const string_t& from_path = *get_value(members.from, "move", "from", true).m_data.m_value.string;
|
const string_t& from_path = *get_value("move", "from", true).m_data.m_value.string;
|
||||||
json_pointer from_ptr(from_path);
|
json_pointer from_ptr(from_path);
|
||||||
|
|
||||||
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
|
if (JSON_HEDLEY_UNLIKELY(is_proper_prefix(from_ptr, ptr)))
|
||||||
@@ -39842,7 +39568,7 @@ public:
|
|||||||
|
|
||||||
case patch_operations::copy:
|
case patch_operations::copy:
|
||||||
{
|
{
|
||||||
const string_t& from_path = *get_value(members.from, "copy", "from", true).m_data.m_value.string;
|
const string_t& from_path = *get_value("copy", "from", true).m_data.m_value.string;
|
||||||
const json_pointer from_ptr(from_path);
|
const json_pointer from_ptr(from_path);
|
||||||
|
|
||||||
// The copy is functionally identical to an "add"
|
// The copy is functionally identical to an "add"
|
||||||
@@ -39860,7 +39586,7 @@ public:
|
|||||||
{
|
{
|
||||||
// check if "value" matches the one at "path"
|
// check if "value" matches the one at "path"
|
||||||
// the "path" location must exist - use at()
|
// the "path" location must exist - use at()
|
||||||
success = (result.at(ptr) == get_value(members.value, "test", "value", false));
|
success = (result.at(ptr) == get_value("test", "value", false));
|
||||||
}
|
}
|
||||||
JSON_INTERNAL_CATCH (out_of_range&)
|
JSON_INTERNAL_CATCH (out_of_range&)
|
||||||
{
|
{
|
||||||
@@ -39952,18 +39678,13 @@ public:
|
|||||||
/// @brief append an operation @a op for @a path to @a result, with @a value if it is not null
|
/// @brief append an operation @a op for @a path to @a result, with @a value if it is not null
|
||||||
static void diff_operation(basic_json& result, const char* op, const string_t& path, const basic_json* value)
|
static void diff_operation(basic_json& result, const char* op, const string_t& path, const basic_json* value)
|
||||||
{
|
{
|
||||||
// the keys are distinct and in the order of std::map; the members are
|
|
||||||
// added without looking them up
|
|
||||||
basic_json entry(value_t::object);
|
basic_json entry(value_t::object);
|
||||||
auto& members = *entry.m_data.m_value.object;
|
entry["op"] = op;
|
||||||
typename object_t::iterator hint = members.end();
|
entry["path"] = path;
|
||||||
json_pointer::add_flattened(members, hint, string_t("op"), string_t(op), detail::priority_tag<2> {});
|
|
||||||
json_pointer::add_flattened(members, hint, string_t("path"), path, detail::priority_tag<2> {});
|
|
||||||
if (value != nullptr)
|
if (value != nullptr)
|
||||||
{
|
{
|
||||||
json_pointer::add_flattened(members, hint, string_t("value"), *value, detail::priority_tag<2> {});
|
entry["value"] = *value;
|
||||||
}
|
}
|
||||||
entry.set_parents();
|
|
||||||
result.push_back(std::move(entry));
|
result.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39985,50 +39706,6 @@ public:
|
|||||||
diff_operation(result, "add", path, &value);
|
diff_operation(result, "add", path, &value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief the values in the target of the members of a source object, for the
|
|
||||||
/// objects of up to 16 members without allocating
|
|
||||||
///
|
|
||||||
/// reserve() must be called with the number of values that are added.
|
|
||||||
class diff_target_values
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
diff_target_values() = default;
|
|
||||||
diff_target_values(const diff_target_values&) = delete;
|
|
||||||
diff_target_values& operator=(const diff_target_values&) = delete;
|
|
||||||
~diff_target_values() = default;
|
|
||||||
|
|
||||||
void reserve(const std::size_t n)
|
|
||||||
{
|
|
||||||
if (n > inline_size)
|
|
||||||
{
|
|
||||||
large.resize(n);
|
|
||||||
items = large.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_back(const basic_json* value)
|
|
||||||
{
|
|
||||||
items[count++] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t size() const noexcept
|
|
||||||
{
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
const basic_json* operator[](const std::size_t i) const noexcept
|
|
||||||
{
|
|
||||||
return items[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr std::size_t inline_size = 16;
|
|
||||||
std::size_t count = 0;
|
|
||||||
const basic_json** items = inline_items;
|
|
||||||
std::vector<const basic_json*> large{}; // NOLINT(readability-redundant-member-init)
|
|
||||||
const basic_json* inline_items[inline_size]; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @brief append "/" and the decimal representation of @a index to @a path
|
/// @brief append "/" and the decimal representation of @a index to @a path
|
||||||
static void diff_append_index(string_t& path, std::size_t index)
|
static void diff_append_index(string_t& path, std::size_t index)
|
||||||
{
|
{
|
||||||
@@ -40109,9 +39786,8 @@ public:
|
|||||||
operations that remove every member of @a source and add every member of
|
operations that remove every member of @a source and add every member of
|
||||||
@a target to @a result, and returns false.
|
@a target to @a result, and returns false.
|
||||||
*/
|
*/
|
||||||
template<typename TargetValues>
|
|
||||||
static bool diff_object_keys(basic_json& result, const basic_json& source, const basic_json& target,
|
static bool diff_object_keys(basic_json& result, const basic_json& source, const basic_json& target,
|
||||||
const string_t& path, TargetValues& target_values,
|
const string_t& path, std::vector<const basic_json*>& target_values,
|
||||||
std::vector<const_iterator>& added)
|
std::vector<const_iterator>& added)
|
||||||
{
|
{
|
||||||
// first pass: one lookup per source key
|
// first pass: one lookup per source key
|
||||||
@@ -40325,7 +40001,7 @@ public:
|
|||||||
|
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
{
|
{
|
||||||
diff_target_values target_values;
|
std::vector<const basic_json*> target_values;
|
||||||
std::vector<const_iterator> added;
|
std::vector<const_iterator> added;
|
||||||
if (diff_object_keys(result, source, target, path, target_values, added))
|
if (diff_object_keys(result, source, target, path, target_values, added))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user