diff --git a/Makefile b/Makefile index c5273407..4199a5e4 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,10 @@ generate: build ./tests/escaping.go bin/easyjson -all \ ./tests/data.go \ - ./tests/nothing.go \ - ./tests/errors.go \ - ./tests/html.go \ - ./tests/type_declaration_skip.go + ./tests/nothing.go \ + ./tests/errors.go \ + ./tests/html.go \ + ./tests/type_declaration_skip.go bin/easyjson \ ./tests/nested_easy.go \ ./tests/named_type.go \ diff --git a/gen/decoder.go b/gen/decoder.go index 0a0faa26..a73fe937 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -61,26 +61,34 @@ var customDecoders = map[string]string{ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, indent int) error { ws := strings.Repeat(" ", indent) - unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(unmarshalerIface) { - fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)") - return nil - } + // If the type is unnamed, then this is an anonymous struct. If that anon + // struct embeds another, named struct that implements one of these + // interfaces, then Implements(...) will return true, even if the anon + // struct has other fields. That causes us to skip code gen for those + // extra fields. This addresses + // https://github.com/mailru/easyjson/issues/337. + if t.Name() != "" { + unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)") + return nil + } - unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(unmarshalerIface) { - fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {") - fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )") - fmt.Fprintln(g.out, ws+"}") - return nil - } + unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {") + fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )") + fmt.Fprintln(g.out, ws+"}") + return nil + } - unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(unmarshalerIface) { - fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {") - fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )") - fmt.Fprintln(g.out, ws+"}") - return nil + unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {") + fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )") + fmt.Fprintln(g.out, ws+"}") + return nil + } } err := g.genTypeDecoderNoCheck(t, out, tags, indent) diff --git a/gen/encoder.go b/gen/encoder.go index 74c530fc..286043b9 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -94,22 +94,25 @@ func parseFieldTags(f reflect.StructField) fieldTags { func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error { ws := strings.Repeat(" ", indent) - marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(marshalerIface) { - fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)") - return nil - } + // See the comment in genTypeDecoder for why we check the name. + if t.Name() != "" { + marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)") + return nil + } - marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(marshalerIface) { - fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )") - return nil - } + marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )") + return nil + } - marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(marshalerIface) { - fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )") - return nil + marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )") + return nil + } } err := g.genTypeEncoderNoCheck(t, in, tags, indent, assumeNonEmpty) diff --git a/tests/basic_test.go b/tests/basic_test.go index e18e515c..6fbb9667 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -59,6 +59,7 @@ var testCases = []struct { {&myTypeDeclaredValue, myTypeDeclaredString}, {&myTypeNotSkippedValue, myTypeNotSkippedString}, {&intern, internString}, + {&embedAnonWithEmbedValue, embedAnonWithEmbedString}, } func TestMarshal(t *testing.T) { diff --git a/tests/data.go b/tests/data.go index f64a48e2..cac61e2e 100644 --- a/tests/data.go +++ b/tests/data.go @@ -820,3 +820,27 @@ type MyUInt8Array [2]MyUInt8 var myUInt8ArrayValue = MyUInt8Array{1, 2} var myUInt8ArrayString = `[1,2]` + +type GrandChild struct { + V string `json:"v"` +} + +//easyjson:json +type EmbedAnonWithEmbed struct { + Child struct { + GrandChild + Sibling string `json:"sibling"` + } `json:"child"` +} + +var embedAnonWithEmbedValue = EmbedAnonWithEmbed{ + Child: struct { + GrandChild + Sibling string `json:"sibling"` + }{ + GrandChild: GrandChild{V: "val"}, + Sibling: "joe", + }, +} + +var embedAnonWithEmbedString = `{"child":{"sibling":"joe","v":"val"}}`