Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdd9d576a2 | ||
|
|
ae792a4c95 |
No files matched your search
@@ -120,16 +120,6 @@ overload (3).
|
|||||||
See [Number conversion](../../features/types/number_handling.md#number-conversion)
|
See [Number conversion](../../features/types/number_handling.md#number-conversion)
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
!!! note "Booleans as numbers"
|
|
||||||
|
|
||||||
A JSON boolean converts to `#!cpp 0` or `#!cpp 1` for every arithmetic type **except** [`number_integer_t`](number_integer_t.md),
|
|
||||||
[`number_unsigned_t`](number_unsigned_t.md) and [`number_float_t`](number_float_t.md) (by default `#!cpp std::int64_t`,
|
|
||||||
`#!cpp std::uint64_t`, and `#!cpp double`), for which `#!cpp get<T>()` throws
|
|
||||||
[`type_error.302`](../../home/exceptions.md#jsonexceptiontype_error302) ("type must be number, but is boolean"). So
|
|
||||||
`#!cpp json(true).get<int>()` is `#!cpp 1`, while `#!cpp json(true).get<std::int64_t>()` and
|
|
||||||
`#!cpp json(true).get<double>()` throw. This is the behavior of all 3.x versions;
|
|
||||||
[`basic_json_view::get<T>()`](../basic_json_view/get.md) behaves the same.
|
|
||||||
|
|
||||||
!!! note "`std::optional` conversions"
|
!!! note "`std::optional` conversions"
|
||||||
|
|
||||||
Prior to version 3.13.0, `#!cpp get<std::optional<T>>()` (and other conversions to `std::optional<T>`) failed to
|
Prior to version 3.13.0, `#!cpp get<std::optional<T>>()` (and other conversions to `std::optional<T>`) failed to
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ For the types below, the conversion works directly on the flat index — no `Bas
|
|||||||
|
|
||||||
- `#!cpp bool`
|
- `#!cpp bool`
|
||||||
- arithmetic types other than `#!cpp bool` (for a boolean, `#!cpp 0`/`#!cpp 1`, exactly as for
|
- arithmetic types other than `#!cpp bool` (for a boolean, `#!cpp 0`/`#!cpp 1`, exactly as for
|
||||||
[`BasicJsonType::get<T>()`](../basic_json/get.md#notes), except for `number_integer_t`, `number_unsigned_t` and
|
[`BasicJsonType::get<T>()`](../basic_json/get.md))
|
||||||
`number_float_t`, which throw `type_error.302` for a boolean, as `BasicJsonType::get<T>()` does)
|
|
||||||
- `#!cpp std::nullptr_t`
|
- `#!cpp std::nullptr_t`
|
||||||
- `#!cpp std::basic_string<char, Traits, Alloc>` (including `string_t`) — a copy of the string
|
- `#!cpp std::basic_string<char, Traits, Alloc>` (including `string_t`) — a copy of the string
|
||||||
- [`string_view_t`](index.md#member-types) — **no copy**: the returned view points into the document's
|
- [`string_view_t`](index.md#member-types) — **no copy**: the returned view points into the document's
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ Note that enabling this macro increases the size of every JSON value by two `std
|
|||||||
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
|
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
|
||||||
these values to be reported in those error messages.
|
these values to be reported in those error messages.
|
||||||
|
|
||||||
|
If [`JSON_DIAGNOSTICS`](json_diagnostics.md) is also set, the message contains the path and the byte range, such as
|
||||||
|
`(/1) (bytes 4-8)`. An exception about the root value has no path and only the range, such as `(bytes 0-4)`.
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
The default value is `0` (position diagnostics are switched off).
|
The default value is `0` (position diagnostics are switched off).
|
||||||
|
|||||||
@@ -198,18 +198,19 @@ class exception : public std::exception
|
|||||||
// all other parent types (primitives) contribute no token
|
// all other parent types (primitives) contribute no token
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.empty())
|
// the root value has no path, but still has a byte range
|
||||||
|
std::string prefix;
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
return "";
|
const auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
||||||
|
[](const std::string & a, const std::string & b)
|
||||||
|
{
|
||||||
|
return concat(a, '/', detail::escape(b));
|
||||||
|
});
|
||||||
|
prefix = concat('(', str, ") ");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
return concat(prefix, get_byte_positions(leaf_element));
|
||||||
[](const std::string & a, const std::string & b)
|
|
||||||
{
|
|
||||||
return concat(a, '/', detail::escape(b));
|
|
||||||
});
|
|
||||||
|
|
||||||
return concat('(', str, ") ", get_byte_positions(leaf_element));
|
|
||||||
#else
|
#else
|
||||||
return get_byte_positions(leaf_element);
|
return get_byte_positions(leaf_element);
|
||||||
#endif
|
#endif
|
||||||
@@ -222,7 +223,8 @@ class exception : public std::exception
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
||||||
{
|
{
|
||||||
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
|
// a value-initialized iterator has no value (invalid_iterator.212)
|
||||||
|
if (leaf_element != nullptr && leaf_element->start_pos() != std::string::npos && leaf_element->end_pos() != std::string::npos)
|
||||||
{
|
{
|
||||||
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,8 +234,7 @@ void value_range(const document_data& d, const node* n, std::size_t& start, std:
|
|||||||
|
|
||||||
/// the context basic_json puts in front of the message of an exception about
|
/// the context basic_json puts in front of the message of an exception about
|
||||||
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
||||||
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (with JSON_DIAGNOSTICS, only
|
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root)
|
||||||
/// if there is a path)
|
|
||||||
///
|
///
|
||||||
/// This and the throw helpers that take an error_site are templates, so that
|
/// This and the throw helpers that take an error_site are templates, so that
|
||||||
/// the path and range code is instantiated only where an exception can be
|
/// the path and range code is instantiated only where an exception can be
|
||||||
@@ -256,19 +255,19 @@ std::string diagnostics(const error_site site)
|
|||||||
std::string context;
|
std::string context;
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
const bool found = edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens);
|
// a value that is not found leaves no tokens, like the root
|
||||||
if (!found || tokens.empty())
|
static_cast<void>(edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens));
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
// context is still empty; returning it keeps the copy elided (-Wnrvo)
|
// the root value has no path, but still has a byte range
|
||||||
return context;
|
std::string path;
|
||||||
|
for (const std::string& token : tokens)
|
||||||
|
{
|
||||||
|
path.push_back('/');
|
||||||
|
path += detail::escape(token);
|
||||||
|
}
|
||||||
|
context = concat('(', path, ") ");
|
||||||
}
|
}
|
||||||
std::string path;
|
|
||||||
for (const std::string& token : tokens)
|
|
||||||
{
|
|
||||||
path.push_back('/');
|
|
||||||
path += detail::escape(token);
|
|
||||||
}
|
|
||||||
context = concat('(', path, ") ");
|
|
||||||
#endif
|
#endif
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
std::size_t start = 0;
|
std::size_t start = 0;
|
||||||
|
|||||||
@@ -33,37 +33,12 @@ namespace view
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
struct value_tag {};
|
struct value_tag {};
|
||||||
|
|
||||||
/// whether basic_json reads T with a dedicated from_json() overload that does
|
|
||||||
/// not accept a boolean: exactly number_integer_t, number_unsigned_t and
|
|
||||||
/// number_float_t (the other arithmetic types convert a boolean to 0 or 1)
|
|
||||||
template<typename T, typename BasicJsonType>
|
|
||||||
struct rejects_boolean : std::integral_constant < bool,
|
|
||||||
std::is_same<T, typename BasicJsonType::number_integer_t>::value
|
|
||||||
|| std::is_same<T, typename BasicJsonType::number_unsigned_t>::value
|
|
||||||
|| std::is_same<T, typename BasicJsonType::number_float_t>::value >
|
|
||||||
{};
|
|
||||||
|
|
||||||
/// a boolean as T: 0 or 1
|
|
||||||
template<typename T>
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE T boolean_as(const document_data& /*d*/, const node& n, std::false_type /*rejects*/)
|
|
||||||
{
|
|
||||||
return static_cast<T>((n.flags & node_flags::is_true) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// a boolean as number_integer_t, number_unsigned_t or number_float_t: type_error.302, as in basic_json
|
|
||||||
template<typename T>
|
|
||||||
[[noreturn]] NLOHMANN_VIEW_ALWAYS_INLINE T boolean_as(const document_data& d, const node& n, std::true_type /*rejects*/)
|
|
||||||
{
|
|
||||||
throw_type_error(error_site(&d, &n), exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief the number or boolean of a node converted to an arithmetic type
|
@brief the number or boolean of a node converted to an arithmetic type
|
||||||
|
|
||||||
As basic_json's get<T>() for arithmetic types: integers and floats are
|
As basic_json's get<T>() for arithmetic types: integers and floats are
|
||||||
converted with static_cast, booleans give 0 or 1 (but throw type_error.302 for
|
converted with static_cast, booleans give 0 or 1, and other types throw
|
||||||
number_integer_t, number_unsigned_t and number_float_t, as basic_json does),
|
type_error.302.
|
||||||
and other types throw type_error.302.
|
|
||||||
*/
|
*/
|
||||||
template<typename T, typename BasicJsonType>
|
template<typename T, typename BasicJsonType>
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const node& n)
|
NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const node& n)
|
||||||
@@ -77,7 +52,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod
|
|||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
return static_cast<T>(float_value<typename BasicJsonType::number_float_t>(d, n));
|
return static_cast<T>(float_value<typename BasicJsonType::number_float_t>(d, n));
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
return boolean_as<T>(d, n, rejects_boolean<T, BasicJsonType> {});
|
return static_cast<T>((n.flags & node_flags::is_true) != 0);
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
|
|||||||
Generated
+12
-10
@@ -5695,18 +5695,19 @@ class exception : public std::exception
|
|||||||
// all other parent types (primitives) contribute no token
|
// all other parent types (primitives) contribute no token
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.empty())
|
// the root value has no path, but still has a byte range
|
||||||
|
std::string prefix;
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
return "";
|
const auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
||||||
|
[](const std::string & a, const std::string & b)
|
||||||
|
{
|
||||||
|
return concat(a, '/', detail::escape(b));
|
||||||
|
});
|
||||||
|
prefix = concat('(', str, ") ");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
return concat(prefix, get_byte_positions(leaf_element));
|
||||||
[](const std::string & a, const std::string & b)
|
|
||||||
{
|
|
||||||
return concat(a, '/', detail::escape(b));
|
|
||||||
});
|
|
||||||
|
|
||||||
return concat('(', str, ") ", get_byte_positions(leaf_element));
|
|
||||||
#else
|
#else
|
||||||
return get_byte_positions(leaf_element);
|
return get_byte_positions(leaf_element);
|
||||||
#endif
|
#endif
|
||||||
@@ -5719,7 +5720,8 @@ class exception : public std::exception
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
||||||
{
|
{
|
||||||
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
|
// a value-initialized iterator has no value (invalid_iterator.212)
|
||||||
|
if (leaf_element != nullptr && leaf_element->start_pos() != std::string::npos && leaf_element->end_pos() != std::string::npos)
|
||||||
{
|
{
|
||||||
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+15
-41
@@ -5076,8 +5076,7 @@ void value_range(const document_data& d, const node* n, std::size_t& start, std:
|
|||||||
|
|
||||||
/// the context basic_json puts in front of the message of an exception about
|
/// the context basic_json puts in front of the message of an exception about
|
||||||
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
||||||
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (with JSON_DIAGNOSTICS, only
|
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root)
|
||||||
/// if there is a path)
|
|
||||||
///
|
///
|
||||||
/// This and the throw helpers that take an error_site are templates, so that
|
/// This and the throw helpers that take an error_site are templates, so that
|
||||||
/// the path and range code is instantiated only where an exception can be
|
/// the path and range code is instantiated only where an exception can be
|
||||||
@@ -5098,19 +5097,19 @@ std::string diagnostics(const error_site site)
|
|||||||
std::string context;
|
std::string context;
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
const bool found = edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens);
|
// a value that is not found leaves no tokens, like the root
|
||||||
if (!found || tokens.empty())
|
static_cast<void>(edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens));
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
// context is still empty; returning it keeps the copy elided (-Wnrvo)
|
// the root value has no path, but still has a byte range
|
||||||
return context;
|
std::string path;
|
||||||
|
for (const std::string& token : tokens)
|
||||||
|
{
|
||||||
|
path.push_back('/');
|
||||||
|
path += detail::escape(token);
|
||||||
|
}
|
||||||
|
context = concat('(', path, ") ");
|
||||||
}
|
}
|
||||||
std::string path;
|
|
||||||
for (const std::string& token : tokens)
|
|
||||||
{
|
|
||||||
path.push_back('/');
|
|
||||||
path += detail::escape(token);
|
|
||||||
}
|
|
||||||
context = concat('(', path, ") ");
|
|
||||||
#endif
|
#endif
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
std::size_t start = 0;
|
std::size_t start = 0;
|
||||||
@@ -10067,37 +10066,12 @@ namespace view
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
struct value_tag {};
|
struct value_tag {};
|
||||||
|
|
||||||
/// whether basic_json reads T with a dedicated from_json() overload that does
|
|
||||||
/// not accept a boolean: exactly number_integer_t, number_unsigned_t and
|
|
||||||
/// number_float_t (the other arithmetic types convert a boolean to 0 or 1)
|
|
||||||
template<typename T, typename BasicJsonType>
|
|
||||||
struct rejects_boolean : std::integral_constant < bool,
|
|
||||||
std::is_same<T, typename BasicJsonType::number_integer_t>::value
|
|
||||||
|| std::is_same<T, typename BasicJsonType::number_unsigned_t>::value
|
|
||||||
|| std::is_same<T, typename BasicJsonType::number_float_t>::value >
|
|
||||||
{};
|
|
||||||
|
|
||||||
/// a boolean as T: 0 or 1
|
|
||||||
template<typename T>
|
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE T boolean_as(const document_data& /*d*/, const node& n, std::false_type /*rejects*/)
|
|
||||||
{
|
|
||||||
return static_cast<T>((n.flags & node_flags::is_true) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// a boolean as number_integer_t, number_unsigned_t or number_float_t: type_error.302, as in basic_json
|
|
||||||
template<typename T>
|
|
||||||
[[noreturn]] NLOHMANN_VIEW_ALWAYS_INLINE T boolean_as(const document_data& d, const node& n, std::true_type /*rejects*/)
|
|
||||||
{
|
|
||||||
throw_type_error(error_site(&d, &n), exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief the number or boolean of a node converted to an arithmetic type
|
@brief the number or boolean of a node converted to an arithmetic type
|
||||||
|
|
||||||
As basic_json's get<T>() for arithmetic types: integers and floats are
|
As basic_json's get<T>() for arithmetic types: integers and floats are
|
||||||
converted with static_cast, booleans give 0 or 1 (but throw type_error.302 for
|
converted with static_cast, booleans give 0 or 1, and other types throw
|
||||||
number_integer_t, number_unsigned_t and number_float_t, as basic_json does),
|
type_error.302.
|
||||||
and other types throw type_error.302.
|
|
||||||
*/
|
*/
|
||||||
template<typename T, typename BasicJsonType>
|
template<typename T, typename BasicJsonType>
|
||||||
NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const node& n)
|
NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const node& n)
|
||||||
@@ -10111,7 +10085,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod
|
|||||||
case value_t::number_float:
|
case value_t::number_float:
|
||||||
return static_cast<T>(float_value<typename BasicJsonType::number_float_t>(d, n));
|
return static_cast<T>(float_value<typename BasicJsonType::number_float_t>(d, n));
|
||||||
case value_t::boolean:
|
case value_t::boolean:
|
||||||
return boolean_as<T>(d, n, rejects_boolean<T, BasicJsonType> {});
|
return static_cast<T>((n.flags & node_flags::is_true) != 0);
|
||||||
case value_t::null:
|
case value_t::null:
|
||||||
case value_t::object:
|
case value_t::object:
|
||||||
case value_t::array:
|
case value_t::array:
|
||||||
|
|||||||
@@ -1260,44 +1260,6 @@ TEST_CASE("value conversion")
|
|||||||
#undef JSON_HAS_CPP_17
|
#undef JSON_HAS_CPP_17
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST_CASE("get<T>() of a boolean (#5988)")
|
|
||||||
{
|
|
||||||
// a boolean converts to 0/1 for every arithmetic type except number_integer_t, number_unsigned_t and
|
|
||||||
// number_float_t, whose dedicated from_json() overloads throw
|
|
||||||
const json t = true;
|
|
||||||
const json f = false;
|
|
||||||
|
|
||||||
SECTION("0 and 1")
|
|
||||||
{
|
|
||||||
// long and unsigned long are left out: they are std::int64_t and std::uint64_t on some platforms
|
|
||||||
CHECK(t.get<bool>() == true);
|
|
||||||
CHECK(f.get<bool>() == false);
|
|
||||||
CHECK(t.get<char>() == 1);
|
|
||||||
CHECK(t.get<signed char>() == 1);
|
|
||||||
CHECK(t.get<unsigned char>() == 1);
|
|
||||||
CHECK(t.get<short>() == 1); // NOLINT(google-runtime-int)
|
|
||||||
CHECK(t.get<unsigned short>() == 1); // NOLINT(google-runtime-int)
|
|
||||||
CHECK(t.get<int>() == 1);
|
|
||||||
CHECK(t.get<unsigned>() == 1);
|
|
||||||
CHECK(t.get<float>() == 1.0F);
|
|
||||||
CHECK(t.get<long double>() == 1.0L);
|
|
||||||
CHECK(f.get<int>() == 0);
|
|
||||||
CHECK(f.get<float>() == 0.0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
SECTION("number_integer_t, number_unsigned_t and number_float_t throw")
|
|
||||||
{
|
|
||||||
CHECK_THROWS_WITH_AS(t.get<json::number_integer_t>(), "[json.exception.type_error.302] type must be number, but is boolean", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(t.get<json::number_unsigned_t>(), "[json.exception.type_error.302] type must be number, but is boolean", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(t.get<json::number_float_t>(), "[json.exception.type_error.302] type must be number, but is boolean", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(f.get<json::number_integer_t>(), "[json.exception.type_error.302] type must be number, but is boolean", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(f.get<json::number_unsigned_t>(), "[json.exception.type_error.302] type must be number, but is boolean", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(f.get<json::number_float_t>(), "[json.exception.type_error.302] type must be number, but is boolean", json::type_error&);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_14
|
#ifdef JSON_HAS_CPP_14
|
||||||
#undef JSON_HAS_CPP_14
|
#undef JSON_HAS_CPP_14
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -193,6 +193,28 @@ TEST_CASE("Better diagnostics with positions")
|
|||||||
check_conversion(300);
|
check_conversion(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("exception about the root value (#5987)")
|
||||||
|
{
|
||||||
|
// the root has no path, but still has a byte range
|
||||||
|
const json root = json::parse("true");
|
||||||
|
const json nested = json::parse("[1, true]");
|
||||||
|
const json constructed = true;
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(root.get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (bytes 0-4) type must be string, but is boolean", json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(nested[1].get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (/1) (bytes 4-8) type must be string, but is boolean", json::type_error);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(root.get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (bytes 0-4) type must be string, but is boolean", json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(nested[1].get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (bytes 4-8) type must be string, but is boolean", json::type_error);
|
||||||
|
#endif
|
||||||
|
// a value without positions gets no range and no stray whitespace
|
||||||
|
CHECK_THROWS_WITH_AS(constructed.get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] type must be string, but is boolean", json::type_error);
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("JSON patch add to primitive parent (#4292)")
|
SECTION("JSON patch add to primitive parent (#4292)")
|
||||||
{
|
{
|
||||||
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
||||||
@@ -243,3 +265,14 @@ TEST_CASE("values read from a binary format have no positions")
|
|||||||
check_no_positions(j);
|
check_no_positions(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("comparing a value-initialized iterator throws invalid_iterator.212")
|
||||||
|
{
|
||||||
|
// such an iterator has no value to take the byte positions from; the
|
||||||
|
// comparison used to dereference the null pointer instead of throwing
|
||||||
|
json j = {1, 2};
|
||||||
|
json::iterator it{};
|
||||||
|
CHECK_THROWS_WITH_AS(it == j.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator);
|
||||||
|
CHECK_THROWS_WITH_AS(it < j.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator);
|
||||||
|
CHECK_THROWS_WITH_AS(j.begin() == it, "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator);
|
||||||
|
}
|
||||||
@@ -1551,85 +1551,6 @@ TEST_CASE("json_view values")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// get<T>() of a boolean: the same value or the same exception in basic_json and in the view (#5988)
|
|
||||||
template<typename T>
|
|
||||||
void check_boolean_get_parity()
|
|
||||||
{
|
|
||||||
for (const bool b :
|
|
||||||
{
|
|
||||||
true, false
|
|
||||||
})
|
|
||||||
{
|
|
||||||
const std::string text = b ? "true" : "false";
|
|
||||||
const json j = json::parse(text);
|
|
||||||
const json_document d = json_document::parse(text);
|
|
||||||
CAPTURE(text)
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
T dom_value {};
|
|
||||||
T view_value{};
|
|
||||||
const std::string dom_error = json_view_test::exception_of_call([&] { dom_value = j.get<T>(); });
|
|
||||||
const std::string view_error = json_view_test::exception_of_call([&] { view_value = d.root().template get<T>(); });
|
|
||||||
CHECK(dom_error == view_error);
|
|
||||||
if (dom_error.empty())
|
|
||||||
{
|
|
||||||
CHECK(dom_value == view_value);
|
|
||||||
CHECK(view_value == static_cast<T>(b));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// without exceptions, the combinations that throw (number_integer_t, number_unsigned_t, number_float_t)
|
|
||||||
// cannot be run: JSON_THROW aborts
|
|
||||||
const bool throws = std::is_same<T, json::number_integer_t>::value
|
|
||||||
|| std::is_same<T, json::number_unsigned_t>::value
|
|
||||||
|| std::is_same<T, json::number_float_t>::value;
|
|
||||||
if (!throws)
|
|
||||||
{
|
|
||||||
CHECK(j.get<T>() == d.root().template get<T>());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST_CASE("json_view get<T>() of a boolean (#5988)")
|
|
||||||
{
|
|
||||||
check_boolean_get_parity<bool>();
|
|
||||||
check_boolean_get_parity<char>();
|
|
||||||
check_boolean_get_parity<signed char>();
|
|
||||||
check_boolean_get_parity<unsigned char>();
|
|
||||||
check_boolean_get_parity<short>(); // NOLINT(google-runtime-int)
|
|
||||||
check_boolean_get_parity<unsigned short>(); // NOLINT(google-runtime-int)
|
|
||||||
check_boolean_get_parity<int>();
|
|
||||||
check_boolean_get_parity<unsigned>();
|
|
||||||
check_boolean_get_parity<long>(); // NOLINT(google-runtime-int)
|
|
||||||
check_boolean_get_parity<unsigned long>(); // NOLINT(google-runtime-int)
|
|
||||||
check_boolean_get_parity<long long>(); // NOLINT(google-runtime-int)
|
|
||||||
check_boolean_get_parity<unsigned long long>(); // NOLINT(google-runtime-int)
|
|
||||||
check_boolean_get_parity<std::int64_t>();
|
|
||||||
check_boolean_get_parity<std::uint64_t>();
|
|
||||||
check_boolean_get_parity<float>();
|
|
||||||
check_boolean_get_parity<double>();
|
|
||||||
check_boolean_get_parity<long double>();
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
SECTION("the exceptions of the exact number types")
|
|
||||||
{
|
|
||||||
const json_document d = json_document::parse("true");
|
|
||||||
CHECK_THROWS_WITH_AS(d.root().get<json::number_integer_t>(), json_view_test::message("type_error.302", "", 0, 4, "type must be number, but is boolean").c_str(), json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(d.root().get<json::number_unsigned_t>(), json_view_test::message("type_error.302", "", 0, 4, "type must be number, but is boolean").c_str(), json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(d.root().get<json::number_float_t>(), json_view_test::message("type_error.302", "", 0, 4, "type must be number, but is boolean").c_str(), json::type_error&);
|
|
||||||
CHECK(d.root().get<int>() == 1);
|
|
||||||
// get_to() and value() go through get<T>()
|
|
||||||
json::number_float_t x = 0;
|
|
||||||
CHECK_THROWS_AS(d.root().get_to(x), json::type_error&);
|
|
||||||
const json_document o = json_document::parse("{\"b\":true}");
|
|
||||||
CHECK_THROWS_AS(static_cast<void>(o.root().value("b", 0.0)), json::type_error&);
|
|
||||||
CHECK(o.root().value("b", 0) == 1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("json_view JSON pointers")
|
TEST_CASE("json_view JSON pointers")
|
||||||
{
|
{
|
||||||
SECTION("every value of generated documents")
|
SECTION("every value of generated documents")
|
||||||
|
|||||||
@@ -300,6 +300,27 @@ TEST_CASE("json_view materialize() source positions")
|
|||||||
static_cast<void>(j.at("list").at(1).get<int>());
|
static_cast<void>(j.at("list").at(1).get<int>());
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("exception about the root value (#5987)")
|
||||||
|
{
|
||||||
|
const json_document root = json_document::parse("true");
|
||||||
|
const json_document nested = json_document::parse("[1, true]");
|
||||||
|
CHECK(json_view_test::exception_of_call([&]
|
||||||
|
{
|
||||||
|
static_cast<void>(root.root().get<std::string>());
|
||||||
|
}) == "[json.exception.type_error.302] (bytes 0-4) type must be string, but is boolean");
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK(json_view_test::exception_of_call([&]
|
||||||
|
{
|
||||||
|
static_cast<void>(nested.root()[1].get<std::string>());
|
||||||
|
}) == "[json.exception.type_error.302] (/1) (bytes 4-8) type must be string, but is boolean");
|
||||||
|
#else
|
||||||
|
CHECK(json_view_test::exception_of_call([&]
|
||||||
|
{
|
||||||
|
static_cast<void>(nested.root()[1].get<std::string>());
|
||||||
|
}) == "[json.exception.type_error.302] (bytes 4-8) type must be string, but is boolean");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SECTION("edited documents")
|
SECTION("edited documents")
|
||||||
@@ -550,12 +571,8 @@ TEST_CASE("json_view exceptions name the path and the bytes of the value")
|
|||||||
expected_zz += "(bytes 12-35) key 'zz' not found";
|
expected_zz += "(bytes 12-35) key 'zz' not found";
|
||||||
CHECK(exception_of_call([&] { static_cast<void>(d.root().at(json::json_pointer("/people/0/zz"))); }) == expected_zz);
|
CHECK(exception_of_call([&] { static_cast<void>(d.root().at(json::json_pointer("/people/0/zz"))); }) == expected_zz);
|
||||||
|
|
||||||
// the root has no path, and with JSON_DIAGNOSTICS then no bytes either (as basic_json)
|
// the root has no path, but still its bytes (#5987)
|
||||||
std::string expected_missing = "[json.exception.out_of_range.403] ";
|
const std::string expected_missing = "[json.exception.out_of_range.403] (bytes 0-79) key 'missing' not found";
|
||||||
#if !JSON_DIAGNOSTICS
|
|
||||||
expected_missing += "(bytes 0-79) ";
|
|
||||||
#endif
|
|
||||||
expected_missing += "key 'missing' not found";
|
|
||||||
CHECK(exception_of_call([&] { static_cast<void>(d.root().at("missing")); }) == expected_missing);
|
CHECK(exception_of_call([&] { static_cast<void>(d.root().at("missing")); }) == expected_missing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user