Disallow floating point numbers.
Floating point numbers are not allowed in canonical JSON. Neither are leading zeros or "minus 0" for integers. according to http://wiki.laptop.org/go/Canonical_JSON Signed-off-by: Jessica Frazelle <acidburn@docker.com>
This commit is contained in:
Родитель
8439c05729
Коммит
b0b3e735f4
|
@ -36,14 +36,14 @@ type V struct {
|
|||
var ifaceNumAsFloat64 = map[string]interface{}{
|
||||
"k1": float64(1),
|
||||
"k2": "s",
|
||||
"k3": []interface{}{float64(1), float64(2.0), float64(3e-3)},
|
||||
"k3": []interface{}{float64(1), float64(2.0), float64(3)},
|
||||
"k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)},
|
||||
}
|
||||
|
||||
var ifaceNumAsNumber = map[string]interface{}{
|
||||
"k1": Number("1"),
|
||||
"k2": "s",
|
||||
"k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")},
|
||||
"k3": []interface{}{Number("1"), Number("2.0"), Number("3")},
|
||||
"k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")},
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,7 @@ var unmarshalTests = []unmarshalTest{
|
|||
// basic types
|
||||
{in: `true`, ptr: new(bool), out: true},
|
||||
{in: `1`, ptr: new(int), out: 1},
|
||||
{in: `1.2`, ptr: new(float64), out: 1.2},
|
||||
{in: `1.0`, ptr: new(float64), out: 1.0},
|
||||
{in: `-5`, ptr: new(int16), out: int16(-5)},
|
||||
{in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
|
||||
{in: `2`, ptr: new(Number), out: Number("2")},
|
||||
|
@ -236,13 +236,13 @@ var unmarshalTests = []unmarshalTest{
|
|||
{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
|
||||
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
|
||||
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
|
||||
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
|
||||
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true},
|
||||
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
|
||||
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true},
|
||||
|
||||
// raw values with whitespace
|
||||
{in: "\n true ", ptr: new(bool), out: true},
|
||||
{in: "\t 1 ", ptr: new(int), out: 1},
|
||||
{in: "\r 1.2 ", ptr: new(float64), out: 1.2},
|
||||
{in: "\r 1.0 ", ptr: new(float64), out: 1.0},
|
||||
{in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
|
||||
{in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
|
||||
|
||||
|
@ -836,8 +836,8 @@ var allValue = All{
|
|||
Uint32: 10,
|
||||
Uint64: 11,
|
||||
Uintptr: 12,
|
||||
Float32: 14.1,
|
||||
Float64: 15.1,
|
||||
Float32: 14,
|
||||
Float64: 15,
|
||||
Foo: "foo",
|
||||
Foo2: "foo2",
|
||||
IntStr: 42,
|
||||
|
@ -858,7 +858,7 @@ var allValue = All{
|
|||
ByteSlice: []byte{27, 28, 29},
|
||||
Small: Small{Tag: "tag30"},
|
||||
PSmall: &Small{Tag: "tag31"},
|
||||
Interface: 5.2,
|
||||
Interface: 5.0,
|
||||
}
|
||||
|
||||
var pallValue = All{
|
||||
|
@ -898,8 +898,8 @@ var allValueIndent = `{
|
|||
"Uint32": 10,
|
||||
"Uint64": 11,
|
||||
"Uintptr": 12,
|
||||
"Float32": 14.1,
|
||||
"Float64": 15.1,
|
||||
"Float32": 14,
|
||||
"Float64": 15,
|
||||
"bar": "foo",
|
||||
"bar2": "foo2",
|
||||
"IntStr": "42",
|
||||
|
@ -971,7 +971,7 @@ var allValueIndent = `{
|
|||
"Tag": "tag31"
|
||||
},
|
||||
"PPSmall": null,
|
||||
"Interface": 5.2,
|
||||
"Interface": 5,
|
||||
"PInterface": null
|
||||
}`
|
||||
|
||||
|
@ -1007,8 +1007,8 @@ var pallValueIndent = `{
|
|||
"PUint32": 10,
|
||||
"PUint64": 11,
|
||||
"PUintptr": 12,
|
||||
"PFloat32": 14.1,
|
||||
"PFloat64": 15.1,
|
||||
"PFloat32": 14,
|
||||
"PFloat64": 15,
|
||||
"String": "",
|
||||
"PString": "16",
|
||||
"Map": null,
|
||||
|
@ -1060,7 +1060,7 @@ var pallValueIndent = `{
|
|||
"Tag": "tag31"
|
||||
},
|
||||
"Interface": null,
|
||||
"PInterface": 5.2
|
||||
"PInterface": 5
|
||||
}`
|
||||
|
||||
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
|
||||
|
@ -1209,8 +1209,8 @@ func TestUnmarshalNulls(t *testing.T) {
|
|||
Uint16: 9,
|
||||
Uint32: 10,
|
||||
Uint64: 11,
|
||||
Float32: 12.1,
|
||||
Float64: 13.1,
|
||||
Float32: 12.0,
|
||||
Float64: 13.0,
|
||||
String: "14"}
|
||||
|
||||
err := Unmarshal(jsonData, &nulls)
|
||||
|
@ -1219,7 +1219,7 @@ func TestUnmarshalNulls(t *testing.T) {
|
|||
}
|
||||
if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
|
||||
nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
|
||||
nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
|
||||
nulls.Float32 != 12.0 || nulls.Float64 != 13.0 || nulls.String != "14" {
|
||||
|
||||
t.Errorf("Unmarshal of null values affected primitives")
|
||||
}
|
||||
|
@ -1367,13 +1367,13 @@ func TestPrefilled(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
in: `{"X": 1, "Y": 2}`,
|
||||
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
|
||||
out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
|
||||
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1},
|
||||
out: &XYZ{X: float64(1), Y: float64(2), Z: 1},
|
||||
},
|
||||
{
|
||||
in: `{"X": 1, "Y": 2}`,
|
||||
ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}),
|
||||
out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}),
|
||||
ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1}),
|
||||
out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1}),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"bytes"
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
@ -519,8 +520,11 @@ type floatEncoder int // number of bits
|
|||
|
||||
func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
|
||||
f := v.Float()
|
||||
if math.IsInf(f, 0) || math.IsNaN(f) {
|
||||
e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
|
||||
if math.IsInf(f, 0) || math.IsNaN(f) || math.Floor(f) != f {
|
||||
e.error(&UnsupportedValueError{
|
||||
v,
|
||||
fmt.Sprintf("floating point number, %s", strconv.FormatFloat(f, 'g', -1, int(bits))),
|
||||
})
|
||||
}
|
||||
b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
|
||||
if quoted {
|
||||
|
|
|
@ -532,34 +532,43 @@ func TestEncodeString(t *testing.T) {
|
|||
}
|
||||
|
||||
type ObjEnc struct {
|
||||
A int
|
||||
B OrderedObject
|
||||
C interface{}
|
||||
A int
|
||||
B OrderedObject
|
||||
C interface{}
|
||||
}
|
||||
|
||||
var orderedObjectTests = []struct {
|
||||
in interface{}
|
||||
out string
|
||||
in interface{}
|
||||
out string
|
||||
}{
|
||||
{OrderedObject{}, `{}`},
|
||||
{OrderedObject{Member{"A", []int{1, 2, 3}}, Member{"B", 23}, Member{"C", "C"}}, `{"A":[1,2,3],"B":23,"C":"C"}`},
|
||||
{ObjEnc{A: 234, B: OrderedObject{Member{"K", "V"}, Member{"V", 4}}}, `{"A":234,"B":{"K":"V","V":4},"C":null}`},
|
||||
{ObjEnc{A: 234, B: OrderedObject{}, C: OrderedObject{{"A", 0}}}, `{"A":234,"B":{},"C":{"A":0}}`},
|
||||
{[]OrderedObject{{{"A", "Ay"}, {"B", "Bee"}}, {{"A", "Nay"}}}, `[{"A":"Ay","B":"Bee"},{"A":"Nay"}]`},
|
||||
{map[string]OrderedObject{"A": {{"A", "Ay"}, {"B", "Bee"}}}, `{"A":{"A":"Ay","B":"Bee"}}`},
|
||||
{map[string]interface{}{"A": OrderedObject{{"A", "Ay"}, {"B", "Bee"}}}, `{"A":{"A":"Ay","B":"Bee"}}`},
|
||||
{OrderedObject{}, `{}`},
|
||||
{OrderedObject{Member{"A", []int{1, 2, 3}}, Member{"B", 23}, Member{"C", "C"}}, `{"A":[1,2,3],"B":23,"C":"C"}`},
|
||||
{ObjEnc{A: 234, B: OrderedObject{Member{"K", "V"}, Member{"V", 4}}}, `{"A":234,"B":{"K":"V","V":4},"C":null}`},
|
||||
{ObjEnc{A: 234, B: OrderedObject{}, C: OrderedObject{{"A", 0}}}, `{"A":234,"B":{},"C":{"A":0}}`},
|
||||
{[]OrderedObject{{{"A", "Ay"}, {"B", "Bee"}}, {{"A", "Nay"}}}, `[{"A":"Ay","B":"Bee"},{"A":"Nay"}]`},
|
||||
{map[string]OrderedObject{"A": {{"A", "Ay"}, {"B", "Bee"}}}, `{"A":{"A":"Ay","B":"Bee"}}`},
|
||||
{map[string]interface{}{"A": OrderedObject{{"A", "Ay"}, {"B", "Bee"}}}, `{"A":{"A":"Ay","B":"Bee"}}`},
|
||||
}
|
||||
|
||||
func TestEncodeOrderedObject(t *testing.T) {
|
||||
for i, o := range orderedObjectTests {
|
||||
d, err := Marshal(o.in)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
continue
|
||||
}
|
||||
ds := string(d)
|
||||
if o.out != ds {
|
||||
t.Errorf("#%d expected '%v', was '%v'", i, o.out, ds)
|
||||
}
|
||||
}
|
||||
for i, o := range orderedObjectTests {
|
||||
d, err := Marshal(o.in)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
continue
|
||||
}
|
||||
ds := string(d)
|
||||
if o.out != ds {
|
||||
t.Errorf("#%d expected '%v', was '%v'", i, o.out, ds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloatError(t *testing.T) {
|
||||
input := struct{ A float64 }{1.1}
|
||||
|
||||
_, err := Marshal(input)
|
||||
if err == nil {
|
||||
t.Errorf("want float error, got nil")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,7 +264,7 @@ func genValue(n int) interface{} {
|
|||
case 0:
|
||||
return rand.Intn(2) == 0
|
||||
case 1:
|
||||
return rand.NormFloat64()
|
||||
return math.Floor(rand.NormFloat64())
|
||||
case 2:
|
||||
return genString(30)
|
||||
}
|
||||
|
|
|
@ -16,24 +16,24 @@ import (
|
|||
// Test values for the stream test.
|
||||
// One of each JSON kind.
|
||||
var streamTest = []interface{}{
|
||||
0.1,
|
||||
1.0,
|
||||
"hello",
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
[]interface{}{"a", "b", "c"},
|
||||
map[string]interface{}{"K": "Kelvin", "ß": "long s"},
|
||||
3.14, // another value to make sure something can follow map
|
||||
3.0, // another value to make sure something can follow map
|
||||
}
|
||||
|
||||
var streamEncoded = `0.1
|
||||
var streamEncoded = `1
|
||||
"hello"
|
||||
null
|
||||
true
|
||||
false
|
||||
["a","b","c"]
|
||||
{"ß":"long s","K":"Kelvin"}
|
||||
3.14
|
||||
3
|
||||
`
|
||||
|
||||
func TestEncoder(t *testing.T) {
|
||||
|
@ -129,7 +129,7 @@ func TestRawMessage(t *testing.T) {
|
|||
Y float32
|
||||
}
|
||||
const raw = `["\u0056",null]`
|
||||
const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
|
||||
const msg = `{"X":1,"Id":["\u0056",null],"Y":2}`
|
||||
err := Unmarshal([]byte(msg), &data)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
|
@ -154,7 +154,7 @@ func TestNullRawMessage(t *testing.T) {
|
|||
Y float32
|
||||
}
|
||||
data.Id = new(RawMessage)
|
||||
const msg = `{"X":0.1,"Id":null,"Y":0.2}`
|
||||
const msg = `{"X":1,"Id":null,"Y":2}`
|
||||
err := Unmarshal([]byte(msg), &data)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
|
|
Загрузка…
Ссылка в новой задаче