mirror of
https://github.com/nlohmann/json.git
synced 2026-08-27 03:23:27 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
01853ed6bc | ||
|
|
2f025f401e |
@@ -52,6 +52,11 @@ optional, `#!cpp bjdata_version_t::draft2` by default.
|
|||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
|
is false.
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`.
|
Linear in the size of the JSON value `j`.
|
||||||
|
|||||||
@@ -45,6 +45,11 @@ The exact mapping and its limitations are described on a [dedicated page](../../
|
|||||||
|
|
||||||
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
Strong guarantee: if an exception is thrown, there are no changes in the JSON value.
|
||||||
|
|
||||||
|
## Exceptions
|
||||||
|
|
||||||
|
- Throws [`other_error.502`](../../home/exceptions.md#jsonexceptionother_error502) if `use_type` is true and `use_size`
|
||||||
|
is false.
|
||||||
|
|
||||||
## Complexity
|
## Complexity
|
||||||
|
|
||||||
Linear in the size of the JSON value `j`.
|
Linear in the size of the JSON value `j`.
|
||||||
|
|||||||
@@ -98,6 +98,17 @@ The library maps BSON record types to JSON value types as follows:
|
|||||||
This library deserializes BSON type `0x11` (Timestamp) as a `number_unsigned` value. The 64-bit value is preserved,
|
This library deserializes BSON type `0x11` (Timestamp) as a `number_unsigned` value. The 64-bit value is preserved,
|
||||||
but the Timestamp type information is not.
|
but the Timestamp type information is not.
|
||||||
|
|
||||||
|
!!! warning "Lenient BSON input handling"
|
||||||
|
|
||||||
|
The BSON reader is lenient in a few areas where the BSON specification is more restrictive:
|
||||||
|
|
||||||
|
- array element keys are not checked against the required decimal sequence (`0`, `1`, `2`, ...),
|
||||||
|
- any non-zero byte is accepted as `true` for the boolean type, and
|
||||||
|
- the payload for binary subtype `0x02` is returned as-is, including its inner length prefix.
|
||||||
|
|
||||||
|
If BSON input must be validated for strict specification compliance, validate it separately before passing it to
|
||||||
|
`from_bson()`.
|
||||||
|
|
||||||
??? example
|
??? example
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
|||||||
@@ -965,3 +965,19 @@ A JSON Patch operation 'test' failed. The unsuccessful operation is also printed
|
|||||||
```
|
```
|
||||||
[json.exception.other_error.501] unsuccessful: {"op":"test","path":"/baz","value":"bar"}
|
[json.exception.other_error.501] unsuccessful: {"op":"test","path":"/baz","value":"bar"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### json.exception.other_error.502
|
||||||
|
|
||||||
|
[`to_ubjson`](../api/basic_json/to_ubjson.md) and [`to_bjdata`](../api/basic_json/to_bjdata.md) were called with
|
||||||
|
`use_type = true` but `use_size = false`. UBJSON requires a size marker (`#`) after a type marker (`$`).
|
||||||
|
|
||||||
|
!!! failure "Example message"
|
||||||
|
|
||||||
|
```
|
||||||
|
[json.exception.other_error.502] use_type requires use_size = true
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
This exception was added in version 3.13.0. Before that, debug builds aborted on an assertion and release builds
|
||||||
|
wrote a `$` marker without `#`, which [`from_ubjson`](../api/basic_json/from_ubjson.md) then rejected.
|
||||||
|
|||||||
@@ -813,7 +813,10 @@ class binary_writer
|
|||||||
bool prefix_required = true;
|
bool prefix_required = true;
|
||||||
if (use_type && !j.m_data.m_value.array->empty())
|
if (use_type && !j.m_data.m_value.array->empty())
|
||||||
{
|
{
|
||||||
JSON_ASSERT(use_count);
|
if (!use_count)
|
||||||
|
{
|
||||||
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
|
}
|
||||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||||
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
||||||
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
||||||
@@ -859,7 +862,10 @@ class binary_writer
|
|||||||
|
|
||||||
if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
|
if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(use_count);
|
if (!use_count)
|
||||||
|
{
|
||||||
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
|
}
|
||||||
oa->write_character(to_char_type('$'));
|
oa->write_character(to_char_type('$'));
|
||||||
oa->write_character(bjdata_draft3 ? 'B' : 'U');
|
oa->write_character(bjdata_draft3 ? 'B' : 'U');
|
||||||
}
|
}
|
||||||
@@ -911,7 +917,10 @@ class binary_writer
|
|||||||
bool prefix_required = true;
|
bool prefix_required = true;
|
||||||
if (use_type && !j.m_data.m_value.object->empty())
|
if (use_type && !j.m_data.m_value.object->empty())
|
||||||
{
|
{
|
||||||
JSON_ASSERT(use_count);
|
if (!use_count)
|
||||||
|
{
|
||||||
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
|
}
|
||||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||||
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
||||||
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
||||||
|
|||||||
@@ -17756,7 +17756,10 @@ class binary_writer
|
|||||||
bool prefix_required = true;
|
bool prefix_required = true;
|
||||||
if (use_type && !j.m_data.m_value.array->empty())
|
if (use_type && !j.m_data.m_value.array->empty())
|
||||||
{
|
{
|
||||||
JSON_ASSERT(use_count);
|
if (!use_count)
|
||||||
|
{
|
||||||
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
|
}
|
||||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||||
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
|
||||||
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
||||||
@@ -17802,7 +17805,10 @@ class binary_writer
|
|||||||
|
|
||||||
if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
|
if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
|
||||||
{
|
{
|
||||||
JSON_ASSERT(use_count);
|
if (!use_count)
|
||||||
|
{
|
||||||
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
|
}
|
||||||
oa->write_character(to_char_type('$'));
|
oa->write_character(to_char_type('$'));
|
||||||
oa->write_character(bjdata_draft3 ? 'B' : 'U');
|
oa->write_character(bjdata_draft3 ? 'B' : 'U');
|
||||||
}
|
}
|
||||||
@@ -17854,7 +17860,10 @@ class binary_writer
|
|||||||
bool prefix_required = true;
|
bool prefix_required = true;
|
||||||
if (use_type && !j.m_data.m_value.object->empty())
|
if (use_type && !j.m_data.m_value.object->empty())
|
||||||
{
|
{
|
||||||
JSON_ASSERT(use_count);
|
if (!use_count)
|
||||||
|
{
|
||||||
|
JSON_THROW(other_error::create(502, "use_type requires use_size = true", &j));
|
||||||
|
}
|
||||||
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
|
||||||
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
const bool same_prefix = std::all_of(j.begin(), j.end(),
|
||||||
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
[this, first_prefix, use_bjdata](const BasicJsonType & v)
|
||||||
|
|||||||
@@ -3843,6 +3843,48 @@ TEST_CASE("all BJData first bytes")
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("BJData use_type requires use_size")
|
||||||
|
{
|
||||||
|
SECTION("non-empty object throws other_error.502")
|
||||||
|
{
|
||||||
|
const json j = {{"a", 1}, {"b", 2}};
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(j, false, true),
|
||||||
|
"[json.exception.other_error.502] use_type requires use_size = true",
|
||||||
|
json::other_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("non-empty array throws other_error.502")
|
||||||
|
{
|
||||||
|
const json j = {1, 2, 3};
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bjdata(j, false, true),
|
||||||
|
"[json.exception.other_error.502] use_type requires use_size = true",
|
||||||
|
json::other_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("scalars do not throw with use_type=true, use_count=false")
|
||||||
|
{
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(42, false, true));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(3.14, false, true));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata("hello", false, true));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(true, false, true));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(nullptr, false, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("empty containers do not throw with use_type=true, use_count=false")
|
||||||
|
{
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(json::array(), false, true));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(json::object(), false, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("valid combinations on non-empty containers")
|
||||||
|
{
|
||||||
|
const json j = {{"a", 1}, {"b", 2}};
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(j, false, false));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(j, true, false));
|
||||||
|
CHECK_NOTHROW(json::to_bjdata(j, true, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("BJData roundtrips" * doctest::skip())
|
TEST_CASE("BJData roundtrips" * doctest::skip())
|
||||||
{
|
{
|
||||||
SECTION("input from self-generated BJData files")
|
SECTION("input from self-generated BJData files")
|
||||||
|
|||||||
@@ -2503,6 +2503,48 @@ TEST_CASE("all UBJSON first bytes")
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("UBJSON use_type requires use_size")
|
||||||
|
{
|
||||||
|
SECTION("non-empty array throws other_error.502")
|
||||||
|
{
|
||||||
|
const json j = {1, 2, 3};
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(j, false, true),
|
||||||
|
"[json.exception.other_error.502] use_type requires use_size = true",
|
||||||
|
json::other_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("non-empty object throws other_error.502")
|
||||||
|
{
|
||||||
|
const json j = {{"a", 1}, {"b", 2}};
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_ubjson(j, false, true),
|
||||||
|
"[json.exception.other_error.502] use_type requires use_size = true",
|
||||||
|
json::other_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("scalars do not throw with use_type=true, use_count=false")
|
||||||
|
{
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(42, false, true));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(3.14, false, true));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson("hello", false, true));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(true, false, true));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(nullptr, false, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("empty containers do not throw with use_type=true, use_count=false")
|
||||||
|
{
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(json::array(), false, true));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(json::object(), false, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("valid combinations on non-empty containers")
|
||||||
|
{
|
||||||
|
const json j = {1, 2, 3};
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(j, false, false));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(j, true, false));
|
||||||
|
CHECK_NOTHROW(json::to_ubjson(j, true, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("UBJSON roundtrips" * doctest::skip())
|
TEST_CASE("UBJSON roundtrips" * doctest::skip())
|
||||||
{
|
{
|
||||||
SECTION("input from self-generated UBJSON files")
|
SECTION("input from self-generated UBJSON files")
|
||||||
|
|||||||
Reference in New Issue
Block a user