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:
Jessica Frazelle 2015-08-01 18:28:34 -07:00
Родитель 8439c05729
Коммит b0b3e735f4
5 изменённых файлов: 67 добавлений и 54 удалений

Просмотреть файл

@ -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 {

Просмотреть файл

@ -563,3 +563,12 @@ func TestEncodeOrderedObject(t *testing.T) {
}
}
}
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{}{"": "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","":"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)