diff options
Diffstat (limited to 'src/pkg/encoding/json')
-rw-r--r-- | src/pkg/encoding/json/bench_test.go | 34 | ||||
-rw-r--r-- | src/pkg/encoding/json/decode.go | 244 | ||||
-rw-r--r-- | src/pkg/encoding/json/decode_test.go | 607 | ||||
-rw-r--r-- | src/pkg/encoding/json/encode.go | 266 | ||||
-rw-r--r-- | src/pkg/encoding/json/encode_test.go | 20 | ||||
-rw-r--r-- | src/pkg/encoding/json/scanner_test.go | 9 | ||||
-rw-r--r-- | src/pkg/encoding/json/stream.go | 13 | ||||
-rw-r--r-- | src/pkg/encoding/json/stream_test.go | 46 | ||||
-rw-r--r-- | src/pkg/encoding/json/tagkey_test.go | 10 |
9 files changed, 999 insertions, 250 deletions
diff --git a/src/pkg/encoding/json/bench_test.go b/src/pkg/encoding/json/bench_test.go index 333c1c0ce..29dbc26d4 100644 --- a/src/pkg/encoding/json/bench_test.go +++ b/src/pkg/encoding/json/bench_test.go @@ -153,5 +153,37 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) { b.Fatal("Unmmarshal:", err) } } - b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkUnmarshalString(b *testing.B) { + data := []byte(`"hello, world"`) + var s string + + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &s); err != nil { + b.Fatal("Unmarshal:", err) + } + } +} + +func BenchmarkUnmarshalFloat64(b *testing.B) { + var f float64 + data := []byte(`3.14`) + + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &f); err != nil { + b.Fatal("Unmarshal:", err) + } + } +} + +func BenchmarkUnmarshalInt64(b *testing.B) { + var x int64 + data := []byte(`3`) + + for i := 0; i < b.N; i++ { + if err := Unmarshal(data, &x); err != nil { + b.Fatal("Unmarshal:", err) + } + } } diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go index d61f88706..f2ec9cb67 100644 --- a/src/pkg/encoding/json/decode.go +++ b/src/pkg/encoding/json/decode.go @@ -33,6 +33,10 @@ import ( // the value pointed at by the pointer. If the pointer is nil, Unmarshal // allocates a new value for it to point to. // +// To unmarshal JSON into a struct, Unmarshal matches incoming object +// keys to the keys used by Marshal (either the struct field name or its tag), +// preferring an exact match but also accepting a case-insensitive match. +// // To unmarshal JSON into an interface value, Unmarshal unmarshals // the JSON into the concrete value contained in the interface value. // If the interface value is nil, that is, has no concrete value stored in it, @@ -51,24 +55,29 @@ import ( // If no more serious errors are encountered, Unmarshal returns // an UnmarshalTypeError describing the earliest such error. // +// When unmarshaling quoted strings, invalid UTF-8 or +// invalid UTF-16 surrogate pairs are not treated as an error. +// Instead, they are replaced by the Unicode replacement +// character U+FFFD. +// func Unmarshal(data []byte, v interface{}) error { - d := new(decodeState).init(data) - - // Quick check for well-formedness. + // Check for well-formedness. // Avoids filling out half a data structure // before discovering a JSON syntax error. + var d decodeState err := checkValid(data, &d.scan) if err != nil { return err } + d.init(data) return d.unmarshal(v) } // Unmarshaler is the interface implemented by objects // that can unmarshal a JSON description of themselves. -// The input can be assumed to be a valid JSON object -// encoding. UnmarshalJSON must copy the JSON data +// The input can be assumed to be a valid encoding of +// a JSON value. UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. type Unmarshaler interface { UnmarshalJSON([]byte) error @@ -87,6 +96,7 @@ func (e *UnmarshalTypeError) Error() string { // An UnmarshalFieldError describes a JSON object key that // led to an unexported (and therefore unwritable) struct field. +// (No longer used; kept for compatibility.) type UnmarshalFieldError struct { Key string Type reflect.Type @@ -125,18 +135,33 @@ func (d *decodeState) unmarshal(v interface{}) (err error) { }() rv := reflect.ValueOf(v) - pv := rv - if pv.Kind() != reflect.Ptr || pv.IsNil() { + if rv.Kind() != reflect.Ptr || rv.IsNil() { return &InvalidUnmarshalError{reflect.TypeOf(v)} } d.scan.reset() - // We decode rv not pv.Elem because the Unmarshaler interface + // We decode rv not rv.Elem because the Unmarshaler interface // test must be applied at the top level of the value. d.value(rv) return d.savedError } +// A Number represents a JSON number literal. +type Number string + +// String returns the literal text of the number. +func (n Number) String() string { return string(n) } + +// Float64 returns the number as a float64. +func (n Number) Float64() (float64, error) { + return strconv.ParseFloat(string(n), 64) +} + +// Int64 returns the number as an int64. +func (n Number) Int64() (int64, error) { + return strconv.ParseInt(string(n), 10, 64) +} + // decodeState represents the state while decoding a JSON value. type decodeState struct { data []byte @@ -145,6 +170,7 @@ type decodeState struct { nextscan scanner // for calls to nextValue savedError error tempstr string // scratch space to avoid some allocations + useNumber bool } // errPhase is used for errors that should not happen unless @@ -265,47 +291,32 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, v = v.Addr() } for { - var isUnmarshaler bool - if v.Type().NumMethod() > 0 { - // Remember that this is an unmarshaler, - // but wait to return it until after allocating - // the pointer (if necessary). - _, isUnmarshaler = v.Interface().(Unmarshaler) - } - // Load value from interface, but only if the result will be // usefully addressable. - if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() { - e := iv.Elem() + if v.Kind() == reflect.Interface && !v.IsNil() { + e := v.Elem() if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { v = e continue } } - pv := v - if pv.Kind() != reflect.Ptr { + if v.Kind() != reflect.Ptr { break } - if pv.Elem().Kind() != reflect.Ptr && decodingNull && pv.CanSet() { - return nil, pv + if v.Elem().Kind() != reflect.Ptr && decodingNull && v.CanSet() { + break } - if pv.IsNil() { - pv.Set(reflect.New(pv.Type().Elem())) + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) } - if isUnmarshaler { - // Using v.Interface().(Unmarshaler) - // here means that we have to use a pointer - // as the struct field. We cannot use a value inside - // a pointer to a struct, because in that case - // v.Interface() is the value (x.f) not the pointer (&x.f). - // This is an unfortunate consequence of reflect. - // An alternative would be to look up the - // UnmarshalJSON method and return a FuncValue. - return v.Interface().(Unmarshaler), reflect.Value{} + if v.Type().NumMethod() > 0 { + if unmarshaler, ok := v.Interface().(Unmarshaler); ok { + return unmarshaler, reflect.Value{} + } } - v = pv.Elem() + v = v.Elem() } return nil, v } @@ -327,15 +338,19 @@ func (d *decodeState) array(v reflect.Value) { // Check type of target. switch v.Kind() { + case reflect.Interface: + if v.NumMethod() == 0 { + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + } + // Otherwise it's invalid. + fallthrough default: d.saveError(&UnmarshalTypeError{"array", v.Type()}) d.off-- d.next() return - case reflect.Interface: - // Decoding into nil interface? Switch to non-reflect code. - v.Set(reflect.ValueOf(d.arrayInterface())) - return case reflect.Array: case reflect.Slice: break @@ -421,36 +436,27 @@ func (d *decodeState) object(v reflect.Value) { v = pv // Decoding into nil interface? Switch to non-reflect code. - iv := v - if iv.Kind() == reflect.Interface { - iv.Set(reflect.ValueOf(d.objectInterface())) + if v.Kind() == reflect.Interface && v.NumMethod() == 0 { + v.Set(reflect.ValueOf(d.objectInterface())) return } // Check type of target: struct or map[string]T - var ( - mv reflect.Value - sv reflect.Value - ) switch v.Kind() { case reflect.Map: - // map must have string type + // map must have string kind t := v.Type() - if t.Key() != reflect.TypeOf("") { + if t.Key().Kind() != reflect.String { d.saveError(&UnmarshalTypeError{"object", v.Type()}) break } - mv = v - if mv.IsNil() { - mv.Set(reflect.MakeMap(t)) + if v.IsNil() { + v.Set(reflect.MakeMap(t)) } case reflect.Struct: - sv = v + default: d.saveError(&UnmarshalTypeError{"object", v.Type()}) - } - - if !mv.IsValid() && !sv.IsValid() { d.off-- d.next() // skip over { } in input return @@ -482,8 +488,8 @@ func (d *decodeState) object(v reflect.Value) { var subv reflect.Value destring := false // whether the value is wrapped in a string to be decoded first - if mv.IsValid() { - elemType := mv.Type().Elem() + if v.Kind() == reflect.Map { + elemType := v.Type().Elem() if !mapElem.IsValid() { mapElem = reflect.New(elemType).Elem() } else { @@ -491,51 +497,30 @@ func (d *decodeState) object(v reflect.Value) { } subv = mapElem } else { - var f reflect.StructField - var ok bool - st := sv.Type() - for i := 0; i < sv.NumField(); i++ { - sf := st.Field(i) - tag := sf.Tag.Get("json") - if tag == "-" { - // Pretend this field doesn't exist. - continue - } - if sf.Anonymous { - // Pretend this field doesn't exist, - // so that we can do a good job with - // these in a later version. - continue - } - // First, tag match - tagName, _ := parseTag(tag) - if tagName == key { - f = sf - ok = true - break // no better match possible - } - // Second, exact field name match - if sf.Name == key { - f = sf - ok = true + var f *field + fields := cachedTypeFields(v.Type()) + for i := range fields { + ff := &fields[i] + if ff.name == key { + f = ff + break } - // Third, case-insensitive field name match, - // but only if a better match hasn't already been seen - if !ok && strings.EqualFold(sf.Name, key) { - f = sf - ok = true + if f == nil && strings.EqualFold(ff.name, key) { + f = ff } } - - // Extract value; name must be exported. - if ok { - if f.PkgPath != "" { - d.saveError(&UnmarshalFieldError{key, st, f}) - } else { - subv = sv.FieldByIndex(f.Index) + if f != nil { + subv = v + destring = f.quoted + for _, i := range f.index { + if subv.Kind() == reflect.Ptr { + if subv.IsNil() { + subv.Set(reflect.New(subv.Type().Elem())) + } + subv = subv.Elem() + } + subv = subv.Field(i) } - _, opts := parseTag(f.Tag.Get("json")) - destring = opts.Contains("string") } } @@ -554,10 +539,12 @@ func (d *decodeState) object(v reflect.Value) { } else { d.value(subv) } + // Write value back to map; // if using struct, subv points into struct already. - if mv.IsValid() { - mv.SetMapIndex(reflect.ValueOf(key), subv) + if v.Kind() == reflect.Map { + kv := reflect.ValueOf(key).Convert(v.Type().Key()) + v.SetMapIndex(kv, subv) } // Next token must be , or }. @@ -586,6 +573,21 @@ func (d *decodeState) literal(v reflect.Value) { d.literalStore(d.data[start:d.off], v, false) } +// convertNumber converts the number literal s to a float64 or a Number +// depending on the setting of d.useNumber. +func (d *decodeState) convertNumber(s string) (interface{}, error) { + if d.useNumber { + return Number(s), nil + } + f, err := strconv.ParseFloat(s, 64) + if err != nil { + return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0)} + } + return f, nil +} + +var numberType = reflect.TypeOf(Number("")) + // literalStore decodes a literal stored in item into v. // // fromQuoted indicates whether this literal came from unwrapping a @@ -612,12 +614,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool switch c := item[0]; c { case 'n': // null switch v.Kind() { - default: - d.saveError(&UnmarshalTypeError{"null", v.Type()}) case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: v.Set(reflect.Zero(v.Type())) + // otherwise, ignore null for primitives/string } - case 't', 'f': // true, false value := c == 't' switch v.Kind() { @@ -630,7 +630,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.Bool: v.SetBool(value) case reflect.Interface: - v.Set(reflect.ValueOf(value)) + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(value)) + } else { + d.saveError(&UnmarshalTypeError{"bool", v.Type()}) + } } case '"': // string @@ -660,7 +664,11 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool case reflect.String: v.SetString(string(s)) case reflect.Interface: - v.Set(reflect.ValueOf(string(s))) + if v.NumMethod() == 0 { + v.Set(reflect.ValueOf(string(s))) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + } } default: // number @@ -674,15 +682,23 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool s := string(item) switch v.Kind() { default: + if v.Kind() == reflect.String && v.Type() == numberType { + v.SetString(s) + break + } if fromQuoted { d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) } else { d.error(&UnmarshalTypeError{"number", v.Type()}) } case reflect.Interface: - n, err := strconv.ParseFloat(s, 64) + n, err := d.convertNumber(s) if err != nil { - d.saveError(&UnmarshalTypeError{"number " + s, v.Type()}) + d.saveError(err) + break + } + if v.NumMethod() != 0 { + d.saveError(&UnmarshalTypeError{"number", v.Type()}) break } v.Set(reflect.ValueOf(n)) @@ -735,7 +751,7 @@ func (d *decodeState) valueInterface() interface{} { // arrayInterface is like array but returns []interface{}. func (d *decodeState) arrayInterface() []interface{} { - var v []interface{} + var v = make([]interface{}, 0) for { // Look ahead for ] - can only happen on first iteration. op := d.scanWhile(scanSkipSpace) @@ -836,9 +852,9 @@ func (d *decodeState) literalInterface() interface{} { if c != '-' && (c < '0' || c > '9') { d.error(errPhase) } - n, err := strconv.ParseFloat(string(item), 64) + n, err := d.convertNumber(string(item)) if err != nil { - d.saveError(&UnmarshalTypeError{"number " + string(item), reflect.TypeOf(0.0)}) + d.saveError(err) } return n } @@ -979,11 +995,3 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { } return b[0:w], true } - -// The following is issue 3069. - -// BUG(rsc): This package ignores anonymous (embedded) struct fields -// during encoding and decoding. A future version may assign meaning -// to them. To force an anonymous field to be ignored in all future -// versions of this package, use an explicit `json:"-"` tag in the struct -// definition. diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go index 6fac22c4a..e1bd918dd 100644 --- a/src/pkg/encoding/json/decode_test.go +++ b/src/pkg/encoding/json/decode_test.go @@ -7,9 +7,11 @@ package json import ( "bytes" "fmt" + "image" "reflect" "strings" "testing" + "time" ) type T struct { @@ -18,6 +20,32 @@ type T struct { Z int `json:"-"` } +type U struct { + Alphabet string `json:"alpha"` +} + +type V struct { + F1 interface{} + F2 int32 + F3 Number +} + +// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshalling with and +// without UseNumber +var ifaceNumAsFloat64 = map[string]interface{}{ + "k1": float64(1), + "k2": "s", + "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("3e-3")}, + "k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")}, +} + type tx struct { x int } @@ -48,55 +76,297 @@ var ( umstruct = ustruct{unmarshaler{true}} ) +// Test data structures for anonymous fields. + +type Point struct { + Z int +} + +type Top struct { + Level0 int + Embed0 + *Embed0a + *Embed0b `json:"e,omitempty"` // treated as named + Embed0c `json:"-"` // ignored + Loop + Embed0p // has Point with X, Y, used + Embed0q // has Point with Z, used +} + +type Embed0 struct { + Level1a int // overridden by Embed0a's Level1a with json tag + Level1b int // used because Embed0a's Level1b is renamed + Level1c int // used because Embed0a's Level1c is ignored + Level1d int // annihilated by Embed0a's Level1d + Level1e int `json:"x"` // annihilated by Embed0a.Level1e +} + +type Embed0a struct { + Level1a int `json:"Level1a,omitempty"` + Level1b int `json:"LEVEL1B,omitempty"` + Level1c int `json:"-"` + Level1d int // annihilated by Embed0's Level1d + Level1f int `json:"x"` // annihilated by Embed0's Level1e +} + +type Embed0b Embed0 + +type Embed0c Embed0 + +type Embed0p struct { + image.Point +} + +type Embed0q struct { + Point +} + +type Loop struct { + Loop1 int `json:",omitempty"` + Loop2 int `json:",omitempty"` + *Loop +} + +// From reflect test: +// The X in S6 and S7 annihilate, but they also block the X in S8.S9. +type S5 struct { + S6 + S7 + S8 +} + +type S6 struct { + X int +} + +type S7 S6 + +type S8 struct { + S9 +} + +type S9 struct { + X int + Y int +} + +// From reflect test: +// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9. +type S10 struct { + S11 + S12 + S13 +} + +type S11 struct { + S6 +} + +type S12 struct { + S6 +} + +type S13 struct { + S8 +} + type unmarshalTest struct { - in string - ptr interface{} - out interface{} - err error + in string + ptr interface{} + out interface{} + err error + useNumber bool +} + +type Ambig struct { + // Given "hello", the first match should win. + First int `json:"HELLO"` + Second int `json:"Hello"` } var unmarshalTests = []unmarshalTest{ // basic types - {`true`, new(bool), true, nil}, - {`1`, new(int), 1, nil}, - {`1.2`, new(float64), 1.2, nil}, - {`-5`, new(int16), int16(-5), nil}, - {`"a\u1234"`, new(string), "a\u1234", nil}, - {`"http:\/\/"`, new(string), "http://", nil}, - {`"g-clef: \uD834\uDD1E"`, new(string), "g-clef: \U0001D11E", nil}, - {`"invalid: \uD834x\uDD1E"`, new(string), "invalid: \uFFFDx\uFFFD", nil}, - {"null", new(interface{}), nil, nil}, - {`{"X": [1,2,3], "Y": 4}`, new(T), T{Y: 4}, &UnmarshalTypeError{"array", reflect.TypeOf("")}}, - {`{"x": 1}`, new(tx), tx{}, &UnmarshalFieldError{"x", txType, txType.Field(0)}}, + {in: `true`, ptr: new(bool), out: true}, + {in: `1`, ptr: new(int), out: 1}, + {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")}, + {in: `2`, ptr: new(interface{}), out: float64(2.0)}, + {in: `2`, ptr: new(interface{}), out: Number("2"), useNumber: true}, + {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"}, + {in: `"http:\/\/"`, ptr: new(string), out: "http://"}, + {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"}, + {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"}, + {in: "null", ptr: new(interface{}), out: nil}, + {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf("")}}, + {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}, + + // 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: "\t -5 \n", ptr: new(int16), out: int16(-5)}, + {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"}, // Z has a "-" tag. - {`{"Y": 1, "Z": 2}`, new(T), T{Y: 1}, nil}, + {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, + + {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}}, + {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}}, + {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}}, // syntax errors - {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, - {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}}, + {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}}, + {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, + {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, + + // raw value errors + {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}}, + {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}}, + {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}}, + {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, + {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}}, // array tests - {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil}, - {`[1, 2, 3]`, new([1]int), [1]int{1}, nil}, - {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil}, + {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}}, + {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}}, + {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}}, + + // empty array to interface test + {in: `[]`, ptr: new([]interface{}), out: []interface{}{}}, + {in: `null`, ptr: new([]interface{}), out: []interface{}(nil)}, + {in: `{"T":[]}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}}, + {in: `{"T":null}`, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": interface{}(nil)}}, // composite tests - {allValueIndent, new(All), allValue, nil}, - {allValueCompact, new(All), allValue, nil}, - {allValueIndent, new(*All), &allValue, nil}, - {allValueCompact, new(*All), &allValue, nil}, - {pallValueIndent, new(All), pallValue, nil}, - {pallValueCompact, new(All), pallValue, nil}, - {pallValueIndent, new(*All), &pallValue, nil}, - {pallValueCompact, new(*All), &pallValue, nil}, + {in: allValueIndent, ptr: new(All), out: allValue}, + {in: allValueCompact, ptr: new(All), out: allValue}, + {in: allValueIndent, ptr: new(*All), out: &allValue}, + {in: allValueCompact, ptr: new(*All), out: &allValue}, + {in: pallValueIndent, ptr: new(All), out: pallValue}, + {in: pallValueCompact, ptr: new(All), out: pallValue}, + {in: pallValueIndent, ptr: new(*All), out: &pallValue}, + {in: pallValueCompact, ptr: new(*All), out: &pallValue}, // unmarshal interface test - {`{"T":false}`, &um0, umtrue, nil}, // use "false" so test will fail if custom unmarshaler is not called - {`{"T":false}`, &ump, &umtrue, nil}, - {`[{"T":false}]`, &umslice, umslice, nil}, - {`[{"T":false}]`, &umslicep, &umslice, nil}, - {`{"M":{"T":false}}`, &umstruct, umstruct, nil}, + {in: `{"T":false}`, ptr: &um0, out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called + {in: `{"T":false}`, ptr: &ump, out: &umtrue}, + {in: `[{"T":false}]`, ptr: &umslice, out: umslice}, + {in: `[{"T":false}]`, ptr: &umslicep, out: &umslice}, + {in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct}, + + { + in: `{ + "Level0": 1, + "Level1b": 2, + "Level1c": 3, + "x": 4, + "Level1a": 5, + "LEVEL1B": 6, + "e": { + "Level1a": 8, + "Level1b": 9, + "Level1c": 10, + "Level1d": 11, + "x": 12 + }, + "Loop1": 13, + "Loop2": 14, + "X": 15, + "Y": 16, + "Z": 17 + }`, + ptr: new(Top), + out: Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{ + Level1a: 5, + Level1b: 6, + }, + Embed0b: &Embed0b{ + Level1a: 8, + Level1b: 9, + Level1c: 10, + Level1d: 11, + Level1e: 12, + }, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + }, + Embed0p: Embed0p{ + Point: image.Point{X: 15, Y: 16}, + }, + Embed0q: Embed0q{ + Point: Point{Z: 17}, + }, + }, + }, + { + in: `{"hello": 1}`, + ptr: new(Ambig), + out: Ambig{First: 1}, + }, + + { + in: `{"X": 1,"Y":2}`, + ptr: new(S5), + out: S5{S8: S8{S9: S9{Y: 2}}}, + }, + { + in: `{"X": 1,"Y":2}`, + ptr: new(S10), + out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}}, + }, + + // invalid UTF-8 is coerced to valid UTF-8. + { + in: "\"hello\xffworld\"", + ptr: new(string), + out: "hello\ufffdworld", + }, + { + in: "\"hello\xc2\xc2world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\xc2\xffworld\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\\ud800world\"", + ptr: new(string), + out: "hello\ufffdworld", + }, + { + in: "\"hello\\ud800\\ud800world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\\ud800\\ud800world\"", + ptr: new(string), + out: "hello\ufffd\ufffdworld", + }, + { + in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"", + ptr: new(string), + out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld", + }, } func TestMarshal(t *testing.T) { @@ -135,6 +405,18 @@ func TestMarshalBadUTF8(t *testing.T) { } } +func TestMarshalNumberZeroVal(t *testing.T) { + var n Number + out, err := Marshal(n) + if err != nil { + t.Fatal(err) + } + outStr := string(out) + if outStr != "0" { + t.Fatalf("Invalid zero val for Number: %q", outStr) + } +} + func TestUnmarshal(t *testing.T) { for i, tt := range unmarshalTests { var scan scanner @@ -150,7 +432,11 @@ func TestUnmarshal(t *testing.T) { } // v = new(right-type) v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) - if err := Unmarshal([]byte(in), v.Interface()); !reflect.DeepEqual(err, tt.err) { + dec := NewDecoder(bytes.NewBuffer(in)) + if tt.useNumber { + dec.UseNumber() + } + if err := dec.Decode(v.Interface()); !reflect.DeepEqual(err, tt.err) { t.Errorf("#%d: %v want %v", i, err, tt.err) continue } @@ -162,6 +448,28 @@ func TestUnmarshal(t *testing.T) { println(string(data)) continue } + + // Check round trip. + if tt.err == nil { + enc, err := Marshal(v.Interface()) + if err != nil { + t.Errorf("#%d: error re-marshaling: %v", i, err) + continue + } + vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) + dec = NewDecoder(bytes.NewBuffer(enc)) + if tt.useNumber { + dec.UseNumber() + } + if err := dec.Decode(vv.Interface()); err != nil { + t.Errorf("#%d: error re-unmarshaling: %v", i, err) + continue + } + if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) { + t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) + continue + } + } } } @@ -175,13 +483,45 @@ func TestUnmarshalMarshal(t *testing.T) { if err != nil { t.Fatalf("Marshal: %v", err) } - if bytes.Compare(jsonBig, b) != 0 { + if !bytes.Equal(jsonBig, b) { t.Errorf("Marshal jsonBig") diff(t, b, jsonBig) return } } +var numberTests = []struct { + in string + i int64 + intErr string + f float64 + floatErr string +}{ + {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1}, + {in: "-12", i: -12, f: -12.0}, + {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"}, +} + +// Independent of Decode, basic coverage of the accessors in Number +func TestNumberAccessors(t *testing.T) { + for _, tt := range numberTests { + n := Number(tt.in) + if s := n.String(); s != tt.in { + t.Errorf("Number(%q).String() is %q", tt.in, s) + } + if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i { + t.Errorf("Number(%q).Int64() is %d", tt.in, i) + } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) { + t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err) + } + if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f { + t.Errorf("Number(%q).Float64() is %g", tt.in, f) + } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) { + t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err) + } + } +} + func TestLargeByteSlice(t *testing.T) { s0 := make([]byte, 2000) for i := range s0 { @@ -195,7 +535,7 @@ func TestLargeByteSlice(t *testing.T) { if err := Unmarshal(b, &s1); err != nil { t.Fatalf("Unmarshal: %v", err) } - if bytes.Compare(s0, s1) != 0 { + if !bytes.Equal(s0, s1) { t.Errorf("Marshal large byte slice") diff(t, s0, s1) } @@ -610,35 +950,6 @@ func TestRefUnmarshal(t *testing.T) { } } -// Test that anonymous fields are ignored. -// We may assign meaning to them later. -func TestAnonymous(t *testing.T) { - type S struct { - T - N int - } - - data, err := Marshal(new(S)) - if err != nil { - t.Fatalf("Marshal: %v", err) - } - want := `{"N":0}` - if string(data) != want { - t.Fatalf("Marshal = %#q, want %#q", string(data), want) - } - - var s S - if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil { - t.Fatalf("Unmarshal: %v", err) - } - if s.N != 2 { - t.Fatal("Unmarshal: did not set N") - } - if s.T.Y != 0 { - t.Fatal("Unmarshal: did set T.Y") - } -} - // Test that the empty string doesn't panic decoding when ,string is specified // Issue 3450 func TestEmptyString(t *testing.T) { @@ -703,3 +1014,167 @@ func TestInterfaceSet(t *testing.T) { } } } + +// JSON null values should be ignored for primitives and string values instead of resulting in an error. +// Issue 2540 +func TestUnmarshalNulls(t *testing.T) { + jsonData := []byte(`{ + "Bool" : null, + "Int" : null, + "Int8" : null, + "Int16" : null, + "Int32" : null, + "Int64" : null, + "Uint" : null, + "Uint8" : null, + "Uint16" : null, + "Uint32" : null, + "Uint64" : null, + "Float32" : null, + "Float64" : null, + "String" : null}`) + + nulls := All{ + Bool: true, + Int: 2, + Int8: 3, + Int16: 4, + Int32: 5, + Int64: 6, + Uint: 7, + Uint8: 8, + Uint16: 9, + Uint32: 10, + Uint64: 11, + Float32: 12.1, + Float64: 13.1, + String: "14"} + + err := Unmarshal(jsonData, &nulls) + if err != nil { + t.Errorf("Unmarshal of null values failed: %v", err) + } + 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" { + + t.Errorf("Unmarshal of null values affected primitives") + } +} + +func TestStringKind(t *testing.T) { + type stringKind string + type aMap map[stringKind]int + + var m1, m2 map[stringKind]int + m1 = map[stringKind]int{ + "foo": 42, + } + + data, err := Marshal(m1) + if err != nil { + t.Errorf("Unexpected error marshalling: %v", err) + } + + err = Unmarshal(data, &m2) + if err != nil { + t.Errorf("Unexpected error unmarshalling: %v", err) + } + + if !reflect.DeepEqual(m1, m2) { + t.Error("Items should be equal after encoding and then decoding") + } + +} + +var decodeTypeErrorTests = []struct { + dest interface{} + src string +}{ + {new(string), `{"user": "name"}`}, // issue 4628. + {new(error), `{}`}, // issue 4222 + {new(error), `[]`}, + {new(error), `""`}, + {new(error), `123`}, + {new(error), `true`}, +} + +func TestUnmarshalTypeError(t *testing.T) { + for _, item := range decodeTypeErrorTests { + err := Unmarshal([]byte(item.src), item.dest) + if _, ok := err.(*UnmarshalTypeError); !ok { + t.Errorf("expected type error for Unmarshal(%q, type %T): got %T", + item.src, item.dest, err) + } + } +} + +var unmarshalSyntaxTests = []string{ + "tru", + "fals", + "nul", + "123e", + `"hello`, + `[1,2,3`, + `{"key":1`, + `{"key":1,`, +} + +func TestUnmarshalSyntax(t *testing.T) { + var x interface{} + for _, src := range unmarshalSyntaxTests { + err := Unmarshal([]byte(src), &x) + if _, ok := err.(*SyntaxError); !ok { + t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err) + } + } +} + +// Test handling of unexported fields that should be ignored. +// Issue 4660 +type unexportedFields struct { + Name string + m map[string]interface{} `json:"-"` + m2 map[string]interface{} `json:"abcd"` +} + +func TestUnmarshalUnexported(t *testing.T) { + input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}}` + want := &unexportedFields{Name: "Bob"} + + out := &unexportedFields{} + err := Unmarshal([]byte(input), out) + if err != nil { + t.Errorf("got error %v, expected nil", err) + } + if !reflect.DeepEqual(out, want) { + t.Errorf("got %q, want %q", out, want) + } +} + +// Time3339 is a time.Time which encodes to and from JSON +// as an RFC 3339 time in UTC. +type Time3339 time.Time + +func (t *Time3339) UnmarshalJSON(b []byte) error { + if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { + return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b) + } + tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1])) + if err != nil { + return err + } + *t = Time3339(tm) + return nil +} + +func TestUnmarshalJSONLiteralError(t *testing.T) { + var t3 Time3339 + err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3) + if err == nil { + t.Fatalf("expected error; got time %v", time.Time(t3)) + } + if !strings.Contains(err.Error(), "range") { + t.Errorf("got err = %v; want out of range error", err) + } +} diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go index b6e1cb16e..fb57f1d51 100644 --- a/src/pkg/encoding/json/encode.go +++ b/src/pkg/encoding/json/encode.go @@ -36,7 +36,7 @@ import ( // // Boolean values encode as JSON booleans. // -// Floating point and integer values encode as JSON numbers. +// Floating point, integer, and Number values encode as JSON numbers. // // String values encode as JSON strings, with each invalid UTF-8 sequence // replaced by the encoding of the Unicode replacement character U+FFFD. @@ -55,7 +55,7 @@ import ( // nil pointer or interface value, and any array, slice, map, or string of // length zero. The object's default key string is the struct field name // but can be specified in the struct field's tag value. The "json" key in -// struct field's tag value is the key name, followed by an optional comma +// the struct field's tag value is the key name, followed by an optional comma // and options. Examples: // // // Field is ignored by this package. @@ -75,8 +75,9 @@ import ( // Field int `json:",omitempty"` // // The "string" option signals that a field is stored as JSON inside a -// JSON-encoded string. This extra level of encoding is sometimes -// used when communicating with JavaScript programs: +// JSON-encoded string. It applies only to fields of string, floating point, +// or integer types. This extra level of encoding is sometimes used when +// communicating with JavaScript programs: // // Int64String int64 `json:",string"` // @@ -84,6 +85,16 @@ import ( // only Unicode letters, digits, dollar signs, percent signs, hyphens, // underscores and slashes. // +// Anonymous struct fields are usually marshaled as if their inner exported fields +// were fields in the outer struct, subject to the usual Go visibility rules. +// An anonymous struct field with a name given in its JSON tag is treated as +// having that name instead of as anonymous. +// +// Handling of anonymous struct fields is new in Go 1.1. +// Prior to Go 1.1, anonymous struct fields were ignored. To force ignoring of +// an anonymous struct field in both current and earlier versions, give the field +// a JSON tag of "-". +// // Map values encode as JSON objects. // The map's key type must be string; the object keys are used directly // as map keys. @@ -312,6 +323,14 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { e.Write(b) } case reflect.String: + if v.Type() == numberType { + numStr := v.String() + if numStr == "" { + numStr = "0" // Number's zero-val + } + e.WriteString(numStr) + break + } if quoted { sb, err := Marshal(v.String()) if err != nil { @@ -325,9 +344,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { case reflect.Struct: e.WriteByte('{') first := true - for _, ef := range encodeFields(v.Type()) { - fieldValue := v.Field(ef.i) - if ef.omitEmpty && isEmptyValue(fieldValue) { + for _, f := range cachedTypeFields(v.Type()) { + fv := fieldByIndex(v, f.index) + if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { continue } if first { @@ -335,9 +354,9 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { } else { e.WriteByte(',') } - e.string(ef.tag) + e.string(f.name) e.WriteByte(':') - e.reflectValueQuoted(fieldValue, ef.quoted) + e.reflectValueQuoted(fv, f.quoted) } e.WriteByte('}') @@ -419,7 +438,7 @@ func isValidTag(s string) bool { } for _, c := range s { switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~", c): + case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. @@ -432,6 +451,19 @@ func isValidTag(s string) bool { return true } +func fieldByIndex(v reflect.Value, index []int) reflect.Value { + for _, i := range index { + if v.Kind() == reflect.Ptr { + if v.IsNil() { + return reflect.Value{} + } + v = v.Elem() + } + v = v.Field(i) + } + return v +} + // stringValues is a slice of reflect.Value holding *reflect.StringValue. // It implements the methods to sort by string. type stringValues []reflect.Value @@ -490,67 +522,185 @@ func (e *encodeState) string(s string) (int, error) { return e.Len() - len0, nil } -// encodeField contains information about how to encode a field of a -// struct. -type encodeField struct { - i int // field index in struct - tag string - quoted bool +// A field represents a single field found in a struct. +type field struct { + name string + tag bool + index []int + typ reflect.Type omitEmpty bool + quoted bool } -var ( - typeCacheLock sync.RWMutex - encodeFieldsCache = make(map[reflect.Type][]encodeField) -) +// byName sorts field by name, breaking ties with depth, +// then breaking ties with "name came from json tag", then +// breaking ties with index sequence. +type byName []field -// encodeFields returns a slice of encodeField for a given -// struct type. -func encodeFields(t reflect.Type) []encodeField { - typeCacheLock.RLock() - fs, ok := encodeFieldsCache[t] - typeCacheLock.RUnlock() - if ok { - return fs - } +func (x byName) Len() int { return len(x) } + +func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } - typeCacheLock.Lock() - defer typeCacheLock.Unlock() - fs, ok = encodeFieldsCache[t] - if ok { - return fs +func (x byName) Less(i, j int) bool { + if x[i].name != x[j].name { + return x[i].name < x[j].name } + if len(x[i].index) != len(x[j].index) { + return len(x[i].index) < len(x[j].index) + } + if x[i].tag != x[j].tag { + return x[i].tag + } + return byIndex(x).Less(i, j) +} - v := reflect.Zero(t) - n := v.NumField() - for i := 0; i < n; i++ { - f := t.Field(i) - if f.PkgPath != "" { - continue +// byIndex sorts field by index sequence. +type byIndex []field + +func (x byIndex) Len() int { return len(x) } + +func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] } + +func (x byIndex) Less(i, j int) bool { + for k, xik := range x[i].index { + if k >= len(x[j].index) { + return false } - if f.Anonymous { - // We want to do a better job with these later, - // so for now pretend they don't exist. - continue + if xik != x[j].index[k] { + return xik < x[j].index[k] } - var ef encodeField - ef.i = i - ef.tag = f.Name + } + return len(x[i].index) < len(x[j].index) +} + +// typeFields returns a list of fields that JSON should recognize for the given type. +// The algorithm is breadth-first search over the set of structs to include - the top struct +// and then any reachable anonymous structs. +func typeFields(t reflect.Type) []field { + // Anonymous fields to explore at the current level and the next. + current := []field{} + next := []field{{typ: t}} + + // Count of queued names for current level and the next. + count := map[reflect.Type]int{} + nextCount := map[reflect.Type]int{} + + // Types already visited at an earlier level. + visited := map[reflect.Type]bool{} + + // Fields found. + var fields []field + + for len(next) > 0 { + current, next = next, current[:0] + count, nextCount = nextCount, map[reflect.Type]int{} - tv := f.Tag.Get("json") - if tv != "" { - if tv == "-" { + for _, f := range current { + if visited[f.typ] { continue } - name, opts := parseTag(tv) - if isValidTag(name) { - ef.tag = name + visited[f.typ] = true + + // Scan f.typ for fields to include. + for i := 0; i < f.typ.NumField(); i++ { + sf := f.typ.Field(i) + if sf.PkgPath != "" { // unexported + continue + } + tag := sf.Tag.Get("json") + if tag == "-" { + continue + } + name, opts := parseTag(tag) + if !isValidTag(name) { + name = "" + } + index := make([]int, len(f.index)+1) + copy(index, f.index) + index[len(f.index)] = i + + ft := sf.Type + if ft.Name() == "" && ft.Kind() == reflect.Ptr { + // Follow pointer. + ft = ft.Elem() + } + + // Record found field and index sequence. + if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct { + tagged := name != "" + if name == "" { + name = sf.Name + } + fields = append(fields, field{name, tagged, index, ft, + opts.Contains("omitempty"), opts.Contains("string")}) + if count[f.typ] > 1 { + // If there were multiple instances, add a second, + // so that the annihilation code will see a duplicate. + // It only cares about the distinction between 1 or 2, + // so don't bother generating any more copies. + fields = append(fields, fields[len(fields)-1]) + } + continue + } + + // Record new anonymous struct to explore in next round. + nextCount[ft]++ + if nextCount[ft] == 1 { + next = append(next, field{name: ft.Name(), index: index, typ: ft}) + } } - ef.omitEmpty = opts.Contains("omitempty") - ef.quoted = opts.Contains("string") } - fs = append(fs, ef) } - encodeFieldsCache[t] = fs - return fs + + sort.Sort(byName(fields)) + + // Remove fields with annihilating name collisions + // and also fields shadowed by fields with explicit JSON tags. + name := "" + out := fields[:0] + for _, f := range fields { + if f.name != name { + name = f.name + out = append(out, f) + continue + } + if n := len(out); n > 0 && out[n-1].name == name && (!out[n-1].tag || f.tag) { + out = out[:n-1] + } + } + fields = out + + sort.Sort(byIndex(fields)) + + return fields +} + +var fieldCache struct { + sync.RWMutex + m map[reflect.Type][]field +} + +// cachedTypeFields is like typeFields but uses a cache to avoid repeated work. +func cachedTypeFields(t reflect.Type) []field { + fieldCache.RLock() + f := fieldCache.m[t] + fieldCache.RUnlock() + if f != nil { + return f + } + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = typeFields(t) + if f == nil { + f = []field{} + } + + fieldCache.Lock() + if fieldCache.m == nil { + fieldCache.m = map[reflect.Type][]field{} + } + fieldCache.m[t] = f + fieldCache.Unlock() + return f } diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go index cb1c77eb5..be74c997c 100644 --- a/src/pkg/encoding/json/encode_test.go +++ b/src/pkg/encoding/json/encode_test.go @@ -186,3 +186,23 @@ func TestMarshalerEscaping(t *testing.T) { t.Errorf("got %q, want %q", got, want) } } + +type IntType int + +type MyStruct struct { + IntType +} + +func TestAnonymousNonstruct(t *testing.T) { + var i IntType = 11 + a := MyStruct{i} + const want = `{"IntType":11}` + + b, err := Marshal(a) + if err != nil { + t.Fatalf("Marshal: %v", err) + } + if got := string(b); got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/src/pkg/encoding/json/scanner_test.go b/src/pkg/encoding/json/scanner_test.go index 14d850865..77d3455d3 100644 --- a/src/pkg/encoding/json/scanner_test.go +++ b/src/pkg/encoding/json/scanner_test.go @@ -92,7 +92,7 @@ func TestCompactBig(t *testing.T) { t.Fatalf("Compact: %v", err) } b := buf.Bytes() - if bytes.Compare(b, jsonBig) != 0 { + if !bytes.Equal(b, jsonBig) { t.Error("Compact(jsonBig) != jsonBig") diff(t, b, jsonBig) return @@ -118,7 +118,7 @@ func TestIndentBig(t *testing.T) { t.Fatalf("Indent2: %v", err) } b1 := buf1.Bytes() - if bytes.Compare(b1, b) != 0 { + if !bytes.Equal(b1, b) { t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)") diff(t, b1, b) return @@ -130,7 +130,7 @@ func TestIndentBig(t *testing.T) { t.Fatalf("Compact: %v", err) } b1 = buf1.Bytes() - if bytes.Compare(b1, jsonBig) != 0 { + if !bytes.Equal(b1, jsonBig) { t.Error("Compact(Indent(jsonBig)) != jsonBig") diff(t, b1, jsonBig) return @@ -277,9 +277,6 @@ func genArray(n int) []interface{} { if f > n { f = n } - if n > 0 && f == 0 { - f = 1 - } x := make([]interface{}, f) for i := range x { x[i] = genValue(((i+1)*n)/f - (i*n)/f) diff --git a/src/pkg/encoding/json/stream.go b/src/pkg/encoding/json/stream.go index 7d1cc5f11..00f4726cf 100644 --- a/src/pkg/encoding/json/stream.go +++ b/src/pkg/encoding/json/stream.go @@ -5,6 +5,7 @@ package json import ( + "bytes" "errors" "io" ) @@ -26,6 +27,10 @@ func NewDecoder(r io.Reader) *Decoder { return &Decoder{r: r} } +// UseNumber causes the Decoder to unmarshal a number into an interface{} as a +// Number instead of as a float64. +func (dec *Decoder) UseNumber() { dec.d.useNumber = true } + // Decode reads the next JSON-encoded value from its // input and stores it in the value pointed to by v. // @@ -54,6 +59,12 @@ func (dec *Decoder) Decode(v interface{}) error { return err } +// Buffered returns a reader of the data remaining in the Decoder's +// buffer. The reader is valid until the next call to Decode. +func (dec *Decoder) Buffered() io.Reader { + return bytes.NewReader(dec.buf) +} + // readValue reads a JSON value into dec.buf. // It returns the length of the encoding. func (dec *Decoder) readValue() (int, error) { @@ -74,7 +85,7 @@ Input: // scanEnd is delayed one byte. // We might block trying to get that byte from src, // so instead invent a space byte. - if v == scanEndObject && dec.scan.step(&dec.scan, ' ') == scanEnd { + if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { scanp += i + 1 break Input } diff --git a/src/pkg/encoding/json/stream_test.go b/src/pkg/encoding/json/stream_test.go index ce5a7e6d6..07c9e1d39 100644 --- a/src/pkg/encoding/json/stream_test.go +++ b/src/pkg/encoding/json/stream_test.go @@ -6,7 +6,10 @@ package json import ( "bytes" + "io/ioutil" + "net" "reflect" + "strings" "testing" ) @@ -82,6 +85,28 @@ func TestDecoder(t *testing.T) { } } +func TestDecoderBuffered(t *testing.T) { + r := strings.NewReader(`{"Name": "Gopher"} extra `) + var m struct { + Name string + } + d := NewDecoder(r) + err := d.Decode(&m) + if err != nil { + t.Fatal(err) + } + if m.Name != "Gopher" { + t.Errorf("Name = %q; want Gopher", m.Name) + } + rest, err := ioutil.ReadAll(d.Buffered()) + if err != nil { + t.Fatal(err) + } + if g, w := string(rest), " extra "; g != w { + t.Errorf("Remaining = %q; want %q", g, w) + } +} + func nlines(s string, n int) string { if n <= 0 { return "" @@ -145,3 +170,24 @@ func TestNullRawMessage(t *testing.T) { t.Fatalf("Marshal: have %#q want %#q", b, msg) } } + +var blockingTests = []string{ + `{"x": 1}`, + `[1, 2, 3]`, +} + +func TestBlocking(t *testing.T) { + for _, enc := range blockingTests { + r, w := net.Pipe() + go w.Write([]byte(enc)) + var val interface{} + + // If Decode reads beyond what w.Write writes above, + // it will block, and the test will deadlock. + if err := NewDecoder(r).Decode(&val); err != nil { + t.Errorf("decoding %s: %v", enc, err) + } + r.Close() + w.Close() + } +} diff --git a/src/pkg/encoding/json/tagkey_test.go b/src/pkg/encoding/json/tagkey_test.go index da8b12bd8..23e71c752 100644 --- a/src/pkg/encoding/json/tagkey_test.go +++ b/src/pkg/encoding/json/tagkey_test.go @@ -60,6 +60,14 @@ type badCodeTag struct { Z string `json:" !\"#&'()*+,."` } +type spaceTag struct { + Q string `json:"With space"` +} + +type unicodeTag struct { + W string `json:"Ελλάδα"` +} + var structTagObjectKeyTests = []struct { raw interface{} value string @@ -78,6 +86,8 @@ var structTagObjectKeyTests = []struct { {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, {percentSlashTag{"brut"}, "brut", "text/html%"}, {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, + {spaceTag{"Perreddu"}, "Perreddu", "With space"}, + {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, } func TestStructTagObjectKey(t *testing.T) { |