1
0
mirror of https://github.com/nlohmann/json.git synced 2025-07-31 10:24:23 +03:00

♻️ allow patch and diff to be used with arbitrary string types (#4536)

This commit is contained in:
Niels Lohmann
2024-12-13 07:24:50 +01:00
committed by GitHub
parent e6cafa573a
commit 620034ecec
5 changed files with 150 additions and 45 deletions

View File

@ -10,7 +10,6 @@
#include <cstddef> // size_t
#include <iterator> // forward_iterator_tag
#include <string> // string, to_string
#include <tuple> // tuple_size, get, tuple_element
#include <utility> // move
@ -20,19 +19,13 @@
#include <nlohmann/detail/abi_macros.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/string_utils.hpp>
#include <nlohmann/detail/value_t.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
template<typename string_type>
void int_to_string( string_type& target, std::size_t value )
{
// For ADL
using std::to_string;
target = to_string(value);
}
template<typename IteratorType> class iteration_proxy_value
{
public:

View File

@ -0,0 +1,37 @@
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#pragma once
#include <cstddef> // size_t
#include <string> // string, to_string
#include <nlohmann/detail/abi_macros.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
template<typename StringType>
void int_to_string(StringType& target, std::size_t value)
{
// For ADL
using std::to_string;
target = to_string(value);
}
template<typename StringType>
StringType to_string(std::size_t value)
{
StringType result;
int_to_string(result, value);
return result;
}
} // namespace detail
NLOHMANN_JSON_NAMESPACE_END

View File

@ -52,6 +52,7 @@
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/string_concat.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/string_utils.hpp>
#include <nlohmann/detail/meta/cpp_future.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/output/binary_writer.hpp>
@ -4702,7 +4703,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// the valid JSON Patch operations
enum class patch_operations {add, remove, replace, move, copy, test, invalid};
const auto get_op = [](const std::string & op)
const auto get_op = [](const string_t& op)
{
if (op == "add")
{
@ -4839,8 +4840,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
for (const auto& val : json_patch)
{
// wrapper to get a value for an operation
const auto get_value = [&val](const std::string & op,
const std::string & member,
const auto get_value = [&val](const string_t& op,
const string_t& member,
bool string_type) -> basic_json &
{
// find value
@ -4874,8 +4875,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// collect mandatory members
const auto op = get_value("op", "op", true).template get<std::string>();
const auto path = get_value(op, "path", true).template get<std::string>();
const auto op = get_value("op", "op", true).template get<string_t>();
const auto path = get_value(op, "path", true).template get<string_t>();
json_pointer ptr(path);
switch (get_op(op))
@ -4901,7 +4902,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case patch_operations::move:
{
const auto from_path = get_value("move", "from", true).template get<std::string>();
const auto from_path = get_value("move", "from", true).template get<string_t>();
json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
@ -4918,7 +4919,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case patch_operations::copy:
{
const auto from_path = get_value("copy", "from", true).template get<std::string>();
const auto from_path = get_value("copy", "from", true).template get<string_t>();
const json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
@ -4978,7 +4979,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/diff/
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json diff(const basic_json& source, const basic_json& target,
const std::string& path = "")
const string_t& path = "")
{
// the patch
basic_json result(value_t::array);
@ -5008,7 +5009,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
while (i < source.size() && i < target.size())
{
// recursive call to compare array values at index i
auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
auto temp_diff = diff(source[i], target[i], detail::concat<string_t>(path, '/', detail::to_string<string_t>(i)));
result.insert(result.end(), temp_diff.begin(), temp_diff.end());
++i;
}
@ -5025,7 +5026,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result.insert(result.begin() + end_index, object(
{
{"op", "remove"},
{"path", detail::concat(path, '/', std::to_string(i))}
{"path", detail::concat<string_t>(path, '/', detail::to_string<string_t>(i))}
}));
++i;
}
@ -5036,7 +5037,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result.push_back(
{
{"op", "add"},
{"path", detail::concat(path, "/-")},
{"path", detail::concat<string_t>(path, "/-")},
{"value", target[i]}
});
++i;
@ -5051,7 +5052,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));
if (target.find(it.key()) != target.end())
{
@ -5075,7 +5076,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (source.find(it.key()) == source.end())
{
// found a key that is not in this -> add it
const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));
result.push_back(
{
{"op", "add"}, {"path", path_key},

View File

@ -5275,7 +5275,6 @@ NLOHMANN_JSON_NAMESPACE_END
#include <cstddef> // size_t
#include <iterator> // forward_iterator_tag
#include <string> // string, to_string
#include <tuple> // tuple_size, get, tuple_element
#include <utility> // move
@ -5287,6 +5286,46 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/meta/type_traits.hpp>
// #include <nlohmann/detail/string_utils.hpp>
// __ _____ _____ _____
// __| | __| | | | JSON for Modern C++
// | | |__ | | | | | | version 3.11.3
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013 - 2024 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT
#include <cstddef> // size_t
#include <string> // string, to_string
// #include <nlohmann/detail/abi_macros.hpp>
NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
template<typename StringType>
void int_to_string(StringType& target, std::size_t value)
{
// For ADL
using std::to_string;
target = to_string(value);
}
template<typename StringType>
StringType to_string(std::size_t value)
{
StringType result;
int_to_string(result, value);
return result;
}
} // namespace detail
NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/value_t.hpp>
@ -5294,13 +5333,6 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
template<typename string_type>
void int_to_string( string_type& target, std::size_t value )
{
// For ADL
using std::to_string;
target = to_string(value);
}
template<typename IteratorType> class iteration_proxy_value
{
public:
@ -15125,6 +15157,8 @@ NLOHMANN_JSON_NAMESPACE_END
// #include <nlohmann/detail/string_escape.hpp>
// #include <nlohmann/detail/string_utils.hpp>
// #include <nlohmann/detail/meta/cpp_future.hpp>
// #include <nlohmann/detail/meta/type_traits.hpp>
@ -24249,7 +24283,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// the valid JSON Patch operations
enum class patch_operations {add, remove, replace, move, copy, test, invalid};
const auto get_op = [](const std::string & op)
const auto get_op = [](const string_t& op)
{
if (op == "add")
{
@ -24386,8 +24420,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
for (const auto& val : json_patch)
{
// wrapper to get a value for an operation
const auto get_value = [&val](const std::string & op,
const std::string & member,
const auto get_value = [&val](const string_t& op,
const string_t& member,
bool string_type) -> basic_json &
{
// find value
@ -24421,8 +24455,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
}
// collect mandatory members
const auto op = get_value("op", "op", true).template get<std::string>();
const auto path = get_value(op, "path", true).template get<std::string>();
const auto op = get_value("op", "op", true).template get<string_t>();
const auto path = get_value(op, "path", true).template get<string_t>();
json_pointer ptr(path);
switch (get_op(op))
@ -24448,7 +24482,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case patch_operations::move:
{
const auto from_path = get_value("move", "from", true).template get<std::string>();
const auto from_path = get_value("move", "from", true).template get<string_t>();
json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
@ -24465,7 +24499,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
case patch_operations::copy:
{
const auto from_path = get_value("copy", "from", true).template get<std::string>();
const auto from_path = get_value("copy", "from", true).template get<string_t>();
const json_pointer from_ptr(from_path);
// the "from" location must exist - use at()
@ -24525,7 +24559,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
/// @sa https://json.nlohmann.me/api/basic_json/diff/
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json diff(const basic_json& source, const basic_json& target,
const std::string& path = "")
const string_t& path = "")
{
// the patch
basic_json result(value_t::array);
@ -24555,7 +24589,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
while (i < source.size() && i < target.size())
{
// recursive call to compare array values at index i
auto temp_diff = diff(source[i], target[i], detail::concat(path, '/', std::to_string(i)));
auto temp_diff = diff(source[i], target[i], detail::concat<string_t>(path, '/', detail::to_string<string_t>(i)));
result.insert(result.end(), temp_diff.begin(), temp_diff.end());
++i;
}
@ -24572,7 +24606,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result.insert(result.begin() + end_index, object(
{
{"op", "remove"},
{"path", detail::concat(path, '/', std::to_string(i))}
{"path", detail::concat<string_t>(path, '/', detail::to_string<string_t>(i))}
}));
++i;
}
@ -24583,7 +24617,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
result.push_back(
{
{"op", "add"},
{"path", detail::concat(path, "/-")},
{"path", detail::concat<string_t>(path, "/-")},
{"value", target[i]}
});
++i;
@ -24598,7 +24632,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));
if (target.find(it.key()) != target.end())
{
@ -24622,7 +24656,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (source.find(it.key()) == source.end())
{
// found a key that is not in this -> add it
const auto path_key = detail::concat(path, '/', detail::escape(it.key()));
const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));
result.push_back(
{
{"op", "add"}, {"path", path_key},

View File

@ -35,10 +35,21 @@ class alt_string
alt_string(size_t count, char chr): str_impl(count, chr) {}
alt_string() = default;
template <typename...TParams>
alt_string& append(TParams&& ...params)
alt_string& append(char ch)
{
str_impl.append(std::forward<TParams>(params)...);
str_impl.push_back(ch);
return *this;
}
alt_string& append(const alt_string& str)
{
str_impl.append(str.str_impl);
return *this;
}
alt_string& append(const char* s, std::size_t length)
{
str_impl.append(s, length);
return *this;
}
@ -157,6 +168,11 @@ class alt_string
return *this;
}
void reserve( std::size_t new_cap = 0 )
{
str_impl.reserve(new_cap);
}
private:
std::string str_impl {}; // NOLINT(readability-redundant-member-init)
@ -319,4 +335,28 @@ TEST_CASE("alternative string type")
CHECK(j.at(alt_json::json_pointer("/foo/0")) == j["foo"][0]);
CHECK(j.at(alt_json::json_pointer("/foo/1")) == j["foo"][1]);
}
SECTION("patch")
{
alt_json const patch1 = alt_json::parse(R"([{ "op": "add", "path": "/a/b", "value": [ "foo", "bar" ] }])");
alt_json const doc1 = alt_json::parse(R"({ "a": { "foo": 1 } })");
CHECK_NOTHROW(doc1.patch(patch1));
alt_json doc1_ans = alt_json::parse(R"(
{
"a": {
"foo": 1,
"b": [ "foo", "bar" ]
}
}
)");
CHECK(doc1.patch(patch1) == doc1_ans);
}
SECTION("diff")
{
alt_json const j1 = {"foo", "bar", "baz"};
alt_json const j2 = {"foo", "bam"};
CHECK(alt_json::diff(j1, j2).dump() == "[{\"op\":\"replace\",\"path\":\"/1\",\"value\":\"bam\"},{\"op\":\"remove\",\"path\":\"/2\"}]");
}
}