From 4f910b676a1aea196de86e1cb833e00264b6ec5e Mon Sep 17 00:00:00 2001 From: Jens Neuse Date: Mon, 6 Jan 2025 13:22:32 +0100 Subject: [PATCH] chore: improve null merging behaviour --- mergevalues.go | 12 +++++++----- mergevalues_test.go | 45 +++++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/mergevalues.go b/mergevalues.go index d3d0f53..632bda4 100644 --- a/mergevalues.go +++ b/mergevalues.go @@ -18,15 +18,17 @@ func MergeValues(a, b *Value) (v *Value, changed bool, err error) { if b == nil { return a, false, nil } + if b.Type() == TypeNull && a.Type() == TypeObject { + // we assume that null was returned in an error case for resolving a nested object field + // as we've got an object on the left side, we don't override the whole object with null + // instead, we keep the left object and discard the null on the right side + return a, false, nil + } aBool, bBool := a.Type() == TypeTrue || a.Type() == TypeFalse, b.Type() == TypeTrue || b.Type() == TypeFalse booleans := aBool && bBool - oneIsNull := a.Type() == TypeNull || b.Type() == TypeNull - if a.Type() != b.Type() && !booleans && !oneIsNull { + if a.Type() != b.Type() && !booleans { return nil, false, ErrMergeDifferentTypes } - if b.Type() == TypeNull && a.Type() != TypeNull { - return b, true, nil - } switch a.Type() { case TypeObject: ao, _ := a.Object() diff --git a/mergevalues_test.go b/mergevalues_test.go index 445bbc2..00e56d9 100644 --- a/mergevalues_test.go +++ b/mergevalues_test.go @@ -183,20 +183,14 @@ func TestMergeValues(t *testing.T) { t.Run("null not null", func(t *testing.T) { t.Parallel() a, b := MustParse(`null`), MustParse(`1`) - merged, changed, err := MergeValues(a, b) - require.NoError(t, err) - require.Equal(t, true, changed) - out := merged.MarshalTo(nil) - require.Equal(t, `1`, string(out)) + _, _, err := MergeValues(a, b) + require.Error(t, err) }) t.Run("null not null reverse", func(t *testing.T) { t.Parallel() a, b := MustParse(`1`), MustParse(`null`) - merged, changed, err := MergeValues(a, b) - require.NoError(t, err) - require.Equal(t, true, changed) - out := merged.MarshalTo(nil) - require.Equal(t, `null`, string(out)) + _, _, err := MergeValues(a, b) + require.Error(t, err) }) t.Run("array objects", func(t *testing.T) { t.Parallel() @@ -272,32 +266,43 @@ func TestMergeValues(t *testing.T) { t.Parallel() left := MustParse(`null`) right := MustParse(`true`) - out, _, err := MergeValues(left, right) - require.NoError(t, err) - require.Equal(t, `true`, out.String()) + _, _, err := MergeValues(left, right) + require.Error(t, err) }) t.Run("true null", func(t *testing.T) { t.Parallel() left := MustParse(`true`) right := MustParse(`null`) - out, _, err := MergeValues(left, right) - require.NoError(t, err) - require.Equal(t, `null`, out.String()) + _, _, err := MergeValues(left, right) + require.Error(t, err) }) t.Run("nested null true", func(t *testing.T) { t.Parallel() left := MustParse(`{"a":null}`) right := MustParse(`{"a":true}`) - out, _, err := MergeValues(left, right) - require.NoError(t, err) - require.Equal(t, `{"a":true}`, out.String()) + _, _, err := MergeValues(left, right) + require.Error(t, err) }) t.Run("nested true null", func(t *testing.T) { t.Parallel() left := MustParse(`{"a":true}`) right := MustParse(`{"a":null}`) + _, _, err := MergeValues(left, right) + require.Error(t, err) + }) + t.Run("nested null into nested object", func(t *testing.T) { + t.Parallel() + left := MustParse(`{"a":{"b":"c"}}`) + right := MustParse(`{"a":null}`) out, _, err := MergeValues(left, right) require.NoError(t, err) - require.Equal(t, `{"a":null}`, out.String()) + require.Equal(t, `{"a":{"b":"c"}}`, out.String()) + }) + t.Run("nested object into nested null", func(t *testing.T) { + t.Parallel() + left := MustParse(`{"a":null}`) + right := MustParse(`{"a":{"b":"c"}}`) + _, _, err := MergeValues(left, right) + require.Error(t, err) }) }