fix for #133
added value() function to get object value at given key or a default value if key does not exist
This commit is contained in:
parent
2ba901bbf6
commit
cd04a7d3e9
7 changed files with 325 additions and 7 deletions
|
@ -395,7 +395,7 @@ $ make
|
||||||
$ ./json_unit "*"
|
$ ./json_unit "*"
|
||||||
|
|
||||||
===============================================================================
|
===============================================================================
|
||||||
All tests passed (3341803 assertions in 28 test cases)
|
All tests passed (3341846 assertions in 28 test cases)
|
||||||
```
|
```
|
||||||
|
|
||||||
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
|
For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
|
||||||
|
|
29
doc/examples/basic_json__value.cpp
Normal file
29
doc/examples/basic_json__value.cpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#include <json.hpp>
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
// create a JSON object with different entry types
|
||||||
|
json j =
|
||||||
|
{
|
||||||
|
{"integer", 1},
|
||||||
|
{"floating", 42.23},
|
||||||
|
{"string", "hello world"},
|
||||||
|
{"boolean", true},
|
||||||
|
{"object", {{"key1", 1}, {"key2", 2}}},
|
||||||
|
{"array", {1, 2, 3}}
|
||||||
|
};
|
||||||
|
|
||||||
|
// access existing values
|
||||||
|
int v_integer = j.value("integer", 0);
|
||||||
|
double v_floating = j.value("floating", 47.11);
|
||||||
|
|
||||||
|
// access nonexisting values and rely on default value
|
||||||
|
std::string v_string = j.value("nonexisting", "oops");
|
||||||
|
bool v_boolean = j.value("nonexisting", false);
|
||||||
|
|
||||||
|
// output values
|
||||||
|
std::cout << std::boolalpha << v_integer << " " << v_floating
|
||||||
|
<< " " << v_string << " " << v_boolean << "\n";
|
||||||
|
}
|
1
doc/examples/basic_json__value.link
Normal file
1
doc/examples/basic_json__value.link
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<a target="_blank" href="http://melpon.org/wandbox/permlink/pehnjdIduvv90qfX"><b>online</b></a>
|
1
doc/examples/basic_json__value.output
Normal file
1
doc/examples/basic_json__value.output
Normal file
|
@ -0,0 +1 @@
|
||||||
|
1 42.23 oops false
|
102
src/json.hpp
102
src/json.hpp
|
@ -2514,6 +2514,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read and
|
@liveexample{The example below shows how object elements can be read and
|
||||||
written using at.,at__object_t_key_type}
|
written using at.,at__object_t_key_type}
|
||||||
|
|
||||||
|
@sa @ref operator[](const typename object_t::key_type&) for unchecked
|
||||||
|
access by reference
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
reference at(const typename object_t::key_type& key)
|
reference at(const typename object_t::key_type& key)
|
||||||
{
|
{
|
||||||
|
@ -2546,6 +2550,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read using
|
@liveexample{The example below shows how object elements can be read using
|
||||||
at.,at__object_t_key_type_const}
|
at.,at__object_t_key_type_const}
|
||||||
|
|
||||||
|
@sa @ref operator[](const typename object_t::key_type&) for unchecked
|
||||||
|
access by reference
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
const_reference at(const typename object_t::key_type& key) const
|
const_reference at(const typename object_t::key_type& key) const
|
||||||
{
|
{
|
||||||
|
@ -2655,6 +2663,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read and
|
@liveexample{The example below shows how object elements can be read and
|
||||||
written using the [] operator.,operatorarray__key_type}
|
written using the [] operator.,operatorarray__key_type}
|
||||||
|
|
||||||
|
@sa @ref at(const typename object_t::key_type&) for access by reference
|
||||||
|
with range checking
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
reference operator[](const typename object_t::key_type& key)
|
reference operator[](const typename object_t::key_type& key)
|
||||||
{
|
{
|
||||||
|
@ -2697,6 +2709,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read and
|
@liveexample{The example below shows how object elements can be read and
|
||||||
written using the [] operator.,operatorarray__key_type}
|
written using the [] operator.,operatorarray__key_type}
|
||||||
|
|
||||||
|
@sa @ref at(const typename object_t::key_type&) for access by reference
|
||||||
|
with range checking
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
template<typename T, std::size_t n>
|
template<typename T, std::size_t n>
|
||||||
reference operator[](const T (&key)[n])
|
reference operator[](const T (&key)[n])
|
||||||
|
@ -2719,6 +2735,86 @@ class basic_json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief access specified object element with default value
|
||||||
|
|
||||||
|
Returns either a copy of an object's element at the specified key @a key or
|
||||||
|
a given default value if no element with key @a key exists.
|
||||||
|
|
||||||
|
The function is basically equivalent to executing
|
||||||
|
@code{.cpp}
|
||||||
|
try {
|
||||||
|
return at(key);
|
||||||
|
} catch(std::out_of_range) {
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@note Unlike @ref at(const typename object_t::key_type&), this function
|
||||||
|
does not throw if the given key @a key was not found.
|
||||||
|
|
||||||
|
@note Unlike @ref operator[](const typename object_t::key_type& key), this
|
||||||
|
function does not implicitly add an element to the position defined by @a
|
||||||
|
key. This function is furthermore also applicable to const objects.
|
||||||
|
|
||||||
|
@param[in] key key of the element to access
|
||||||
|
@param[in] default_value the value to return if @a key is not found
|
||||||
|
|
||||||
|
@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.
|
||||||
|
|
||||||
|
@return copy of the element at key @a key or @a default_value if @a key
|
||||||
|
is not found
|
||||||
|
|
||||||
|
@throw std::domain_error if JSON is not an object
|
||||||
|
|
||||||
|
@complexity Logarithmic in the size of the container.
|
||||||
|
|
||||||
|
@liveexample{The example below shows how object elements can be queried
|
||||||
|
with a default value.,basic_json__value}
|
||||||
|
|
||||||
|
@sa @ref at(const typename object_t::key_type&) for access by reference
|
||||||
|
with range checking
|
||||||
|
@sa @ref operator[](const typename object_t::key_type&) for unchecked
|
||||||
|
access by reference
|
||||||
|
*/
|
||||||
|
template <class ValueType, typename
|
||||||
|
std::enable_if<
|
||||||
|
std::is_convertible<basic_json_t, ValueType>::value
|
||||||
|
, int>::type = 0>
|
||||||
|
ValueType value(const typename object_t::key_type& key, ValueType default_value) const
|
||||||
|
{
|
||||||
|
// at only works for objects
|
||||||
|
if (is_object())
|
||||||
|
{
|
||||||
|
// if key is found, return value and given default value otherwise
|
||||||
|
const auto it = find(key);
|
||||||
|
if (it != end())
|
||||||
|
{
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::domain_error("cannot use value() with " + type_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
// overload for a default value of type const char*
|
||||||
|
@copydoc basic_json::value()
|
||||||
|
*/
|
||||||
|
string_t value(const typename object_t::key_type& key, const char* default_value) const
|
||||||
|
{
|
||||||
|
return value(key, string_t(default_value));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief access the first element
|
@brief access the first element
|
||||||
|
|
||||||
|
@ -5660,9 +5756,9 @@ class basic_json
|
||||||
/*!
|
/*!
|
||||||
@brief wrapper to access iterator member functions in range-based for
|
@brief wrapper to access iterator member functions in range-based for
|
||||||
|
|
||||||
This class allows to access @ref key() and @ref value() during range-based
|
This class allows to access @ref iterator::key() and @ref iterator::value()
|
||||||
for loops. In these loops, a reference to the JSON values is returned, so
|
during range-based for loops. In these loops, a reference to the JSON
|
||||||
there is no access to the underlying iterator.
|
values is returned, so there is no access to the underlying iterator.
|
||||||
*/
|
*/
|
||||||
class iterator_wrapper
|
class iterator_wrapper
|
||||||
{
|
{
|
||||||
|
|
|
@ -2514,6 +2514,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read and
|
@liveexample{The example below shows how object elements can be read and
|
||||||
written using at.,at__object_t_key_type}
|
written using at.,at__object_t_key_type}
|
||||||
|
|
||||||
|
@sa @ref operator[](const typename object_t::key_type&) for unchecked
|
||||||
|
access by reference
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
reference at(const typename object_t::key_type& key)
|
reference at(const typename object_t::key_type& key)
|
||||||
{
|
{
|
||||||
|
@ -2546,6 +2550,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read using
|
@liveexample{The example below shows how object elements can be read using
|
||||||
at.,at__object_t_key_type_const}
|
at.,at__object_t_key_type_const}
|
||||||
|
|
||||||
|
@sa @ref operator[](const typename object_t::key_type&) for unchecked
|
||||||
|
access by reference
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
const_reference at(const typename object_t::key_type& key) const
|
const_reference at(const typename object_t::key_type& key) const
|
||||||
{
|
{
|
||||||
|
@ -2655,6 +2663,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read and
|
@liveexample{The example below shows how object elements can be read and
|
||||||
written using the [] operator.,operatorarray__key_type}
|
written using the [] operator.,operatorarray__key_type}
|
||||||
|
|
||||||
|
@sa @ref at(const typename object_t::key_type&) for access by reference
|
||||||
|
with range checking
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
reference operator[](const typename object_t::key_type& key)
|
reference operator[](const typename object_t::key_type& key)
|
||||||
{
|
{
|
||||||
|
@ -2697,6 +2709,10 @@ class basic_json
|
||||||
|
|
||||||
@liveexample{The example below shows how object elements can be read and
|
@liveexample{The example below shows how object elements can be read and
|
||||||
written using the [] operator.,operatorarray__key_type}
|
written using the [] operator.,operatorarray__key_type}
|
||||||
|
|
||||||
|
@sa @ref at(const typename object_t::key_type&) for access by reference
|
||||||
|
with range checking
|
||||||
|
@sa @ref value() for access by value with a default value
|
||||||
*/
|
*/
|
||||||
template<typename T, std::size_t n>
|
template<typename T, std::size_t n>
|
||||||
reference operator[](const T (&key)[n])
|
reference operator[](const T (&key)[n])
|
||||||
|
@ -2719,6 +2735,86 @@ class basic_json
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief access specified object element with default value
|
||||||
|
|
||||||
|
Returns either a copy of an object's element at the specified key @a key or
|
||||||
|
a given default value if no element with key @a key exists.
|
||||||
|
|
||||||
|
The function is basically equivalent to executing
|
||||||
|
@code{.cpp}
|
||||||
|
try {
|
||||||
|
return at(key);
|
||||||
|
} catch(std::out_of_range) {
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@note Unlike @ref at(const typename object_t::key_type&), this function
|
||||||
|
does not throw if the given key @a key was not found.
|
||||||
|
|
||||||
|
@note Unlike @ref operator[](const typename object_t::key_type& key), this
|
||||||
|
function does not implicitly add an element to the position defined by @a
|
||||||
|
key. This function is furthermore also applicable to const objects.
|
||||||
|
|
||||||
|
@param[in] key key of the element to access
|
||||||
|
@param[in] default_value the value to return if @a key is not found
|
||||||
|
|
||||||
|
@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.
|
||||||
|
|
||||||
|
@return copy of the element at key @a key or @a default_value if @a key
|
||||||
|
is not found
|
||||||
|
|
||||||
|
@throw std::domain_error if JSON is not an object
|
||||||
|
|
||||||
|
@complexity Logarithmic in the size of the container.
|
||||||
|
|
||||||
|
@liveexample{The example below shows how object elements can be queried
|
||||||
|
with a default value.,basic_json__value}
|
||||||
|
|
||||||
|
@sa @ref at(const typename object_t::key_type&) for access by reference
|
||||||
|
with range checking
|
||||||
|
@sa @ref operator[](const typename object_t::key_type&) for unchecked
|
||||||
|
access by reference
|
||||||
|
*/
|
||||||
|
template <class ValueType, typename
|
||||||
|
std::enable_if<
|
||||||
|
std::is_convertible<basic_json_t, ValueType>::value
|
||||||
|
, int>::type = 0>
|
||||||
|
ValueType value(const typename object_t::key_type& key, ValueType default_value) const
|
||||||
|
{
|
||||||
|
// at only works for objects
|
||||||
|
if (is_object())
|
||||||
|
{
|
||||||
|
// if key is found, return value and given default value otherwise
|
||||||
|
const auto it = find(key);
|
||||||
|
if (it != end())
|
||||||
|
{
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw std::domain_error("cannot use value() with " + type_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
// overload for a default value of type const char*
|
||||||
|
@copydoc basic_json::value()
|
||||||
|
*/
|
||||||
|
string_t value(const typename object_t::key_type& key, const char* default_value) const
|
||||||
|
{
|
||||||
|
return value(key, string_t(default_value));
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief access the first element
|
@brief access the first element
|
||||||
|
|
||||||
|
@ -5660,9 +5756,9 @@ class basic_json
|
||||||
/*!
|
/*!
|
||||||
@brief wrapper to access iterator member functions in range-based for
|
@brief wrapper to access iterator member functions in range-based for
|
||||||
|
|
||||||
This class allows to access @ref key() and @ref value() during range-based
|
This class allows to access @ref iterator::key() and @ref iterator::value()
|
||||||
for loops. In these loops, a reference to the JSON values is returned, so
|
during range-based for loops. In these loops, a reference to the JSON
|
||||||
there is no access to the underlying iterator.
|
values is returned, so there is no access to the underlying iterator.
|
||||||
*/
|
*/
|
||||||
class iterator_wrapper
|
class iterator_wrapper
|
||||||
{
|
{
|
||||||
|
|
|
@ -3082,6 +3082,101 @@ TEST_CASE("element access")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("access specified element with default value")
|
||||||
|
{
|
||||||
|
SECTION("access existing value")
|
||||||
|
{
|
||||||
|
CHECK(j.value("integer", 2) == 1);
|
||||||
|
CHECK(j.value("integer", 1.0) == Approx(1));
|
||||||
|
CHECK(j.value("null", json(1)) == json());
|
||||||
|
CHECK(j.value("boolean", false) == true);
|
||||||
|
CHECK(j.value("string", "bar") == "hello world");
|
||||||
|
CHECK(j.value("string", std::string("bar")) == "hello world");
|
||||||
|
CHECK(j.value("floating", 12.34) == Approx(42.23));
|
||||||
|
CHECK(j.value("floating", 12) == 42);
|
||||||
|
CHECK(j.value("object", json({{"foo", "bar"}})) == json(json::object()));
|
||||||
|
CHECK(j.value("array", json({10, 100})) == json({1, 2, 3}));
|
||||||
|
|
||||||
|
CHECK(j_const.value("integer", 2) == 1);
|
||||||
|
CHECK(j_const.value("integer", 1.0) == Approx(1));
|
||||||
|
CHECK(j_const.value("boolean", false) == true);
|
||||||
|
CHECK(j_const.value("string", "bar") == "hello world");
|
||||||
|
CHECK(j_const.value("string", std::string("bar")) == "hello world");
|
||||||
|
CHECK(j_const.value("floating", 12.34) == Approx(42.23));
|
||||||
|
CHECK(j_const.value("floating", 12) == 42);
|
||||||
|
CHECK(j_const.value("object", json({{"foo", "bar"}})) == json(json::object()));
|
||||||
|
CHECK(j_const.value("array", json({10, 100})) == json({1, 2, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("access non-existing value")
|
||||||
|
{
|
||||||
|
CHECK(j.value("_", 2) == 2);
|
||||||
|
CHECK(j.value("_", false) == false);
|
||||||
|
CHECK(j.value("_", "bar") == "bar");
|
||||||
|
CHECK(j.value("_", 12.34) == Approx(12.34));
|
||||||
|
CHECK(j.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
|
||||||
|
CHECK(j.value("_", json({10, 100})) == json({10, 100}));
|
||||||
|
|
||||||
|
CHECK(j_const.value("_", 2) == 2);
|
||||||
|
CHECK(j_const.value("_", false) == false);
|
||||||
|
CHECK(j_const.value("_", "bar") == "bar");
|
||||||
|
CHECK(j_const.value("_", 12.34) == Approx(12.34));
|
||||||
|
CHECK(j_const.value("_", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
|
||||||
|
CHECK(j_const.value("_", 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", 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("boolean")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::boolean);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("string")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::string);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("array")
|
||||||
|
{
|
||||||
|
json j_nonobject(json::value_t::array);
|
||||||
|
const json j_nonobject_const(j_nonobject);
|
||||||
|
CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
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", 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
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", 1), std::domain_error);
|
||||||
|
CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("front and back")
|
SECTION("front and back")
|
||||||
{
|
{
|
||||||
// "array" is the smallest key
|
// "array" is the smallest key
|
||||||
|
|
Loading…
Reference in a new issue