Commit 794ea3ee authored by Petr Lapukhov's avatar Petr Lapukhov Committed by Facebook Github Bot

Add merge patch functionality from RFC7386

Summary: Merge patch is simple method of editing JSON documents. This implementation uses the recursive version documented in the RFC.

Reviewed By: yfeldblum

Differential Revision: D6777104

fbshipit-source-id: 554f801fe793a6fe004a8321ca41510b48225aa9
parent f3db6f96
...@@ -641,6 +641,29 @@ inline void dynamic::update_missing(const dynamic& mergeObj1) { ...@@ -641,6 +641,29 @@ inline void dynamic::update_missing(const dynamic& mergeObj1) {
} }
} }
inline void dynamic::merge_patch(const dynamic& patch) {
auto& self = *this;
if (!patch.isObject()) {
self = patch;
return;
}
// if we are not an object, erase all contents, reset to object
if (!isObject()) {
self = object;
}
for (const auto& pair : patch.items()) {
if (pair.second.isNull()) {
// if name could be found in current object, remove it
auto it = self.find(pair.first);
if (it != self.items().end()) {
self.erase(it);
}
} else {
self[pair.first].merge_patch(pair.second);
}
}
}
inline dynamic dynamic::merge( inline dynamic dynamic::merge(
const dynamic& mergeObj1, const dynamic& mergeObj1,
const dynamic& mergeObj2) { const dynamic& mergeObj2) {
......
...@@ -465,6 +465,12 @@ struct dynamic : private boost::operators<dynamic> { ...@@ -465,6 +465,12 @@ struct dynamic : private boost::operators<dynamic> {
void update_missing(const dynamic& other); void update_missing(const dynamic& other);
static dynamic merge(const dynamic& mergeObj1, const dynamic& mergeObj2); static dynamic merge(const dynamic& mergeObj1, const dynamic& mergeObj2);
/*
* Implement recursive version of RFC7386: JSON merge patch. This modifies
* the current object.
*/
void merge_patch(const dynamic& patch);
/* /*
* Erase an element from a dynamic object, by key. * Erase an element from a dynamic object, by key.
* *
......
...@@ -623,3 +623,98 @@ TEST(Dynamic, ObjectIteratorInterop) { ...@@ -623,3 +623,98 @@ TEST(Dynamic, ObjectIteratorInterop) {
decltype(cit) cit2 = it2; decltype(cit) cit2 = it2;
EXPECT_EQ(cit, cit2); EXPECT_EQ(cit, cit2);
} }
TEST(Dynamic, MergePatchWithNonObject) {
dynamic target = dynamic::object("a", "b")("c", "d");
dynamic patch = dynamic::array(1, 2, 3);
target.merge_patch(patch);
EXPECT_TRUE(target.isArray());
}
TEST(Dynamic, MergePatchReplaceInFlatObject) {
dynamic target = dynamic::object("a", "b")("c", "d");
dynamic patch = dynamic::object("a", "z");
target.merge_patch(patch);
EXPECT_EQ("z", target["a"].getString());
EXPECT_EQ("d", target["c"].getString());
}
TEST(Dynamic, MergePatchAddInFlatObject) {
dynamic target = dynamic::object("a", "b")("c", "d");
dynamic patch = dynamic::object("e", "f");
target.merge_patch(patch);
EXPECT_EQ("b", target["a"].getString());
EXPECT_EQ("d", target["c"].getString());
EXPECT_EQ("f", target["e"].getString());
}
TEST(Dynamic, MergePatchReplaceInNestedObject) {
dynamic target = dynamic::object("a", dynamic::object("d", 10))("b", "c");
dynamic patch = dynamic::object("a", dynamic::object("d", 100));
target.merge_patch(patch);
EXPECT_EQ(100, target["a"]["d"].getInt());
EXPECT_EQ("c", target["b"].getString());
}
TEST(Dynamic, MergePatchAddInNestedObject) {
dynamic target = dynamic::object("a", dynamic::object("d", 10))("b", "c");
dynamic patch = dynamic::object("a", dynamic::object("e", "f"));
target.merge_patch(patch);
EXPECT_EQ(10, target["a"]["d"].getInt());
EXPECT_EQ("f", target["a"]["e"].getString());
EXPECT_EQ("c", target["b"].getString());
}
TEST(Dynamic, MergeNestePatch) {
dynamic target = dynamic::object("a", dynamic::object("d", 10))("b", "c");
dynamic patch = dynamic::object(
"a", dynamic::object("d", dynamic::array(1, 2, 3)))("b", 100);
target.merge_patch(patch);
EXPECT_EQ(100, target["b"].getInt());
{
auto ary = patch["a"]["d"];
ASSERT_TRUE(ary.isArray());
EXPECT_EQ(1, ary[0].getInt());
EXPECT_EQ(2, ary[1].getInt());
EXPECT_EQ(3, ary[2].getInt());
}
}
TEST(Dynamic, MergePatchRemoveInFlatObject) {
dynamic target = dynamic::object("a", "b")("c", "d");
dynamic patch = dynamic::object("c", nullptr);
target.merge_patch(patch);
EXPECT_EQ("b", target["a"].getString());
EXPECT_EQ(0, target.count("c"));
}
TEST(Dynamic, MergePatchRemoveInNestedObject) {
dynamic target =
dynamic::object("a", dynamic::object("d", 10)("e", "f"))("b", "c");
dynamic patch = dynamic::object("a", dynamic::object("e", nullptr));
target.merge_patch(patch);
EXPECT_EQ(10, target["a"]["d"].getInt());
EXPECT_EQ(0, target["a"].count("e"));
EXPECT_EQ("c", target["b"].getString());
}
TEST(Dynamic, MergePatchRemoveNonExistent) {
dynamic target = dynamic::object("a", "b")("c", "d");
dynamic patch = dynamic::object("e", nullptr);
target.merge_patch(patch);
EXPECT_EQ("b", target["a"].getString());
EXPECT_EQ("d", target["c"].getString());
EXPECT_EQ(2, target.size());
}
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