diff --git a/README.md b/README.md index faf414c..b3e3e59 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ Sets `value` on `data` at that json path Note: you'll want to pass in a pointer to `data` so that the side effect actually is usable +### jsonpath.SetKeepNilAsNull(data interface{}, path string, value interface{}) (error) + +Sets `value` on `data` at that json path while translating `nil` values as `null` instead of removing the key. + +Note: you'll want to pass in a pointer to `data` so that the side effect actually is usable + ### jsonpath.DoesNotExist error Returned by `jsonpath.Get` on a nonexistent path: diff --git a/jsonpath.go b/jsonpath.go index 19622e1..f3bc0e1 100644 --- a/jsonpath.go +++ b/jsonpath.go @@ -107,8 +107,16 @@ func Get(data interface{}, path string) (interface{}, error) { return getByTokens(data, tokens) } -// Set value on data at that json path func Set(data interface{}, path string, value interface{}) error { + return set(data, path, value, false) +} + +func SetKeepNilAsNull(data interface{}, path string, value interface{}) error { + return set(data, path, value, true) +} + +// Set value on data at that json path +func set(data interface{}, path string, value interface{}, keepNilAsNull bool) error { tokens, err := tokenizePath(path) if err != nil { return nil @@ -118,7 +126,9 @@ func Set(data interface{}, path string, value interface{}) error { last := tokens[len(tokens)-1] data = followPtr(data) - value = followPtr(value) + if value != nil { + value = followPtr(value) + } child := data parent := data @@ -163,7 +173,12 @@ func Set(data interface{}, path string, value interface{}) error { switch reflect.ValueOf(child).Kind() { case reflect.Map: - reflect.ValueOf(child).SetMapIndex(reflect.ValueOf(last), reflect.ValueOf(value)) + if value == nil && keepNilAsNull { + var nilvalue interface{} + reflect.ValueOf(child).SetMapIndex(reflect.ValueOf(last), reflect.ValueOf(&nilvalue).Elem()) + } else { + reflect.ValueOf(child).SetMapIndex(reflect.ValueOf(last), reflect.ValueOf(value)) + } return nil case reflect.Slice: sliceValue := reflect.ValueOf(child) diff --git a/jsonpath_test.go b/jsonpath_test.go index 1a0f828..d468e4f 100644 --- a/jsonpath_test.go +++ b/jsonpath_test.go @@ -109,10 +109,10 @@ func TestJSON(t *testing.T) { "pet": { "name": "baxter", "owner": { - "name": "john doe", - "contact": { - "phone": "859-289-9290" - } + "name": "john doe", + "contact": { + "phone": "859-289-9290" + } }, "type": "dog", "age": "4" @@ -192,3 +192,37 @@ func TestErrors(t *testing.T) { t.Errorf("error retrieving value %v", err) } } + +func TestNullRemovesKey(t *testing.T) { + var payload = map[string]interface{}{ + "key1": "val1", + } + err := Set(&payload, "key1", nil) + if err != nil { + t.Error("error setting a nil value failed", err) + } + + _, ok := payload["key1"] + if ok { + t.Error("nil values assigned with Set should remove the key", err) + } +} + +func TestNullSetWithSetKeepNilAsNullKeepKey(t *testing.T) { + var payload = map[string]interface{}{ + "key1": "val1", + } + + err := SetKeepNilAsNull(&payload, "key1", nil) + if err != nil { + t.Error("error setting a nil value failed", err) + } + + val, ok := payload["key1"] + if !ok { + t.Error("nil values assigned with SetKeepNilAsNull should keep the key", err) + } + if val != nil { + t.Error("nil values assigned with SetKeepNilAsNull should set the key to null", err) + } +}