Skip to content

Commit

Permalink
properly handle embedded (anonymous) fields
Browse files Browse the repository at this point in the history
  • Loading branch information
faryon93 committed Oct 14, 2017
1 parent 2245bc6 commit d0d9181
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 22 deletions.
65 changes: 43 additions & 22 deletions sheriff.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,40 +80,61 @@ func Marshal(options *Options, data interface{}) (interface{}, error) {
continue
}

if checkGroups {
groups := strings.Split(field.Tag.Get("groups"), ",")

shouldShow := listContains(groups, options.Groups)
if !shouldShow || len(groups) == 0 {
continue
}
// if there is an anonymous field which is a struct
// we want the childs exposed at the toplevel to be
// consistent with the embedded json marshaller
if val.Kind() == reflect.Ptr {
val = val.Elem()
}

if since := field.Tag.Get("since"); since != "" {
sinceVersion, err := version.NewVersion(since)
if err != nil {
return nil, err
}
if options.ApiVersion.LessThan(sinceVersion) {
continue
// we can skip the group checkif if the field is a composition field
isEmbeddedField := field.Anonymous && val.Kind() == reflect.Struct
if !isEmbeddedField {
if checkGroups {
groups := strings.Split(field.Tag.Get("groups"), ",")

shouldShow := listContains(groups, options.Groups)
if !shouldShow || len(groups) == 0 {
continue
}
}
}

if until := field.Tag.Get("until"); until != "" {
untilVersion, err := version.NewVersion(until)
if err != nil {
return nil, err
if since := field.Tag.Get("since"); since != "" {
sinceVersion, err := version.NewVersion(since)
if err != nil {
return nil, err
}
if options.ApiVersion.LessThan(sinceVersion) {
continue
}
}
if options.ApiVersion.GreaterThan(untilVersion) {
continue

if until := field.Tag.Get("until"); until != "" {
untilVersion, err := version.NewVersion(until)
if err != nil {
return nil, err
}
if options.ApiVersion.GreaterThan(untilVersion) {
continue
}
}
}

v, err := marshalValue(options, val)
if err != nil {
return nil, err
}
dest[jsonTag] = v

// when a composition field we want to bring the child
// nodes to the top
nestedVal, ok := v.(map[string]interface{})
if isEmbeddedField && ok {
for key, value := range nestedVal {
dest[key] = value
}
} else {
dest[jsonTag] = v
}
}

return dest, nil
Expand Down
61 changes: 61 additions & 0 deletions sheriff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,64 @@ func TestMarshal_EmptyMap(t *testing.T) {

assert.Equal(t, string(expected), string(actual))
}

type TestMarshal_Embedded struct {
Foo string `json:"foo" groups:"test"`
}

type TestMarshal_EmbeddedParent struct {
*TestMarshal_Embedded
Bar string `json:"bar" groups:"test"`
}

func TestMarshal_EmbeddedField(t *testing.T) {
v := TestMarshal_EmbeddedParent{
&TestMarshal_Embedded{"Hello"},
"World",
}
o := &Options{Groups: []string{"test"}}

actualMap, err := Marshal(o, v)
assert.NoError(t, err)

actual, err := json.Marshal(actualMap)
assert.NoError(t, err)

expected, err := json.Marshal(map[string]interface{}{
"bar": "World",
"foo": "Hello",
})
assert.NoError(t, err)

assert.Equal(t, string(expected), string(actual))
}

type TestMarshal_EmbeddedEmpty struct {
Foo string
}

type TestMarshal_EmbeddedParentEmpty struct {
*TestMarshal_EmbeddedEmpty
Bar string `json:"bar" groups:"test"`
}

func TestMarshal_EmbeddedFieldEmpty(t *testing.T) {
v := TestMarshal_EmbeddedParentEmpty{
&TestMarshal_EmbeddedEmpty{"Hello"},
"World",
}
o := &Options{Groups: []string{"test"}}

actualMap, err := Marshal(o, v)
assert.NoError(t, err)

actual, err := json.Marshal(actualMap)
assert.NoError(t, err)

expected, err := json.Marshal(map[string]interface{}{
"bar": "World",
})
assert.NoError(t, err)

assert.Equal(t, string(expected), string(actual))
}

0 comments on commit d0d9181

Please sign in to comment.