Skip to content

Commit 0cee4b7

Browse files
committed
encoding/json: always ignore embedded pointers to unexported struct types
CL 60410 fixes a bug in reflect that allows assignments to an embedded field of a pointer to an unexported struct type. This breaks the json package because unmarshal is now unable to assign a newly allocated struct to such fields. In order to be consistent in the behavior for marshal and unmarshal, this CL changes both marshal and unmarshal to always ignore embedded pointers to unexported structs. Fixes #21357 Change-Id: If62ea11155555e61115ebb9cfa5305caf101bde5 Reviewed-on: https://go-review.googlesource.com/76851 Run-TryBot: Joe Tsai <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 5103270 commit 0cee4b7

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

src/encoding/json/decode_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ type embed struct {
195195
Q int
196196
}
197197

198+
type Issue21357 struct {
199+
*embed
200+
R int
201+
}
202+
198203
type Loop struct {
199204
Loop1 int `json:",omitempty"`
200205
Loop2 int `json:",omitempty"`
@@ -866,6 +871,20 @@ var unmarshalTests = []unmarshalTest{
866871
err: fmt.Errorf("json: unknown field \"extra\""),
867872
disallowUnknownFields: true,
868873
},
874+
875+
// Issue 21357.
876+
// Ignore any embedded fields that are pointers to unexported structs.
877+
{
878+
in: `{"Q":1,"R":2}`,
879+
ptr: new(Issue21357),
880+
out: Issue21357{R: 2},
881+
},
882+
{
883+
in: `{"Q":1,"R":2}`,
884+
ptr: new(Issue21357),
885+
err: fmt.Errorf("json: unknown field \"Q\""),
886+
disallowUnknownFields: true,
887+
},
869888
}
870889

871890
func TestMarshal(t *testing.T) {

src/encoding/json/encode.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,11 +1094,18 @@ func typeFields(t reflect.Type) []field {
10941094
isUnexported := sf.PkgPath != ""
10951095
if sf.Anonymous {
10961096
t := sf.Type
1097-
if t.Kind() == reflect.Ptr {
1097+
isPointer := t.Kind() == reflect.Ptr
1098+
if isPointer {
10981099
t = t.Elem()
10991100
}
1100-
if isUnexported && t.Kind() != reflect.Struct {
1101-
// Ignore embedded fields of unexported non-struct types.
1101+
isStruct := t.Kind() == reflect.Struct
1102+
if isUnexported && (!isStruct || isPointer) {
1103+
// Ignore embedded fields of unexported non-struct types
1104+
// or pointers to unexported struct types.
1105+
//
1106+
// The latter is forbidden because unmarshal is unable
1107+
// to assign a new struct to the unexported field.
1108+
// See https://golang.org/issue/21357
11021109
continue
11031110
}
11041111
// Do not ignore embedded fields of unexported struct types

src/encoding/json/encode_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,9 @@ func TestAnonymousFields(t *testing.T) {
364364
want: `{"X":2,"Y":4}`,
365365
}, {
366366
// Exported fields of pointers to embedded structs should have their
367-
// exported fields be serialized regardless of whether the struct types
368-
// themselves are exported.
367+
// exported fields be serialized only for exported struct types.
368+
// Pointers to unexported structs are not allowed since the decoder
369+
// is unable to allocate a struct for that field
369370
label: "EmbeddedStructPointer",
370371
makeInput: func() interface{} {
371372
type (
@@ -378,7 +379,7 @@ func TestAnonymousFields(t *testing.T) {
378379
)
379380
return S{&s1{1, 2}, &S2{3, 4}}
380381
},
381-
want: `{"X":2,"Y":4}`,
382+
want: `{"Y":4}`,
382383
}, {
383384
// Exported fields on embedded unexported structs at multiple levels
384385
// of nesting should still be serialized.

0 commit comments

Comments
 (0)