started to implement #283
diff --git a/src/json.hpp b/src/json.hpp
index 614a700..a5bc864 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -3633,8 +3633,8 @@
/*!
@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.
+ 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}
@@ -3706,7 +3706,7 @@
/*!
@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
{
@@ -3714,6 +3714,53 @@
}
/*!
+ @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
Returns a reference to the first element in the container. For a JSON
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index a7c6f7e..2f5ecd2 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -3633,8 +3633,8 @@
/*!
@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.
+ 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}
@@ -3706,7 +3706,7 @@
/*!
@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
{
@@ -3714,6 +3714,53 @@
}
/*!
+ @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
Returns a reference to the first element in the container. For a JSON
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index 2a92b32..2952708 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -3768,123 +3768,176 @@
SECTION("access specified element with default value")
{
- SECTION("access existing value")
+ SECTION("given a key")
{
- CHECK(j.value("integer", 2) == 1);
- CHECK(j.value("integer", 1.0) == Approx(1));
- CHECK(j.value("unsigned", 2) == 1u);
- CHECK(j.value("unsigned", 1.0) == Approx(1u));
- 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}));
+ SECTION("access existing value")
+ {
+ CHECK(j.value("integer", 2) == 1);
+ CHECK(j.value("integer", 1.0) == Approx(1));
+ CHECK(j.value("unsigned", 2) == 1u);
+ CHECK(j.value("unsigned", 1.0) == Approx(1u));
+ 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("unsigned", 2) == 1u);
- CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
- 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}));
+ CHECK(j_const.value("integer", 2) == 1);
+ CHECK(j_const.value("integer", 1.0) == Approx(1));
+ CHECK(j_const.value("unsigned", 2) == 1u);
+ CHECK(j_const.value("unsigned", 1.0) == Approx(1u));
+ 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("_", 2u) == 2u);
+ 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("_", 2u) == 2u);
+ 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);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
+ CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
+ CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
+ CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+ }
+ }
}
- SECTION("access non-existing value")
+ SECTION("given a JSON pointer")
{
- CHECK(j.value("_", 2) == 2);
- CHECK(j.value("_", 2u) == 2u);
- 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("_", 2u) == 2u);
- 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")
+ SECTION("access existing value")
{
- 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);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
+ 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("boolean")
+ SECTION("access non-existing value")
{
- 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);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
- }
+ CHECK(j.value("/not/existing", 2) == 2);
+ CHECK(j.value("/not/existing", 2u) == 2u);
+ CHECK(j.value("/not/existing", false) == false);
+ CHECK(j.value("/not/existing", "bar") == "bar");
+ CHECK(j.value("/not/existing", 12.34) == Approx(12.34));
+ CHECK(j.value("/not/existing", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+ CHECK(j.value("/not/existing", json({10, 100})) == json({10, 100}));
- 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);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 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", 1), std::domain_error);
- CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
- CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
- CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+ CHECK(j_const.value("/not/existing", 2) == 2);
+ CHECK(j_const.value("/not/existing", 2u) == 2u);
+ CHECK(j_const.value("/not/existing", false) == false);
+ CHECK(j_const.value("/not/existing", "bar") == "bar");
+ CHECK(j_const.value("/not/existing", 12.34) == Approx(12.34));
+ CHECK(j_const.value("/not/existing", json({{"foo", "bar"}})) == json({{"foo", "bar"}}));
+ CHECK(j_const.value("/not/existing", json({10, 100})) == json({10, 100}));
}
}
}