diff --git a/encode_compile.go b/encode_compile.go index 832cd29..3cc1ff3 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -1116,7 +1116,7 @@ func encodeAnonymousStructFieldPairMap(tags structTags, named string, valueCode isTaggedKey: f.isTaggedKey, }) if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField { - for k, v := range encodeAnonymousStructFieldPairMap(tags, named, f.next) { + for k, v := range encodeAnonymousFieldPairRecursively(named, f.next) { anonymousFields[k] = append(anonymousFields[k], v...) } } @@ -1129,6 +1129,33 @@ func encodeAnonymousStructFieldPairMap(tags structTags, named string, valueCode return anonymousFields } +func encodeAnonymousFieldPairRecursively(named string, valueCode *opcode) map[string][]structFieldPair { + anonymousFields := map[string][]structFieldPair{} + f := valueCode + var prevAnonymousField *opcode + for { + if f.displayKey != "" && strings.Contains(f.op.String(), "Anonymous") { + key := fmt.Sprintf("%s.%s", named, f.displayKey) + anonymousFields[key] = append(anonymousFields[key], structFieldPair{ + prevField: prevAnonymousField, + curField: f, + isTaggedKey: f.isTaggedKey, + }) + if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField { + for k, v := range encodeAnonymousFieldPairRecursively(named, f.next) { + anonymousFields[k] = append(anonymousFields[k], v...) + } + } + } + if f.nextField == nil { + break + } + prevAnonymousField = f + f = f.nextField + } + return anonymousFields +} + func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) { removedFields := map[*opcode]struct{}{} for _, fieldPairs := range anonymousFields { diff --git a/encode_test.go b/encode_test.go index 2ac1eb4..cfdfa6a 100644 --- a/encode_test.go +++ b/encode_test.go @@ -409,6 +409,53 @@ func Test_Marshal(t *testing.T) { }) } +func TestIssue116(t *testing.T) { + t.Run("first", func(t *testing.T) { + type Boo struct{ B string } + type Struct struct { + A int + Boo *Boo + } + type Embedded struct { + Struct + } + b, err := json.Marshal(Embedded{Struct: Struct{ + A: 1, + Boo: &Boo{B: "foo"}, + }}) + if err != nil { + t.Fatal(err) + } + expected := `{"A":1,"Boo":{"B":"foo"}}` + actual := string(b) + if actual != expected { + t.Fatalf("expected %s but got %s", expected, actual) + } + }) + t.Run("second", func(t *testing.T) { + type Boo struct{ B string } + type Struct struct { + A int + B *Boo + } + type Embedded struct { + Struct + } + b, err := json.Marshal(Embedded{Struct: Struct{ + A: 1, + B: &Boo{B: "foo"}, + }}) + if err != nil { + t.Fatal(err) + } + actual := string(b) + expected := `{"A":1,"B":{"B":"foo"}}` + if actual != expected { + t.Fatalf("expected %s but got %s", expected, actual) + } + }) +} + type marshalJSON struct{} func (*marshalJSON) MarshalJSON() ([]byte, error) {