Skip to content

Commit 8c10cd2

Browse files
support JSON Merge Patch (RFC 7396) diff creation
The JSON merge patch document format describes the set of modifications to a resource's content, that more closely mimics the syntax of the resource being modified. However, and in contrast to JSON Patch (RFC 6902), a JSON Merge Patch cannot express certain modifications, e.g., changing an array element at a specific index, or setting a specific object value to null. The null value in a JSON Merge Patch is used to remove the key from the object. The diff algorithm is not part of the RFC 7396, but it was tested against all examples provided, plus additional cases on how null values are handled. Signed-off-by: Luís Murta <luis@murta.dev>
1 parent 29913ca commit 8c10cd2

2 files changed

Lines changed: 484 additions & 0 deletions

File tree

include/nlohmann/json.hpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5254,6 +5254,57 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
52545254
}
52555255
}
52565256

5257+
/// @brief creates a diff as a JSON Merge Patch
5258+
JSON_HEDLEY_WARN_UNUSED_RESULT
5259+
static basic_json merge_diff(const basic_json& source, const basic_json& target)
5260+
{
5261+
if (!target.is_object())
5262+
{
5263+
return target;
5264+
}
5265+
5266+
basic_json result(value_t::object);
5267+
5268+
if (source.is_object())
5269+
{
5270+
for (auto it = source.begin(); it != source.end(); ++it)
5271+
{
5272+
auto itf = target.find(it.key());
5273+
if (itf != target.end())
5274+
{
5275+
if (it.value() != itf.value())
5276+
{
5277+
auto diff = merge_diff(it.value(), itf.value());
5278+
if (diff.is_null())
5279+
{
5280+
JSON_THROW(other_error::create(503, detail::concat("cannot set \"", itf.key(), "\" to null"), &target));
5281+
}
5282+
result[it.key()] = merge_diff(it.value(), itf.value());
5283+
}
5284+
}
5285+
else
5286+
{
5287+
result[it.key()] = value_t::null;
5288+
}
5289+
}
5290+
}
5291+
5292+
for (auto it = target.begin(); it != target.end(); ++it)
5293+
{
5294+
auto itf = source.find(it.key());
5295+
if (itf == source.end())
5296+
{
5297+
if (it.value().is_null())
5298+
{
5299+
JSON_THROW(other_error::create(503, detail::concat("cannot set \"", it.key(), "\" to null"), &target));
5300+
}
5301+
result[it.key()] = it.value();
5302+
}
5303+
}
5304+
5305+
return result;
5306+
}
5307+
52575308
/// @}
52585309
};
52595310

0 commit comments

Comments
 (0)