From a06e7f5d80af9b5a97f4a3ddcc25dd97c0b1d9dc Mon Sep 17 00:00:00 2001
From: Patrick Boettcher
Date: Thu, 24 Jan 2019 16:46:51 +0100
Subject: [PATCH 1/6] JSON-pointer: add operator+() returning a new
json_pointer
---
include/nlohmann/detail/json_pointer.hpp | 23 +++++++++-
single_include/nlohmann/json.hpp | 23 +++++++++-
test/src/unit-json_pointer.cpp | 54 ++++++++++++++++++++++++
3 files changed, 96 insertions(+), 4 deletions(-)
diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index 033625bc4c..705346c50c 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -114,14 +114,33 @@ class json_pointer
}
/*!
- @brief remove and return last reference pointer
- @throw out_of_range.405 if JSON pointer has no parent
+ @brief append a token at the end of the reference pointer
*/
void push_back(const std::string& tok)
{
reference_tokens.push_back(tok);
}
+ /*!
+ @brief append a key-token at the end of the reference pointer and return a new json-pointer.
+ */
+ json_pointer operator+(const std::string& tok) const
+ {
+ auto ptr = *this;
+ ptr.push_back(tok);
+ return ptr;
+ }
+
+ /*!
+ @brief append a array-index-token at the end of the reference pointer and return a new json-pointer.
+ */
+ json_pointer operator+(const size_t& index) const
+ {
+ auto ptr = *this;
+ ptr.push_back(std::to_string(index));
+ return ptr;
+ }
+
private:
/// return whether pointer points to the root document
bool is_root() const noexcept
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index ee72531b7b..d845f77ee4 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -11924,14 +11924,33 @@ class json_pointer
}
/*!
- @brief remove and return last reference pointer
- @throw out_of_range.405 if JSON pointer has no parent
+ @brief append a token at the end of the reference pointer
*/
void push_back(const std::string& tok)
{
reference_tokens.push_back(tok);
}
+ /*!
+ @brief append a key-token at the end of the reference pointer and return a new json-pointer.
+ */
+ json_pointer operator+(const std::string& tok) const
+ {
+ auto ptr = *this;
+ ptr.push_back(tok);
+ return ptr;
+ }
+
+ /*!
+ @brief append a array-index-token at the end of the reference pointer and return a new json-pointer.
+ */
+ json_pointer operator+(const size_t& index) const
+ {
+ auto ptr = *this;
+ ptr.push_back(std::to_string(index));
+ return ptr;
+ }
+
private:
/// return whether pointer points to the root document
bool is_root() const noexcept
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index 1bcd3938a7..373b2c2183 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -513,4 +513,58 @@ TEST_CASE("JSON pointers")
CHECK(j[ptr] == j["object"]["/"]);
CHECK(ptr.to_string() == "/object/~1");
}
+
+ 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
+ ptr = ptr + "answer";
+ ptr = ptr + "everything";
+ CHECK(j[ptr] == j["answer"]["everything"]);
+
+ ptr.pop_back();
+ ptr.pop_back();
+ CHECK(j[ptr] == j);
+
+ // push key which has to be encoded
+ ptr = ptr + "object";
+ ptr = ptr + "/";
+ CHECK(j[ptr] == j["object"]["/"]);
+ CHECK(ptr.to_string() == "/object/~1");
+ }
}
From c850e9d82db4af16d995a455d8c0d20fb4c8448d Mon Sep 17 00:00:00 2001
From: garethsb-sony
Date: Thu, 31 Jan 2019 19:15:36 +0000
Subject: [PATCH 2/6] Add operator/= and operator/ to construct a JSON pointer
by appending two JSON pointers, as well as convenience op/= and op= to append
a single unescaped token or array index; inspired by std::filesystem::path
---
include/nlohmann/detail/json_pointer.hpp | 74 +++++++++++++++++-------
single_include/nlohmann/json.hpp | 74 +++++++++++++++++-------
test/src/unit-json_pointer.cpp | 20 +++++--
3 files changed, 120 insertions(+), 48 deletions(-)
diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index 705346c50c..8ec4179e2d 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -76,6 +76,52 @@ 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;
+ }
+
/*!
@param[in] s reference token to be converted into an array index
@@ -98,7 +144,7 @@ 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()
@@ -114,31 +160,17 @@ class json_pointer
}
/*!
- @brief append a token at the end of the reference pointer
- */
- void push_back(const std::string& tok)
- {
- reference_tokens.push_back(tok);
- }
-
- /*!
- @brief append a key-token at the end of the reference pointer and return a new json-pointer.
+ @brief append an unescaped token at the end of the reference pointer
*/
- json_pointer operator+(const std::string& tok) const
+ void push_back(const std::string& token)
{
- auto ptr = *this;
- ptr.push_back(tok);
- return ptr;
+ reference_tokens.push_back(token);
}
- /*!
- @brief append a array-index-token at the end of the reference pointer and return a new json-pointer.
- */
- json_pointer operator+(const size_t& index) const
+ /// @copydoc push_back(const std::string&)
+ void push_back(std::string&& token)
{
- auto ptr = *this;
- ptr.push_back(std::to_string(index));
- return ptr;
+ reference_tokens.push_back(std::move(token));
}
private:
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index d845f77ee4..dd7236c842 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -11886,6 +11886,52 @@ 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;
+ }
+
/*!
@param[in] s reference token to be converted into an array index
@@ -11908,7 +11954,7 @@ 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()
@@ -11924,31 +11970,17 @@ class json_pointer
}
/*!
- @brief append a token at the end of the reference pointer
- */
- void push_back(const std::string& tok)
- {
- reference_tokens.push_back(tok);
- }
-
- /*!
- @brief append a key-token at the end of the reference pointer and return a new json-pointer.
+ @brief append an unescaped token at the end of the reference pointer
*/
- json_pointer operator+(const std::string& tok) const
+ void push_back(const std::string& token)
{
- auto ptr = *this;
- ptr.push_back(tok);
- return ptr;
+ reference_tokens.push_back(token);
}
- /*!
- @brief append a array-index-token at the end of the reference pointer and return a new json-pointer.
- */
- json_pointer operator+(const size_t& index) const
+ /// @copydoc push_back(const std::string&)
+ void push_back(std::string&& token)
{
- auto ptr = *this;
- ptr.push_back(std::to_string(index));
- return ptr;
+ reference_tokens.push_back(std::move(token));
}
private:
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index 373b2c2183..b16603a7b9 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -499,7 +499,8 @@ TEST_CASE("JSON pointers")
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(j[ptr] == j["answer"]["everything"]);
@@ -546,24 +547,31 @@ TEST_CASE("JSON pointers")
CHECK(j[ptr] == j);
// simple field access
- ptr = ptr + "pi";
+ ptr = ptr / "pi";
CHECK(j[ptr] == j["pi"]);
ptr.pop_back();
CHECK(j[ptr] == j);
// object and children access
- ptr = ptr + "answer";
- ptr = ptr + "everything";
+ 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 = ptr + "object";
- ptr = ptr + "/";
+ ptr /= "object";
+ ptr = ptr / "/";
CHECK(j[ptr] == j["object"]["/"]);
CHECK(ptr.to_string() == "/object/~1");
}
From 5da757bbb346749dba6bc21296f8c9e84b1adbda Mon Sep 17 00:00:00 2001
From: garethsb-sony
Date: Thu, 31 Jan 2019 23:39:12 +0000
Subject: [PATCH 3/6] Attempt to satisfy Coveralls by adding a test for
(unchanged) operator std::string
---
test/src/unit-json_pointer.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index b16603a7b9..765c6b4894 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -438,6 +438,7 @@ TEST_CASE("JSON pointers")
})
{
CHECK(json::json_pointer(ptr).to_string() == ptr);
+ CHECK(std::string(json::json_pointer(ptr)) == ptr);
}
}
From 164e0e54d97b445b39436ee337df64b75bbd0f60 Mon Sep 17 00:00:00 2001
From: garethsb-sony
Date: Mon, 25 Feb 2019 08:57:12 +0000
Subject: [PATCH 4/6] Rename private json_pointer::is_root as public
json_pointer::empty for consistency with std::filesystem::path
---
include/nlohmann/detail/json_pointer.hpp | 8 ++++----
include/nlohmann/json.hpp | 2 +-
single_include/nlohmann/json.hpp | 10 +++++-----
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index 8ec4179e2d..1efb504974 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -149,7 +149,7 @@ class json_pointer
*/
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"));
}
@@ -173,16 +173,16 @@ class json_pointer
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"));
}
diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp
index d13f8a9e8d..b10c9f5b27 100644
--- a/include/nlohmann/json.hpp
+++ b/include/nlohmann/json.hpp
@@ -7466,7 +7466,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;
}
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index dd7236c842..f3688a1cad 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -11959,7 +11959,7 @@ class json_pointer
*/
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"));
}
@@ -11983,16 +11983,16 @@ class json_pointer
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"));
}
@@ -20016,7 +20016,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;
}
From 08de9eeaca3b1b757586f32ff1932a62322f17e5 Mon Sep 17 00:00:00 2001
From: garethsb-sony
Date: Mon, 25 Feb 2019 09:10:45 +0000
Subject: [PATCH 5/6] Add json_pointer::parent_pointer (cf.
std::filesystem::path::parent_path)
---
include/nlohmann/detail/json_pointer.hpp | 15 +++++++++++++++
single_include/nlohmann/json.hpp | 15 +++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp
index 1efb504974..28a7e8f317 100644
--- a/include/nlohmann/detail/json_pointer.hpp
+++ b/include/nlohmann/detail/json_pointer.hpp
@@ -122,6 +122,21 @@ class json_pointer
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
diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp
index f3688a1cad..15ec3b86f1 100644
--- a/single_include/nlohmann/json.hpp
+++ b/single_include/nlohmann/json.hpp
@@ -11932,6 +11932,21 @@ class json_pointer
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
From d183bd0456e9ef459b1a26179a0215f4817179e6 Mon Sep 17 00:00:00 2001
From: garethsb-sony
Date: Mon, 25 Feb 2019 09:25:02 +0000
Subject: [PATCH 6/6] Tests for json_pointer::empty and
json_pointer::parent_pointer
---
test/src/unit-json_pointer.cpp | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/test/src/unit-json_pointer.cpp b/test/src/unit-json_pointer.cpp
index 765c6b4894..349bdf6cb4 100644
--- a/test/src/unit-json_pointer.cpp
+++ b/test/src/unit-json_pointer.cpp
@@ -461,7 +461,7 @@ TEST_CASE("JSON pointers")
}
}
- SECTION("push and pop")
+ SECTION("empty, push, pop and parent")
{
const json j =
{
@@ -490,23 +490,28 @@ 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
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
@@ -514,6 +519,18 @@ TEST_CASE("JSON pointers")
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")