Skip to content

Commit 76cbb4c

Browse files
authored
Merge pull request #40 from goccy/feature/fix-anonymous-struct
Fix anonymous struct fields
2 parents 97787fd + 6a0fd17 commit 76cbb4c

File tree

5 files changed

+310
-55
lines changed

5 files changed

+310
-55
lines changed

encode_compile.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,6 @@ type structFieldPair struct {
622622
}
623623

624624
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *structFieldCode) map[string][]structFieldPair {
625-
//fmt.Println("type = ", typ, "valueCode = ", valueCode.dump())
626625
anonymousFields := map[string][]structFieldPair{}
627626
f := valueCode
628627
var prevAnonymousField *structFieldCode

encode_opcode.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,15 +112,24 @@ func (c *opcode) dump() string {
112112
codes := []string{}
113113
for code := c; code.op != opEnd; {
114114
indent := strings.Repeat(" ", code.indent)
115-
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
116115
switch code.op.codeType() {
117116
case codeArrayElem:
117+
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
118118
code = code.toArrayElemCode().end
119119
case codeSliceElem:
120+
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
120121
code = code.toSliceElemCode().end
121122
case codeMapKey:
123+
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
122124
code = code.toMapKeyCode().end
125+
case codeStructField:
126+
sf := code.toStructFieldCode()
127+
key := sf.displayKey
128+
offset := sf.offset
129+
codes = append(codes, fmt.Sprintf("%s%s [%s:%d] ( %p )", indent, code.op, key, offset, unsafe.Pointer(code)))
130+
code = code.next
123131
default:
132+
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
124133
code = code.next
125134
}
126135
}

encode_test.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,3 +1392,180 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
13921392
t.Fatalf("Marshal: got %s want %s", got, want)
13931393
}
13941394
}
1395+
1396+
func TestAnonymousFields(t *testing.T) {
1397+
tests := []struct {
1398+
label string // Test name
1399+
makeInput func() interface{} // Function to create input value
1400+
want string // Expected JSON output
1401+
}{{
1402+
// Both S1 and S2 have a field named X. From the perspective of S,
1403+
// it is ambiguous which one X refers to.
1404+
// This should not serialize either field.
1405+
label: "AmbiguousField",
1406+
makeInput: func() interface{} {
1407+
type (
1408+
S1 struct{ x, X int }
1409+
S2 struct{ x, X int }
1410+
S struct {
1411+
S1
1412+
S2
1413+
}
1414+
)
1415+
return S{S1{1, 2}, S2{3, 4}}
1416+
},
1417+
want: `{}`,
1418+
}, {
1419+
label: "DominantField",
1420+
// Both S1 and S2 have a field named X, but since S has an X field as
1421+
// well, it takes precedence over S1.X and S2.X.
1422+
makeInput: func() interface{} {
1423+
type (
1424+
S1 struct{ x, X int }
1425+
S2 struct{ x, X int }
1426+
S struct {
1427+
S1
1428+
S2
1429+
x, X int
1430+
}
1431+
)
1432+
return S{S1{1, 2}, S2{3, 4}, 5, 6}
1433+
},
1434+
want: `{"X":6}`,
1435+
}, {
1436+
// Unexported embedded field of non-struct type should not be serialized.
1437+
label: "UnexportedEmbeddedInt",
1438+
makeInput: func() interface{} {
1439+
type (
1440+
myInt int
1441+
S struct{ myInt }
1442+
)
1443+
return S{5}
1444+
},
1445+
want: `{}`,
1446+
}, {
1447+
// Exported embedded field of non-struct type should be serialized.
1448+
label: "ExportedEmbeddedInt",
1449+
makeInput: func() interface{} {
1450+
type (
1451+
MyInt int
1452+
S struct{ MyInt }
1453+
)
1454+
return S{5}
1455+
},
1456+
want: `{"MyInt":5}`,
1457+
}, {
1458+
// Unexported embedded field of pointer to non-struct type
1459+
// should not be serialized.
1460+
label: "UnexportedEmbeddedIntPointer",
1461+
makeInput: func() interface{} {
1462+
type (
1463+
myInt int
1464+
S struct{ *myInt }
1465+
)
1466+
s := S{new(myInt)}
1467+
*s.myInt = 5
1468+
return s
1469+
},
1470+
want: `{}`,
1471+
}, {
1472+
// Exported embedded field of pointer to non-struct type
1473+
// should be serialized.
1474+
label: "ExportedEmbeddedIntPointer",
1475+
makeInput: func() interface{} {
1476+
type (
1477+
MyInt int
1478+
S struct{ *MyInt }
1479+
)
1480+
s := S{new(MyInt)}
1481+
*s.MyInt = 5
1482+
return s
1483+
},
1484+
want: `{"MyInt":5}`,
1485+
}, {
1486+
// Exported fields of embedded structs should have their
1487+
// exported fields be serialized regardless of whether the struct types
1488+
// themselves are exported.
1489+
label: "EmbeddedStruct",
1490+
makeInput: func() interface{} {
1491+
type (
1492+
s1 struct{ x, X int }
1493+
S2 struct{ y, Y int }
1494+
S struct {
1495+
s1
1496+
S2
1497+
}
1498+
)
1499+
return S{s1{1, 2}, S2{3, 4}}
1500+
},
1501+
want: `{"X":2,"Y":4}`,
1502+
}, {
1503+
// Exported fields of pointers to embedded structs should have their
1504+
// exported fields be serialized regardless of whether the struct types
1505+
// themselves are exported.
1506+
label: "EmbeddedStructPointer",
1507+
makeInput: func() interface{} {
1508+
type (
1509+
s1 struct{ x, X int }
1510+
S2 struct{ y, Y int }
1511+
S struct {
1512+
*s1
1513+
*S2
1514+
}
1515+
)
1516+
return S{&s1{1, 2}, &S2{3, 4}}
1517+
},
1518+
want: `{"X":2,"Y":4}`,
1519+
}, {
1520+
// Exported fields on embedded unexported structs at multiple levels
1521+
// of nesting should still be serialized.
1522+
label: "NestedStructAndInts",
1523+
makeInput: func() interface{} {
1524+
type (
1525+
MyInt1 int
1526+
MyInt2 int
1527+
myInt int
1528+
s2 struct {
1529+
MyInt2
1530+
myInt
1531+
}
1532+
s1 struct {
1533+
MyInt1
1534+
myInt
1535+
s2
1536+
}
1537+
S struct {
1538+
s1
1539+
myInt
1540+
}
1541+
)
1542+
return S{s1{1, 2, s2{3, 4}}, 6}
1543+
},
1544+
want: `{"MyInt1":1,"MyInt2":3}`,
1545+
}, {
1546+
// If an anonymous struct pointer field is nil, we should ignore
1547+
// the embedded fields behind it. Not properly doing so may
1548+
// result in the wrong output or reflect panics.
1549+
label: "EmbeddedFieldBehindNilPointer",
1550+
makeInput: func() interface{} {
1551+
type (
1552+
S2 struct{ Field string }
1553+
S struct{ *S2 }
1554+
)
1555+
return S{}
1556+
},
1557+
want: `{}`,
1558+
}}
1559+
1560+
for i, tt := range tests {
1561+
t.Run(tt.label, func(t *testing.T) {
1562+
b, err := json.Marshal(tt.makeInput())
1563+
if err != nil {
1564+
t.Fatalf("%d: Marshal() = %v, want nil error", i, err)
1565+
}
1566+
if string(b) != tt.want {
1567+
t.Fatalf("%d: Marshal() = %q, want %q", i, b, tt.want)
1568+
}
1569+
})
1570+
}
1571+
}

0 commit comments

Comments
 (0)