mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 12:10:32 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7130880754 | ||
|
|
76ce7e2c84 | ||
|
|
b992e1f76a | ||
|
|
a7fa8d04e8 | ||
|
|
677507137b | ||
|
|
21d90fac1c | ||
|
|
e842f3a68f |
@@ -67,6 +67,7 @@ cc_library(
|
|||||||
"include/nlohmann/detail/string_utils.hpp",
|
"include/nlohmann/detail/string_utils.hpp",
|
||||||
"include/nlohmann/detail/value_t.hpp",
|
"include/nlohmann/detail/value_t.hpp",
|
||||||
"include/nlohmann/detail/view/builder.hpp",
|
"include/nlohmann/detail/view/builder.hpp",
|
||||||
|
"include/nlohmann/detail/view/compare.hpp",
|
||||||
"include/nlohmann/detail/view/document_data.hpp",
|
"include/nlohmann/detail/view/document_data.hpp",
|
||||||
"include/nlohmann/detail/view/errors.hpp",
|
"include/nlohmann/detail/view/errors.hpp",
|
||||||
"include/nlohmann/detail/view/input.hpp",
|
"include/nlohmann/detail/view/input.hpp",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@ INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::number_token
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator bool', 'Method', 'api/basic_json_view/operator_bool/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator bool', 'Method', 'api/basic_json_view/operator_bool/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator<<', 'Operator', 'api/basic_json_view/operator_ltlt/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator<<', 'Operator', 'api/basic_json_view/operator_ltlt/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator[]', 'Operator', 'api/basic_json_view/operator[]/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator[]', 'Operator', 'api/basic_json_view/operator[]/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator==', 'Operator', 'api/basic_json_view/operator_eq/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::operator!=', 'Operator', 'api/basic_json_view/operator_ne/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::size', 'Method', 'api/basic_json_view/size/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::size', 'Method', 'api/basic_json_view/size/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::source_offset', 'Method', 'api/basic_json_view/source_offset/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::source_offset', 'Method', 'api/basic_json_view/source_offset/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type', 'Method', 'api/basic_json_view/type/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('basic_json_view::type', 'Method', 'api/basic_json_view/type/index.html');
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ Linear.
|
|||||||
|
|
||||||
- [operator!=](operator_ne.md) compare for inequality
|
- [operator!=](operator_ne.md) compare for inequality
|
||||||
- [operator<=>](operator_spaceship.md) comparison: 3-way (C++20)
|
- [operator<=>](operator_spaceship.md) comparison: 3-way (C++20)
|
||||||
|
- [basic_json_view::operator==](../basic_json_view/operator_eq.md) - the same comparison on a zero-copy view, without
|
||||||
|
building a `basic_json` value for it
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,12 @@ Linear.
|
|||||||
--8<-- "examples/operator__notequal__nullptr_t.output"
|
--8<-- "examples/operator__notequal__nullptr_t.output"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [operator==](operator_eq.md) compare for equality
|
||||||
|
- [basic_json_view::operator!=](../basic_json_view/operator_ne.md) - the same comparison on a zero-copy view, without
|
||||||
|
building a `basic_json` value for it
|
||||||
|
|
||||||
## Version history
|
## Version history
|
||||||
|
|
||||||
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
1. Added in version 1.0.0. Added C++20 member functions in version 3.11.0. Changed in version 3.13.0 to remove
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ Moving the document itself does not invalidate its views: the index is heap-allo
|
|||||||
`basic_json_document` object.
|
`basic_json_document` object.
|
||||||
|
|
||||||
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, element
|
`basic_json_view` provides the read-only part of the `BasicJsonType` interface: the type-inspection functions, element
|
||||||
access, lookup, iteration, and conversion -- [`get<T>()`](get.md), [`get_string()`](get_string.md),
|
access, lookup, iteration, conversion, and comparison -- [`get<T>()`](get.md), [`get_string()`](get_string.md),
|
||||||
[`number_token()`](number_token.md), and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a
|
[`number_token()`](number_token.md), and [`materialize()`](materialize.md) to build the `BasicJsonType` value of a
|
||||||
subtree on demand. [`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`](contains.md), and
|
subtree on demand. [`operator[]`](operator%5B%5D.md), [`at`](at.md), [`contains`](contains.md), and
|
||||||
[`value`](value.md) also accept a [`json_pointer`](../json_pointer/index.md). It does not (yet) provide
|
[`value`](value.md) also accept a [`json_pointer`](../json_pointer/index.md). [`operator==`](operator_eq.md) and
|
||||||
comparison.
|
[`operator!=`](operator_ne.md) compare two views, or a view and a `BasicJsonType` value, without ever building a
|
||||||
|
`BasicJsonType` value for a view; no ordering comparison (`#!cpp operator<`) is provided.
|
||||||
|
|
||||||
## Template parameters
|
## Template parameters
|
||||||
|
|
||||||
@@ -107,6 +108,11 @@ comparison.
|
|||||||
- [**number_token**](number_token.md) - get a number's token text without a copy
|
- [**number_token**](number_token.md) - get a number's token text without a copy
|
||||||
- [**materialize**](materialize.md) - build the `BasicJsonType` value of this subtree
|
- [**materialize**](materialize.md) - build the `BasicJsonType` value of this subtree
|
||||||
|
|
||||||
|
### Comparison
|
||||||
|
|
||||||
|
- [**operator==**](operator_eq.md) - comparison: equal
|
||||||
|
- [**operator!=**](operator_ne.md) - comparison: not equal
|
||||||
|
|
||||||
### Serialization
|
### Serialization
|
||||||
|
|
||||||
- [**dump**](dump.md) - serialize to a JSON-formatted string
|
- [**dump**](dump.md) - serialize to a JSON-formatted string
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>operator==
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// (1)
|
||||||
|
bool operator==(const basic_json_view& lhs, const basic_json_view& rhs);
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
bool operator==(const basic_json_view& lhs, const BasicJsonType& rhs);
|
||||||
|
bool operator==(const BasicJsonType& lhs, const basic_json_view& rhs);
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Compares two views for equality: whether the values [`BasicJsonType::parse()`](../basic_json/parse.md) would
|
||||||
|
produce for `lhs` and `rhs` are equal, according to `BasicJsonType`'s [`operator==`](../basic_json/operator_eq.md).
|
||||||
|
2. Compares a view and a `BasicJsonType` value for equality, in either order: whether the value `parse()` would
|
||||||
|
produce for the view and the other operand are equal, according to `BasicJsonType`'s
|
||||||
|
[`operator==`](../basic_json/operator_eq.md).
|
||||||
|
|
||||||
|
Neither overload builds a `BasicJsonType` value for a view to do the comparison (see [Notes](#notes) below). Numbers
|
||||||
|
compare by value across their types (`#!cpp 1 == 1.0`), and an object compares by its members, with duplicate keys
|
||||||
|
resolved exactly as `parse()` resolves them -- the last value, at the position of the first occurrence of the key.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`lhs` (in)
|
||||||
|
: first value to consider
|
||||||
|
|
||||||
|
`rhs` (in)
|
||||||
|
: second value to consider
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
whether the values `lhs` and `rhs` are equal
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to either operand, or to the document(s) a
|
||||||
|
view refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
May throw `#!cpp std::bad_alloc`. Unlike the other comparison and most other `basic_json_view` functions,
|
||||||
|
`operator==` is not `#!cpp noexcept`: resolving an object's members needs a temporary array to sort them by key (see
|
||||||
|
[Complexity](#complexity) below), and that allocation can fail.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Linear in the size of the compared values: every number, string, array element, and object member is visited at most
|
||||||
|
once, and the walk is iterative, so the nesting depth it can compare is limited by available memory only, not by the
|
||||||
|
call stack (as for [`materialize()`](materialize.md)). Resolving an object's members takes an additional O(n log n)
|
||||||
|
in the number of members at that level, since they are sorted by key to detect and resolve duplicates before being
|
||||||
|
compared. Two arrays of different [`size()`](size.md) are rejected without visiting either one's elements.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Only a single number, boolean, or `#!cpp null` value is ever materialized into a `BasicJsonType`, to reuse its
|
||||||
|
`operator==` -- for numbers, so that values written differently in the source text but equal in value (e.g. an
|
||||||
|
integer and a floating-point literal) still compare equal, following the same rules `BasicJsonType` does for special
|
||||||
|
values such as `#!cpp NaN`. Constructing one of these scalars never allocates. Strings are compared directly, without
|
||||||
|
allocating, either from the source text on both sides or, for overload 2, against `BasicJsonType`'s own string.
|
||||||
|
Arrays and objects are never materialized at all; only their elements or members are visited, one pair at a time.
|
||||||
|
|
||||||
|
!!! info "How objects are compared"
|
||||||
|
|
||||||
|
For a [`json_view`](../json_view.md) (`BasicJsonType::object_t` is `#!cpp std::map`), members are compared by
|
||||||
|
key, regardless of the order they appear in the source text. For an
|
||||||
|
[`ordered_json_view`](../ordered_json_view.md) (`object_t` is `ordered_map`), they are compared in the order
|
||||||
|
they occur, so the very same two objects with their members reordered can compare equal as `json_view`s but not
|
||||||
|
as `ordered_json_view`s. This is exactly how [`json`](../json.md) and [`ordered_json`](../ordered_json.md)
|
||||||
|
compare, see ["Comparing different `basic_json` specializations"](../basic_json/operator_eq.md#notes).
|
||||||
|
|
||||||
|
!!! info "Discarded views"
|
||||||
|
|
||||||
|
A [discarded](is_discarded.md) view compares the same way a discarded `BasicJsonType` value does, which is
|
||||||
|
governed by
|
||||||
|
[`JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON`](../macros/json_use_legacy_discarded_value_comparison.md): by
|
||||||
|
default, a discarded view is never equal to anything, not even another discarded view.
|
||||||
|
|
||||||
|
No ordering comparison (`#!cpp operator<`) is provided for `basic_json_view`; [`materialize()`](materialize.md) is
|
||||||
|
the way to get a `BasicJsonType` value that supports it.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below checks whether a newly received configuration differs from the previous one, and whether a
|
||||||
|
received document matches what a test expects -- directly on views, without ever materializing a `BasicJsonType`
|
||||||
|
value for either side.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__operator_eq.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__operator_eq.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [operator!=](operator_ne.md) - compare for inequality
|
||||||
|
- [materialize](materialize.md) - build a `BasicJsonType` value, e.g. to keep comparing after the document is gone
|
||||||
|
- [`BasicJsonType::operator==`](../basic_json/operator_eq.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
# <small>nlohmann::basic_json_view::</small>operator!=
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// (1)
|
||||||
|
bool operator!=(const basic_json_view& lhs, const basic_json_view& rhs);
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
bool operator!=(const basic_json_view& lhs, const BasicJsonType& rhs);
|
||||||
|
bool operator!=(const BasicJsonType& lhs, const basic_json_view& rhs);
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Compares two views for inequality. Returns `#!cpp !(lhs == rhs)`, see [operator==](operator_eq.md).
|
||||||
|
2. Compares a view and a `BasicJsonType` value for inequality, in either order. Returns `#!cpp !(lhs == rhs)` (or,
|
||||||
|
for the reversed order, `#!cpp !(rhs == lhs)`), see [operator==](operator_eq.md).
|
||||||
|
|
||||||
|
Since `operator!=` is defined as the negation of [`operator==`](operator_eq.md), it follows the same rules for
|
||||||
|
special cases: for instance, since a [discarded](is_discarded.md) view is never equal to anything by default (see
|
||||||
|
[operator=='s Notes](operator_eq.md#notes)), it is never *unequal* to anything either -- `#!cpp discarded != discarded`
|
||||||
|
is also `#!cpp false`, exactly as for a discarded `BasicJsonType` value.
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
`lhs` (in)
|
||||||
|
: first value to consider
|
||||||
|
|
||||||
|
`rhs` (in)
|
||||||
|
: second value to consider
|
||||||
|
|
||||||
|
## Return value
|
||||||
|
|
||||||
|
whether the values `lhs` and `rhs` are not equal
|
||||||
|
|
||||||
|
## Exception safety
|
||||||
|
|
||||||
|
Strong exception safety: if an exception is thrown, there are no changes to either operand, or to the document(s) a
|
||||||
|
view refers to.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
May throw `#!cpp std::bad_alloc`, propagated from [`operator==`](operator_eq.md#exceptions). Unlike most other
|
||||||
|
`basic_json_view` functions, `operator!=` is not `#!cpp noexcept`.
|
||||||
|
|
||||||
|
## Complexity
|
||||||
|
|
||||||
|
Linear, as [`operator==`](operator_eq.md#complexity).
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
See the [Notes](operator_eq.md#notes) of `operator==` -- in particular for how an object's members are compared
|
||||||
|
(order matters for [`ordered_json_view`](../ordered_json_view.md) but not for [`json_view`](../json_view.md)) and
|
||||||
|
for how discarded views compare.
|
||||||
|
|
||||||
|
No ordering comparison (`#!cpp operator<`) is provided for `basic_json_view`; [`materialize()`](materialize.md) is
|
||||||
|
the way to get a `BasicJsonType` value that supports it.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
??? example
|
||||||
|
|
||||||
|
The example below asserts, as a test would, that a received document differs from an unwanted value, and shows
|
||||||
|
that -- as for [`json`](../json.md)/[`ordered_json`](../ordered_json.md) -- reordering an object's members is
|
||||||
|
detected as a difference for an `ordered_json_view` but not for a `json_view`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
--8<-- "examples/basic_json_view__operator_ne.cpp"
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
--8<-- "examples/basic_json_view__operator_ne.output"
|
||||||
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
|
||||||
|
- [operator==](operator_eq.md) - compare for equality
|
||||||
|
- [materialize](materialize.md) - build a `BasicJsonType` value, e.g. to keep comparing after the document is gone
|
||||||
|
- [`BasicJsonType::operator!=`](../basic_json/operator_ne.md) - the corresponding function of `basic_json`
|
||||||
|
|
||||||
|
## Version history
|
||||||
|
|
||||||
|
- Added in version 3.13.0.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// two snapshots of a polled configuration endpoint -- compare them
|
||||||
|
// directly as views, without ever building a nlohmann::json value for
|
||||||
|
// either one
|
||||||
|
const json_document previous = json_document::parse(
|
||||||
|
R"({"name": "cache", "port": 6379, "timeout": 30})");
|
||||||
|
const json_document current = json_document::parse(
|
||||||
|
R"({"port": 6379.0, "timeout": 30, "name": "cache"})");
|
||||||
|
|
||||||
|
// same members, reordered, and 6379 written as a float -- operator==
|
||||||
|
// treats them the same way BasicJsonType::operator== would
|
||||||
|
std::cout << std::boolalpha << (previous.root() == current.root()) << '\n';
|
||||||
|
|
||||||
|
// an actually changed value is detected the same way
|
||||||
|
const json_document changed = json_document::parse(
|
||||||
|
R"({"name": "cache", "port": 6380, "timeout": 30})");
|
||||||
|
std::cout << (previous.root() == changed.root()) << '\n';
|
||||||
|
|
||||||
|
// comparing a view directly against an expected json value -- handy in a
|
||||||
|
// test, without materializing the received document at all
|
||||||
|
const json expected = {{"name", "cache"}, {"port", 6379}, {"timeout", 30}};
|
||||||
|
std::cout << (previous.root() == expected) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
true
|
||||||
|
false
|
||||||
|
true
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <nlohmann/json_view.hpp>
|
||||||
|
|
||||||
|
using json_document = nlohmann::json_document;
|
||||||
|
using ordered_json_document = nlohmann::ordered_json_document;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// assert, as a test would, that a received document differs from an
|
||||||
|
// unwanted shape -- without ever materializing it into a json value just
|
||||||
|
// to compare
|
||||||
|
const json_document received = json_document::parse(
|
||||||
|
R"({"status": "ok", "code": 200})");
|
||||||
|
const json unwanted = {{"status", "error"}, {"code", 500}};
|
||||||
|
std::cout << std::boolalpha << (received.root() != unwanted) << '\n';
|
||||||
|
|
||||||
|
// json (std::map) compares object members regardless of order ...
|
||||||
|
const json_document a = json_document::parse(R"({"a": 1, "b": 2})");
|
||||||
|
const json_document b = json_document::parse(R"({"b": 2, "a": 1})");
|
||||||
|
std::cout << (a.root() != b.root()) << '\n';
|
||||||
|
|
||||||
|
// ... but ordered_json (ordered_map) compares them in the order they
|
||||||
|
// appear, so the very same reordering is detected as a difference
|
||||||
|
const ordered_json_document oa = ordered_json_document::parse(R"({"a": 1, "b": 2})");
|
||||||
|
const ordered_json_document ob = ordered_json_document::parse(R"({"b": 2, "a": 1})");
|
||||||
|
std::cout << (oa.root() != ob.root()) << '\n';
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
true
|
||||||
|
false
|
||||||
|
true
|
||||||
@@ -139,7 +139,11 @@ whenever any of the other conditions above was not met.
|
|||||||
element access and lookup functions never carry the JSON Pointer path `JSON_DIAGNOSTICS` would otherwise add: the
|
element access and lookup functions never carry the JSON Pointer path `JSON_DIAGNOSTICS` would otherwise add: the
|
||||||
view has no `basic_json` value to point at, so the exception is created without one, regardless of how
|
view has no `basic_json` value to point at, so the exception is created without one, regardless of how
|
||||||
`BasicJsonType` was built.
|
`BasicJsonType` was built.
|
||||||
- **Comparison is not (yet) provided** by `basic_json_view`. For now,
|
- **Ordering comparisons are not provided** by `basic_json_view` -- there is no `#!cpp operator<`.
|
||||||
|
[`operator==`](../api/basic_json_view/operator_eq.md) and [`operator!=`](../api/basic_json_view/operator_ne.md) are
|
||||||
|
provided, though: two views, or a view and a `BasicJsonType` value, compare equal exactly when
|
||||||
|
[`materialize()`](../api/basic_json_view/materialize.md) or [`parse()`](../api/basic_json/parse.md) would produce
|
||||||
|
equal values for them, without ever building a tree to do it. For ordering, too,
|
||||||
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
[`materialize()`](../api/basic_json_view/materialize.md) is the way to get a value you can compare.
|
||||||
|
|
||||||
## Getting values out without copying
|
## Getting values out without copying
|
||||||
|
|||||||
@@ -281,6 +281,8 @@ nav:
|
|||||||
- 'operator bool': api/basic_json_view/operator_bool.md
|
- 'operator bool': api/basic_json_view/operator_bool.md
|
||||||
- 'operator<<': api/basic_json_view/operator_ltlt.md
|
- 'operator<<': api/basic_json_view/operator_ltlt.md
|
||||||
- 'operator[]': api/basic_json_view/operator[].md
|
- 'operator[]': api/basic_json_view/operator[].md
|
||||||
|
- 'operator==': api/basic_json_view/operator_eq.md
|
||||||
|
- 'operator!=': api/basic_json_view/operator_ne.md
|
||||||
- 'size': api/basic_json_view/size.md
|
- 'size': api/basic_json_view/size.md
|
||||||
- 'source_offset': api/basic_json_view/source_offset.md
|
- 'source_offset': api/basic_json_view/source_offset.md
|
||||||
- 'type': api/basic_json_view/type.md
|
- 'type': api/basic_json_view/type.md
|
||||||
|
|||||||
@@ -0,0 +1,317 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm> // sort, stable_sort
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <string> // string
|
||||||
|
#include <utility> // move, pair
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
// Equality of views, and of views with basic_json values, with the semantics
|
||||||
|
// of basic_json's operator== applied to the values parse() would produce:
|
||||||
|
// numbers compare by value across their types, an object is compared by its
|
||||||
|
// members with duplicate keys resolved as parse() resolves them (the last
|
||||||
|
// value, at the position of the first occurrence), and in document order if
|
||||||
|
// the object type keeps an order (ordered_json), by key otherwise.
|
||||||
|
|
||||||
|
/// one side of a comparison: a view
|
||||||
|
template<typename BasicJsonType, typename View>
|
||||||
|
class view_side
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_view_t = typename View::string_view_t;
|
||||||
|
|
||||||
|
explicit view_side(const View& v) noexcept
|
||||||
|
: m_view(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
value_t type() const noexcept
|
||||||
|
{
|
||||||
|
return m_view.type();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept
|
||||||
|
{
|
||||||
|
return m_view.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
string_view_t string() const
|
||||||
|
{
|
||||||
|
return m_view.get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a number, boolean, or null as a basic_json value (no allocation)
|
||||||
|
BasicJsonType scalar() const
|
||||||
|
{
|
||||||
|
switch (m_view.type())
|
||||||
|
{
|
||||||
|
case value_t::number_integer:
|
||||||
|
return BasicJsonType(m_view.template get<typename BasicJsonType::number_integer_t>());
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
return BasicJsonType(m_view.template get<typename BasicJsonType::number_unsigned_t>());
|
||||||
|
case value_t::number_float:
|
||||||
|
return BasicJsonType(m_view.template get<typename BasicJsonType::number_float_t>());
|
||||||
|
case value_t::boolean:
|
||||||
|
return BasicJsonType(m_view.template get<bool>());
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
return BasicJsonType(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void elements(std::vector<view_side>& out) const
|
||||||
|
{
|
||||||
|
out.reserve(m_view.size());
|
||||||
|
for (const View e : m_view)
|
||||||
|
{
|
||||||
|
out.emplace_back(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the members as parse() keeps them: one per key, the last value at the
|
||||||
|
/// position of the first occurrence; in that order, or sorted by key
|
||||||
|
void members(std::vector<std::pair<string_view_t, view_side>>& out, bool ordered) const
|
||||||
|
{
|
||||||
|
struct member
|
||||||
|
{
|
||||||
|
string_view_t key;
|
||||||
|
View value;
|
||||||
|
std::size_t position;
|
||||||
|
};
|
||||||
|
std::vector<member> all;
|
||||||
|
all.reserve(m_view.size());
|
||||||
|
std::size_t position = 0;
|
||||||
|
for (auto it = m_view.begin(); it != m_view.end(); ++it)
|
||||||
|
{
|
||||||
|
all.push_back(member{it.key(), it.value(), position++});
|
||||||
|
}
|
||||||
|
std::stable_sort(all.begin(), all.end(), [](const member & a, const member & b)
|
||||||
|
{
|
||||||
|
return a.key < b.key;
|
||||||
|
});
|
||||||
|
std::vector<member> unique;
|
||||||
|
unique.reserve(all.size());
|
||||||
|
for (std::size_t i = 0; i < all.size();)
|
||||||
|
{
|
||||||
|
std::size_t last = i;
|
||||||
|
while (last + 1 < all.size() && all[last + 1].key == all[i].key)
|
||||||
|
{
|
||||||
|
++last;
|
||||||
|
}
|
||||||
|
unique.push_back(member{all[i].key, all[last].value, all[i].position});
|
||||||
|
i = last + 1;
|
||||||
|
}
|
||||||
|
if (ordered)
|
||||||
|
{
|
||||||
|
std::sort(unique.begin(), unique.end(), [](const member & a, const member & b)
|
||||||
|
{
|
||||||
|
return a.position < b.position;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
out.reserve(unique.size());
|
||||||
|
for (const member& m : unique)
|
||||||
|
{
|
||||||
|
out.emplace_back(m.key, view_side(m.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
View m_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the other side of a comparison: a basic_json value
|
||||||
|
template<typename BasicJsonType, typename StringView>
|
||||||
|
class json_side
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_view_t = StringView;
|
||||||
|
|
||||||
|
explicit json_side(const BasicJsonType& j) noexcept
|
||||||
|
: m_json(&j)
|
||||||
|
{}
|
||||||
|
|
||||||
|
value_t type() const noexcept
|
||||||
|
{
|
||||||
|
return m_json->type();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept
|
||||||
|
{
|
||||||
|
return m_json->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
string_view_t string() const
|
||||||
|
{
|
||||||
|
const auto& s = m_json->template get_ref<const typename BasicJsonType::string_t&>();
|
||||||
|
return string_view_t(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicJsonType scalar() const
|
||||||
|
{
|
||||||
|
return *m_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void elements(std::vector<json_side>& out) const
|
||||||
|
{
|
||||||
|
out.reserve(m_json->size());
|
||||||
|
for (const auto& e : *m_json)
|
||||||
|
{
|
||||||
|
out.emplace_back(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void members(std::vector<std::pair<string_view_t, json_side>>& out, bool ordered) const
|
||||||
|
{
|
||||||
|
out.reserve(m_json->size());
|
||||||
|
for (auto it = m_json->cbegin(); it != m_json->cend(); ++it)
|
||||||
|
{
|
||||||
|
out.emplace_back(string_view_t(it.key().data(), it.key().size()), json_side(it.value()));
|
||||||
|
}
|
||||||
|
if (!ordered)
|
||||||
|
{
|
||||||
|
std::sort(out.begin(), out.end(), [](const std::pair<string_view_t, json_side>& a, const std::pair<string_view_t, json_side>& b)
|
||||||
|
{
|
||||||
|
return a.first < b.first;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BasicJsonType* m_json;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// whether two sides are equal; iterative, so that the nesting depth is
|
||||||
|
/// limited by memory only
|
||||||
|
template<typename BasicJsonType, typename A, typename B>
|
||||||
|
bool equal(const A& a0, const B& b0)
|
||||||
|
{
|
||||||
|
using string_view_t = typename A::string_view_t;
|
||||||
|
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
||||||
|
|
||||||
|
struct frame
|
||||||
|
{
|
||||||
|
std::vector<A> elements_a{};
|
||||||
|
std::vector<B> elements_b{};
|
||||||
|
std::vector<std::pair<string_view_t, A>> members_a{};
|
||||||
|
std::vector<std::pair<string_view_t, B>> members_b{};
|
||||||
|
bool object = false;
|
||||||
|
std::size_t next = 0;
|
||||||
|
};
|
||||||
|
std::vector<frame> stack;
|
||||||
|
A a = a0;
|
||||||
|
B b = b0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const value_t ta = a.type();
|
||||||
|
const value_t tb = b.type();
|
||||||
|
const bool numbers = (ta == value_t::number_integer || ta == value_t::number_unsigned || ta == value_t::number_float)
|
||||||
|
&& (tb == value_t::number_integer || tb == value_t::number_unsigned || tb == value_t::number_float);
|
||||||
|
if (ta == value_t::discarded || tb == value_t::discarded)
|
||||||
|
{
|
||||||
|
// basic_json decides (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||||
|
if (ta != tb || !(BasicJsonType(value_t::discarded) == BasicJsonType(value_t::discarded)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!numbers && ta != tb)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ta == value_t::string)
|
||||||
|
{
|
||||||
|
if (!(a.string() == b.string()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ta == value_t::array || ta == value_t::object)
|
||||||
|
{
|
||||||
|
if (a.size() != b.size() && ta == value_t::array)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
frame f;
|
||||||
|
f.object = ta == value_t::object;
|
||||||
|
if (f.object)
|
||||||
|
{
|
||||||
|
a.members(f.members_a, ordered);
|
||||||
|
b.members(f.members_b, ordered);
|
||||||
|
if (f.members_a.size() != f.members_b.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a.elements(f.elements_a);
|
||||||
|
b.elements(f.elements_b);
|
||||||
|
}
|
||||||
|
stack.push_back(std::move(f));
|
||||||
|
}
|
||||||
|
else if (!(a.scalar() == b.scalar())) // numbers (also of different types), null, boolean
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the next pair of values
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (stack.empty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
frame& f = stack.back();
|
||||||
|
const std::size_t count = f.object ? f.members_a.size() : f.elements_a.size();
|
||||||
|
if (f.next == count)
|
||||||
|
{
|
||||||
|
stack.pop_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (f.object)
|
||||||
|
{
|
||||||
|
if (!(f.members_a[f.next].first == f.members_b[f.next].first))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a = f.members_a[f.next].second;
|
||||||
|
b = f.members_b[f.next].second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a = f.elements_a[f.next];
|
||||||
|
b = f.elements_b[f.next];
|
||||||
|
}
|
||||||
|
++f.next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/detail/view/builder.hpp>
|
#include <nlohmann/detail/view/builder.hpp>
|
||||||
|
#include <nlohmann/detail/view/compare.hpp>
|
||||||
#include <nlohmann/detail/view/document_data.hpp>
|
#include <nlohmann/detail/view/document_data.hpp>
|
||||||
#include <nlohmann/detail/view/errors.hpp>
|
#include <nlohmann/detail/view/errors.hpp>
|
||||||
#include <nlohmann/detail/view/input.hpp>
|
#include <nlohmann/detail/view/input.hpp>
|
||||||
@@ -603,6 +604,44 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// comparison //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
/// whether the values parse() would produce for two views are equal, as
|
||||||
|
/// by BasicJsonType's operator== (numbers by value, objects by their
|
||||||
|
/// members with duplicate keys resolved as parse() resolves them)
|
||||||
|
friend bool operator==(const basic_json_view& a, const basic_json_view& b)
|
||||||
|
{
|
||||||
|
return detail::view::equal<BasicJsonType>(side(a), side(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const basic_json_view& a, const basic_json_view& b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether the value parse() would produce for a view equals a value
|
||||||
|
friend bool operator==(const basic_json_view& a, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
return detail::view::equal<BasicJsonType>(side(a), json_side_t(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const BasicJsonType& j, const basic_json_view& a)
|
||||||
|
{
|
||||||
|
return a == j;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const basic_json_view& a, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
return !(a == j);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const BasicJsonType& j, const basic_json_view& a)
|
||||||
|
{
|
||||||
|
return !(a == j);
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// materialize //
|
// materialize //
|
||||||
/////////////////
|
/////////////////
|
||||||
@@ -635,6 +674,13 @@ class basic_json_view
|
|||||||
: m_doc(d), m_node(n)
|
: m_doc(d), m_node(n)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
using json_side_t = detail::view::json_side<BasicJsonType, string_view_t>;
|
||||||
|
|
||||||
|
static detail::view::view_side<BasicJsonType, basic_json_view> side(const basic_json_view& v) noexcept
|
||||||
|
{
|
||||||
|
return detail::view::view_side<BasicJsonType, basic_json_view>(v);
|
||||||
|
}
|
||||||
|
|
||||||
/// the number of source bytes of this value (estimated for values with
|
/// the number of source bytes of this value (estimated for values with
|
||||||
/// decoded strings)
|
/// decoded strings)
|
||||||
std::size_t source_extent() const noexcept
|
std::size_t source_extent() const noexcept
|
||||||
|
|||||||
@@ -1580,6 +1580,326 @@ inline bool build(document_data& d, const char* src, std::size_t size, bool comm
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
NLOHMANN_JSON_NAMESPACE_END
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/view/compare.hpp>
|
||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <algorithm> // sort, stable_sort
|
||||||
|
#include <cstddef> // size_t
|
||||||
|
#include <string> // string
|
||||||
|
#include <utility> // move, pair
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
// #include <nlohmann/json.hpp>
|
||||||
|
// #include <nlohmann/detail/view/macro_scope.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
namespace view
|
||||||
|
{
|
||||||
|
|
||||||
|
// Equality of views, and of views with basic_json values, with the semantics
|
||||||
|
// of basic_json's operator== applied to the values parse() would produce:
|
||||||
|
// numbers compare by value across their types, an object is compared by its
|
||||||
|
// members with duplicate keys resolved as parse() resolves them (the last
|
||||||
|
// value, at the position of the first occurrence), and in document order if
|
||||||
|
// the object type keeps an order (ordered_json), by key otherwise.
|
||||||
|
|
||||||
|
/// one side of a comparison: a view
|
||||||
|
template<typename BasicJsonType, typename View>
|
||||||
|
class view_side
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_view_t = typename View::string_view_t;
|
||||||
|
|
||||||
|
explicit view_side(const View& v) noexcept
|
||||||
|
: m_view(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
value_t type() const noexcept
|
||||||
|
{
|
||||||
|
return m_view.type();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept
|
||||||
|
{
|
||||||
|
return m_view.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
string_view_t string() const
|
||||||
|
{
|
||||||
|
return m_view.get_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// a number, boolean, or null as a basic_json value (no allocation)
|
||||||
|
BasicJsonType scalar() const
|
||||||
|
{
|
||||||
|
switch (m_view.type())
|
||||||
|
{
|
||||||
|
case value_t::number_integer:
|
||||||
|
return BasicJsonType(m_view.template get<typename BasicJsonType::number_integer_t>());
|
||||||
|
case value_t::number_unsigned:
|
||||||
|
return BasicJsonType(m_view.template get<typename BasicJsonType::number_unsigned_t>());
|
||||||
|
case value_t::number_float:
|
||||||
|
return BasicJsonType(m_view.template get<typename BasicJsonType::number_float_t>());
|
||||||
|
case value_t::boolean:
|
||||||
|
return BasicJsonType(m_view.template get<bool>());
|
||||||
|
case value_t::null:
|
||||||
|
case value_t::object:
|
||||||
|
case value_t::array:
|
||||||
|
case value_t::string:
|
||||||
|
case value_t::binary:
|
||||||
|
case value_t::discarded:
|
||||||
|
default:
|
||||||
|
return BasicJsonType(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void elements(std::vector<view_side>& out) const
|
||||||
|
{
|
||||||
|
out.reserve(m_view.size());
|
||||||
|
for (const View e : m_view)
|
||||||
|
{
|
||||||
|
out.emplace_back(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the members as parse() keeps them: one per key, the last value at the
|
||||||
|
/// position of the first occurrence; in that order, or sorted by key
|
||||||
|
void members(std::vector<std::pair<string_view_t, view_side>>& out, bool ordered) const
|
||||||
|
{
|
||||||
|
struct member
|
||||||
|
{
|
||||||
|
string_view_t key;
|
||||||
|
View value;
|
||||||
|
std::size_t position;
|
||||||
|
};
|
||||||
|
std::vector<member> all;
|
||||||
|
all.reserve(m_view.size());
|
||||||
|
std::size_t position = 0;
|
||||||
|
for (auto it = m_view.begin(); it != m_view.end(); ++it)
|
||||||
|
{
|
||||||
|
all.push_back(member{it.key(), it.value(), position++});
|
||||||
|
}
|
||||||
|
std::stable_sort(all.begin(), all.end(), [](const member & a, const member & b)
|
||||||
|
{
|
||||||
|
return a.key < b.key;
|
||||||
|
});
|
||||||
|
std::vector<member> unique;
|
||||||
|
unique.reserve(all.size());
|
||||||
|
for (std::size_t i = 0; i < all.size();)
|
||||||
|
{
|
||||||
|
std::size_t last = i;
|
||||||
|
while (last + 1 < all.size() && all[last + 1].key == all[i].key)
|
||||||
|
{
|
||||||
|
++last;
|
||||||
|
}
|
||||||
|
unique.push_back(member{all[i].key, all[last].value, all[i].position});
|
||||||
|
i = last + 1;
|
||||||
|
}
|
||||||
|
if (ordered)
|
||||||
|
{
|
||||||
|
std::sort(unique.begin(), unique.end(), [](const member & a, const member & b)
|
||||||
|
{
|
||||||
|
return a.position < b.position;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
out.reserve(unique.size());
|
||||||
|
for (const member& m : unique)
|
||||||
|
{
|
||||||
|
out.emplace_back(m.key, view_side(m.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
View m_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// the other side of a comparison: a basic_json value
|
||||||
|
template<typename BasicJsonType, typename StringView>
|
||||||
|
class json_side
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using string_view_t = StringView;
|
||||||
|
|
||||||
|
explicit json_side(const BasicJsonType& j) noexcept
|
||||||
|
: m_json(&j)
|
||||||
|
{}
|
||||||
|
|
||||||
|
value_t type() const noexcept
|
||||||
|
{
|
||||||
|
return m_json->type();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept
|
||||||
|
{
|
||||||
|
return m_json->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
string_view_t string() const
|
||||||
|
{
|
||||||
|
const auto& s = m_json->template get_ref<const typename BasicJsonType::string_t&>();
|
||||||
|
return string_view_t(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicJsonType scalar() const
|
||||||
|
{
|
||||||
|
return *m_json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void elements(std::vector<json_side>& out) const
|
||||||
|
{
|
||||||
|
out.reserve(m_json->size());
|
||||||
|
for (const auto& e : *m_json)
|
||||||
|
{
|
||||||
|
out.emplace_back(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void members(std::vector<std::pair<string_view_t, json_side>>& out, bool ordered) const
|
||||||
|
{
|
||||||
|
out.reserve(m_json->size());
|
||||||
|
for (auto it = m_json->cbegin(); it != m_json->cend(); ++it)
|
||||||
|
{
|
||||||
|
out.emplace_back(string_view_t(it.key().data(), it.key().size()), json_side(it.value()));
|
||||||
|
}
|
||||||
|
if (!ordered)
|
||||||
|
{
|
||||||
|
std::sort(out.begin(), out.end(), [](const std::pair<string_view_t, json_side>& a, const std::pair<string_view_t, json_side>& b)
|
||||||
|
{
|
||||||
|
return a.first < b.first;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BasicJsonType* m_json;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// whether two sides are equal; iterative, so that the nesting depth is
|
||||||
|
/// limited by memory only
|
||||||
|
template<typename BasicJsonType, typename A, typename B>
|
||||||
|
bool equal(const A& a0, const B& b0)
|
||||||
|
{
|
||||||
|
using string_view_t = typename A::string_view_t;
|
||||||
|
const bool ordered = is_ordered_map<typename BasicJsonType::object_t>::value;
|
||||||
|
|
||||||
|
struct frame
|
||||||
|
{
|
||||||
|
std::vector<A> elements_a{};
|
||||||
|
std::vector<B> elements_b{};
|
||||||
|
std::vector<std::pair<string_view_t, A>> members_a{};
|
||||||
|
std::vector<std::pair<string_view_t, B>> members_b{};
|
||||||
|
bool object = false;
|
||||||
|
std::size_t next = 0;
|
||||||
|
};
|
||||||
|
std::vector<frame> stack;
|
||||||
|
A a = a0;
|
||||||
|
B b = b0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const value_t ta = a.type();
|
||||||
|
const value_t tb = b.type();
|
||||||
|
const bool numbers = (ta == value_t::number_integer || ta == value_t::number_unsigned || ta == value_t::number_float)
|
||||||
|
&& (tb == value_t::number_integer || tb == value_t::number_unsigned || tb == value_t::number_float);
|
||||||
|
if (ta == value_t::discarded || tb == value_t::discarded)
|
||||||
|
{
|
||||||
|
// basic_json decides (JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
|
||||||
|
if (ta != tb || !(BasicJsonType(value_t::discarded) == BasicJsonType(value_t::discarded)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!numbers && ta != tb)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ta == value_t::string)
|
||||||
|
{
|
||||||
|
if (!(a.string() == b.string()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ta == value_t::array || ta == value_t::object)
|
||||||
|
{
|
||||||
|
if (a.size() != b.size() && ta == value_t::array)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
frame f;
|
||||||
|
f.object = ta == value_t::object;
|
||||||
|
if (f.object)
|
||||||
|
{
|
||||||
|
a.members(f.members_a, ordered);
|
||||||
|
b.members(f.members_b, ordered);
|
||||||
|
if (f.members_a.size() != f.members_b.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a.elements(f.elements_a);
|
||||||
|
b.elements(f.elements_b);
|
||||||
|
}
|
||||||
|
stack.push_back(std::move(f));
|
||||||
|
}
|
||||||
|
else if (!(a.scalar() == b.scalar())) // numbers (also of different types), null, boolean
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the next pair of values
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (stack.empty())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
frame& f = stack.back();
|
||||||
|
const std::size_t count = f.object ? f.members_a.size() : f.elements_a.size();
|
||||||
|
if (f.next == count)
|
||||||
|
{
|
||||||
|
stack.pop_back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (f.object)
|
||||||
|
{
|
||||||
|
if (!(f.members_a[f.next].first == f.members_b[f.next].first))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
a = f.members_a[f.next].second;
|
||||||
|
b = f.members_b[f.next].second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a = f.elements_a[f.next];
|
||||||
|
b = f.elements_b[f.next];
|
||||||
|
}
|
||||||
|
++f.next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
} // namespace detail
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
// #include <nlohmann/detail/view/document_data.hpp>
|
// #include <nlohmann/detail/view/document_data.hpp>
|
||||||
|
|
||||||
// #include <nlohmann/detail/view/errors.hpp>
|
// #include <nlohmann/detail/view/errors.hpp>
|
||||||
@@ -3766,6 +4086,44 @@ class basic_json_view
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// comparison //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
/// whether the values parse() would produce for two views are equal, as
|
||||||
|
/// by BasicJsonType's operator== (numbers by value, objects by their
|
||||||
|
/// members with duplicate keys resolved as parse() resolves them)
|
||||||
|
friend bool operator==(const basic_json_view& a, const basic_json_view& b)
|
||||||
|
{
|
||||||
|
return detail::view::equal<BasicJsonType>(side(a), side(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const basic_json_view& a, const basic_json_view& b)
|
||||||
|
{
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// whether the value parse() would produce for a view equals a value
|
||||||
|
friend bool operator==(const basic_json_view& a, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
return detail::view::equal<BasicJsonType>(side(a), json_side_t(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const BasicJsonType& j, const basic_json_view& a)
|
||||||
|
{
|
||||||
|
return a == j;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const basic_json_view& a, const BasicJsonType& j)
|
||||||
|
{
|
||||||
|
return !(a == j);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const BasicJsonType& j, const basic_json_view& a)
|
||||||
|
{
|
||||||
|
return !(a == j);
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////
|
/////////////////
|
||||||
// materialize //
|
// materialize //
|
||||||
/////////////////
|
/////////////////
|
||||||
@@ -3798,6 +4156,13 @@ class basic_json_view
|
|||||||
: m_doc(d), m_node(n)
|
: m_doc(d), m_node(n)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
using json_side_t = detail::view::json_side<BasicJsonType, string_view_t>;
|
||||||
|
|
||||||
|
static detail::view::view_side<BasicJsonType, basic_json_view> side(const basic_json_view& v) noexcept
|
||||||
|
{
|
||||||
|
return detail::view::view_side<BasicJsonType, basic_json_view>(v);
|
||||||
|
}
|
||||||
|
|
||||||
/// the number of source bytes of this value (estimated for values with
|
/// the number of source bytes of this value (estimated for values with
|
||||||
/// decoded strings)
|
/// decoded strings)
|
||||||
std::size_t source_extent() const noexcept
|
std::size_t source_extent() const noexcept
|
||||||
|
|||||||
@@ -1185,3 +1185,87 @@ TEST_CASE("json_view dump")
|
|||||||
CHECK(json_view().dump() == json(json::value_t::discarded).dump());
|
CHECK(json_view().dump() == json(json::value_t::discarded).dump());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("json_view comparison")
|
||||||
|
{
|
||||||
|
SECTION("equality of the values parse() produces")
|
||||||
|
{
|
||||||
|
generator g;
|
||||||
|
std::vector<std::string> texts;
|
||||||
|
for (int i = 0; i < 600; ++i)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
g.value(text, 0);
|
||||||
|
texts.push_back(text);
|
||||||
|
// the same value written differently: sorted keys, canonical numbers
|
||||||
|
texts.push_back(json::parse(text).dump(1));
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i + 2 < texts.size(); ++i)
|
||||||
|
{
|
||||||
|
for (std::size_t k = i; k < i + 3; ++k)
|
||||||
|
{
|
||||||
|
CAPTURE(texts[i]);
|
||||||
|
CAPTURE(texts[k]);
|
||||||
|
const json_document a = json_document::parse(texts[i]);
|
||||||
|
const json_document b = json_document::parse(texts[k]);
|
||||||
|
const json ja = json::parse(texts[i]);
|
||||||
|
const json jb = json::parse(texts[k]);
|
||||||
|
CHECK((a.root() == b.root()) == (ja == jb));
|
||||||
|
CHECK((a.root() != b.root()) == (ja != jb));
|
||||||
|
CHECK((a.root() == jb) == (ja == jb));
|
||||||
|
CHECK((jb == a.root()) == (ja == jb));
|
||||||
|
CHECK((a.root() != jb) == (ja != jb));
|
||||||
|
CHECK((jb != a.root()) == (ja != jb));
|
||||||
|
|
||||||
|
// ordered_json compares members in order
|
||||||
|
const ordered_json_document oa = ordered_json_document::parse(texts[i]);
|
||||||
|
const ordered_json_document ob = ordered_json_document::parse(texts[k]);
|
||||||
|
const ordered_json oja = ordered_json::parse(texts[i]);
|
||||||
|
const ordered_json ojb = ordered_json::parse(texts[k]);
|
||||||
|
CHECK((oa.root() == ob.root()) == (oja == ojb));
|
||||||
|
CHECK((oa.root() == ojb) == (oja == ojb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("numbers, duplicate keys, member order")
|
||||||
|
{
|
||||||
|
const auto same = [](const char* x, const char* y)
|
||||||
|
{
|
||||||
|
return json_document::parse(x).root() == json_document::parse(y).root();
|
||||||
|
};
|
||||||
|
CHECK(same("1", "1.0"));
|
||||||
|
CHECK(same("[1, -1, 2.5]", "[1.0, -1.0, 25e-1]"));
|
||||||
|
CHECK(!same("1", "1.5"));
|
||||||
|
CHECK(same("18446744073709551615", "18446744073709551615"));
|
||||||
|
CHECK(same(R"({"a": 1, "a": 2})", R"({"a": 2})"));
|
||||||
|
CHECK(!same(R"({"a": 1, "a": 2})", R"({"a": 1})"));
|
||||||
|
CHECK(same(R"({"a": 1, "b": 2})", R"({"b": 2, "a": 1})"));
|
||||||
|
CHECK(!same(R"({"a": 1})", R"({"a": 1, "b": 2})"));
|
||||||
|
CHECK(!same("[1, 2]", "[2, 1]"));
|
||||||
|
CHECK(!same("\"a\"", "\"b\""));
|
||||||
|
CHECK(same("\"\\u00e9\"", "\"\xc3\xa9\""));
|
||||||
|
CHECK(!same("null", "false"));
|
||||||
|
CHECK(!same("[]", "{}"));
|
||||||
|
CHECK(ordered_json_document::parse(R"({"a": 1, "b": 2, "a": 3})").root() == ordered_json_document::parse(R"({"a": 3, "b": 2})").root());
|
||||||
|
CHECK(ordered_json_document::parse(R"({"a": 1, "b": 2})").root() != ordered_json_document::parse(R"({"b": 2, "a": 1})").root());
|
||||||
|
|
||||||
|
// discarded values compare as basic_json's do
|
||||||
|
const json discarded(json::value_t::discarded);
|
||||||
|
CHECK((json_view() == json_view()) == (discarded == discarded)); // NOLINT(readability-container-size-empty): operator== is tested
|
||||||
|
CHECK((json_view() == discarded) == (discarded == discarded));
|
||||||
|
CHECK(!(json_view() == json_document::parse("null").root())); // NOLINT(readability-container-size-empty)
|
||||||
|
CHECK(!(json_document::parse("null").root() == discarded));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("deep nesting")
|
||||||
|
{
|
||||||
|
const std::string deep = std::string(100000, '[') + std::string(100000, ']');
|
||||||
|
const json_document a = json_document::parse(deep);
|
||||||
|
const json_document b = json_document::parse(deep);
|
||||||
|
CHECK(a.root() == b.root());
|
||||||
|
CHECK(a.root() == json::parse(deep));
|
||||||
|
const std::string other = std::string(100000, '[') + "1" + std::string(100000, ']');
|
||||||
|
CHECK(a.root() != json_document::parse(other).root());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user