Skip to content

Commit

Permalink
Merge pull request #141 from kacejot/preserve-nulls
Browse files Browse the repository at this point in the history
MergeMergePatches preserve nulls in complex objects
  • Loading branch information
evanphx committed Jun 1, 2021
2 parents 0cc7ddb + 10a1c0a commit 3e2cfc0
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 16 deletions.
5 changes: 4 additions & 1 deletion merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ func mergeDocs(doc, patch *partialDoc, mergeMerge bool) {
cur, ok := (*doc)[k]

if !ok || cur == nil {
pruneNulls(v)
if !mergeMerge {
pruneNulls(v)
}

(*doc)[k] = v
} else {
(*doc)[k] = merge(cur, v, mergeMerge)
Expand Down
20 changes: 13 additions & 7 deletions merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func createNestedMap(m map[string]interface{}, depth int, objectCount *int) {
if depth == 0 {
return
}
for i := 0; i< 2;i++ {
for i := 0; i < 2; i++ {
nested := map[string]interface{}{}
*objectCount += 1
createNestedMap(nested, depth-1, objectCount)
Expand Down Expand Up @@ -571,12 +571,12 @@ func benchmarkMatchesValueWithDeeplyNestedFields(depth int, b *testing.B) {
func BenchmarkMatchesValue1(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(1, b) }
func BenchmarkMatchesValue2(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(2, b) }
func BenchmarkMatchesValue3(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(3, b) }
func BenchmarkMatchesValue4(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(4, b) }
func BenchmarkMatchesValue5(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(5, b) }
func BenchmarkMatchesValue6(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(6, b) }
func BenchmarkMatchesValue7(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(7, b) }
func BenchmarkMatchesValue8(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(8, b) }
func BenchmarkMatchesValue9(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(9, b) }
func BenchmarkMatchesValue4(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(4, b) }
func BenchmarkMatchesValue5(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(5, b) }
func BenchmarkMatchesValue6(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(6, b) }
func BenchmarkMatchesValue7(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(7, b) }
func BenchmarkMatchesValue8(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(8, b) }
func BenchmarkMatchesValue9(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(9, b) }
func BenchmarkMatchesValue10(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(10, b) }

func TestCreateMergePatchComplexRemoveAll(t *testing.T) {
Expand Down Expand Up @@ -674,6 +674,12 @@ func TestMergeMergePatches(t *testing.T) {
p2: `{"del2": null}`,
exp: `{"del1": null, "del2": null}`,
},
{
demonstrates: "nulls are kept in complex objects",
p1: `{}`,
p2: `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`,
exp: `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`,
},
{
demonstrates: "a key added then deleted is kept deleted",
p1: `{"add_then_delete": "atd"}`,
Expand Down
4 changes: 3 additions & 1 deletion v5/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ func mergeDocs(doc, patch *partialDoc, mergeMerge bool) {
cur, ok := doc.obj[k]

if !ok || cur == nil {
pruneNulls(v)
if !mergeMerge {
pruneNulls(v)
}
_ = doc.set(k, v, &ApplyOptions{})
} else {
_ = doc.set(k, merge(cur, v, mergeMerge), &ApplyOptions{})
Expand Down
20 changes: 13 additions & 7 deletions v5/merge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ func createNestedMap(m map[string]interface{}, depth int, objectCount *int) {
if depth == 0 {
return
}
for i := 0; i< 2;i++ {
for i := 0; i < 2; i++ {
nested := map[string]interface{}{}
*objectCount += 1
createNestedMap(nested, depth-1, objectCount)
Expand Down Expand Up @@ -571,12 +571,12 @@ func benchmarkMatchesValueWithDeeplyNestedFields(depth int, b *testing.B) {
func BenchmarkMatchesValue1(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(1, b) }
func BenchmarkMatchesValue2(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(2, b) }
func BenchmarkMatchesValue3(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(3, b) }
func BenchmarkMatchesValue4(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(4, b) }
func BenchmarkMatchesValue5(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(5, b) }
func BenchmarkMatchesValue6(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(6, b) }
func BenchmarkMatchesValue7(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(7, b) }
func BenchmarkMatchesValue8(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(8, b) }
func BenchmarkMatchesValue9(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(9, b) }
func BenchmarkMatchesValue4(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(4, b) }
func BenchmarkMatchesValue5(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(5, b) }
func BenchmarkMatchesValue6(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(6, b) }
func BenchmarkMatchesValue7(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(7, b) }
func BenchmarkMatchesValue8(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(8, b) }
func BenchmarkMatchesValue9(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(9, b) }
func BenchmarkMatchesValue10(b *testing.B) { benchmarkMatchesValueWithDeeplyNestedFields(10, b) }

func TestCreateMergePatchComplexRemoveAll(t *testing.T) {
Expand Down Expand Up @@ -674,6 +674,12 @@ func TestMergeMergePatches(t *testing.T) {
p2: `{"del2": null}`,
exp: `{"del1": null, "del2": null}`,
},
{
demonstrates: "nulls are kept in complex objects",
p1: `{}`,
p2: `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`,
exp: `{"request":{"object":{"complex_object_array":["value1","value2","value3"],"complex_object_map":{"key1":"value1","key2":"value2","key3":"value3"},"simple_object_bool":false,"simple_object_float":-5.5,"simple_object_int":5,"simple_object_null":null,"simple_object_string":"example"}}}`,
},
{
demonstrates: "a key added then deleted is kept deleted",
p1: `{"add_then_delete": "atd"}`,
Expand Down

0 comments on commit 3e2cfc0

Please sign in to comment.