Merge pull request #1469 from garethsb-sony/json_pointer-append
Add operator/= and operator/ to construct a JSON pointer by appending two JSON pointers
This commit is contained in:
commit
c983b67112
4 changed files with 238 additions and 26 deletions
|
@ -76,6 +76,67 @@ class json_pointer
|
||||||
return to_string();
|
return to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief append another JSON pointer at the end of this JSON pointer
|
||||||
|
*/
|
||||||
|
json_pointer& operator/=(const json_pointer& ptr)
|
||||||
|
{
|
||||||
|
reference_tokens.insert(reference_tokens.end(), ptr.reference_tokens.begin(), ptr.reference_tokens.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @copydoc push_back(std::string&&)
|
||||||
|
json_pointer& operator/=(std::string token)
|
||||||
|
{
|
||||||
|
push_back(std::move(token));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @copydoc operator/=(std::string)
|
||||||
|
json_pointer& operator/=(std::size_t array_index)
|
||||||
|
{
|
||||||
|
return *this /= std::to_string(array_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||||
|
*/
|
||||||
|
friend json_pointer operator/(const json_pointer& left_ptr, const json_pointer& right_ptr)
|
||||||
|
{
|
||||||
|
return json_pointer(left_ptr) /= right_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
|
||||||
|
*/
|
||||||
|
friend json_pointer operator/(const json_pointer& ptr, std::string token)
|
||||||
|
{
|
||||||
|
return json_pointer(ptr) /= std::move(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
|
||||||
|
*/
|
||||||
|
friend json_pointer operator/(const json_pointer& lhs, std::size_t array_index)
|
||||||
|
{
|
||||||
|
return json_pointer(lhs) /= array_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer that is the parent of this JSON pointer
|
||||||
|
*/
|
||||||
|
json_pointer parent_pointer() const
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_pointer res = *this;
|
||||||
|
res.pop_back();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] s reference token to be converted into an array index
|
@param[in] s reference token to be converted into an array index
|
||||||
|
|
||||||
|
@ -98,12 +159,12 @@ class json_pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief remove and return last reference pointer
|
@brief remove and return last reference token
|
||||||
@throw out_of_range.405 if JSON pointer has no parent
|
@throw out_of_range.405 if JSON pointer has no parent
|
||||||
*/
|
*/
|
||||||
std::string pop_back()
|
std::string pop_back()
|
||||||
{
|
{
|
||||||
if (JSON_UNLIKELY(is_root()))
|
if (JSON_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
||||||
}
|
}
|
||||||
|
@ -114,24 +175,29 @@ class json_pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief remove and return last reference pointer
|
@brief append an unescaped token at the end of the reference pointer
|
||||||
@throw out_of_range.405 if JSON pointer has no parent
|
|
||||||
*/
|
*/
|
||||||
void push_back(const std::string& tok)
|
void push_back(const std::string& token)
|
||||||
{
|
{
|
||||||
reference_tokens.push_back(tok);
|
reference_tokens.push_back(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @copydoc push_back(const std::string&)
|
||||||
|
void push_back(std::string&& token)
|
||||||
|
{
|
||||||
|
reference_tokens.push_back(std::move(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
/// return whether pointer points to the root document
|
/// return whether pointer points to the root document
|
||||||
bool is_root() const noexcept
|
bool empty() const noexcept
|
||||||
{
|
{
|
||||||
return reference_tokens.empty();
|
return reference_tokens.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
json_pointer top() const
|
json_pointer top() const
|
||||||
{
|
{
|
||||||
if (JSON_UNLIKELY(is_root()))
|
if (JSON_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7493,7 +7493,7 @@ class basic_json
|
||||||
const auto operation_add = [&result](json_pointer & ptr, basic_json val)
|
const auto operation_add = [&result](json_pointer & ptr, basic_json val)
|
||||||
{
|
{
|
||||||
// adding to the root of the target document means replacing it
|
// adding to the root of the target document means replacing it
|
||||||
if (ptr.is_root())
|
if (ptr.empty())
|
||||||
{
|
{
|
||||||
result = val;
|
result = val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11896,6 +11896,67 @@ class json_pointer
|
||||||
return to_string();
|
return to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief append another JSON pointer at the end of this JSON pointer
|
||||||
|
*/
|
||||||
|
json_pointer& operator/=(const json_pointer& ptr)
|
||||||
|
{
|
||||||
|
reference_tokens.insert(reference_tokens.end(), ptr.reference_tokens.begin(), ptr.reference_tokens.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @copydoc push_back(std::string&&)
|
||||||
|
json_pointer& operator/=(std::string token)
|
||||||
|
{
|
||||||
|
push_back(std::move(token));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @copydoc operator/=(std::string)
|
||||||
|
json_pointer& operator/=(std::size_t array_index)
|
||||||
|
{
|
||||||
|
return *this /= std::to_string(array_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||||
|
*/
|
||||||
|
friend json_pointer operator/(const json_pointer& left_ptr, const json_pointer& right_ptr)
|
||||||
|
{
|
||||||
|
return json_pointer(left_ptr) /= right_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
|
||||||
|
*/
|
||||||
|
friend json_pointer operator/(const json_pointer& ptr, std::string token)
|
||||||
|
{
|
||||||
|
return json_pointer(ptr) /= std::move(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
|
||||||
|
*/
|
||||||
|
friend json_pointer operator/(const json_pointer& lhs, std::size_t array_index)
|
||||||
|
{
|
||||||
|
return json_pointer(lhs) /= array_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief create a new JSON pointer that is the parent of this JSON pointer
|
||||||
|
*/
|
||||||
|
json_pointer parent_pointer() const
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_pointer res = *this;
|
||||||
|
res.pop_back();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@param[in] s reference token to be converted into an array index
|
@param[in] s reference token to be converted into an array index
|
||||||
|
|
||||||
|
@ -11918,12 +11979,12 @@ class json_pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief remove and return last reference pointer
|
@brief remove and return last reference token
|
||||||
@throw out_of_range.405 if JSON pointer has no parent
|
@throw out_of_range.405 if JSON pointer has no parent
|
||||||
*/
|
*/
|
||||||
std::string pop_back()
|
std::string pop_back()
|
||||||
{
|
{
|
||||||
if (JSON_UNLIKELY(is_root()))
|
if (JSON_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
||||||
}
|
}
|
||||||
|
@ -11934,24 +11995,29 @@ class json_pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief remove and return last reference pointer
|
@brief append an unescaped token at the end of the reference pointer
|
||||||
@throw out_of_range.405 if JSON pointer has no parent
|
|
||||||
*/
|
*/
|
||||||
void push_back(const std::string& tok)
|
void push_back(const std::string& token)
|
||||||
{
|
{
|
||||||
reference_tokens.push_back(tok);
|
reference_tokens.push_back(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @copydoc push_back(const std::string&)
|
||||||
|
void push_back(std::string&& token)
|
||||||
|
{
|
||||||
|
reference_tokens.push_back(std::move(token));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
/// return whether pointer points to the root document
|
/// return whether pointer points to the root document
|
||||||
bool is_root() const noexcept
|
bool empty() const noexcept
|
||||||
{
|
{
|
||||||
return reference_tokens.empty();
|
return reference_tokens.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
json_pointer top() const
|
json_pointer top() const
|
||||||
{
|
{
|
||||||
if (JSON_UNLIKELY(is_root()))
|
if (JSON_UNLIKELY(empty()))
|
||||||
{
|
{
|
||||||
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
|
||||||
}
|
}
|
||||||
|
@ -20002,7 +20068,7 @@ class basic_json
|
||||||
const auto operation_add = [&result](json_pointer & ptr, basic_json val)
|
const auto operation_add = [&result](json_pointer & ptr, basic_json val)
|
||||||
{
|
{
|
||||||
// adding to the root of the target document means replacing it
|
// adding to the root of the target document means replacing it
|
||||||
if (ptr.is_root())
|
if (ptr.empty())
|
||||||
{
|
{
|
||||||
result = val;
|
result = val;
|
||||||
}
|
}
|
||||||
|
|
|
@ -438,6 +438,7 @@ TEST_CASE("JSON pointers")
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CHECK(json::json_pointer(ptr).to_string() == ptr);
|
CHECK(json::json_pointer(ptr).to_string() == ptr);
|
||||||
|
CHECK(std::string(json::json_pointer(ptr)) == ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +461,79 @@ TEST_CASE("JSON pointers")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("push and pop")
|
SECTION("empty, push, pop and parent")
|
||||||
|
{
|
||||||
|
const json j =
|
||||||
|
{
|
||||||
|
{"", "Hello"},
|
||||||
|
{"pi", 3.141},
|
||||||
|
{"happy", true},
|
||||||
|
{"name", "Niels"},
|
||||||
|
{"nothing", nullptr},
|
||||||
|
{
|
||||||
|
"answer", {
|
||||||
|
{"everything", 42}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{"list", {1, 0, 2}},
|
||||||
|
{
|
||||||
|
"object", {
|
||||||
|
{"currency", "USD"},
|
||||||
|
{"value", 42.99},
|
||||||
|
{"", "empty string"},
|
||||||
|
{"/", "slash"},
|
||||||
|
{"~", "tilde"},
|
||||||
|
{"~1", "tilde1"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// empty json_pointer returns the root JSON-object
|
||||||
|
auto ptr = ""_json_pointer;
|
||||||
|
CHECK(ptr.empty());
|
||||||
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
|
// simple field access
|
||||||
|
ptr.push_back("pi");
|
||||||
|
CHECK(!ptr.empty());
|
||||||
|
CHECK(j[ptr] == j["pi"]);
|
||||||
|
|
||||||
|
ptr.pop_back();
|
||||||
|
CHECK(ptr.empty());
|
||||||
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
|
// object and children access
|
||||||
|
const std::string answer("answer");
|
||||||
|
ptr.push_back(answer);
|
||||||
|
ptr.push_back("everything");
|
||||||
|
CHECK(!ptr.empty());
|
||||||
|
CHECK(j[ptr] == j["answer"]["everything"]);
|
||||||
|
|
||||||
|
ptr.pop_back();
|
||||||
|
ptr.pop_back();
|
||||||
|
CHECK(ptr.empty());
|
||||||
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
|
// push key which has to be encoded
|
||||||
|
ptr.push_back("object");
|
||||||
|
ptr.push_back("/");
|
||||||
|
CHECK(j[ptr] == j["object"]["/"]);
|
||||||
|
CHECK(ptr.to_string() == "/object/~1");
|
||||||
|
|
||||||
|
CHECK(j[ptr.parent_pointer()] == j["object"]);
|
||||||
|
ptr = ptr.parent_pointer().parent_pointer();
|
||||||
|
CHECK(ptr.empty());
|
||||||
|
CHECK(j[ptr] == j);
|
||||||
|
// parent-pointer of the empty json_pointer is empty
|
||||||
|
ptr = ptr.parent_pointer();
|
||||||
|
CHECK(ptr.empty());
|
||||||
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH(ptr.pop_back(),
|
||||||
|
"[json.exception.out_of_range.405] JSON pointer has no parent");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("operators")
|
||||||
{
|
{
|
||||||
const json j =
|
const json j =
|
||||||
{
|
{
|
||||||
|
@ -492,24 +565,31 @@ TEST_CASE("JSON pointers")
|
||||||
CHECK(j[ptr] == j);
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
// simple field access
|
// simple field access
|
||||||
ptr.push_back("pi");
|
ptr = ptr / "pi";
|
||||||
CHECK(j[ptr] == j["pi"]);
|
CHECK(j[ptr] == j["pi"]);
|
||||||
|
|
||||||
ptr.pop_back();
|
ptr.pop_back();
|
||||||
CHECK(j[ptr] == j);
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
// object and children access
|
// object and children access
|
||||||
ptr.push_back("answer");
|
const std::string answer("answer");
|
||||||
ptr.push_back("everything");
|
ptr /= answer;
|
||||||
|
ptr = ptr / "everything";
|
||||||
CHECK(j[ptr] == j["answer"]["everything"]);
|
CHECK(j[ptr] == j["answer"]["everything"]);
|
||||||
|
|
||||||
ptr.pop_back();
|
ptr.pop_back();
|
||||||
ptr.pop_back();
|
ptr.pop_back();
|
||||||
CHECK(j[ptr] == j);
|
CHECK(j[ptr] == j);
|
||||||
|
|
||||||
|
CHECK(ptr / ""_json_pointer == ptr);
|
||||||
|
CHECK(j["/answer"_json_pointer / "/everything"_json_pointer] == j["answer"]["everything"]);
|
||||||
|
|
||||||
|
// list children access
|
||||||
|
CHECK(j["/list"_json_pointer / 1] == j["list"][1]);
|
||||||
|
|
||||||
// push key which has to be encoded
|
// push key which has to be encoded
|
||||||
ptr.push_back("object");
|
ptr /= "object";
|
||||||
ptr.push_back("/");
|
ptr = ptr / "/";
|
||||||
CHECK(j[ptr] == j["object"]["/"]);
|
CHECK(j[ptr] == j["object"]["/"]);
|
||||||
CHECK(ptr.to_string() == "/object/~1");
|
CHECK(ptr.to_string() == "/object/~1");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue