Commit 58791d65 authored by Petr Lapukhov's avatar Petr Lapukhov Committed by Facebook Github Bot

Add support for JSON merge diff generation

Summary: Add method to recursively compute RFC7386-compliant merge-diff between two folly::dynamic objects.

Reviewed By: yfeldblum

Differential Revision: D6794339

fbshipit-source-id: f2b807404faa922d5a705f858825329a95fb0d38
parent 30a7a657
......@@ -321,6 +321,34 @@ void dynamic::destroy() noexcept {
u_.nul = nullptr;
}
dynamic dynamic::merge_diff(const dynamic& source, const dynamic& target) {
if (!source.isObject() || source.type() != target.type()) {
return target;
}
dynamic diff = object;
// added/modified keys
for (const auto& pair : target.items()) {
auto it = source.find(pair.first);
if (it == source.items().end()) {
diff[pair.first] = pair.second;
} else {
diff[pair.first] = merge_diff(source[pair.first], target[pair.first]);
}
}
// removed keys
for (const auto& pair : source.items()) {
auto it = target.find(pair.first);
if (it == target.items().end()) {
diff[pair.first] = nullptr;
}
}
return diff;
}
//////////////////////////////////////////////////////////////////////
} // namespace folly
......@@ -471,6 +471,11 @@ struct dynamic : private boost::operators<dynamic> {
*/
void merge_patch(const dynamic& patch);
/*
* Computes JSON merge patch (RFC7386) needed to mutate from source to target
*/
static dynamic merge_diff(const dynamic& source, const dynamic& target);
/*
* Erase an element from a dynamic object, by key.
*
......
......@@ -718,3 +718,41 @@ TEST(Dynamic, MergePatchRemoveNonExistent) {
EXPECT_EQ("d", target["c"].getString());
EXPECT_EQ(2, target.size());
}
TEST(Dynamic, MergeDiffFlatObjects) {
dynamic source = dynamic::object("a", 0)("b", 1)("c", 2);
dynamic target = dynamic::object("a", 1)("b", 2);
auto patch = dynamic::merge_diff(source, target);
EXPECT_EQ(3, patch.size());
EXPECT_EQ(1, patch["a"].getInt());
EXPECT_EQ(2, patch["b"].getInt());
EXPECT_TRUE(patch["c"].isNull());
source.merge_patch(patch);
EXPECT_EQ(source, target);
}
TEST(Dynamic, MergeDiffNestedObjects) {
dynamic source = dynamic::object("a", dynamic::object("b", 1)("c", 2))(
"d", dynamic::array(1, 2, 3));
dynamic target = dynamic::object("a", dynamic::object("b", 2))(
"d", dynamic::array(2, 3, 4));
auto patch = dynamic::merge_diff(source, target);
EXPECT_EQ(2, patch.size());
EXPECT_EQ(2, patch["a"].size());
EXPECT_EQ(2, patch["a"]["b"].getInt());
EXPECT_TRUE(patch["a"]["c"].isNull());
EXPECT_TRUE(patch["d"].isArray());
EXPECT_EQ(3, patch["d"].size());
EXPECT_EQ(2, patch["d"][0].getInt());
EXPECT_EQ(3, patch["d"][1].getInt());
EXPECT_EQ(4, patch["d"][2].getInt());
source.merge_patch(patch);
EXPECT_EQ(source, target);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment