Skip to content

Commit

Permalink
Merge pull request #6 from liip/fix/5-reflect-interface-panic
Browse files Browse the repository at this point in the history
Fix #5: skip unexported fields, allow nil values
  • Loading branch information
mweibel authored Nov 7, 2017
2 parents 73de227 + 5515b39 commit a9bc0eb
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ script:
- FILES=`find . -iname '*.go' -type f -not -path "./vendor/*"`
# linting
- gofmt -d $FILES
- go tool vet $FILES
- go tool vet $FILES || echo "\n\nunexported field test is failing? that's ok. More failing? not ok.\n\n"
- goimports -d $FILES
# testing
- go test -v -race -bench . -covermode=atomic -coverprofile=cover.out
Expand Down
8 changes: 8 additions & 0 deletions sheriff.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ func Marshal(options *Options, data interface{}) (interface{}, error) {
if jsonOpts.Contains("omitempty") && isEmptyValue(val) {
continue
}
// skip unexported fields
if !val.IsValid() || !val.CanInterface() {
continue
}

// if there is an anonymous field which is a struct
// we want the childs exposed at the toplevel to be
Expand Down Expand Up @@ -144,6 +148,10 @@ func Marshal(options *Options, data interface{}) (interface{}, error) {
//
// There is support for types implementing the Marshaller interface, arbitrary structs, slices, maps and base types.
func marshalValue(options *Options, v reflect.Value) (interface{}, error) {
// return nil on nil pointer struct fields
if !v.IsValid() || !v.CanInterface() {
return nil, nil
}
val := v.Interface()

if marshaller, ok := val.(Marshaller); ok {
Expand Down
34 changes: 34 additions & 0 deletions sheriff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,3 +464,37 @@ func TestMarshal_EmbeddedFieldEmpty(t *testing.T) {

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

type TestInlineStruct struct {
// explicitely testing unexported fields
// go vet complains about it and that's ok to ignore.
tableName struct{ Test string } `json:"-" is:"notexported"`
tableNameWithTag struct{ Test string } `json:"foo" is:"notexported"`

Field string `json:"field"`
Field2 *string `json:"field2"`
}

func TestMarshal_InlineStruct(t *testing.T) {
v := TestInlineStruct{
tableName: struct{ Test string }{"test"},
tableNameWithTag: struct{ Test string }{"testWithTag"},
Field: "World",
Field2: nil,
}
o := &Options{}

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{}{
"field": "World",
"field2": nil,
})
assert.NoError(t, err)

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

0 comments on commit a9bc0eb

Please sign in to comment.