Skip to content

Commit e27260c

Browse files
authored
Reject JSON numbers with StringifyNumbers when unmarshaling (#60)
WARNING: This commit includes breaking changes. Previously, if StringifyNumbers was specified, we would permit unmarshaling of a JSON number into a Go number. However, this is not how the v1 equivalent functionality operated. In v1, once the `string` tag option was specified, it would only unmarshal a JSON string containing a JSON number, but would reject a JSON number. It may be reasonable to provide a separate option that allows the more flexible unmarshal behavior that this commit breaks, but that can be a future change. For now, we are working towards implementing v1 entirely in terms of v2.
1 parent d2b1a22 commit e27260c

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

arshal_default.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,9 @@ func makeIntArshaler(t reflect.Type) *arshaler {
409409
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
410410
fallthrough
411411
case '0':
412+
if uo.Flags.Get(jsonflags.StringifyNumbers) && k == '0' {
413+
break
414+
}
412415
var negOffset int
413416
neg := len(val) > 0 && val[0] == '-'
414417
if neg {
@@ -486,6 +489,9 @@ func makeUintArshaler(t reflect.Type) *arshaler {
486489
val = jsonwire.UnquoteMayCopy(val, flags.IsVerbatim())
487490
fallthrough
488491
case '0':
492+
if uo.Flags.Get(jsonflags.StringifyNumbers) && k == '0' {
493+
break
494+
}
489495
n, ok := jsonwire.ParseUint(val)
490496
maxUint := uint64(1) << bits
491497
overflow := n > maxUint-1
@@ -590,6 +596,9 @@ func makeFloatArshaler(t reflect.Type) *arshaler {
590596
}
591597
fallthrough
592598
case '0':
599+
if uo.Flags.Get(jsonflags.StringifyNumbers) && k == '0' {
600+
break
601+
}
593602
fv, ok := jsonwire.ParseFloat(val, bits)
594603
if !ok && uo.Flags.Get(jsonflags.RejectFloatOverflow) {
595604
return &SemanticError{action: "unmarshal", JSONKind: k, GoType: t, Err: strconv.ErrRange}

arshal_test.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4685,6 +4685,13 @@ func TestUnmarshal(t *testing.T) {
46854685
inBuf: `"-6464"`,
46864686
inVal: new(int),
46874687
want: addr(int(-6464)),
4688+
}, {
4689+
name: jsontest.Name("Ints/Stringified/Invalid"),
4690+
opts: []Options{StringifyNumbers(true)},
4691+
inBuf: `-6464`,
4692+
inVal: new(int),
4693+
want: new(int),
4694+
wantErr: &SemanticError{action: "unmarshal", JSONKind: '0', GoType: intType},
46884695
}, {
46894696
name: jsontest.Name("Ints/Stringified/LeadingZero"),
46904697
opts: []Options{StringifyNumbers(true)},
@@ -4869,6 +4876,13 @@ func TestUnmarshal(t *testing.T) {
48694876
inBuf: `"6464"`,
48704877
inVal: new(uint),
48714878
want: addr(uint(6464)),
4879+
}, {
4880+
name: jsontest.Name("Uints/Stringified/Invalid"),
4881+
opts: []Options{StringifyNumbers(true)},
4882+
inBuf: `6464`,
4883+
inVal: new(uint),
4884+
want: new(uint),
4885+
wantErr: &SemanticError{action: "unmarshal", JSONKind: '0', GoType: uintType},
48724886
}, {
48734887
name: jsontest.Name("Uints/Stringified/LeadingZero"),
48744888
opts: []Options{StringifyNumbers(true)},
@@ -5042,6 +5056,13 @@ func TestUnmarshal(t *testing.T) {
50425056
inBuf: `"64.64"`,
50435057
inVal: new(float64),
50445058
want: addr(float64(64.64)),
5059+
}, {
5060+
name: jsontest.Name("Floats/Stringified/Invalid"),
5061+
opts: []Options{StringifyNumbers(true)},
5062+
inBuf: `64.64`,
5063+
inVal: new(float64),
5064+
want: new(float64),
5065+
wantErr: &SemanticError{action: "unmarshal", JSONKind: '0', GoType: float64Type},
50455066
}, {
50465067
name: jsontest.Name("Floats/Escaped"),
50475068
opts: []Options{StringifyNumbers(true)},
@@ -5488,33 +5509,33 @@ func TestUnmarshal(t *testing.T) {
54885509
"Bool": true,
54895510
"String": "hello",
54905511
"Bytes": "AQID",
5491-
"Int": -64,
5492-
"Uint": 64,
5493-
"Float": 3.14159,
5512+
"Int": "-64",
5513+
"Uint": "64",
5514+
"Float": "3.14159",
54945515
"Map": {"key": "value"},
54955516
"StructScalars": {
54965517
"Bool": true,
54975518
"String": "hello",
54985519
"Bytes": "AQID",
5499-
"Int": -64,
5500-
"Uint": 64,
5501-
"Float": 3.14159
5520+
"Int": "-64",
5521+
"Uint": "64",
5522+
"Float": "3.14159"
55025523
},
55035524
"StructMaps": {
55045525
"MapBool": {"": true},
55055526
"MapString": {"": "hello"},
55065527
"MapBytes": {"": "AQID"},
5507-
"MapInt": {"": -64},
5508-
"MapUint": {"": 64},
5509-
"MapFloat": {"": 3.14159}
5528+
"MapInt": {"": "-64"},
5529+
"MapUint": {"": "64"},
5530+
"MapFloat": {"": "3.14159"}
55105531
},
55115532
"StructSlices": {
55125533
"SliceBool": [true],
55135534
"SliceString": ["hello"],
55145535
"SliceBytes": ["AQID"],
5515-
"SliceInt": [-64],
5516-
"SliceUint": [64],
5517-
"SliceFloat": [3.14159]
5536+
"SliceInt": ["-64"],
5537+
"SliceUint": ["64"],
5538+
"SliceFloat": ["3.14159"]
55185539
},
55195540
"Slice": ["fizz","buzz"],
55205541
"Array": ["goodbye"],
@@ -6318,7 +6339,7 @@ func TestUnmarshal(t *testing.T) {
63186339
}, {
63196340
name: jsontest.Name("Structs/InlinedFallback/MapStringInt/StringifiedNumbers"),
63206341
opts: []Options{StringifyNumbers(true)},
6321-
inBuf: `{"zero": 0, "one": "1", "two": 2}`,
6342+
inBuf: `{"zero": "0", "one": "1", "two": "2"}`,
63226343
inVal: new(structInlineMapStringInt),
63236344
want: addr(structInlineMapStringInt{
63246345
X: map[string]int{"zero": 0, "one": 1, "two": 2},

0 commit comments

Comments
 (0)