diff --git a/include/boost/json/impl/pointer.ipp b/include/boost/json/impl/pointer.ipp index 6dc62c29..4f830803 100644 --- a/include/boost/json/impl/pointer.ipp +++ b/include/boost/json/impl/pointer.ipp @@ -341,13 +341,23 @@ walk_pointer( } // namespace detail -value const& -value::at_pointer(string_view ptr) const& +system::result +value::try_at_pointer(string_view ptr) const noexcept { system::error_code ec; auto const found = find_pointer(ptr, ec); if( !found ) - detail::throw_system_error( ec ); + return ec; + return *found; +} + +system::result +value::try_at_pointer(string_view ptr) noexcept +{ + system::error_code ec; + auto const found = find_pointer(ptr, ec); + if( !found ) + return ec; return *found; } @@ -484,15 +494,24 @@ value::set_at_pointer( return result; } +system::result +value::try_set_at_pointer( + string_view sv, + value_ref ref, + set_pointer_options const& opts ) +{ + system::error_code ec; + value* result = set_at_pointer( sv, ref, ec, opts ); + if( result ) + return *result; + return ec; +} + value& value::set_at_pointer( string_view sv, value_ref ref, set_pointer_options const& opts ) { - system::error_code ec; - value* result = set_at_pointer( sv, ref, ec, opts ); - if( !result ) - detail::throw_system_error( ec ); - return *result; + return try_set_at_pointer(sv, ref, opts).value(); } } // namespace json diff --git a/include/boost/json/impl/value.hpp b/include/boost/json/impl/value.hpp index 92e14a29..0969163f 100644 --- a/include/boost/json/impl/value.hpp +++ b/include/boost/json/impl/value.hpp @@ -13,17 +13,22 @@ namespace boost { namespace json { +value const& +value::at_pointer(string_view ptr) const& +{ + return try_at_pointer(ptr).value(); +} + value& value::at_pointer(string_view ptr) & { - auto const& self = *this; - return const_cast( self.at_pointer(ptr) ); + return try_at_pointer(ptr).value(); } value&& value::at_pointer(string_view ptr) && { - return std::move( this->at_pointer(ptr) ); + return std::move( try_at_pointer(ptr).value() ); } } // namespace json diff --git a/include/boost/json/value.hpp b/include/boost/json/value.hpp index a39d82c8..24a90ac9 100644 --- a/include/boost/json/value.hpp +++ b/include/boost/json/value.hpp @@ -3284,6 +3284,53 @@ public: } /** @} */ + /** Access an element via JSON Pointer. + + This function is used to access a (potentially nested) element of the + value using a JSON Pointer string. + + @par Complexity + Linear in the sizes of `ptr` and underlying array, object, or string. + + @par Exception Safety + No-throw guarantee. + + @param ptr JSON Pointer string. + + @return `boost::system::result` containing either a reference + to the element identified by `ptr` or a corresponding `error_code`. + + @see + [RFC 6901 - JavaScript Object Notation (JSON) Pointer](https://datatracker.ietf.org/doc/html/rfc6901). + */ + BOOST_JSON_DECL + system::result + try_at_pointer(string_view ptr) const noexcept; + + /** Access an element via JSON Pointer. + + This function is used to access a (potentially nested) element of the + value using a JSON Pointer string. + + @par Complexity + Linear in the sizes of `ptr` and underlying array, object, or string. + + @par Exception Safety + No-throw guarantee. + + @param ptr JSON Pointer string. + + @return `boost::system::result` containing either a + reference to the element identified by `ptr` or a corresponding + `error_code`. + + @see + [RFC 6901 - JavaScript Object Notation (JSON) Pointer](https://datatracker.ietf.org/doc/html/rfc6901). + */ + BOOST_JSON_DECL + system::result + try_at_pointer(string_view ptr) noexcept; + /** Access an element via JSON Pointer. This function is used to access a (potentially nested) @@ -3306,7 +3353,7 @@ public: RFC 6901 - JavaScript Object Notation (JSON) Pointer */ /** @{ */ - BOOST_JSON_DECL + inline value const& at_pointer(string_view ptr) const&; @@ -3360,6 +3407,72 @@ public: //------------------------------------------------------ + /** Set an element via JSON Pointer. + + This function is used to insert or assign to a potentially nested + element of the value using a JSON Pointer string. The function may + create intermediate elements corresponding to pointer segments. +
+ + The particular conditions when and what kind of intermediate element + is created is governed by the `ptr` parameter. + + Each pointer token is considered in sequence. For each token + + - if the containing value is an @ref object, then a new `null` + element is created with key equal to unescaped token string; + otherwise + + - if the containing value is an @ref array, and the token represents a + past-the-end marker, then a `null` element is appended to the array; + otherwise + + - if the containing value is an @ref array, and the token represents a + number, then if the difference between the number and array's size + is smaller than `opts.max_created_elements`, then the size of the + array is increased, so that the number can reference an element in the + array; otherwise + + - if the containing value is of different @ref kind and + `opts.replace_any_scalar` is `true`, or the value is `null`, then + + - if `opts.create_arrays` is `true` and the token either represents + past-the-end marker or a number, then the value is replaced with + an empty array and the token is considered again; otherwise + + - if `opts.create_objects` is `true`, then the value is replaced + with an empty object and the token is considered again; otherwise + + - an error is produced. + + @par Complexity + Linear in the sum of size of `ptr`, size of underlying array, object, + or string and `opts.max_created_elements`. + + @par Exception Safety + Basic guarantee. + Calls to `memory_resource::allocate` may throw. + + @param sv JSON Pointer string. + + @param ref The value to assign to pointed element. + + @param opts The options for the algorithm. + + @return `boost::json::result` containing either a reference to + the element identified by `ptr` or a corresponding `error_code`. + + @see + @ref set_pointer_options, + [RFC 6901 - JavaScript Object Notation (JSON) Pointer](https://datatracker.ietf.org/doc/html/rfc6901). + */ + BOOST_JSON_DECL + system::result + try_set_at_pointer( + string_view sv, + value_ref ref, + set_pointer_options const& opts = {} ); + /** Set an element via JSON Pointer. This function is used to insert or assign to a potentially nested diff --git a/test/pointer.cpp b/test/pointer.cpp index 55a2d398..5a0e593e 100644 --- a/test/pointer.cpp +++ b/test/pointer.cpp @@ -346,6 +346,22 @@ public: BOOST_TEST( hasLocation(ec) ); } + void + testTry() + { + value jv = testValue(); + BOOST_TEST( &jv.try_at_pointer("/foo").value() == &jv.at("foo") ); + BOOST_TEST_THROWS_WITH_LOCATION( jv.try_at_pointer("foo").value() ); + + value const& cjv = jv; + BOOST_TEST( &cjv.try_at_pointer("/foo").value() == &cjv.at("foo") ); + BOOST_TEST_THROWS_WITH_LOCATION( cjv.try_at_pointer("foo").value() ); + + auto result = jv.try_set_at_pointer("", array()); + BOOST_TEST(( jv == array() )); + BOOST_TEST( &*result == &jv ); + } + void run() { @@ -359,6 +375,7 @@ public: testSet(); testSetNonThrowing(); testSetNonThrowing(); + testTry(); } };