diff options
| author | Joe Tsai <joetsai@digital-static.net> | 2026-02-02 17:52:37 -0800 |
|---|---|---|
| committer | Joseph Tsai <joetsai@digital-static.net> | 2026-02-12 16:09:06 -0800 |
| commit | c9cbeb0a1b08a9830a3d2d4abe0c2108e52f7647 (patch) | |
| tree | f52aba650a85b15a0a009df469e37c4c4ad76b13 /src/encoding/json/v2/fields_test.go | |
| parent | 92c7fcf137848ad74f88f75fc21bcb159eb08104 (diff) | |
| download | go-c9cbeb0a1b08a9830a3d2d4abe0c2108e52f7647.tar.xz | |
encoding/json/v2: remove `unknown` tag option and DiscardUnknownMembers
WARNING: This commit contains breaking changes
for those already using GOEXPERIMENT=jsonv2.
This removes support for the `unknown` tag option and
the DiscardUnknownMembers marshal option.
The `unknown` tag option semantics are a bit too subtle
even for experienced Go programmers to understand.
Remove support for it. The exact same feature (or something similar)
can be added back into a future release of json/v2.
We already support the `inline` tag option,
which can handle most cases of what someone might want to do
with unknown fields (such as preserve them).
Fixes #77271
Updates #76444
Change-Id: I875952f0755e58aac4c571869b2cdb56e75cfda9
Reviewed-on: https://go-review.googlesource.com/c/go/+/741320
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
Diffstat (limited to 'src/encoding/json/v2/fields_test.go')
| -rw-r--r-- | src/encoding/json/v2/fields_test.go | 82 |
1 files changed, 42 insertions, 40 deletions
diff --git a/src/encoding/json/v2/fields_test.go b/src/encoding/json/v2/fields_test.go index ae58182f29..dbfabd4aef 100644 --- a/src/encoding/json/v2/fields_test.go +++ b/src/encoding/json/v2/fields_test.go @@ -183,7 +183,7 @@ func TestMakeStructFields(t *testing.T) { X jsontext.Value `json:",inline"` } `json:",inline"` X2 struct { - X map[string]any `json:",unknown"` + X map[string]any `json:",inline"` } `json:",inline"` }{}, want: structFields{}, @@ -194,12 +194,12 @@ func TestMakeStructFields(t *testing.T) { X jsontext.Value `json:",inline"` } `json:",inline"` X2 struct { - X map[string]any `json:",unknown"` + X map[string]any `json:",inline"` } `json:",inline"` - X map[string]jsontext.Value `json:",unknown"` + X map[string]jsontext.Value `json:",inline"` }{}, want: structFields{ - inlinedFallback: &structField{id: 0, index: []int{2}, typ: T[map[string]jsontext.Value](), fieldOptions: fieldOptions{name: "X", quotedName: `"X"`, unknown: true}}, + inlinedFallback: &structField{id: 0, index: []int{2}, typ: T[map[string]jsontext.Value](), fieldOptions: fieldOptions{name: "X", quotedName: `"X"`, inline: true}}, }, }, { name: jsontest.Name("InlinedFallback/InvalidImplicit"), @@ -232,17 +232,11 @@ func TestMakeStructFields(t *testing.T) { want: structFields{flattened: []structField{}}, wantErr: errors.New(`Go struct fields A and B conflict over JSON object name "same"`), }, { - name: jsontest.Name("BothInlineAndUnknown"), - in: struct { - A struct{} `json:",inline,unknown"` - }{}, - wantErr: errors.New("Go struct field A cannot have both `inline` and `unknown` specified"), - }, { name: jsontest.Name("InlineWithOptions"), in: struct { A struct{} `json:",inline,omitempty"` }{}, - wantErr: errors.New("Go struct field A cannot have any options other than `inline` or `unknown` specified"), + wantErr: errors.New("Go struct field A cannot have any options other than `inline` specified"), }, { name: jsontest.Name("UnknownWithOptions"), in: struct { @@ -257,7 +251,7 @@ func TestMakeStructFields(t *testing.T) { inline: true, }, }}, - wantErr: errors.New("Go struct field A cannot have any options other than `inline` or `unknown` specified"), + wantErr: errors.New("Go struct field A cannot have any options other than `inline` specified"), }, { name: jsontest.Name("InlineTextMarshaler"), in: struct { @@ -287,10 +281,18 @@ func TestMakeStructFields(t *testing.T) { }}}, wantErr: errors.New(`inlined Go struct field A of type struct { encoding.TextAppender } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("UnknownJSONMarshaler"), + name: jsontest.Name("InlineJSONMarshaler"), in: struct { - A struct{ Marshaler } `json:",unknown"` + A struct{ Marshaler } `json:",inline"` }{}, + want: structFields{flattened: []structField{{ + index: []int{0, 0}, + typ: reflect.TypeFor[Marshaler](), + fieldOptions: fieldOptions{ + name: "Marshaler", + quotedName: `"Marshaler"`, + }, + }}}, wantErr: errors.New(`inlined Go struct field A of type struct { json.Marshaler } must not implement marshal or unmarshal methods`), }, { name: jsontest.Name("InlineJSONMarshalerTo"), @@ -307,10 +309,18 @@ func TestMakeStructFields(t *testing.T) { }}}, wantErr: errors.New(`inlined Go struct field A of type struct { json.MarshalerTo } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("UnknownTextUnmarshaler"), + name: jsontest.Name("InlineTextUnmarshaler"), in: struct { - A *struct{ encoding.TextUnmarshaler } `json:",unknown"` + A *struct{ encoding.TextUnmarshaler } `json:",inline"` }{}, + want: structFields{flattened: []structField{{ + index: []int{0, 0}, + typ: reflect.TypeFor[encoding.TextUnmarshaler](), + fieldOptions: fieldOptions{ + name: "TextUnmarshaler", + quotedName: `"TextUnmarshaler"`, + }, + }}}, wantErr: errors.New(`inlined Go struct field A of type struct { encoding.TextUnmarshaler } must not implement marshal or unmarshal methods`), }, { name: jsontest.Name("InlineJSONUnmarshaler"), @@ -327,23 +337,23 @@ func TestMakeStructFields(t *testing.T) { }}}, wantErr: errors.New(`inlined Go struct field A of type struct { json.Unmarshaler } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("UnknownJSONUnmarshalerFrom"), + name: jsontest.Name("InlineJSONUnmarshalerFrom"), in: struct { - A struct{ UnmarshalerFrom } `json:",unknown"` + A struct{ UnmarshalerFrom } `json:",inline"` }{}, + want: structFields{flattened: []structField{{ + index: []int{0, 0}, + typ: reflect.TypeFor[UnmarshalerFrom](), + fieldOptions: fieldOptions{ + name: "UnmarshalerFrom", + quotedName: `"UnmarshalerFrom"`, + }, + }}}, wantErr: errors.New(`inlined Go struct field A of type struct { json.UnmarshalerFrom } must not implement marshal or unmarshal methods`), }, { - name: jsontest.Name("UnknownStruct"), - in: struct { - A struct { - X, Y, Z string - } `json:",unknown"` - }{}, - wantErr: errors.New("inlined Go struct field A of type struct { X string; Y string; Z string } with `unknown` tag must be a Go map of string key or a jsontext.Value"), - }, { name: jsontest.Name("InlineUnsupported/MapIntKey"), in: struct { - A map[int]any `json:",unknown"` + A map[int]any `json:",inline"` }{}, wantErr: errors.New(`inlined Go struct field A of type map[int]interface {} must be a Go struct, Go map of string key, or jsontext.Value`), }, { @@ -633,9 +643,9 @@ func TestParseTagOptions(t *testing.T) { }, { name: jsontest.Name("SuperfluousCommas"), in: struct { - V int `json:",,,,\"\",,inline,unknown,,,,"` + V int `json:",,,,\"\",,inline,,,,,"` }{}, - wantOpts: fieldOptions{name: "V", quotedName: `"V"`, inline: true, unknown: true}, + wantOpts: fieldOptions{name: "V", quotedName: `"V"`, inline: true}, wantErr: errors.New("Go struct field V has malformed `json` tag: invalid character ',' at start of option (expecting Unicode letter or single quote)"), }, { name: jsontest.Name("CaseAloneOption"), @@ -684,12 +694,6 @@ func TestParseTagOptions(t *testing.T) { }{}, wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, inline: true}, }, { - name: jsontest.Name("UnknownOption"), - in: struct { - FieldName int `json:",unknown"` - }{}, - wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`, unknown: true}, - }, { name: jsontest.Name("OmitZeroOption"), in: struct { FieldName int `json:",omitzero"` @@ -743,14 +747,13 @@ func TestParseTagOptions(t *testing.T) { }, { name: jsontest.Name("AllOptions"), in: struct { - FieldName int `json:",case:ignore,inline,unknown,omitzero,omitempty,string,format:format"` + FieldName int `json:",case:ignore,inline,omitzero,omitempty,string,format:format"` }{}, wantOpts: fieldOptions{ name: "FieldName", quotedName: `"FieldName"`, casing: caseIgnore, inline: true, - unknown: true, omitzero: true, omitempty: true, string: true, @@ -759,14 +762,13 @@ func TestParseTagOptions(t *testing.T) { }, { name: jsontest.Name("AllOptionsQuoted"), in: struct { - FieldName int `json:",'case':'ignore','inline','unknown','omitzero','omitempty','string','format':'format'"` + FieldName int `json:",'case':'ignore','inline','omitzero','omitempty','string','format':'format'"` }{}, wantOpts: fieldOptions{ name: "FieldName", quotedName: `"FieldName"`, casing: caseIgnore, inline: true, - unknown: true, omitzero: true, omitempty: true, string: true, @@ -783,7 +785,7 @@ func TestParseTagOptions(t *testing.T) { }, { name: jsontest.Name("AllOptionsSpaceSensitive"), in: struct { - FieldName int `json:", case:ignore , inline , unknown , omitzero , omitempty , string , format:format "` + FieldName int `json:", case:ignore , inline , omitzero , omitempty , string , format:format "` }{}, wantOpts: fieldOptions{name: "FieldName", quotedName: `"FieldName"`}, wantErr: errors.New("Go struct field FieldName has malformed `json` tag: invalid character ' ' at start of option (expecting Unicode letter or single quote)"), |
