Merge branch 'feature/issue283' into develop (fixes #278)
This commit is contained in:
commit
dcea3a283c
3 changed files with 345 additions and 107 deletions
53
src/json.hpp
53
src/json.hpp
|
@ -3633,8 +3633,8 @@ class basic_json
|
||||||
/*!
|
/*!
|
||||||
@brief access specified object element with default value
|
@brief access specified object element with default value
|
||||||
|
|
||||||
Returns either a copy of an object's element at the specified key @a key or
|
Returns either a copy of an object's element at the specified key @a key
|
||||||
a given default value if no element with key @a key exists.
|
or a given default value if no element with key @a key exists.
|
||||||
|
|
||||||
The function is basically equivalent to executing
|
The function is basically equivalent to executing
|
||||||
@code {.cpp}
|
@code {.cpp}
|
||||||
|
@ -3706,13 +3706,60 @@ class basic_json
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief overload for a default value of type const char*
|
@brief overload for a default value of type const char*
|
||||||
@copydoc basic_json::value()
|
@copydoc basic_json::value(const typename object_t::key_type&, ValueType)
|
||||||
*/
|
*/
|
||||||
string_t value(const typename object_t::key_type& key, const char* default_value) const
|
string_t value(const typename object_t::key_type& key, const char* default_value) const
|
||||||
{
|
{
|
||||||
return value(key, string_t(default_value));
|
return value(key, string_t(default_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief access specified object element via JSON Pointer with default value
|
||||||
|
|
||||||
|
@param[in] ptr a JSON pointer to the element to access
|
||||||
|
@param[in] default_value the value to return if @a ptr found no value
|
||||||
|
|
||||||
|
@tparam ValueType type compatible to JSON values, for instance `int` for
|
||||||
|
JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
|
||||||
|
JSON arrays. Note the type of the expected value at @a key and the default
|
||||||
|
value @a default_value must be compatible.
|
||||||
|
|
||||||
|
@since version 2.0.2
|
||||||
|
*/
|
||||||
|
template <class ValueType, typename
|
||||||
|
std::enable_if<
|
||||||
|
std::is_convertible<basic_json_t, ValueType>::value
|
||||||
|
, int>::type = 0>
|
||||||
|
ValueType value(const json_pointer& ptr, ValueType default_value) const
|
||||||
|
{
|
||||||
|
// at only works for objects
|
||||||
|
if (is_object())
|
||||||
|
{
|
||||||
|
// if pointer resolves a value, return it or use default value
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ptr.get_checked(this);
|
||||||
|
}
|
||||||
|
catch (std::out_of_range&)
|
||||||
|
{
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::domain_error("cannot use value() with " + type_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief overload for a default value of type const char*
|
||||||
|
@copydoc basic_json::value(const json_pointer&, ValueType)
|
||||||
|
*/
|
||||||
|
string_t value(const json_pointer& ptr, const char* default_value) const
|
||||||
|
{
|
||||||
|
return value(ptr, string_t(default_value));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief access the first element
|
@brief access the first element
|
||||||
|
|
||||||
|
|
|
@ -3633,8 +3633,8 @@ class basic_json
|
||||||
/*!
|
/*!
|
||||||
@brief access specified object element with default value
|
@brief access specified object element with default value
|
||||||
|
|
||||||
Returns either a copy of an object's element at the specified key @a key or
|
Returns either a copy of an object's element at the specified key @a key
|
||||||
a given default value if no element with key @a key exists.
|
or a given default value if no element with key @a key exists.
|
||||||
|
|
||||||
The function is basically equivalent to executing
|
The function is basically equivalent to executing
|
||||||
@code {.cpp}
|
@code {.cpp}
|
||||||
|
@ -3706,13 +3706,60 @@ class basic_json
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief overload for a default value of type const char*
|
@brief overload for a default value of type const char*
|
||||||
@copydoc basic_json::value()
|
@copydoc basic_json::value(const typename object_t::key_type&, ValueType)
|
||||||
*/
|
*/
|
||||||
string_t value(const typename object_t::key_type& key, const char* default_value) const
|
string_t value(const typename object_t::key_type& key, const char* default_value) const
|
||||||
{
|
{
|
||||||
return value(key, string_t(default_value));
|
return value(key, string_t(default_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief access specified object element via JSON Pointer with default value
|
||||||
|
|
||||||
|
@param[in] ptr a JSON pointer to the element to access
|
||||||
|
@param[in] default_value the value to return if @a ptr found no value
|
||||||
|
|
||||||
|
@tparam ValueType type compatible to JSON values, for instance `int` for
|
||||||
|
JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for
|
||||||
|
JSON arrays. Note the type of the expected value at @a key and the default
|
||||||
|
value @a default_value must be compatible.
|
||||||
|
|
||||||
|
@since version 2.0.2
|
||||||
|
*/
|
||||||
|
template <class ValueType, typename
|
||||||
|
std::enable_if<
|
||||||
|
std::is_convertible<basic_json_t, ValueType>::value
|
||||||
|
, int>::type = 0>
|
||||||
|
ValueType value(const json_pointer& ptr, ValueType default_value) const
|
||||||
|
{
|
||||||
|
// at only works for objects
|
||||||
|
if (is_object())
|
||||||
|
{
|
||||||
|
// if pointer resolves a value, return it or use default value
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ptr.get_checked(this);
|
||||||
|
}
|
||||||
|
catch (std::out_of_range&)
|
||||||
|
{
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::domain_error("cannot use value() with " + type_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief overload for a default value of type const char*
|
||||||
|
@copydoc basic_json::value(const json_pointer&, ValueType)
|
||||||
|
*/
|
||||||
|
string_t value(const json_pointer& ptr, const char* default_value) const
|
||||||
|
{
|
||||||
|
return value(ptr, string_t(default_value));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief access the first element
|
@brief access the first element
|
||||||
|
|
||||||
|
|
|
@ -3767,6 +3767,8 @@ TEST_CASE("element access")
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("access specified element with default value")
|
SECTION("access specified element with default value")
|
||||||
|
{
|
||||||
|
SECTION("given a key")
|
||||||
{
|
{
|
||||||
SECTION("access existing value")
|
SECTION("access existing value")
|
||||||
{
|
{
|
||||||
|
@ -3889,6 +3891,135 @@ TEST_CASE("element access")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("given a JSON pointer")
|
||||||
|
{
|
||||||
|
SECTION("access existing value")
|
||||||
|
{
|
||||||
|
CHECK(j.value("/integer"_json_pointer, 2) == 1);
|
||||||
|
CHECK(j.value("/integer"_json_pointer, 1.0) == Approx(1));
|
||||||
|
CHECK(j.value("/unsigned"_json_pointer, 2) == 1u);
|
||||||
|
CHECK(j.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
|
||||||
|
CHECK(j.value("/null"_json_pointer, json(1)) == json());
|
||||||
|
CHECK(j.value("/boolean"_json_pointer, false) == true);
|
||||||
|
CHECK(j.value("/string"_json_pointer, "bar") == "hello world");
|
||||||
|
CHECK(j.value("/string"_json_pointer, std::string("bar")) == "hello world");
|
||||||
|
CHECK(j.value("/floating"_json_pointer, 12.34) == Approx(42.23));
|
||||||
|
CHECK(j.value("/floating"_json_pointer, 12) == 42);
|
||||||
|
CHECK(j.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
|
||||||
|
CHECK(j.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
|
||||||
|
|
||||||
|
CHECK(j_const.value("/integer"_json_pointer, 2) == 1);
|
||||||
|
CHECK(j_const.value("/integer"_json_pointer, 1.0) == Approx(1));
|
||||||
|
CHECK(j_const.value("/unsigned"_json_pointer, 2) == 1u);
|
||||||
|
CHECK(j_const.value("/unsigned"_json_pointer, 1.0) == Approx(1u));
|
||||||
|
CHECK(j_const.value("/boolean"_json_pointer, false) == true);
|
||||||
|
CHECK(j_const.value("/string"_json_pointer, "bar") == "hello world");
|
||||||
|
CHECK(j_const.value("/string"_json_pointer, std::string("bar")) == "hello world");
|
||||||
|
CHECK(j_const.value("/floating"_json_pointer, 12.34) == Approx(42.23));
|
||||||
|
CHECK(j_const.value("/floating"_json_pointer, 12) == 42);
|
||||||
|
CHECK(j_const.value("/object"_json_pointer, json({{"foo", "bar"}})) == json(json::object()));
|
||||||
|
CHECK(j_const.value("/array"_json_pointer, json({10, 100})) == json({1, 2, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("access non-existing value")
|
||||||
|
{
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, 2) == 2);
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, 2u) == 2u);
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, false) == false);
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, "bar") == "bar");
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
|
||||||
|
CHECK(j.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
|
||||||
|
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, 2) == 2);
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, 2u) == 2u);
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, false) == false);
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, "bar") == "bar");
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, 12.34) == Approx(12.34));
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
|
||||||
|
CHECK(j_const.value("/not/existing"_json_pointer, json({10, 100})) == json({10, 100}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("access on non-object type")
|
||||||
|
{
|
||||||
|
SECTION("null")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::null);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("boolean")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::boolean);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
|
||||||
|
"cannot use value() with boolean");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("string")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::string);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
|
||||||
|
"cannot use value() with string");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("array")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::array);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("number (integer)")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::number_integer);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
|
||||||
|
"cannot use value() with number");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("number (unsigned)")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::number_unsigned);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
|
||||||
|
"cannot use value() with number");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("number (floating-point)")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::number_float);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
|
||||||
|
CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
|
||||||
|
CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
|
||||||
|
"cannot use value() with number");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("front and back")
|
SECTION("front and back")
|
||||||
{
|
{
|
||||||
// "array" is the smallest key
|
// "array" is the smallest key
|
||||||
|
@ -14200,6 +14331,19 @@ TEST_CASE("regression tests")
|
||||||
// check roundtrip
|
// check roundtrip
|
||||||
CHECK(doc.patch(json::diff(doc, expected)) == expected);
|
CHECK(doc.patch(json::diff(doc, expected)) == expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("issue #283 - value() does not work with _json_pointer types")
|
||||||
|
{
|
||||||
|
json j =
|
||||||
|
{
|
||||||
|
{"object", {{"key1", 1}, {"key2", 2}}},
|
||||||
|
};
|
||||||
|
|
||||||
|
int at_integer = j.at("/object/key2"_json_pointer);
|
||||||
|
int val_integer = j.value("/object/key2"_json_pointer, 0);
|
||||||
|
|
||||||
|
CHECK(at_integer == val_integer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// special test case to check if memory is leaked if constructor throws
|
// special test case to check if memory is leaked if constructor throws
|
||||||
|
|
Loading…
Reference in a new issue