Skip to content

Commit

Permalink
Merge pull request #1469 from garethsb-sony/json_pointer-append
Browse files Browse the repository at this point in the history
Add operator/= and operator/ to construct a JSON pointer by appending two JSON pointers
  • Loading branch information
nlohmann authored Mar 11, 2019
2 parents c11bead + d183bd0 commit c983b67
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 22 deletions.
84 changes: 75 additions & 9 deletions include/nlohmann/detail/json_pointer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,67 @@ class json_pointer
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
Expand All @@ -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
*/
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"));
}
Expand All @@ -114,24 +175,29 @@ class json_pointer
}

/*!
@brief remove and return last reference pointer
@throw out_of_range.405 if JSON pointer has no parent
@brief append an unescaped token at the end of the reference pointer
*/
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
bool is_root() const noexcept
bool empty() const noexcept
{
return reference_tokens.empty();
}

private:
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"));
}
Expand Down
2 changes: 1 addition & 1 deletion include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7493,7 +7493,7 @@ class basic_json
const auto operation_add = [&result](json_pointer & ptr, basic_json val)
{
// adding to the root of the target document means replacing it
if (ptr.is_root())
if (ptr.empty())
{
result = val;
}
Expand Down
86 changes: 76 additions & 10 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11896,6 +11896,67 @@ class json_pointer
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

Expand All @@ -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
*/
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"));
}
Expand All @@ -11934,24 +11995,29 @@ class json_pointer
}

/*!
@brief remove and return last reference pointer
@throw out_of_range.405 if JSON pointer has no parent
@brief append an unescaped token at the end of the reference pointer
*/
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
bool is_root() const noexcept
bool empty() const noexcept
{
return reference_tokens.empty();
}

private:
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"));
}
Expand Down Expand Up @@ -20002,7 +20068,7 @@ class basic_json
const auto operation_add = [&result](json_pointer & ptr, basic_json val)
{
// adding to the root of the target document means replacing it
if (ptr.is_root())
if (ptr.empty())
{
result = val;
}
Expand Down
84 changes: 82 additions & 2 deletions test/src/unit-json_pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ TEST_CASE("JSON pointers")
})
{
CHECK(json::json_pointer(ptr).to_string() == ptr);
CHECK(std::string(json::json_pointer(ptr)) == ptr);
}
}

Expand All @@ -460,7 +461,7 @@ TEST_CASE("JSON pointers")
}
}

SECTION("push and pop")
SECTION("empty, push, pop and parent")
{
const json j =
{
Expand Down Expand Up @@ -489,28 +490,107 @@ TEST_CASE("JSON pointers")

// 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
ptr.push_back("answer");
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 =
{
{"", "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(j[ptr] == j);

// simple field access
ptr = ptr / "pi";
CHECK(j[ptr] == j["pi"]);

ptr.pop_back();
CHECK(j[ptr] == j);

// object and children access
const std::string answer("answer");
ptr /= answer;
ptr = ptr / "everything";
CHECK(j[ptr] == j["answer"]["everything"]);

ptr.pop_back();
ptr.pop_back();
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
ptr /= "object";
ptr = ptr / "/";
CHECK(j[ptr] == j["object"]["/"]);
CHECK(ptr.to_string() == "/object/~1");
}
}

0 comments on commit c983b67

Please sign in to comment.