diff --git a/dsl/matcher.go b/dsl/matcher.go index d24ae57fd..c3fcb74a8 100644 --- a/dsl/matcher.go +++ b/dsl/matcher.go @@ -299,7 +299,11 @@ func match(srcType reflect.Type, params params) Matcher { for i := 0; i < srcType.NumField(); i++ { field := srcType.Field(i) - result[field.Tag.Get("json")] = match(field.Type, pluckParams(field.Type, field.Tag.Get("pact"))) + fieldName := getJsonFieldName(field) + if fieldName == "" { + continue + } + result[fieldName] = match(field.Type, pluckParams(field.Type, field.Tag.Get("pact"))) } return result case reflect.String: @@ -332,6 +336,24 @@ func match(srcType reflect.Type, params params) Matcher { } } +// getJsonFieldName retrieves the name for a JSON field as +// https://golang.org/pkg/encoding/json/#Marshal would do. +func getJsonFieldName(field reflect.StructField) string { + jsonTag := field.Tag.Get("json") + if jsonTag == "" { + return field.Name + } + // Field should be ignored according to the JSON marshal documentation. + if jsonTag == "-" { + return "" + } + commaIndex := strings.Index(jsonTag, ",") + if commaIndex > -1 { + return jsonTag[:commaIndex] + } + return jsonTag +} + // params are plucked from 'pact' struct tags as match() traverses // struct fields. They are passed back into match() along with their // associated type to serve as parameters for the dsl functions. diff --git a/dsl/matcher_test.go b/dsl/matcher_test.go index 986c9c233..8b4ff5bf9 100644 --- a/dsl/matcher_test.go +++ b/dsl/matcher_test.go @@ -570,6 +570,20 @@ func TestMatch(t *testing.T) { Integer int `json:"integer" pact:"example=42"` Float float32 `json:"float" pact:"example=6.66"` } + type jsonTagOmitemptyDTO struct { + Word string `json:"word,omitempty"` + } + type jsonTagMissingDTO struct { + Word string + } + type jsonTagIgnoreFieldDTO struct { + Word string `json:"word"` + Ignored string `json:"-"` + } + type jsonTagDashDTO struct { + Word string `json:"word"` + WordDash string `json:"-,"` + } str := "str" type args struct { src interface{} @@ -648,6 +662,43 @@ func TestMatch(t *testing.T) { "float": Like(float32(6.66)), }, }, + { + name: "recursive case - struct with json tag including omitempty", + args: args{ + src: jsonTagOmitemptyDTO{}, + }, + want: StructMatcher{ + "word": Like("string"), + }, + }, + { + name: "recursive case - struct without json tag", + args: args{ + src: jsonTagMissingDTO{}, + }, + want: StructMatcher{ + "Word": Like("string"), + }, + }, + { + name: "recursive case - struct with ignored field", + args: args{ + src: jsonTagIgnoreFieldDTO{}, + }, + want: StructMatcher{ + "word": Like("string"), + }, + }, + { + name: "recursive case - struct with field named '-'", + args: args{ + src: jsonTagDashDTO{}, + }, + want: StructMatcher{ + "word": Like("string"), + "-": Like("string"), + }, + }, { name: "base case - string", args: args{