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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*!
 | 
			
		||||
    @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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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"));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -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"));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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"));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -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"));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -438,6 +438,7 @@ TEST_CASE("JSON pointers")
 | 
			
		|||
                })
 | 
			
		||||
        {
 | 
			
		||||
            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 =
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -492,24 +565,31 @@ TEST_CASE("JSON pointers")
 | 
			
		|||
        CHECK(j[ptr] == j);
 | 
			
		||||
 | 
			
		||||
        // simple field access
 | 
			
		||||
        ptr.push_back("pi");
 | 
			
		||||
        ptr = ptr / "pi";
 | 
			
		||||
        CHECK(j[ptr] == j["pi"]);
 | 
			
		||||
 | 
			
		||||
        ptr.pop_back();
 | 
			
		||||
        CHECK(j[ptr] == j);
 | 
			
		||||
 | 
			
		||||
        // object and children access
 | 
			
		||||
        ptr.push_back("answer");
 | 
			
		||||
        ptr.push_back("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.push_back("object");
 | 
			
		||||
        ptr.push_back("/");
 | 
			
		||||
        ptr /= "object";
 | 
			
		||||
        ptr = ptr / "/";
 | 
			
		||||
        CHECK(j[ptr] == j["object"]["/"]);
 | 
			
		||||
        CHECK(ptr.to_string() == "/object/~1");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue