add new patches
Signed-off-by: Jessica Frazelle <acidburn@docker.com>
This commit is contained in:
Родитель
47ec822e24
Коммит
4a314a4dd6
|
@ -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(3)},
|
||||
"k3": []interface{}{float64(1), float64(2.0), float64(3e-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("3")},
|
||||
"k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")},
|
||||
"k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")},
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ var unmarshalTests = []unmarshalTest{
|
|||
// basic types
|
||||
{in: `true`, ptr: new(bool), out: true},
|
||||
{in: `1`, ptr: new(int), out: 1},
|
||||
{in: `1.0`, ptr: new(float64), out: 1.0},
|
||||
{in: `1.2`, ptr: new(float64), out: 1.2},
|
||||
{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")},
|
||||
|
@ -235,13 +235,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,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},
|
||||
{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},
|
||||
|
||||
// raw values with whitespace
|
||||
{in: "\n true ", ptr: new(bool), out: true},
|
||||
{in: "\t 1 ", ptr: new(int), out: 1},
|
||||
{in: "\r 1.0 ", ptr: new(float64), out: 1.0},
|
||||
{in: "\r 1.2 ", ptr: new(float64), out: 1.2},
|
||||
{in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
|
||||
{in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
|
||||
|
||||
|
@ -796,8 +796,8 @@ var allValue = All{
|
|||
Uint32: 10,
|
||||
Uint64: 11,
|
||||
Uintptr: 12,
|
||||
Float32: 14,
|
||||
Float64: 15,
|
||||
Float32: 14.1,
|
||||
Float64: 15.1,
|
||||
Foo: "foo",
|
||||
Foo2: "foo2",
|
||||
IntStr: 42,
|
||||
|
@ -818,7 +818,7 @@ var allValue = All{
|
|||
ByteSlice: []byte{27, 28, 29},
|
||||
Small: Small{Tag: "tag30"},
|
||||
PSmall: &Small{Tag: "tag31"},
|
||||
Interface: 5.0,
|
||||
Interface: 5.2,
|
||||
}
|
||||
|
||||
var pallValue = All{
|
||||
|
@ -858,8 +858,8 @@ var allValueIndent = `{
|
|||
"Uint32": 10,
|
||||
"Uint64": 11,
|
||||
"Uintptr": 12,
|
||||
"Float32": 14,
|
||||
"Float64": 15,
|
||||
"Float32": 14.1,
|
||||
"Float64": 15.1,
|
||||
"bar": "foo",
|
||||
"bar2": "foo2",
|
||||
"IntStr": "42",
|
||||
|
@ -931,7 +931,7 @@ var allValueIndent = `{
|
|||
"Tag": "tag31"
|
||||
},
|
||||
"PPSmall": null,
|
||||
"Interface": 5,
|
||||
"Interface": 5.2,
|
||||
"PInterface": null
|
||||
}`
|
||||
|
||||
|
@ -967,8 +967,8 @@ var pallValueIndent = `{
|
|||
"PUint32": 10,
|
||||
"PUint64": 11,
|
||||
"PUintptr": 12,
|
||||
"PFloat32": 14,
|
||||
"PFloat64": 15,
|
||||
"PFloat32": 14.1,
|
||||
"PFloat64": 15.1,
|
||||
"String": "",
|
||||
"PString": "16",
|
||||
"Map": null,
|
||||
|
@ -1020,7 +1020,7 @@ var pallValueIndent = `{
|
|||
"Tag": "tag31"
|
||||
},
|
||||
"Interface": null,
|
||||
"PInterface": 5
|
||||
"PInterface": 5.2
|
||||
}`
|
||||
|
||||
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
|
||||
|
@ -1169,8 +1169,8 @@ func TestUnmarshalNulls(t *testing.T) {
|
|||
Uint16: 9,
|
||||
Uint32: 10,
|
||||
Uint64: 11,
|
||||
Float32: 12.0,
|
||||
Float64: 13.0,
|
||||
Float32: 12.1,
|
||||
Float64: 13.1,
|
||||
String: "14"}
|
||||
|
||||
err := Unmarshal(jsonData, &nulls)
|
||||
|
@ -1179,7 +1179,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.0 || nulls.Float64 != 13.0 || nulls.String != "14" {
|
||||
nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
|
||||
|
||||
t.Errorf("Unmarshal of null values affected primitives")
|
||||
}
|
||||
|
@ -1327,13 +1327,13 @@ func TestPrefilled(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
in: `{"X": 1, "Y": 2}`,
|
||||
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1},
|
||||
out: &XYZ{X: float64(1), Y: float64(2), Z: 1},
|
||||
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
|
||||
out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
|
||||
},
|
||||
{
|
||||
in: `{"X": 1, "Y": 2}`,
|
||||
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}),
|
||||
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}),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"bytes"
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
@ -392,7 +391,7 @@ func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
|
|||
case reflect.Struct:
|
||||
return newStructEncoder(t)
|
||||
case reflect.Map:
|
||||
return newCondEncoder(canonicalEncoder, newMapEncoder(t))
|
||||
return newMapEncoder(t)
|
||||
case reflect.Slice:
|
||||
return newSliceEncoder(t)
|
||||
case reflect.Array:
|
||||
|
@ -512,11 +511,8 @@ 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) || math.Floor(f) != f {
|
||||
e.error(&UnsupportedValueError{
|
||||
v,
|
||||
fmt.Sprintf("floating point number, %s", strconv.FormatFloat(f, 'g', -1, int(bits))),
|
||||
})
|
||||
if math.IsInf(f, 0) || math.IsNaN(f) {
|
||||
e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
|
||||
}
|
||||
b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
|
||||
if quoted {
|
||||
|
@ -602,67 +598,6 @@ func newStructEncoder(t reflect.Type) encoderFunc {
|
|||
return se.encode
|
||||
}
|
||||
|
||||
func canonicalEncoder(e *encodeState, v reflect.Value, _ bool) {
|
||||
m, ok := v.Interface().(Marshaler)
|
||||
if !ok {
|
||||
// T doesn't match the interface. Check against *T too.
|
||||
if v.Kind() != reflect.Ptr && v.CanAddr() {
|
||||
m, ok = v.Addr().Interface().(Marshaler)
|
||||
if ok {
|
||||
v = v.Addr()
|
||||
}
|
||||
}
|
||||
}
|
||||
b, err := m.MarshalJSON()
|
||||
if err != nil {
|
||||
e.error(&MarshalerError{v.Type(), err})
|
||||
}
|
||||
|
||||
// canonicalize the json if it's an object
|
||||
b = bytes.TrimSpace(b)
|
||||
if len(b) > 0 && b[0] == '{' {
|
||||
var temp interface{}
|
||||
err = Unmarshal(b, &temp)
|
||||
if err != nil {
|
||||
e.error(&MarshalerError{v.Type(), err})
|
||||
}
|
||||
b, err = Marshal(temp)
|
||||
if err != nil {
|
||||
e.error(&MarshalerError{v.Type(), err})
|
||||
}
|
||||
}
|
||||
e.Buffer.Write(b)
|
||||
}
|
||||
|
||||
type condEncoder struct {
|
||||
canEnc, elseEnc encoderFunc
|
||||
}
|
||||
|
||||
func (ce *condEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
|
||||
_, ok := v.Interface().(Marshaler)
|
||||
if !ok {
|
||||
// T doesn't match the interface. Check against *T too.
|
||||
if v.Kind() != reflect.Ptr && v.CanAddr() {
|
||||
_, ok = v.Addr().Interface().(Marshaler)
|
||||
if ok {
|
||||
v = v.Addr()
|
||||
}
|
||||
}
|
||||
}
|
||||
if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
|
||||
ce.canEnc(e, v, quoted)
|
||||
} else {
|
||||
ce.elseEnc(e, v, quoted)
|
||||
}
|
||||
}
|
||||
|
||||
// newCondEncoder returns an encoder that checks whether its value
|
||||
// Can and delegates to canEnc if so, else to elseEnc.
|
||||
func newCondEncoder(canEnc, elseEnc encoderFunc) encoderFunc {
|
||||
enc := &condEncoder{canEnc: canEnc, elseEnc: elseEnc}
|
||||
return enc.encode
|
||||
}
|
||||
|
||||
type mapEncoder struct {
|
||||
elemEnc encoderFunc
|
||||
}
|
||||
|
@ -853,7 +788,7 @@ func (e *encodeState) string(s string) (int, error) {
|
|||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if b != '\\' && b != '"' {
|
||||
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
@ -864,6 +799,23 @@ func (e *encodeState) string(s string) (int, error) {
|
|||
case '\\', '"':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte(b)
|
||||
case '\n':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte('n')
|
||||
case '\r':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte('r')
|
||||
case '\t':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte('t')
|
||||
default:
|
||||
// This encodes bytes < 0x20 except for \n and \r,
|
||||
// as well as <, > and &. The latter are escaped because they
|
||||
// can lead to security holes when user-controlled strings
|
||||
// are rendered into JSON and served to some browsers.
|
||||
e.WriteString(`\u00`)
|
||||
e.WriteByte(hex[b>>4])
|
||||
e.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
|
@ -912,7 +864,7 @@ func (e *encodeState) stringBytes(s []byte) (int, error) {
|
|||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if 0x20 <= b && b != '\\' {
|
||||
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
@ -923,6 +875,23 @@ func (e *encodeState) stringBytes(s []byte) (int, error) {
|
|||
case '\\', '"':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte(b)
|
||||
case '\n':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte('n')
|
||||
case '\r':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte('r')
|
||||
case '\t':
|
||||
e.WriteByte('\\')
|
||||
e.WriteByte('t')
|
||||
default:
|
||||
// This encodes bytes < 0x20 except for \n and \r,
|
||||
// as well as <, >, and &. The latter are escaped because they
|
||||
// can lead to security holes when user-controlled strings
|
||||
// are rendered into JSON and served to some browsers.
|
||||
e.WriteString(`\u00`)
|
||||
e.WriteByte(hex[b>>4])
|
||||
e.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
|
|
|
@ -530,68 +530,3 @@ func TestEncodeString(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ObjEnc struct {
|
||||
A int
|
||||
B map[string]interface{}
|
||||
C interface{}
|
||||
}
|
||||
|
||||
var orderedObjectTests = []struct {
|
||||
in interface{}
|
||||
out string
|
||||
}{
|
||||
{map[string]interface{}{}, `{}`},
|
||||
{map[string]interface{}{"A": []int{1, 2, 3}, "B": 23, "C": "C"}, `{"A":[1,2,3],"B":23,"C":"C"}`},
|
||||
{ObjEnc{A: 234, B: map[string]interface{}{"K": "V", "V": 4}}, `{"A":234,"B":{"K":"V","V":4},"C":null}`},
|
||||
{ObjEnc{A: 234, B: map[string]interface{}{}, C: map[string]interface{}{"A": 0}}, `{"A":234,"B":{},"C":{"A":0}}`},
|
||||
{map[string]interface{}{"A": "Ay", "B": "Bee", "C": "Nay"}, `{"A":"Ay","B":"Bee","C":"Nay"}`},
|
||||
{map[string]map[string]interface{}{"A": {"A": "Ay", "B": "Bee"}}, `{"A":{"A":"Ay","B":"Bee"}}`},
|
||||
{map[string]interface{}{"A": map[string]interface{}{"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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type marshaling struct{}
|
||||
|
||||
func (m marshaling) MarshalJSON() ([]byte, error) { return []byte("\t\n {\"b\":234,\"a\":123}"), nil }
|
||||
|
||||
func TestCanonicalization(t *testing.T) {
|
||||
input := struct {
|
||||
C map[string]interface{} `json:"c"`
|
||||
A string `json:"a"`
|
||||
D []int `json:"d"`
|
||||
B int `json:"b"`
|
||||
E *marshaling `json:"e"`
|
||||
}{map[string]interface{}{"b": "b", "a": "\n\r", "c": "\"\\<>"}, "a", []int{1, 2, 3}, 1, &marshaling{}}
|
||||
expected := `{"a":"a","b":1,"c":{"a":"` + "\n\r" + `","b":"b","c":"\"\\<>"},"d":[1,2,3],"e":{"a":123,"b":234}}`
|
||||
|
||||
output, err := Marshal(input)
|
||||
if err != nil {
|
||||
t.Errorf("got err = %v, want nil", err)
|
||||
}
|
||||
if expected != string(output) {
|
||||
t.Errorf("got %s, want %s", string(output), expected)
|
||||
}
|
||||
}
|
||||
|
||||
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 math.Floor(rand.NormFloat64())
|
||||
return 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{}{
|
||||
1.0,
|
||||
0.1,
|
||||
"hello",
|
||||
nil,
|
||||
true,
|
||||
false,
|
||||
[]interface{}{"a", "b", "c"},
|
||||
map[string]interface{}{"K": "Kelvin", "ß": "long s"},
|
||||
3.0, // another value to make sure something can follow map
|
||||
3.14, // another value to make sure something can follow map
|
||||
}
|
||||
|
||||
var streamEncoded = `1
|
||||
var streamEncoded = `0.1
|
||||
"hello"
|
||||
null
|
||||
true
|
||||
false
|
||||
["a","b","c"]
|
||||
{"ß":"long s","K":"Kelvin"}
|
||||
3
|
||||
3.14
|
||||
`
|
||||
|
||||
func TestEncoder(t *testing.T) {
|
||||
|
@ -129,7 +129,7 @@ func TestRawMessage(t *testing.T) {
|
|||
Y float32
|
||||
}
|
||||
const raw = `["\u0056",null]`
|
||||
const msg = `{"X":1,"Id":["\u0056",null],"Y":2}`
|
||||
const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.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":1,"Id":null,"Y":2}`
|
||||
const msg = `{"X":0.1,"Id":null,"Y":0.2}`
|
||||
err := Unmarshal([]byte(msg), &data)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal: %v", err)
|
||||
|
|
|
@ -1,439 +0,0 @@
|
|||
From 8439c0572954d57f6a25c55232b7b49ca4fbf693 Mon Sep 17 00:00:00 2001
|
||||
From: Jessica Frazelle <acidburn@docker.com>
|
||||
Date: Sat, 1 Aug 2015 16:33:26 -0700
|
||||
Subject: [PATCH 1/2] Add OrderedObject Type for canonical json
|
||||
|
||||
Signed-off-by: Jessica Frazelle <acidburn@docker.com>
|
||||
---
|
||||
canonical/json/decode.go | 38 +++++++++++++++--------
|
||||
canonical/json/decode_test.go | 69 +++++++++++++++++++++++++++++++++++++++---
|
||||
canonical/json/encode.go | 26 ++++++++++++++++
|
||||
canonical/json/encode_test.go | 33 ++++++++++++++++++++
|
||||
canonical/json/example_test.go | 43 +++++++++++++++++++++++++-
|
||||
canonical/json/stream.go | 53 ++++++++++++++++++++++++++++++++
|
||||
6 files changed, 244 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/canonical/json/decode.go b/canonical/json/decode.go
|
||||
index 705bc2e..41f12d9 100644
|
||||
--- a/canonical/json/decode.go
|
||||
+++ b/canonical/json/decode.go
|
||||
@@ -168,12 +168,13 @@ func (n Number) Int64() (int64, error) {
|
||||
|
||||
// decodeState represents the state while decoding a JSON value.
|
||||
type decodeState struct {
|
||||
- data []byte
|
||||
- off int // read offset in data
|
||||
- scan scanner
|
||||
- nextscan scanner // for calls to nextValue
|
||||
- savedError error
|
||||
- useNumber bool
|
||||
+ data []byte
|
||||
+ off int // read offset in data
|
||||
+ scan scanner
|
||||
+ nextscan scanner // for calls to nextValue
|
||||
+ savedError error
|
||||
+ useNumber bool
|
||||
+ useOrderedObject bool
|
||||
}
|
||||
|
||||
// errPhase is used for errors that should not happen unless
|
||||
@@ -492,9 +493,13 @@ func (d *decodeState) object(v reflect.Value) {
|
||||
}
|
||||
v = pv
|
||||
|
||||
+ // If we are decoding into a OrderedObject then use an OrderedObject
|
||||
+ // just for this particular object even if UseOrderedObject was not called.
|
||||
+ forceOrderedObject := v.Type() == orderedObjectType
|
||||
+
|
||||
// Decoding into nil interface? Switch to non-reflect code.
|
||||
- if v.Kind() == reflect.Interface && v.NumMethod() == 0 {
|
||||
- v.Set(reflect.ValueOf(d.objectInterface()))
|
||||
+ if (v.Kind() == reflect.Interface && v.NumMethod() == 0) || forceOrderedObject {
|
||||
+ v.Set(reflect.ValueOf(d.objectInterface(forceOrderedObject)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -831,7 +836,7 @@ func (d *decodeState) valueInterface() interface{} {
|
||||
case scanBeginArray:
|
||||
return d.arrayInterface()
|
||||
case scanBeginObject:
|
||||
- return d.objectInterface()
|
||||
+ return d.objectInterface(false)
|
||||
case scanBeginLiteral:
|
||||
return d.literalInterface()
|
||||
}
|
||||
@@ -865,9 +870,10 @@ func (d *decodeState) arrayInterface() []interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
-// objectInterface is like object but returns map[string]interface{}.
|
||||
-func (d *decodeState) objectInterface() map[string]interface{} {
|
||||
+// objectInterface is like object but returns map[string]interface{} or []OrderedObject.
|
||||
+func (d *decodeState) objectInterface(forceOrderedObject bool) interface{} {
|
||||
m := make(map[string]interface{})
|
||||
+ v := make(OrderedObject, 0)
|
||||
for {
|
||||
// Read opening " of string key or closing }.
|
||||
op := d.scanWhile(scanSkipSpace)
|
||||
@@ -897,7 +903,11 @@ func (d *decodeState) objectInterface() map[string]interface{} {
|
||||
}
|
||||
|
||||
// Read value.
|
||||
- m[key] = d.valueInterface()
|
||||
+ if d.useOrderedObject || forceOrderedObject {
|
||||
+ v = append(v, Member{Key: key, Value: d.valueInterface()})
|
||||
+ } else {
|
||||
+ m[key] = d.valueInterface()
|
||||
+ }
|
||||
|
||||
// Next token must be , or }.
|
||||
op = d.scanWhile(scanSkipSpace)
|
||||
@@ -908,6 +918,10 @@ func (d *decodeState) objectInterface() map[string]interface{} {
|
||||
d.error(errPhase)
|
||||
}
|
||||
}
|
||||
+
|
||||
+ if d.useOrderedObject || forceOrderedObject {
|
||||
+ return v
|
||||
+ }
|
||||
return m
|
||||
}
|
||||
|
||||
diff --git a/canonical/json/decode_test.go b/canonical/json/decode_test.go
|
||||
index 7235969..bbdd004 100644
|
||||
--- a/canonical/json/decode_test.go
|
||||
+++ b/canonical/json/decode_test.go
|
||||
@@ -197,11 +197,12 @@ type S13 struct {
|
||||
}
|
||||
|
||||
type unmarshalTest struct {
|
||||
- in string
|
||||
- ptr interface{}
|
||||
- out interface{}
|
||||
- err error
|
||||
- useNumber bool
|
||||
+ in string
|
||||
+ ptr interface{}
|
||||
+ out interface{}
|
||||
+ err error
|
||||
+ useNumber bool
|
||||
+ useOrderedObject bool
|
||||
}
|
||||
|
||||
type Ambig struct {
|
||||
@@ -413,6 +414,39 @@ var unmarshalTests = []unmarshalTest{
|
||||
ptr: &map[time.Time]string{},
|
||||
err: &UnmarshalTypeError{"object", reflect.TypeOf(map[time.Time]string{})},
|
||||
},
|
||||
+
|
||||
+ // OrderedObject tests - into interface{}
|
||||
+ {in: `{"a":"1","b":2,"c":3}`, ptr: new(interface{}), useOrderedObject: true,
|
||||
+ out: OrderedObject{{"a", "1"}, {"b", float64(2)}, {"c", float64(3)}}},
|
||||
+ {in: `[{"a":"1","b":2}]`, ptr: new(interface{}), useOrderedObject: true,
|
||||
+ out: []interface{}{OrderedObject{{"a", "1"}, {"b", float64(2)}}}},
|
||||
+ {in: `{"a":null,"b": {"c":true} }`, ptr: new(interface{}), useOrderedObject: true,
|
||||
+ out: OrderedObject{{"a", nil}, {"b", OrderedObject{{"c", true}}}}},
|
||||
+ {in: `{"T":[]}`, ptr: new(interface{}), useOrderedObject: true,
|
||||
+ out: OrderedObject{{Key: "T", Value: []interface{}{}}}},
|
||||
+ {in: `{"T":null}`, ptr: new(interface{}), useOrderedObject: true,
|
||||
+ out: OrderedObject{{Key: "T", Value: nil}}},
|
||||
+ // OrderedObject tests - into map[string]interface{}
|
||||
+ {in: `{"a":"1","b":2,"c":3}`, ptr: new(map[string]interface{}), useOrderedObject: true,
|
||||
+ out: map[string]interface{}{"a": "1", "b": float64(2), "c": float64(3)}},
|
||||
+ {in: `{"a":null,"b": {"c":true} }`, ptr: new(map[string]interface{}), useOrderedObject: true,
|
||||
+ out: map[string]interface{}{"a": nil, "b": OrderedObject{{"c", true}}}},
|
||||
+ {in: `{"T":[]}`, ptr: new(map[string]interface{}), useOrderedObject: true,
|
||||
+ out: map[string]interface{}{"T": []interface{}{}}},
|
||||
+ {in: `{"T":null}`, ptr: new(map[string]interface{}), useOrderedObject: true,
|
||||
+ out: map[string]interface{}{"T": nil}},
|
||||
+ // OrderedObject tests - into OrderedObject
|
||||
+ {in: `{"a":"1","b":2,"c":3}`, ptr: new(OrderedObject), useOrderedObject: true,
|
||||
+ out: OrderedObject{{"a", "1"}, {"b", float64(2)}, {"c", float64(3)}}},
|
||||
+ {in: `{"a":"1","b":2,"c":3}`, ptr: new(OrderedObject),
|
||||
+ out: OrderedObject{{"a", "1"}, {"b", float64(2)}, {"c", float64(3)}}},
|
||||
+ {in: `{"a":null,"b": {"c":true} }`, ptr: new(OrderedObject), useOrderedObject: true,
|
||||
+ out: OrderedObject{{"a", nil}, {"b", OrderedObject{{"c", true}}}}},
|
||||
+ {in: `{"a":null,"b": {"c":true} }`, ptr: new(OrderedObject),
|
||||
+ out: OrderedObject{{"a", nil}, {"b", map[string]interface{}{"c": true}}}},
|
||||
+ // OrderedObject tests -into []OrderedObject
|
||||
+ {in: `[{"a":"1","b":2},{"c":3}]`, ptr: &[]OrderedObject{},
|
||||
+ out: []OrderedObject{{{"a", "1"}, {"b", float64(2)}}, {{"c", float64(3)}}}},
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
@@ -528,6 +562,9 @@ func TestUnmarshal(t *testing.T) {
|
||||
if tt.useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
+ if tt.useOrderedObject {
|
||||
+ dec.UseOrderedObject()
|
||||
+ }
|
||||
if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) {
|
||||
t.Errorf("#%d: %v, want %v", i, err, tt.err)
|
||||
continue
|
||||
@@ -555,6 +592,9 @@ func TestUnmarshal(t *testing.T) {
|
||||
if tt.useNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
+ if tt.useOrderedObject {
|
||||
+ dec.UseOrderedObject()
|
||||
+ }
|
||||
if err := dec.Decode(vv.Interface()); err != nil {
|
||||
t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
|
||||
continue
|
||||
@@ -1371,3 +1411,22 @@ func TestInvalidUnmarshal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+type Obj struct {
|
||||
+ A int
|
||||
+ B OrderedObject
|
||||
+ C string
|
||||
+}
|
||||
+
|
||||
+func TestUnmarshalOrdered(t *testing.T) {
|
||||
+ var v Obj
|
||||
+ buf := []byte(`{"A": 3, "B": { "A": 5, "B": null}, "C": "C"}`)
|
||||
+ exp := Obj{A: 3, B: OrderedObject{{"A", float64(5)}, {"B", nil}}, C: "C"}
|
||||
+ err := Unmarshal(buf, &v)
|
||||
+ if err != nil {
|
||||
+ t.Errorf("Unmarshal unexpected error: %v", err)
|
||||
+ }
|
||||
+ if !reflect.DeepEqual(exp, v) {
|
||||
+ t.Errorf("%v, want %v", v, exp)
|
||||
+ }
|
||||
+}
|
||||
diff --git a/canonical/json/encode.go b/canonical/json/encode.go
|
||||
index fca2a09..7793733 100644
|
||||
--- a/canonical/json/encode.go
|
||||
+++ b/canonical/json/encode.go
|
||||
@@ -116,6 +116,9 @@ import (
|
||||
// The map's key type must be string; the object keys are used directly
|
||||
// as map keys.
|
||||
//
|
||||
+// OrderedObject values encode as JSON objects.
|
||||
+// The JSON keys are encoded in the order in which they appear in the OrderedObject.
|
||||
+//
|
||||
// Pointer values encode as the value pointed to.
|
||||
// A nil pointer encodes as the null JSON object.
|
||||
//
|
||||
@@ -350,6 +353,7 @@ func typeEncoder(t reflect.Type) encoderFunc {
|
||||
var (
|
||||
marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
||||
textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
|
||||
+ orderedObjectType = reflect.TypeOf(new(OrderedObject)).Elem()
|
||||
)
|
||||
|
||||
// newTypeEncoder constructs an encoderFunc for a type.
|
||||
@@ -373,6 +377,10 @@ func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
|
||||
}
|
||||
}
|
||||
|
||||
+ if t == orderedObjectType {
|
||||
+ return orderedObjectEncoder
|
||||
+ }
|
||||
+
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return boolEncoder
|
||||
@@ -629,6 +637,24 @@ func newMapEncoder(t reflect.Type) encoderFunc {
|
||||
return me.encode
|
||||
}
|
||||
|
||||
+func orderedObjectEncoder(e *encodeState, v reflect.Value, quoted bool) {
|
||||
+ if v.IsNil() {
|
||||
+ e.WriteString("null")
|
||||
+ return
|
||||
+ }
|
||||
+ e.WriteByte('{')
|
||||
+ var ov OrderedObject = v.Interface().(OrderedObject)
|
||||
+ for i, o := range ov {
|
||||
+ if i > 0 {
|
||||
+ e.WriteByte(',')
|
||||
+ }
|
||||
+ e.string(o.Key)
|
||||
+ e.WriteByte(':')
|
||||
+ e.reflectValue(reflect.ValueOf(o.Value))
|
||||
+ }
|
||||
+ e.WriteByte('}')
|
||||
+}
|
||||
+
|
||||
func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) {
|
||||
if v.IsNil() {
|
||||
e.WriteString("null")
|
||||
diff --git a/canonical/json/encode_test.go b/canonical/json/encode_test.go
|
||||
index 7abfa85..d497bd9 100644
|
||||
--- a/canonical/json/encode_test.go
|
||||
+++ b/canonical/json/encode_test.go
|
||||
@@ -530,3 +530,36 @@ func TestEncodeString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+type ObjEnc struct {
|
||||
+ A int
|
||||
+ B OrderedObject
|
||||
+ C interface{}
|
||||
+}
|
||||
+
|
||||
+var orderedObjectTests = []struct {
|
||||
+ 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"}}`},
|
||||
+}
|
||||
+
|
||||
+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)
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/canonical/json/example_test.go b/canonical/json/example_test.go
|
||||
index ca4e5ae..6b09118 100644
|
||||
--- a/canonical/json/example_test.go
|
||||
+++ b/canonical/json/example_test.go
|
||||
@@ -6,12 +6,13 @@ package json_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
- "encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
+
|
||||
+ "github.com/jfrazelle/go/canonical/json"
|
||||
)
|
||||
|
||||
func ExampleMarshal() {
|
||||
@@ -159,3 +160,43 @@ func ExampleIndent() {
|
||||
// = }
|
||||
// =]
|
||||
}
|
||||
+
|
||||
+func ExampleOrderedObject() {
|
||||
+ var jsonBlob = []byte(`[
|
||||
+ {"name": "Issac Newton", "born": 1643, "died": 1727 },
|
||||
+ {"name": "André-Marie Ampère", "born": 1777, "died": 1836 }
|
||||
+ ]`)
|
||||
+ var people []json.OrderedObject
|
||||
+
|
||||
+ // Decode JSON while preserving the order of JSON key pairs.
|
||||
+ err := json.Unmarshal(jsonBlob, &people)
|
||||
+ if err != nil {
|
||||
+ fmt.Println("unmarshalling error:", err)
|
||||
+ }
|
||||
+ fmt.Printf("Decoded:\n")
|
||||
+ for _, v := range people {
|
||||
+ for _, a := range v {
|
||||
+ fmt.Printf(" %v=%v", a.Key, a.Value)
|
||||
+ }
|
||||
+ fmt.Printf("\n")
|
||||
+ }
|
||||
+
|
||||
+ // Encode JSON keys in the order defined by the OrderedObject.
|
||||
+ person := json.OrderedObject{
|
||||
+ {Key: "name", Value: "Hans Christian Ørsted"},
|
||||
+ {Key: "born", Value: 1777},
|
||||
+ {Key: "died", Value: 1851},
|
||||
+ {Key: "nationality", Value: "Danish"},
|
||||
+ }
|
||||
+ b, err := json.Marshal(person)
|
||||
+ if err != nil {
|
||||
+ fmt.Println("marshalling error:", err)
|
||||
+ }
|
||||
+ fmt.Printf("Encoded:\n %v", string(b))
|
||||
+ // Output:
|
||||
+ // Decoded:
|
||||
+ // name=Issac Newton born=1643 died=1727
|
||||
+ // name=André-Marie Ampère born=1777 died=1836
|
||||
+ // Encoded:
|
||||
+ // {"name":"Hans Christian Ørsted","born":1777,"died":1851,"nationality":"Danish"}
|
||||
+}
|
||||
diff --git a/canonical/json/stream.go b/canonical/json/stream.go
|
||||
index 9566eca..7e71bc3 100644
|
||||
--- a/canonical/json/stream.go
|
||||
+++ b/canonical/json/stream.go
|
||||
@@ -31,6 +31,10 @@ func NewDecoder(r io.Reader) *Decoder {
|
||||
// Number instead of as a float64.
|
||||
func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
|
||||
|
||||
+// UseOrderedObject causes the Decoder to unmarshal an object into an interface{}
|
||||
+// as a OrderedObject instead of as a map[string]interface{}.
|
||||
+func (dec *Decoder) UseOrderedObject() { dec.d.useOrderedObject = true }
|
||||
+
|
||||
// Decode reads the next JSON-encoded value from its
|
||||
// input and stores it in the value pointed to by v.
|
||||
//
|
||||
@@ -198,3 +202,52 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||
|
||||
var _ Marshaler = (*RawMessage)(nil)
|
||||
var _ Unmarshaler = (*RawMessage)(nil)
|
||||
+
|
||||
+// Member is used to store key/value pairs in an OrderedObject.
|
||||
+type Member struct {
|
||||
+ Key string
|
||||
+ Value interface{}
|
||||
+}
|
||||
+
|
||||
+// OrderedObject stores the key/value pairs of a JSON object in the order
|
||||
+// in which they appeared in the original document. See the documentation for UseOrderedObject on the Decoder.
|
||||
+//
|
||||
+// OrderedObject is used to enable decoding of arbitrary JSON objects while preserving
|
||||
+// the order of the keys. Unmarshal and Decoder.Decode are supported.
|
||||
+//
|
||||
+// var o OrderedObject
|
||||
+// Unmarshal(json, &o) // decode JSON object, while preserving key order
|
||||
+//
|
||||
+// var oa []OrderedObject
|
||||
+// Unmarshal(json, &oa) // decode an array of JSON objects, while preserving key order
|
||||
+//
|
||||
+// var v interface{}
|
||||
+// d := new Decoder(json)
|
||||
+// d.UseOrderedObject() // decode all JSON objects as OrderedObject rather than map[string]interface{}
|
||||
+// d.Decode(&v)
|
||||
+//
|
||||
+// type A struct {
|
||||
+// B bool
|
||||
+// Inner OrderedObject
|
||||
+// I int
|
||||
+// }
|
||||
+// var a A
|
||||
+// Unmarshal(&a) // decode A as a JSON object with Inner as a nested object, preserving key order
|
||||
+//
|
||||
+// OrderedObject can also be used to encode a JSON object in
|
||||
+// a specified order. Marshal and Encoder.Encode are supported.
|
||||
+//
|
||||
+// var o OrderedObject
|
||||
+// Marshal(o) // encode JSON object, each with keys in OrderedObject order
|
||||
+//
|
||||
+// var oa []OrderedObject
|
||||
+// Marshal(oa) // encode an array of JSON objects, with keys in OrderedObject order
|
||||
+//
|
||||
+// type A struct {
|
||||
+// B bool
|
||||
+// Inner OrderedObject
|
||||
+// I int
|
||||
+// }
|
||||
+// var a A = createA()
|
||||
+// Marshal(a) // encode A as a JSON object with Inner as a nested object
|
||||
+type OrderedObject []Member
|
||||
--
|
||||
2.5.0
|
||||
|
|
@ -1,328 +0,0 @@
|
|||
From b0b3e735f416b83cb19102508dc41f6443232ff6 Mon Sep 17 00:00:00 2001
|
||||
From: Jessica Frazelle <acidburn@docker.com>
|
||||
Date: Sat, 1 Aug 2015 18:28:34 -0700
|
||||
Subject: [PATCH 2/2] 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>
|
||||
---
|
||||
canonical/json/decode_test.go | 44 ++++++++++++++++-----------------
|
||||
canonical/json/encode.go | 8 ++++--
|
||||
canonical/json/encode_test.go | 55 ++++++++++++++++++++++++------------------
|
||||
canonical/json/scanner_test.go | 2 +-
|
||||
canonical/json/stream_test.go | 12 ++++-----
|
||||
5 files changed, 67 insertions(+), 54 deletions(-)
|
||||
|
||||
diff --git a/canonical/json/decode_test.go b/canonical/json/decode_test.go
|
||||
index bbdd004..e4ed977 100644
|
||||
--- a/canonical/json/decode_test.go
|
||||
+++ b/canonical/json/decode_test.go
|
||||
@@ -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}),
|
||||
},
|
||||
}
|
||||
|
||||
diff --git a/canonical/json/encode.go b/canonical/json/encode.go
|
||||
index 7793733..f5dcb62 100644
|
||||
--- a/canonical/json/encode.go
|
||||
+++ b/canonical/json/encode.go
|
||||
@@ -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 {
|
||||
diff --git a/canonical/json/encode_test.go b/canonical/json/encode_test.go
|
||||
index d497bd9..bcb1cf5 100644
|
||||
--- a/canonical/json/encode_test.go
|
||||
+++ b/canonical/json/encode_test.go
|
||||
@@ -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")
|
||||
+ }
|
||||
}
|
||||
diff --git a/canonical/json/scanner_test.go b/canonical/json/scanner_test.go
|
||||
index 7880342..fee7b50 100644
|
||||
--- a/canonical/json/scanner_test.go
|
||||
+++ b/canonical/json/scanner_test.go
|
||||
@@ -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)
|
||||
}
|
||||
diff --git a/canonical/json/stream_test.go b/canonical/json/stream_test.go
|
||||
index b562e87..66cd0b1 100644
|
||||
--- a/canonical/json/stream_test.go
|
||||
+++ b/canonical/json/stream_test.go
|
||||
@@ -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)
|
||||
--
|
||||
2.5.0
|
||||
|
Загрузка…
Ссылка в новой задаче