diff --git a/sheriff.go b/sheriff.go index d4ddd2f..30ae2ef 100644 --- a/sheriff.go +++ b/sheriff.go @@ -283,10 +283,21 @@ func marshalValue(options *Options, v reflect.Value) (interface{}, error) { // types which are e.g. structs, slices or maps and implement one of the following interfaces should not be // marshalled by sheriff because they'll be correctly marshalled by json.Marshal instead. // Otherwise (e.g. net.IP) a byte slice may be output as a list of uints instead of as an IP string. + // This needs to be checked for both value and pointer types. switch val.(type) { case json.Marshaler, encoding.TextMarshaler, fmt.Stringer: return val, nil } + + if v.CanAddr() { + addrVal := v.Addr().Interface() + + switch addrVal.(type) { + case json.Marshaler, encoding.TextMarshaler, fmt.Stringer: + return addrVal, nil + } + } + k := v.Kind() switch k { diff --git a/sheriff_test.go b/sheriff_test.go index 6801fad..85f7510 100644 --- a/sheriff_test.go +++ b/sheriff_test.go @@ -2,6 +2,7 @@ package sheriff import ( "encoding/json" + "fmt" "net" "reflect" "testing" @@ -583,9 +584,39 @@ 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 + Set bool +} + +func (t TestMarshal_EmbeddedCustom) MarshalJSON() ([]byte, error) { + if t.Set { + return []byte(fmt.Sprintf("%d", t.Val)), nil + } + + return nil, nil +} + +// TestMarshal_EmbeddedCustomPtr is used to test an embedded struct with a custom marshaler that is a pointer. +type TestMarshal_EmbeddedCustomPtr struct { + Val int + Set bool +} + +func (t *TestMarshal_EmbeddedCustomPtr) MarshalJSON() ([]byte, error) { + if t.Set { + return []byte(fmt.Sprintf("%d", t.Val)), nil + } + + return nil, nil +} + 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"` } @@ -593,6 +624,8 @@ func TestMarshal_EmbeddedField(t *testing.T) { v := TestMarshal_EmbeddedParent{ &TestMarshal_Embedded{"Hello"}, &TestMarshal_NamedEmbedded{"Big"}, + &TestMarshal_EmbeddedCustom{10, true}, + &TestMarshal_EmbeddedCustomPtr{20, true}, "World", } o := &Options{Groups: []string{"test"}} @@ -614,6 +647,8 @@ func TestMarshal_EmbeddedField(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", },