diff --git a/conversions.go b/conversions.go index 01c63d7..7ba478e 100644 --- a/conversions.go +++ b/conversions.go @@ -60,6 +60,9 @@ func (m Map) JSON() (string, error) { } func cleanUpInterfaceArray(in []interface{}) []interface{} { + if in == nil { + return nil + } result := make([]interface{}, len(in)) for i, v := range in { result[i] = cleanUp(v) @@ -68,6 +71,9 @@ func cleanUpInterfaceArray(in []interface{}) []interface{} { } func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { + if in == nil { + return nil + } result := Map{} for k, v := range in { result[fmt.Sprintf("%v", k)] = cleanUp(v) @@ -76,6 +82,9 @@ func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { } func cleanUpStringMap(in map[string]interface{}) Map { + if in == nil { + return nil + } result := Map{} for k, v := range in { result[k] = cleanUp(v) @@ -84,6 +93,9 @@ func cleanUpStringMap(in map[string]interface{}) Map { } func cleanUpMSIArray(in []map[string]interface{}) []Map { + if in == nil { + return nil + } result := make([]Map, len(in)) for i, v := range in { result[i] = cleanUpStringMap(v) @@ -92,6 +104,9 @@ func cleanUpMSIArray(in []map[string]interface{}) []Map { } func cleanUpMapArray(in []Map) []Map { + if in == nil { + return nil + } result := make([]Map, len(in)) for i, v := range in { result[i] = cleanUpStringMap(v) diff --git a/conversions_nil_test.go b/conversions_nil_test.go new file mode 100644 index 0000000..6ef72f8 --- /dev/null +++ b/conversions_nil_test.go @@ -0,0 +1,62 @@ +package objx_test + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/objx" +) + +func TestConversionJSONPreservesNilContainers(t *testing.T) { + tests := []struct { + name string + value interface{} + json string + }{ + {"nil interface slice", []interface{}(nil), "null"}, + {"nil map slice", []objx.Map(nil), "null"}, + {"nil string map slice", []map[string]interface{}(nil), "null"}, + {"nil map", objx.Map(nil), "null"}, + {"nil interface map", map[interface{}]interface{}(nil), "null"}, + {"nil string map", map[string]interface{}(nil), "null"}, + {"nil string slice", []string(nil), "null"}, + {"nil interface", nil, "null"}, + {"empty interface slice", []interface{}{}, "[]"}, + {"empty map slice", []objx.Map{}, "[]"}, + {"empty string map slice", []map[string]interface{}{}, "[]"}, + {"empty map", objx.Map{}, "{}"}, + {"empty interface map", map[interface{}]interface{}{}, "{}"}, + {"populated interface slice", []interface{}{1}, "[1]"}, + {"populated map slice", []objx.Map{{"key": "value"}}, `[{"key":"value"}]`}, + {"populated string map slice", []map[string]interface{}{{"key": "value"}}, `[{"key":"value"}]`}, + {"populated map", objx.Map{"key": "value"}, `{"key":"value"}`}, + {"populated interface map", map[interface{}]interface{}{1: "value"}, `{"1":"value"}`}, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + for _, context := range []struct { + name string + input objx.Map + want string + }{ + {"direct", objx.Map{"value": test.value}, `{"value":` + test.json + `}`}, + {"in slice", objx.Map{"value": []interface{}{test.value}}, `{"value":[` + test.json + `]}`}, + {"in map", objx.Map{"value": objx.Map{"nested": test.value}}, `{"value":{"nested":` + test.json + `}}`}, + } { + t.Run(context.name, func(t *testing.T) { + got, err := context.input.JSON() + require.NoError(t, err) + assert.Equal(t, context.want, got) + assert.Equal(t, context.want, context.input.MustJSON()) + encoded, err := context.input.Base64() + require.NoError(t, err) + decoded, err := base64.StdEncoding.DecodeString(encoded) + require.NoError(t, err) + assert.Equal(t, context.want, string(decoded)) + }) + } + }) + } +}