diff --git a/sheriff.go b/sheriff.go index 0d4844e..30ae2ef 100644 --- a/sheriff.go +++ b/sheriff.go @@ -121,7 +121,8 @@ func Marshal(options *Options, data interface{}) (interface{}, error) { field := t.Field(i) val := v.Field(i) - jsonTag, jsonOpts := parseTag(field.Tag.Get("json")) + jsonTagVal, jsonTagExists := field.Tag.Lookup("json") + jsonTag, jsonOpts := parseTag(jsonTagVal) // If no json tag is provided, use the field Name if jsonTag == "" { @@ -194,7 +195,7 @@ func Marshal(options *Options, data interface{}) (interface{}, error) { // when a composition field we want to bring the child // nodes to the top nestedVal, ok := v.(KVStore) - if isEmbeddedField && ok { + if !jsonTagExists && isEmbeddedField && ok { nestedVal.Each(func(k string, v interface{}) { dest.Set(k, v) }) diff --git a/sheriff_test.go b/sheriff_test.go index 68bb4c1..85f7510 100644 --- a/sheriff_test.go +++ b/sheriff_test.go @@ -580,6 +580,10 @@ type TestMarshal_Embedded struct { Foo string `json:"foo" groups:"test"` } +type TestMarshal_NamedEmbedded struct { + Qux string `json:"qux" groups:"test"` +} + // TestMarshal_EmbeddedCustom is used to test an embedded struct with a custom marshaler that is not a pointer. type TestMarshal_EmbeddedCustom struct { Val int @@ -610,14 +614,16 @@ func (t *TestMarshal_EmbeddedCustomPtr) MarshalJSON() ([]byte, error) { type TestMarshal_EmbeddedParent struct { *TestMarshal_Embedded + *TestMarshal_NamedEmbedded `json:"embedded"` *TestMarshal_EmbeddedCustom `json:"value"` *TestMarshal_EmbeddedCustomPtr `json:"value_ptr"` - Bar string `json:"bar" groups:"test"` + Bar string `json:"bar" groups:"test"` } func TestMarshal_EmbeddedField(t *testing.T) { v := TestMarshal_EmbeddedParent{ &TestMarshal_Embedded{"Hello"}, + &TestMarshal_NamedEmbedded{"Big"}, &TestMarshal_EmbeddedCustom{10, true}, &TestMarshal_EmbeddedCustomPtr{20, true}, "World", @@ -630,15 +636,26 @@ func TestMarshal_EmbeddedField(t *testing.T) { actual, err := json.Marshal(actualMap) assert.NoError(t, err) - expected, err := json.Marshal(map[string]interface{}{ - "bar": "World", - "foo": "Hello", - "value": 10, - "value_ptr": 20, + t.Run("should match the original json marshal", func(t *testing.T) { + expected, err := json.Marshal(v) + assert.NoError(t, err) + + assert.JSONEq(t, string(expected), string(actual)) }) - assert.NoError(t, err) - assert.Equal(t, string(expected), string(actual)) + t.Run("should match the expected map", func(t *testing.T) { + expectedMap, err := json.Marshal(map[string]interface{}{ + "bar": "World", + "foo": "Hello", + "value": 10, + "value_ptr": 20, + "embedded": map[string]interface{}{ + "qux": "Big", + }, + }) + assert.NoError(t, err) + assert.JSONEq(t, string(expectedMap), string(actual)) + }) } type TestMarshal_EmbeddedEmpty struct {