out: (chan int)(nil),
err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[chan int](), Offset: 1},
},
+
+ // #75619
+ {
+ CaseName: Name("QuotedInt/GoSyntax"),
+ in: `{"X": "-0000123"}`,
+ ptr: new(struct {
+ X int64 `json:",string"`
+ }),
+ out: struct {
+ X int64 `json:",string"`
+ }{-123},
+ },
+ {
+ CaseName: Name("QuotedInt/Invalid"),
+ in: `{"X": "123 "}`,
+ ptr: new(struct {
+ X int64 `json:",string"`
+ }),
+ err: &UnmarshalTypeError{Value: "number 123 ", Type: reflect.TypeFor[int64](), Field: "X", Offset: int64(len(`{"X": "123 "`))},
+ },
+ {
+ CaseName: Name("QuotedUint/GoSyntax"),
+ in: `{"X": "0000123"}`,
+ ptr: new(struct {
+ X uint64 `json:",string"`
+ }),
+ out: struct {
+ X uint64 `json:",string"`
+ }{123},
+ },
+ {
+ CaseName: Name("QuotedUint/Invalid"),
+ in: `{"X": "0x123"}`,
+ ptr: new(struct {
+ X uint64 `json:",string"`
+ }),
+ err: &UnmarshalTypeError{Value: "number 0x123", Type: reflect.TypeFor[uint64](), Field: "X", Offset: int64(len(`{"X": "0x123"`))},
+ },
+ {
+ CaseName: Name("QuotedFloat/GoSyntax"),
+ in: `{"X": "0x1_4p-2"}`,
+ ptr: new(struct {
+ X float64 `json:",string"`
+ }),
+ out: struct {
+ X float64 `json:",string"`
+ }{0x1_4p-2},
+ },
+ {
+ CaseName: Name("QuotedFloat/Invalid"),
+ in: `{"X": "1.5e1_"}`,
+ ptr: new(struct {
+ X float64 `json:",string"`
+ }),
+ err: &UnmarshalTypeError{Value: "number 1.5e1_", Type: reflect.TypeFor[float64](), Field: "X", Offset: int64(len(`{"X": "1.5e1_"`))},
+ },
}
func TestMarshal(t *testing.T) {
break
}
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
- if uo.Flags.Get(jsonflags.StringifyWithLegacySemantics) && string(val) == "null" {
- if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
- va.SetInt(0)
+ if uo.Flags.Get(jsonflags.StringifyWithLegacySemantics) {
+ // For historical reasons, v1 parsed a quoted number
+ // according to the Go syntax and permitted a quoted null.
+ // See https://go.dev/issue/75619
+ n, err := strconv.ParseInt(string(val), 10, bits)
+ if err != nil {
+ if string(val) == "null" {
+ if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
+ va.SetInt(0)
+ }
+ return nil
+ }
+ return newUnmarshalErrorAfterWithValue(dec, t, errors.Unwrap(err))
}
+ va.SetInt(n)
return nil
}
fallthrough
break
}
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
- if uo.Flags.Get(jsonflags.StringifyWithLegacySemantics) && string(val) == "null" {
- if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
- va.SetUint(0)
+ if uo.Flags.Get(jsonflags.StringifyWithLegacySemantics) {
+ // For historical reasons, v1 parsed a quoted number
+ // according to the Go syntax and permitted a quoted null.
+ // See https://go.dev/issue/75619
+ n, err := strconv.ParseUint(string(val), 10, bits)
+ if err != nil {
+ if string(val) == "null" {
+ if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
+ va.SetUint(0)
+ }
+ return nil
+ }
+ return newUnmarshalErrorAfterWithValue(dec, t, errors.Unwrap(err))
}
+ va.SetUint(n)
return nil
}
fallthrough
if !stringify {
break
}
- if uo.Flags.Get(jsonflags.StringifyWithLegacySemantics) && string(val) == "null" {
- if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
- va.SetFloat(0)
+ if uo.Flags.Get(jsonflags.StringifyWithLegacySemantics) {
+ // For historical reasons, v1 parsed a quoted number
+ // according to the Go syntax and permitted a quoted null.
+ // See https://go.dev/issue/75619
+ n, err := strconv.ParseFloat(string(val), bits)
+ if err != nil {
+ if string(val) == "null" {
+ if !uo.Flags.Get(jsonflags.MergeWithLegacySemantics) {
+ va.SetFloat(0)
+ }
+ return nil
+ }
+ return newUnmarshalErrorAfterWithValue(dec, t, errors.Unwrap(err))
}
+ va.SetFloat(n)
return nil
}
if n, err := jsonwire.ConsumeNumber(val); n != len(val) || err != nil {
out: (chan int)(nil),
err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[chan int]()},
},
+
+ // #75619
+ {
+ CaseName: Name("QuotedInt/GoSyntax"),
+ in: `{"X": "-0000123"}`,
+ ptr: new(struct {
+ X int64 `json:",string"`
+ }),
+ out: struct {
+ X int64 `json:",string"`
+ }{-123},
+ },
+ {
+ CaseName: Name("QuotedInt/Invalid"),
+ in: `{"X": "123 "}`,
+ ptr: new(struct {
+ X int64 `json:",string"`
+ }),
+ err: &UnmarshalTypeError{Value: "number 123 ", Type: reflect.TypeFor[int64](), Field: "X", Offset: int64(len(`{"X": `))},
+ },
+ {
+ CaseName: Name("QuotedUint/GoSyntax"),
+ in: `{"X": "0000123"}`,
+ ptr: new(struct {
+ X uint64 `json:",string"`
+ }),
+ out: struct {
+ X uint64 `json:",string"`
+ }{123},
+ },
+ {
+ CaseName: Name("QuotedUint/Invalid"),
+ in: `{"X": "0x123"}`,
+ ptr: new(struct {
+ X uint64 `json:",string"`
+ }),
+ err: &UnmarshalTypeError{Value: "number 0x123", Type: reflect.TypeFor[uint64](), Field: "X", Offset: int64(len(`{"X": `))},
+ },
+ {
+ CaseName: Name("QuotedFloat/GoSyntax"),
+ in: `{"X": "0x1_4p-2"}`,
+ ptr: new(struct {
+ X float64 `json:",string"`
+ }),
+ out: struct {
+ X float64 `json:",string"`
+ }{0x1_4p-2},
+ },
+ {
+ CaseName: Name("QuotedFloat/Invalid"),
+ in: `{"X": "1.5e1_"}`,
+ ptr: new(struct {
+ X float64 `json:",string"`
+ }),
+ err: &UnmarshalTypeError{Value: "number 1.5e1_", Type: reflect.TypeFor[float64](), Field: "X", Offset: int64(len(`{"X": `))},
+ },
}
func TestMarshal(t *testing.T) {
// When marshaling, such Go values are serialized as their usual
// JSON representation, but quoted within a JSON string.
// When unmarshaling, such Go values must be deserialized from
-// a JSON string containing their usual JSON representation.
+// a JSON string containing their usual JSON representation or
+// Go number representation for that numeric kind.
+// Note that the Go number grammar is a superset of the JSON number grammar.
// A JSON null quoted in a JSON string is a valid substitute for JSON null
// while unmarshaling into a Go value that `string` takes effect on.
//