summaryrefslogtreecommitdiff
path: root/libgo/go/encoding/json
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/encoding/json')
-rw-r--r--libgo/go/encoding/json/bench_test.go6
-rw-r--r--libgo/go/encoding/json/decode.go197
-rw-r--r--libgo/go/encoding/json/decode_test.go650
-rw-r--r--libgo/go/encoding/json/encode.go367
-rw-r--r--libgo/go/encoding/json/encode_test.go398
-rw-r--r--libgo/go/encoding/json/example_marshaling_test.go75
-rw-r--r--libgo/go/encoding/json/indent.go2
-rw-r--r--libgo/go/encoding/json/number_test.go2
-rw-r--r--libgo/go/encoding/json/scanner.go4
-rw-r--r--libgo/go/encoding/json/scanner_test.go3
-rw-r--r--libgo/go/encoding/json/stream.go65
-rw-r--r--libgo/go/encoding/json/stream_test.go68
-rw-r--r--libgo/go/encoding/json/tables.go218
-rw-r--r--libgo/go/encoding/json/tagkey_test.go7
-rw-r--r--libgo/go/encoding/json/tags_test.go2
-rw-r--r--libgo/go/encoding/json/testdata/code.json.gzbin120432 -> 120432 bytes
16 files changed, 1742 insertions, 322 deletions
diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go
index ed89d1156e..cd7380b1ef 100644
--- a/libgo/go/encoding/json/bench_test.go
+++ b/libgo/go/encoding/json/bench_test.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -158,7 +158,7 @@ func BenchmarkCodeUnmarshal(b *testing.B) {
for i := 0; i < b.N; i++ {
var r codeResponse
if err := Unmarshal(codeJSON, &r); err != nil {
- b.Fatal("Unmmarshal:", err)
+ b.Fatal("Unmarshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
@@ -173,7 +173,7 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) {
var r codeResponse
for i := 0; i < b.N; i++ {
if err := Unmarshal(codeJSON, &r); err != nil {
- b.Fatal("Unmmarshal:", err)
+ b.Fatal("Unmarshal:", err)
}
}
}
diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go
index 539d952ad6..77fc4607db 100644
--- a/libgo/go/encoding/json/decode.go
+++ b/libgo/go/encoding/json/decode.go
@@ -29,11 +29,18 @@ import (
// with the following additional rules:
//
// To unmarshal JSON into a pointer, Unmarshal first handles the case of
-// the JSON being the JSON literal null. In that case, Unmarshal sets
-// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
-// the value pointed at by the pointer. If the pointer is nil, Unmarshal
+// the JSON being the JSON literal null. In that case, Unmarshal sets
+// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
+// 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 value implementing the Unmarshaler interface,
+// Unmarshal calls that value's UnmarshalJSON method, including
+// when the input is a JSON null.
+// Otherwise, if the value implements encoding.TextUnmarshaler
+// and the input is a JSON quoted string, Unmarshal calls that value's
+// UnmarshalText method with the unquoted form of the string.
+//
// 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.
@@ -61,10 +68,11 @@ import (
// If the JSON array is smaller than the Go array,
// the additional Go array elements are set to zero values.
//
-// To unmarshal a JSON object into a string-keyed map, Unmarshal first
-// establishes a map to use, If the map is nil, Unmarshal allocates a new map.
-// Otherwise Unmarshal reuses the existing map, keeping existing entries.
-// Unmarshal then stores key-value pairs from the JSON object into the map.
+// To unmarshal a JSON object into a map, Unmarshal first establishes a map to
+// use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal
+// reuses the existing map, keeping existing entries. Unmarshal then stores
+// key-value pairs from the JSON object into the map. The map's key type must
+// either be a string, an integer, or implement encoding.TextUnmarshaler.
//
// If a JSON value is not appropriate for a given target type,
// or if a JSON number overflows the target type, Unmarshal
@@ -96,11 +104,14 @@ func Unmarshal(data []byte, v interface{}) error {
return d.unmarshal(v)
}
-// Unmarshaler is the interface implemented by objects
+// Unmarshaler is the interface implemented by types
// that can unmarshal a JSON description of themselves.
// 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.
+//
+// By convention, to approximate the behavior of Unmarshal itself,
+// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op.
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
@@ -111,9 +122,14 @@ type UnmarshalTypeError struct {
Value string // description of JSON value - "bool", "array", "number -5"
Type reflect.Type // type of Go value it could not be assigned to
Offset int64 // error occurred after reading Offset bytes
+ Struct string // name of the struct type containing the field
+ Field string // name of the field holding the Go value
}
func (e *UnmarshalTypeError) Error() string {
+ if e.Struct != "" || e.Field != "" {
+ return "json: cannot unmarshal " + e.Value + " into Go struct field " + e.Struct + "." + e.Field + " of type " + e.Type.String()
+ }
return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}
@@ -247,10 +263,14 @@ func isValidNumber(s string) bool {
// 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
+ data []byte
+ off int // read offset in data
+ scan scanner
+ nextscan scanner // for calls to nextValue
+ errorContext struct { // provides context for type errors
+ Struct string
+ Field string
+ }
savedError error
useNumber bool
}
@@ -264,22 +284,37 @@ func (d *decodeState) init(data []byte) *decodeState {
d.data = data
d.off = 0
d.savedError = nil
+ d.errorContext.Struct = ""
+ d.errorContext.Field = ""
return d
}
// error aborts the decoding by panicking with err.
func (d *decodeState) error(err error) {
- panic(err)
+ panic(d.addErrorContext(err))
}
// saveError saves the first err it is called with,
// for reporting at the end of the unmarshal.
func (d *decodeState) saveError(err error) {
if d.savedError == nil {
- d.savedError = err
+ d.savedError = d.addErrorContext(err)
}
}
+// addErrorContext returns a new error enhanced with information from d.errorContext
+func (d *decodeState) addErrorContext(err error) error {
+ if d.errorContext.Struct != "" || d.errorContext.Field != "" {
+ switch err := err.(type) {
+ case *UnmarshalTypeError:
+ err.Struct = d.errorContext.Struct
+ err.Field = d.errorContext.Field
+ return err
+ }
+ }
+ return err
+}
+
// next cuts off and returns the next full JSON value in d.data[d.off:].
// The next value is known to be an object or array, not a literal.
func (d *decodeState) next() []byte {
@@ -433,8 +468,10 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
if u, ok := v.Interface().(Unmarshaler); ok {
return u, nil, reflect.Value{}
}
- if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
- return nil, u, reflect.Value{}
+ if !decodingNull {
+ if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
+ return nil, u, reflect.Value{}
+ }
}
}
v = v.Elem()
@@ -456,7 +493,7 @@ func (d *decodeState) array(v reflect.Value) {
return
}
if ut != nil {
- d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
d.off--
d.next()
return
@@ -475,7 +512,7 @@ func (d *decodeState) array(v reflect.Value) {
// Otherwise it's invalid.
fallthrough
default:
- d.saveError(&UnmarshalTypeError{"array", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "array", Type: v.Type(), Offset: int64(d.off)})
d.off--
d.next()
return
@@ -534,7 +571,7 @@ func (d *decodeState) array(v reflect.Value) {
if i < v.Len() {
if v.Kind() == reflect.Array {
- // Array. Zero the rest.
+ // Array. Zero the rest.
z := reflect.Zero(v.Type().Elem())
for ; i < v.Len(); i++ {
v.Index(i).Set(z)
@@ -549,6 +586,7 @@ func (d *decodeState) array(v reflect.Value) {
}
var nullLiteral = []byte("null")
+var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
// object consumes an object from d.data[d.off-1:], decoding into the value v.
// the first byte ('{') of the object has been read already.
@@ -564,7 +602,7 @@ func (d *decodeState) object(v reflect.Value) {
return
}
if ut != nil {
- d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
d.off--
d.next() // skip over { } in input
return
@@ -577,24 +615,34 @@ func (d *decodeState) object(v reflect.Value) {
return
}
- // Check type of target: struct or map[string]T
+ // Check type of target:
+ // struct or
+ // map[T1]T2 where T1 is string, an integer type,
+ // or an encoding.TextUnmarshaler
switch v.Kind() {
case reflect.Map:
- // map must have string kind
+ // Map key must either have string kind, have an integer kind,
+ // or be an encoding.TextUnmarshaler.
t := v.Type()
- if t.Key().Kind() != reflect.String {
- d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
- d.off--
- d.next() // skip over { } in input
- return
+ switch t.Key().Kind() {
+ case reflect.String,
+ reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ default:
+ if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
+ d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
+ d.off--
+ d.next() // skip over { } in input
+ return
+ }
}
if v.IsNil() {
v.Set(reflect.MakeMap(t))
}
case reflect.Struct:
-
+ // ok
default:
- d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)})
d.off--
d.next() // skip over { } in input
return
@@ -659,6 +707,8 @@ func (d *decodeState) object(v reflect.Value) {
}
subv = subv.Field(i)
}
+ d.errorContext.Field = f.name
+ d.errorContext.Struct = v.Type().Name()
}
}
@@ -670,7 +720,6 @@ func (d *decodeState) object(v reflect.Value) {
d.error(errPhase)
}
- // Read value.
if destring {
switch qv := d.valueQuoted().(type) {
case nil:
@@ -687,7 +736,37 @@ func (d *decodeState) object(v reflect.Value) {
// Write value back to map;
// if using struct, subv points into struct already.
if v.Kind() == reflect.Map {
- kv := reflect.ValueOf(key).Convert(v.Type().Key())
+ kt := v.Type().Key()
+ var kv reflect.Value
+ switch {
+ case kt.Kind() == reflect.String:
+ kv = reflect.ValueOf(key).Convert(kt)
+ case reflect.PtrTo(kt).Implements(textUnmarshalerType):
+ kv = reflect.New(v.Type().Key())
+ d.literalStore(item, kv, true)
+ kv = kv.Elem()
+ default:
+ switch kt.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ s := string(key)
+ n, err := strconv.ParseInt(s, 10, 64)
+ if err != nil || reflect.Zero(kt).OverflowInt(n) {
+ d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)})
+ return
+ }
+ kv = reflect.ValueOf(n).Convert(kt)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ s := string(key)
+ n, err := strconv.ParseUint(s, 10, 64)
+ if err != nil || reflect.Zero(kt).OverflowUint(n) {
+ d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)})
+ return
+ }
+ kv = reflect.ValueOf(n).Convert(kt)
+ default:
+ panic("json: Unexpected key type") // should never occur
+ }
+ }
v.SetMapIndex(kv, subv)
}
@@ -699,6 +778,9 @@ func (d *decodeState) object(v reflect.Value) {
if op != scanObjectValue {
d.error(errPhase)
}
+
+ d.errorContext.Struct = ""
+ d.errorContext.Field = ""
}
}
@@ -725,7 +807,7 @@ func (d *decodeState) convertNumber(s string) (interface{}, error) {
}
f, err := strconv.ParseFloat(s, 64)
if err != nil {
- return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
+ return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)}
}
return f, nil
}
@@ -744,8 +826,8 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
return
}
- wantptr := item[0] == 'n' // null
- u, ut, pv := d.indirect(v, wantptr)
+ isNull := item[0] == 'n' // null
+ u, ut, pv := d.indirect(v, isNull)
if u != nil {
err := u.UnmarshalJSON(item)
if err != nil {
@@ -758,7 +840,16 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
if fromQuoted {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
- d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+ var val string
+ switch item[0] {
+ case 'n':
+ val = "null"
+ case 't', 'f':
+ val = "bool"
+ default:
+ val = "number"
+ }
+ d.saveError(&UnmarshalTypeError{Value: val, Type: v.Type(), Offset: int64(d.off)})
}
return
}
@@ -781,19 +872,31 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
switch c := item[0]; c {
case 'n': // null
+ // The main parser checks that only true and false can reach here,
+ // but if this was a quoted string input, it could be anything.
+ if fromQuoted && string(item) != "null" {
+ d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ break
+ }
switch v.Kind() {
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'
+ value := item[0] == 't'
+ // The main parser checks that only true and false can reach here,
+ // but if this was a quoted string input, it could be anything.
+ if fromQuoted && string(item) != "true" && string(item) != "false" {
+ d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
+ break
+ }
switch v.Kind() {
default:
if fromQuoted {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
- d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)})
}
case reflect.Bool:
v.SetBool(value)
@@ -801,7 +904,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
if v.NumMethod() == 0 {
v.Set(reflect.ValueOf(value))
} else {
- d.saveError(&UnmarshalTypeError{"bool", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "bool", Type: v.Type(), Offset: int64(d.off)})
}
}
@@ -816,10 +919,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
}
switch v.Kind() {
default:
- d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
case reflect.Slice:
if v.Type().Elem().Kind() != reflect.Uint8 {
- d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
break
}
b := make([]byte, base64.StdEncoding.DecodedLen(len(s)))
@@ -835,7 +938,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
if v.NumMethod() == 0 {
v.Set(reflect.ValueOf(string(s)))
} else {
- d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "string", Type: v.Type(), Offset: int64(d.off)})
}
}
@@ -860,7 +963,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
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(), int64(d.off)})
+ d.error(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)})
}
case reflect.Interface:
n, err := d.convertNumber(s)
@@ -869,7 +972,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
break
}
if v.NumMethod() != 0 {
- d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.off)})
break
}
v.Set(reflect.ValueOf(n))
@@ -877,7 +980,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, err := strconv.ParseInt(s, 10, 64)
if err != nil || v.OverflowInt(n) {
- d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
break
}
v.SetInt(n)
@@ -885,7 +988,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n, err := strconv.ParseUint(s, 10, 64)
if err != nil || v.OverflowUint(n) {
- d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
break
}
v.SetUint(n)
@@ -893,7 +996,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
case reflect.Float32, reflect.Float64:
n, err := strconv.ParseFloat(s, v.Type().Bits())
if err != nil || v.OverflowFloat(n) {
- d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
+ d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.off)})
break
}
v.SetFloat(n)
@@ -902,7 +1005,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
}
// The xxxInterface routines build up a value to be stored
-// in an empty interface. They are not strictly necessary,
+// in an empty interface. They are not strictly necessary,
// but they avoid the weight of reflection in this common case.
// valueInterface is like value but returns interface{}
diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go
index 9546ae459c..bd38ddd319 100644
--- a/libgo/go/encoding/json/decode_test.go
+++ b/libgo/go/encoding/json/decode_test.go
@@ -7,10 +7,14 @@ package json
import (
"bytes"
"encoding"
+ "errors"
"fmt"
"image"
+ "math"
+ "math/big"
"net"
"reflect"
+ "strconv"
"strings"
"testing"
"time"
@@ -30,6 +34,11 @@ type V struct {
F1 interface{}
F2 int32
F3 Number
+ F4 *VOuter
+}
+
+type VOuter struct {
+ V V
}
// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
@@ -52,6 +61,8 @@ type tx struct {
x int
}
+type u8 uint8
+
// A type that can unmarshal itself.
type unmarshaler struct {
@@ -68,16 +79,20 @@ type ustruct struct {
}
type unmarshalerText struct {
- T bool
+ A, B string
}
// needed for re-marshaling tests
-func (u *unmarshalerText) MarshalText() ([]byte, error) {
- return []byte(""), nil
+func (u unmarshalerText) MarshalText() ([]byte, error) {
+ return []byte(u.A + ":" + u.B), nil
}
func (u *unmarshalerText) UnmarshalText(b []byte) error {
- *u = unmarshalerText{true} // All we need to see that UnmarshalText is called.
+ pos := bytes.Index(b, []byte(":"))
+ if pos == -1 {
+ return errors.New("missing separator")
+ }
+ u.A, u.B = string(b[:pos]), string(b[pos+1:])
return nil
}
@@ -87,6 +102,29 @@ type ustructText struct {
M unmarshalerText
}
+// u8marshal is an integer type that can marshal/unmarshal itself.
+type u8marshal uint8
+
+func (u8 u8marshal) MarshalText() ([]byte, error) {
+ return []byte(fmt.Sprintf("u%d", u8)), nil
+}
+
+var errMissingU8Prefix = errors.New("missing 'u' prefix")
+
+func (u8 *u8marshal) UnmarshalText(b []byte) error {
+ if !bytes.HasPrefix(b, []byte{'u'}) {
+ return errMissingU8Prefix
+ }
+ n, err := strconv.Atoi(string(b[1:]))
+ if err != nil {
+ return err
+ }
+ *u8 = u8marshal(n)
+ return nil
+}
+
+var _ encoding.TextUnmarshaler = (*u8marshal)(nil)
+
var (
um0, um1 unmarshaler // target2 of unmarshaling
ump = &um1
@@ -95,12 +133,16 @@ var (
umslicep = new([]unmarshaler)
umstruct = ustruct{unmarshaler{true}}
- um0T, um1T unmarshalerText // target2 of unmarshaling
- umpT = &um1T
- umtrueT = unmarshalerText{true}
- umsliceT = []unmarshalerText{{true}}
- umslicepT = new([]unmarshalerText)
- umstructT = ustructText{unmarshalerText{true}}
+ um0T, um1T unmarshalerText // target2 of unmarshaling
+ umpType = &um1T
+ umtrueXY = unmarshalerText{"x", "y"}
+ umsliceXY = []unmarshalerText{{"x", "y"}}
+ umslicepType = new([]unmarshalerText)
+ umstructType = new(ustructText)
+ umstructXY = ustructText{unmarshalerText{"x", "y"}}
+
+ ummapType = map[unmarshalerText]bool{}
+ ummapXY = map[unmarshalerText]bool{unmarshalerText{"x", "y"}: true}
)
// Test data structures for anonymous fields.
@@ -202,14 +244,6 @@ type S13 struct {
S8
}
-type unmarshalTest struct {
- in string
- ptr interface{}
- out interface{}
- err error
- useNumber bool
-}
-
type Ambig struct {
// Given "hello", the first match should win.
First int `json:"HELLO"`
@@ -225,6 +259,131 @@ type XYZ struct {
func sliceAddr(x []int) *[]int { return &x }
func mapAddr(x map[string]int) *map[string]int { return &x }
+type byteWithMarshalJSON byte
+
+func (b byteWithMarshalJSON) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf(`"Z%.2x"`, byte(b))), nil
+}
+
+func (b *byteWithMarshalJSON) UnmarshalJSON(data []byte) error {
+ if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {
+ return fmt.Errorf("bad quoted string")
+ }
+ i, err := strconv.ParseInt(string(data[2:4]), 16, 8)
+ if err != nil {
+ return fmt.Errorf("bad hex")
+ }
+ *b = byteWithMarshalJSON(i)
+ return nil
+}
+
+type byteWithPtrMarshalJSON byte
+
+func (b *byteWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {
+ return byteWithMarshalJSON(*b).MarshalJSON()
+}
+
+func (b *byteWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {
+ return (*byteWithMarshalJSON)(b).UnmarshalJSON(data)
+}
+
+type byteWithMarshalText byte
+
+func (b byteWithMarshalText) MarshalText() ([]byte, error) {
+ return []byte(fmt.Sprintf(`Z%.2x`, byte(b))), nil
+}
+
+func (b *byteWithMarshalText) UnmarshalText(data []byte) error {
+ if len(data) != 3 || data[0] != 'Z' {
+ return fmt.Errorf("bad quoted string")
+ }
+ i, err := strconv.ParseInt(string(data[1:3]), 16, 8)
+ if err != nil {
+ return fmt.Errorf("bad hex")
+ }
+ *b = byteWithMarshalText(i)
+ return nil
+}
+
+type byteWithPtrMarshalText byte
+
+func (b *byteWithPtrMarshalText) MarshalText() ([]byte, error) {
+ return byteWithMarshalText(*b).MarshalText()
+}
+
+func (b *byteWithPtrMarshalText) UnmarshalText(data []byte) error {
+ return (*byteWithMarshalText)(b).UnmarshalText(data)
+}
+
+type intWithMarshalJSON int
+
+func (b intWithMarshalJSON) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf(`"Z%.2x"`, int(b))), nil
+}
+
+func (b *intWithMarshalJSON) UnmarshalJSON(data []byte) error {
+ if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {
+ return fmt.Errorf("bad quoted string")
+ }
+ i, err := strconv.ParseInt(string(data[2:4]), 16, 8)
+ if err != nil {
+ return fmt.Errorf("bad hex")
+ }
+ *b = intWithMarshalJSON(i)
+ return nil
+}
+
+type intWithPtrMarshalJSON int
+
+func (b *intWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {
+ return intWithMarshalJSON(*b).MarshalJSON()
+}
+
+func (b *intWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {
+ return (*intWithMarshalJSON)(b).UnmarshalJSON(data)
+}
+
+type intWithMarshalText int
+
+func (b intWithMarshalText) MarshalText() ([]byte, error) {
+ return []byte(fmt.Sprintf(`Z%.2x`, int(b))), nil
+}
+
+func (b *intWithMarshalText) UnmarshalText(data []byte) error {
+ if len(data) != 3 || data[0] != 'Z' {
+ return fmt.Errorf("bad quoted string")
+ }
+ i, err := strconv.ParseInt(string(data[1:3]), 16, 8)
+ if err != nil {
+ return fmt.Errorf("bad hex")
+ }
+ *b = intWithMarshalText(i)
+ return nil
+}
+
+type intWithPtrMarshalText int
+
+func (b *intWithPtrMarshalText) MarshalText() ([]byte, error) {
+ return intWithMarshalText(*b).MarshalText()
+}
+
+func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error {
+ return (*intWithMarshalText)(b).UnmarshalText(data)
+}
+
+type unmarshalTest struct {
+ in string
+ ptr interface{}
+ out interface{}
+ err error
+ useNumber bool
+ golden bool
+}
+
+type B struct {
+ B bool `json:",string"`
+}
+
var unmarshalTests = []unmarshalTest{
// basic types
{in: `true`, ptr: new(bool), out: true},
@@ -240,7 +399,7 @@ var unmarshalTests = []unmarshalTest{
{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(""), 7}},
+ {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}},
{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},
@@ -302,14 +461,81 @@ var unmarshalTests = []unmarshalTest{
{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: `{"M":{"T":"x:y"}}`, ptr: &umstruct, out: umstruct},
// UnmarshalText interface test
- {in: `"X"`, ptr: &um0T, out: umtrueT}, // use "false" so test will fail if custom unmarshaler is not called
- {in: `"X"`, ptr: &umpT, out: &umtrueT},
- {in: `["X"]`, ptr: &umsliceT, out: umsliceT},
- {in: `["X"]`, ptr: &umslicepT, out: &umsliceT},
- {in: `{"M":"X"}`, ptr: &umstructT, out: umstructT},
+ {in: `"x:y"`, ptr: &um0T, out: umtrueXY},
+ {in: `"x:y"`, ptr: &umpType, out: &umtrueXY},
+ {in: `["x:y"]`, ptr: &umsliceXY, out: umsliceXY},
+ {in: `["x:y"]`, ptr: &umslicepType, out: &umsliceXY},
+ {in: `{"M":"x:y"}`, ptr: umstructType, out: umstructXY},
+
+ // integer-keyed map test
+ {
+ in: `{"-1":"a","0":"b","1":"c"}`,
+ ptr: new(map[int]string),
+ out: map[int]string{-1: "a", 0: "b", 1: "c"},
+ },
+ {
+ in: `{"0":"a","10":"c","9":"b"}`,
+ ptr: new(map[u8]string),
+ out: map[u8]string{0: "a", 9: "b", 10: "c"},
+ },
+ {
+ in: `{"-9223372036854775808":"min","9223372036854775807":"max"}`,
+ ptr: new(map[int64]string),
+ out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},
+ },
+ {
+ in: `{"18446744073709551615":"max"}`,
+ ptr: new(map[uint64]string),
+ out: map[uint64]string{math.MaxUint64: "max"},
+ },
+ {
+ in: `{"0":false,"10":true}`,
+ ptr: new(map[uintptr]bool),
+ out: map[uintptr]bool{0: false, 10: true},
+ },
+
+ // Check that MarshalText and UnmarshalText take precedence
+ // over default integer handling in map keys.
+ {
+ in: `{"u2":4}`,
+ ptr: new(map[u8marshal]int),
+ out: map[u8marshal]int{2: 4},
+ },
+ {
+ in: `{"2":4}`,
+ ptr: new(map[u8marshal]int),
+ err: errMissingU8Prefix,
+ },
+
+ // integer-keyed map errors
+ {
+ in: `{"abc":"abc"}`,
+ ptr: new(map[int]string),
+ err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeOf(0), Offset: 2},
+ },
+ {
+ in: `{"256":"abc"}`,
+ ptr: new(map[uint8]string),
+ err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2},
+ },
+ {
+ in: `{"128":"abc"}`,
+ ptr: new(map[int8]string),
+ err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2},
+ },
+ {
+ in: `{"-1":"abc"}`,
+ ptr: new(map[uint8]string),
+ err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2},
+ },
+
+ // Map keys can be encoding.TextUnmarshalers.
+ {in: `{"x:y":true}`, ptr: &ummapType, out: ummapXY},
+ // If multiple values for the same key exists, only the most recent value is used.
+ {in: `{"x:y":false,"x:y":true}`, ptr: &ummapType, out: ummapXY},
// Overwriting of data.
// This is different from package xml, but it's what we've always done.
@@ -426,12 +652,147 @@ var unmarshalTests = []unmarshalTest{
out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
},
- // issue 8305
+ // Used to be issue 8305, but time.Time implements encoding.TextUnmarshaler so this works now.
{
in: `{"2009-11-10T23:00:00Z": "hello world"}`,
ptr: &map[time.Time]string{},
- err: &UnmarshalTypeError{"object", reflect.TypeOf(map[time.Time]string{}), 1},
+ out: map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},
+ },
+
+ // issue 8305
+ {
+ in: `{"2009-11-10T23:00:00Z": "hello world"}`,
+ ptr: &map[Point]string{},
+ err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1},
+ },
+ {
+ in: `{"asdf": "hello world"}`,
+ ptr: &map[unmarshaler]string{},
+ err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[unmarshaler]string{}), Offset: 1},
+ },
+
+ // related to issue 13783.
+ // Go 1.7 changed marshaling a slice of typed byte to use the methods on the byte type,
+ // similar to marshaling a slice of typed int.
+ // These tests check that, assuming the byte type also has valid decoding methods,
+ // either the old base64 string encoding or the new per-element encoding can be
+ // successfully unmarshaled. The custom unmarshalers were accessible in earlier
+ // versions of Go, even though the custom marshaler was not.
+ {
+ in: `"AQID"`,
+ ptr: new([]byteWithMarshalJSON),
+ out: []byteWithMarshalJSON{1, 2, 3},
+ },
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithMarshalJSON),
+ out: []byteWithMarshalJSON{1, 2, 3},
+ golden: true,
+ },
+ {
+ in: `"AQID"`,
+ ptr: new([]byteWithMarshalText),
+ out: []byteWithMarshalText{1, 2, 3},
+ },
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithMarshalText),
+ out: []byteWithMarshalText{1, 2, 3},
+ golden: true,
+ },
+ {
+ in: `"AQID"`,
+ ptr: new([]byteWithPtrMarshalJSON),
+ out: []byteWithPtrMarshalJSON{1, 2, 3},
+ },
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithPtrMarshalJSON),
+ out: []byteWithPtrMarshalJSON{1, 2, 3},
+ golden: true,
+ },
+ {
+ in: `"AQID"`,
+ ptr: new([]byteWithPtrMarshalText),
+ out: []byteWithPtrMarshalText{1, 2, 3},
},
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]byteWithPtrMarshalText),
+ out: []byteWithPtrMarshalText{1, 2, 3},
+ golden: true,
+ },
+
+ // ints work with the marshaler but not the base64 []byte case
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithMarshalJSON),
+ out: []intWithMarshalJSON{1, 2, 3},
+ golden: true,
+ },
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithMarshalText),
+ out: []intWithMarshalText{1, 2, 3},
+ golden: true,
+ },
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithPtrMarshalJSON),
+ out: []intWithPtrMarshalJSON{1, 2, 3},
+ golden: true,
+ },
+ {
+ in: `["Z01","Z02","Z03"]`,
+ ptr: new([]intWithPtrMarshalText),
+ out: []intWithPtrMarshalText{1, 2, 3},
+ golden: true,
+ },
+
+ {in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
+ {in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
+ {in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
+ {in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
+ {in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
+ {in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
+ {in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
+ {in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
+ {in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
+ {in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
+ {in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
+
+ {
+ in: `{"V": {"F2": "hello"}}`,
+ ptr: new(VOuter),
+ err: &UnmarshalTypeError{
+ Value: "string",
+ Struct: "V",
+ Field: "F2",
+ Type: reflect.TypeOf(int32(0)),
+ Offset: 20,
+ },
+ },
+ {
+ in: `{"V": {"F4": {}, "F2": "hello"}}`,
+ ptr: new(VOuter),
+ err: &UnmarshalTypeError{
+ Value: "string",
+ Struct: "V",
+ Field: "F2",
+ Type: reflect.TypeOf(int32(0)),
+ Offset: 30,
+ },
+ },
+
+ // issue 15146.
+ // invalid inputs in wrongStringTests below.
+ {in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
+ {in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
+ {in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
+ {in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
+ {in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
+ {in: `{"B": "null"}`, ptr: new(B), out: B{false}},
+ {in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
}
func TestMarshal(t *testing.T) {
@@ -565,13 +926,16 @@ func TestUnmarshal(t *testing.T) {
continue
}
- // Check round trip.
+ // Check round trip also decodes correctly.
if tt.err == nil {
enc, err := Marshal(v.Interface())
if err != nil {
t.Errorf("#%d: error re-marshaling: %v", i, err)
continue
}
+ if tt.golden && !bytes.Equal(enc, in) {
+ t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
+ }
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
dec = NewDecoder(bytes.NewReader(enc))
if tt.useNumber {
@@ -1161,40 +1525,148 @@ func TestInterfaceSet(t *testing.T) {
}
}
+type NullTest struct {
+ Bool bool
+ Int int
+ Int8 int8
+ Int16 int16
+ Int32 int32
+ Int64 int64
+ Uint uint
+ Uint8 uint8
+ Uint16 uint16
+ Uint32 uint32
+ Uint64 uint64
+ Float32 float32
+ Float64 float64
+ String string
+ PBool *bool
+ Map map[string]string
+ Slice []string
+ Interface interface{}
+
+ PRaw *RawMessage
+ PTime *time.Time
+ PBigInt *big.Int
+ PText *MustNotUnmarshalText
+ PBuffer *bytes.Buffer // has methods, just not relevant ones
+ PStruct *struct{}
+
+ Raw RawMessage
+ Time time.Time
+ BigInt big.Int
+ Text MustNotUnmarshalText
+ Buffer bytes.Buffer
+ Struct struct{}
+}
+
+type NullTestStrings struct {
+ Bool bool `json:",string"`
+ Int int `json:",string"`
+ Int8 int8 `json:",string"`
+ Int16 int16 `json:",string"`
+ Int32 int32 `json:",string"`
+ Int64 int64 `json:",string"`
+ Uint uint `json:",string"`
+ Uint8 uint8 `json:",string"`
+ Uint16 uint16 `json:",string"`
+ Uint32 uint32 `json:",string"`
+ Uint64 uint64 `json:",string"`
+ Float32 float32 `json:",string"`
+ Float64 float64 `json:",string"`
+ String string `json:",string"`
+ PBool *bool `json:",string"`
+ Map map[string]string `json:",string"`
+ Slice []string `json:",string"`
+ Interface interface{} `json:",string"`
+
+ PRaw *RawMessage `json:",string"`
+ PTime *time.Time `json:",string"`
+ PBigInt *big.Int `json:",string"`
+ PText *MustNotUnmarshalText `json:",string"`
+ PBuffer *bytes.Buffer `json:",string"`
+ PStruct *struct{} `json:",string"`
+
+ Raw RawMessage `json:",string"`
+ Time time.Time `json:",string"`
+ BigInt big.Int `json:",string"`
+ Text MustNotUnmarshalText `json:",string"`
+ Buffer bytes.Buffer `json:",string"`
+ Struct struct{} `json:",string"`
+}
+
// JSON null values should be ignored for primitives and string values instead of resulting in an error.
// Issue 2540
func TestUnmarshalNulls(t *testing.T) {
+ // Unmarshal docs:
+ // The JSON null value unmarshals into an interface, map, pointer, or slice
+ // by setting that Go value to nil. Because null is often used in JSON to mean
+ // ``not present,'' unmarshaling a JSON null into any other Go type has no effect
+ // on the value and produces no error.
+
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"}
+ "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,
+ "PBool": null,
+ "Map": null,
+ "Slice": null,
+ "Interface": null,
+ "PRaw": null,
+ "PTime": null,
+ "PBigInt": null,
+ "PText": null,
+ "PBuffer": null,
+ "PStruct": null,
+ "Raw": null,
+ "Time": null,
+ "BigInt": null,
+ "Text": null,
+ "Buffer": null,
+ "Struct": null
+ }`)
+ nulls := NullTest{
+ 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",
+ PBool: new(bool),
+ Map: map[string]string{},
+ Slice: []string{},
+ Interface: new(MustNotUnmarshalJSON),
+ PRaw: new(RawMessage),
+ PTime: new(time.Time),
+ PBigInt: new(big.Int),
+ PText: new(MustNotUnmarshalText),
+ PStruct: new(struct{}),
+ PBuffer: new(bytes.Buffer),
+ Raw: RawMessage("123"),
+ Time: time.Unix(123456789, 0),
+ BigInt: *big.NewInt(123),
+ }
+
+ before := nulls.Time.String()
err := Unmarshal(jsonData, &nulls)
if err != nil {
@@ -1203,9 +1675,61 @@ 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" {
-
t.Errorf("Unmarshal of null values affected primitives")
}
+
+ if nulls.PBool != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PBool")
+ }
+ if nulls.Map != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.Map")
+ }
+ if nulls.Slice != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.Slice")
+ }
+ if nulls.Interface != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.Interface")
+ }
+ if nulls.PRaw != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PRaw")
+ }
+ if nulls.PTime != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PTime")
+ }
+ if nulls.PBigInt != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PBigInt")
+ }
+ if nulls.PText != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PText")
+ }
+ if nulls.PBuffer != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PBuffer")
+ }
+ if nulls.PStruct != nil {
+ t.Errorf("Unmarshal of null did not clear nulls.PStruct")
+ }
+
+ if string(nulls.Raw) != "null" {
+ t.Errorf("Unmarshal of RawMessage null did not record null: %v", string(nulls.Raw))
+ }
+ if nulls.Time.String() != before {
+ t.Errorf("Unmarshal of time.Time null set time to %v", nulls.Time.String())
+ }
+ if nulls.BigInt.String() != "123" {
+ t.Errorf("Unmarshal of big.Int null set int to %v", nulls.BigInt.String())
+ }
+}
+
+type MustNotUnmarshalJSON struct{}
+
+func (x MustNotUnmarshalJSON) UnmarshalJSON(data []byte) error {
+ return errors.New("MustNotUnmarshalJSON was used")
+}
+
+type MustNotUnmarshalText struct{}
+
+func (x MustNotUnmarshalText) UnmarshalText(text []byte) error {
+ return errors.New("MustNotUnmarshalText was used")
}
func TestStringKind(t *testing.T) {
@@ -1231,8 +1755,8 @@ func TestStringKind(t *testing.T) {
}
}
-// Custom types with []byte as underlying type could not be marshalled
-// and then unmarshalled.
+// Custom types with []byte as underlying type could not be marshaled
+// and then unmarshaled.
// Issue 8962.
func TestByteKind(t *testing.T) {
type byteKind []byte
@@ -1270,7 +1794,7 @@ func TestSliceOfCustomByte(t *testing.T) {
t.Fatal(err)
}
if !reflect.DeepEqual(a, b) {
- t.Fatal("expected %v == %v", a, b)
+ t.Fatalf("expected %v == %v", a, b)
}
}
@@ -1444,7 +1968,7 @@ var invalidUnmarshalTextTests = []struct {
{nil, "json: Unmarshal(nil)"},
{struct{}{}, "json: Unmarshal(non-pointer struct {})"},
{(*int)(nil), "json: Unmarshal(nil *int)"},
- {new(net.IP), "json: cannot unmarshal string into Go value of type *net.IP"},
+ {new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
}
func TestInvalidUnmarshalText(t *testing.T) {
diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go
index 69ac7e03c8..8f21ddaed9 100644
--- a/libgo/go/encoding/json/encode.go
+++ b/libgo/go/encoding/json/encode.go
@@ -1,9 +1,9 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package json implements encoding and decoding of JSON objects as defined in
-// RFC 4627. The mapping between JSON objects and Go values is described
+// Package json implements encoding and decoding of JSON as defined in
+// RFC 4627. The mapping between JSON and Go values is described
// in the documentation for the Marshal and Unmarshal functions.
//
// See "JSON and Go" for an introduction to this package:
@@ -22,6 +22,7 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"unicode"
"unicode/utf8"
)
@@ -33,7 +34,7 @@ import (
// and is not a nil pointer, Marshal calls its MarshalJSON method
// to produce JSON. If no MarshalJSON method is present but the
// value implements encoding.TextMarshaler instead, Marshal calls
-// its MarshalText method.
+// its MarshalText method and encodes the result as a JSON string.
// The nil pointer exception is not strictly necessary
// but mimics a similar, necessary exception in the behavior of
// UnmarshalJSON.
@@ -49,24 +50,33 @@ import (
// The angle brackets "<" and ">" are escaped to "\u003c" and "\u003e"
// to keep some browsers from misinterpreting JSON output as HTML.
// Ampersand "&" is also escaped to "\u0026" for the same reason.
+// This escaping can be disabled using an Encoder that had SetEscapeHTML(false)
+// called on it.
//
// Array and slice values encode as JSON arrays, except that
// []byte encodes as a base64-encoded string, and a nil slice
-// encodes as the null JSON object.
+// encodes as the null JSON value.
//
-// Struct values encode as JSON objects. Each exported struct field
-// becomes a member of the object unless
-// - the field's tag is "-", or
-// - the field is empty and its tag specifies the "omitempty" option.
-// The empty values are false, 0, any
-// 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
-// the struct field's tag value is the key name, followed by an optional comma
-// and options. Examples:
+// Struct values encode as JSON objects.
+// Each exported struct field becomes a member of the object, using the
+// field name as the object key, unless the field is omitted for one of the
+// reasons given below.
//
-// // Field is ignored by this package.
-// Field int `json:"-"`
+// The encoding of each struct field can be customized by the format string
+// stored under the "json" key in the struct field's tag.
+// The format string gives the name of the field, possibly followed by a
+// comma-separated list of options. The name may be empty in order to
+// specify options without overriding the default field name.
+//
+// The "omitempty" option specifies that the field should be omitted
+// from the encoding if the field has an empty value, defined as
+// false, 0, a nil pointer, a nil interface value, and any empty array,
+// slice, map, or string.
+//
+// As a special case, if the field tag is "-", the field is always omitted.
+// Note that a field with name "-" can still be generated using the tag "-,".
+//
+// Examples of struct field tags and their meanings:
//
// // Field appears in JSON as key "myName".
// Field int `json:"myName"`
@@ -81,6 +91,12 @@ import (
// // Note the leading comma.
// Field int `json:",omitempty"`
//
+// // Field is ignored by this package.
+// Field int `json:"-"`
+//
+// // Field appears in JSON as key "-".
+// Field int `json:"-,"`
+//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
@@ -89,8 +105,8 @@ import (
// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
-// only Unicode letters, digits, dollar signs, percent signs, hyphens,
-// underscores and slashes.
+// only Unicode letters, digits, and ASCII punctuation except quotation
+// marks, backslash, and comma.
//
// 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 amended
@@ -108,7 +124,9 @@ import (
//
// 1) Of those fields, if any are JSON-tagged, only tagged fields are considered,
// even if there are multiple untagged fields that would otherwise conflict.
+//
// 2) If there is exactly one field (tagged or not according to the first rule), that is selected.
+//
// 3) Otherwise there are multiple fields, and all are ignored; no error occurs.
//
// Handling of anonymous struct fields is new in Go 1.1.
@@ -116,27 +134,31 @@ import (
// 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 map keys are used as JSON object
-// keys, subject to the UTF-8 coercion described for string values above.
+// Map values encode as JSON objects. The map's key type must either be a
+// string, an integer type, or implement encoding.TextMarshaler. The map keys
+// are sorted and used as JSON object keys by applying the following rules,
+// subject to the UTF-8 coercion described for string values above:
+// - string keys are used directly
+// - encoding.TextMarshalers are marshaled
+// - integer keys are converted to strings
//
// Pointer values encode as the value pointed to.
-// A nil pointer encodes as the null JSON object.
+// A nil pointer encodes as the null JSON value.
//
// Interface values encode as the value contained in the interface.
-// A nil interface value encodes as the null JSON object.
+// A nil interface value encodes as the null JSON value.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an UnsupportedTypeError.
//
// JSON cannot represent cyclic data structures and Marshal does not
-// handle them. Passing cyclic structures to Marshal will result in
+// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(v interface{}) ([]byte, error) {
e := &encodeState{}
- err := e.marshal(v)
+ err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
@@ -192,7 +214,7 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
}
}
-// Marshaler is the interface implemented by objects that
+// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler interface {
MarshalJSON() ([]byte, error)
@@ -259,7 +281,7 @@ func newEncodeState() *encodeState {
return new(encodeState)
}
-func (e *encodeState) marshal(v interface{}) (err error) {
+func (e *encodeState) marshal(v interface{}, opts encOpts) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
@@ -271,7 +293,7 @@ func (e *encodeState) marshal(v interface{}) (err error) {
err = r.(error)
}
}()
- e.reflectValue(reflect.ValueOf(v))
+ e.reflectValue(reflect.ValueOf(v), opts)
return nil
}
@@ -297,11 +319,18 @@ func isEmptyValue(v reflect.Value) bool {
return false
}
-func (e *encodeState) reflectValue(v reflect.Value) {
- valueEncoder(v)(e, v, false)
+func (e *encodeState) reflectValue(v reflect.Value, opts encOpts) {
+ valueEncoder(v)(e, v, opts)
+}
+
+type encOpts struct {
+ // quoted causes primitive fields to be encoded inside JSON strings.
+ quoted bool
+ // escapeHTML causes '<', '>', and '&' to be escaped in JSON strings.
+ escapeHTML bool
}
-type encoderFunc func(e *encodeState, v reflect.Value, quoted bool)
+type encoderFunc func(e *encodeState, v reflect.Value, opts encOpts)
var encoderCache struct {
sync.RWMutex
@@ -325,7 +354,7 @@ func typeEncoder(t reflect.Type) encoderFunc {
// To deal with recursive types, populate the map with an
// indirect func before we build it. This type waits on the
- // real func (f) to be ready and then calls it. This indirect
+ // real func (f) to be ready and then calls it. This indirect
// func is only used for recursive types.
encoderCache.Lock()
if encoderCache.m == nil {
@@ -333,9 +362,9 @@ func typeEncoder(t reflect.Type) encoderFunc {
}
var wg sync.WaitGroup
wg.Add(1)
- encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) {
+ encoderCache.m[t] = func(e *encodeState, v reflect.Value, opts encOpts) {
wg.Wait()
- f(e, v, quoted)
+ f(e, v, opts)
}
encoderCache.Unlock()
@@ -405,27 +434,31 @@ func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
}
}
-func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func invalidValueEncoder(e *encodeState, v reflect.Value, _ encOpts) {
e.WriteString("null")
}
-func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func marshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Ptr && v.IsNil() {
e.WriteString("null")
return
}
- m := v.Interface().(Marshaler)
+ m, ok := v.Interface().(Marshaler)
+ if !ok {
+ e.WriteString("null")
+ return
+ }
b, err := m.MarshalJSON()
if err == nil {
// copy JSON into buffer, checking validity.
- err = compact(&e.Buffer, b, true)
+ err = compact(&e.Buffer, b, opts.escapeHTML)
}
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
}
-func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func addrMarshalerEncoder(e *encodeState, v reflect.Value, _ encOpts) {
va := v.Addr()
if va.IsNil() {
e.WriteString("null")
@@ -442,7 +475,7 @@ func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
}
}
-func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func textMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Kind() == reflect.Ptr && v.IsNil() {
e.WriteString("null")
return
@@ -452,10 +485,10 @@ func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
- e.stringBytes(b)
+ e.stringBytes(b, opts.escapeHTML)
}
-func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, opts encOpts) {
va := v.Addr()
if va.IsNil() {
e.WriteString("null")
@@ -466,11 +499,11 @@ func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
- e.stringBytes(b)
+ e.stringBytes(b, opts.escapeHTML)
}
-func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
- if quoted {
+func boolEncoder(e *encodeState, v reflect.Value, opts encOpts) {
+ if opts.quoted {
e.WriteByte('"')
}
if v.Bool() {
@@ -478,46 +511,70 @@ func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
} else {
e.WriteString("false")
}
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
-func intEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func intEncoder(e *encodeState, v reflect.Value, opts encOpts) {
b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
e.Write(b)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
-func uintEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func uintEncoder(e *encodeState, v reflect.Value, opts encOpts) {
b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
e.Write(b)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
type floatEncoder int // number of bits
-func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (bits floatEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
f := v.Float()
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 {
+
+ // Convert as if by ES6 number to string conversion.
+ // This matches most other JSON generators.
+ // See golang.org/issue/6384 and golang.org/issue/14135.
+ // Like fmt %g, but the exponent cutoffs are different
+ // and exponents themselves are not padded to two digits.
+ b := e.scratch[:0]
+ abs := math.Abs(f)
+ fmt := byte('f')
+ // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
+ if abs != 0 {
+ if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
+ fmt = 'e'
+ }
+ }
+ b = strconv.AppendFloat(b, f, fmt, -1, int(bits))
+ if fmt == 'e' {
+ // clean up e-09 to e-9
+ n := len(b)
+ if n >= 4 && b[n-4] == 'e' && b[n-3] == '-' && b[n-2] == '0' {
+ b[n-2] = b[n-1]
+ b = b[:n-1]
+ }
+ }
+
+ if opts.quoted {
e.WriteByte('"')
}
e.Write(b)
- if quoted {
+ if opts.quoted {
e.WriteByte('"')
}
}
@@ -527,7 +584,7 @@ var (
float64Encoder = (floatEncoder(64)).encode
)
-func stringEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.Type() == numberType {
numStr := v.String()
// In Go1.5 the empty string encodes to "0", while this is not a valid number literal
@@ -541,26 +598,26 @@ func stringEncoder(e *encodeState, v reflect.Value, quoted bool) {
e.WriteString(numStr)
return
}
- if quoted {
+ if opts.quoted {
sb, err := Marshal(v.String())
if err != nil {
e.error(err)
}
- e.string(string(sb))
+ e.string(string(sb), opts.escapeHTML)
} else {
- e.string(v.String())
+ e.string(v.String(), opts.escapeHTML)
}
}
-func interfaceEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func interfaceEncoder(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
- e.reflectValue(v.Elem())
+ e.reflectValue(v.Elem(), opts)
}
-func unsupportedTypeEncoder(e *encodeState, v reflect.Value, quoted bool) {
+func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) {
e.error(&UnsupportedTypeError{v.Type()})
}
@@ -569,7 +626,7 @@ type structEncoder struct {
fieldEncs []encoderFunc
}
-func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte('{')
first := true
for i, f := range se.fields {
@@ -582,9 +639,10 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
} else {
e.WriteByte(',')
}
- e.string(f.name)
+ e.string(f.name, opts.escapeHTML)
e.WriteByte(':')
- se.fieldEncs[i](e, fv, f.quoted)
+ opts.quoted = f.quoted
+ se.fieldEncs[i](e, fv, opts)
}
e.WriteByte('}')
}
@@ -605,34 +663,50 @@ type mapEncoder struct {
elemEnc encoderFunc
}
-func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
e.WriteByte('{')
- var sv stringValues = v.MapKeys()
- sort.Sort(sv)
- for i, k := range sv {
+
+ // Extract and sort the keys.
+ keys := v.MapKeys()
+ sv := make([]reflectWithString, len(keys))
+ for i, v := range keys {
+ sv[i].v = v
+ if err := sv[i].resolve(); err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+ }
+ sort.Slice(sv, func(i, j int) bool { return sv[i].s < sv[j].s })
+
+ for i, kv := range sv {
if i > 0 {
e.WriteByte(',')
}
- e.string(k.String())
+ e.string(kv.s, opts.escapeHTML)
e.WriteByte(':')
- me.elemEnc(e, v.MapIndex(k), false)
+ me.elemEnc(e, v.MapIndex(kv.v), opts)
}
e.WriteByte('}')
}
func newMapEncoder(t reflect.Type) encoderFunc {
- if t.Key().Kind() != reflect.String {
- return unsupportedTypeEncoder
+ switch t.Key().Kind() {
+ case reflect.String,
+ reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ default:
+ if !t.Key().Implements(textMarshalerType) {
+ return unsupportedTypeEncoder
+ }
}
me := &mapEncoder{typeEncoder(t.Elem())}
return me.encode
}
-func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) {
+func encodeByteSlice(e *encodeState, v reflect.Value, _ encOpts) {
if v.IsNil() {
e.WriteString("null")
return
@@ -659,18 +733,21 @@ type sliceEncoder struct {
arrayEnc encoderFunc
}
-func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
- se.arrayEnc(e, v, false)
+ se.arrayEnc(e, v, opts)
}
func newSliceEncoder(t reflect.Type) encoderFunc {
// Byte slices get special treatment; arrays don't.
if t.Elem().Kind() == reflect.Uint8 {
- return encodeByteSlice
+ p := reflect.PtrTo(t.Elem())
+ if !p.Implements(marshalerType) && !p.Implements(textMarshalerType) {
+ return encodeByteSlice
+ }
}
enc := &sliceEncoder{newArrayEncoder(t)}
return enc.encode
@@ -680,14 +757,14 @@ type arrayEncoder struct {
elemEnc encoderFunc
}
-func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
e.WriteByte('[')
n := v.Len()
for i := 0; i < n; i++ {
if i > 0 {
e.WriteByte(',')
}
- ae.elemEnc(e, v.Index(i), false)
+ ae.elemEnc(e, v.Index(i), opts)
}
e.WriteByte(']')
}
@@ -701,12 +778,12 @@ type ptrEncoder struct {
elemEnc encoderFunc
}
-func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.IsNil() {
e.WriteString("null")
return
}
- pe.elemEnc(e, v.Elem(), quoted)
+ pe.elemEnc(e, v.Elem(), opts)
}
func newPtrEncoder(t reflect.Type) encoderFunc {
@@ -718,11 +795,11 @@ type condAddrEncoder struct {
canAddrEnc, elseEnc encoderFunc
}
-func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) {
if v.CanAddr() {
- ce.canAddrEnc(e, v, quoted)
+ ce.canAddrEnc(e, v, opts)
} else {
- ce.elseEnc(e, v, quoted)
+ ce.elseEnc(e, v, opts)
}
}
@@ -775,23 +852,40 @@ func typeByIndex(t reflect.Type, index []int) reflect.Type {
return t
}
-// stringValues is a slice of reflect.Value holding *reflect.StringValue.
-// It implements the methods to sort by string.
-type stringValues []reflect.Value
+type reflectWithString struct {
+ v reflect.Value
+ s string
+}
-func (sv stringValues) Len() int { return len(sv) }
-func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] }
-func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) }
-func (sv stringValues) get(i int) string { return sv[i].String() }
+func (w *reflectWithString) resolve() error {
+ if w.v.Kind() == reflect.String {
+ w.s = w.v.String()
+ return nil
+ }
+ if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
+ buf, err := tm.MarshalText()
+ w.s = string(buf)
+ return err
+ }
+ switch w.v.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ w.s = strconv.FormatInt(w.v.Int(), 10)
+ return nil
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ w.s = strconv.FormatUint(w.v.Uint(), 10)
+ return nil
+ }
+ panic("unexpected map key type")
+}
// NOTE: keep in sync with stringBytes below.
-func (e *encodeState) string(s string) int {
+func (e *encodeState) string(s string, escapeHTML bool) int {
len0 := e.Len()
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
- if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
+ if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
i++
continue
}
@@ -812,10 +906,11 @@ func (e *encodeState) string(s string) int {
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.
+ // This encodes bytes < 0x20 except for \t, \n and \r.
+ // If escapeHTML is set, it also escapes <, >, and &
+ // 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])
@@ -861,13 +956,13 @@ func (e *encodeState) string(s string) int {
}
// NOTE: keep in sync with string above.
-func (e *encodeState) stringBytes(s []byte) int {
+func (e *encodeState) stringBytes(s []byte, escapeHTML bool) int {
len0 := e.Len()
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
- if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
+ if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
i++
continue
}
@@ -888,10 +983,11 @@ func (e *encodeState) stringBytes(s []byte) int {
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.
+ // This encodes bytes < 0x20 except for \t, \n and \r.
+ // If escapeHTML is set, it also escapes <, >, and &
+ // 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])
@@ -955,28 +1051,6 @@ func fillField(f field) field {
return f
}
-// 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
-
-func (x byName) Len() int { return len(x) }
-
-func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
-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)
-}
-
// byIndex sorts field by index sequence.
type byIndex []field
@@ -1094,7 +1168,22 @@ func typeFields(t reflect.Type) []field {
}
}
- sort.Sort(byName(fields))
+ sort.Slice(fields, func(i, j int) bool {
+ x := fields
+ // sort field by name, breaking ties with depth, then
+ // breaking ties with "name came from json tag", then
+ // breaking ties with index sequence.
+ 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)
+ })
// Delete all fields that are hidden by the Go rules for embedded fields,
// except that fields with JSON tags are promoted.
@@ -1169,15 +1258,14 @@ func dominantField(fields []field) (field, bool) {
}
var fieldCache struct {
- sync.RWMutex
- m map[reflect.Type][]field
+ value atomic.Value // map[reflect.Type][]field
+ mu sync.Mutex // used only by writers
}
// 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()
+ m, _ := fieldCache.value.Load().(map[reflect.Type][]field)
+ f := m[t]
if f != nil {
return f
}
@@ -1189,11 +1277,14 @@ func cachedTypeFields(t reflect.Type) []field {
f = []field{}
}
- fieldCache.Lock()
- if fieldCache.m == nil {
- fieldCache.m = map[reflect.Type][]field{}
+ fieldCache.mu.Lock()
+ m, _ = fieldCache.value.Load().(map[reflect.Type][]field)
+ newM := make(map[reflect.Type][]field, len(m)+1)
+ for k, v := range m {
+ newM[k] = v
}
- fieldCache.m[t] = f
- fieldCache.Unlock()
+ newM[t] = f
+ fieldCache.value.Store(newM)
+ fieldCache.mu.Unlock()
return f
}
diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go
index c00491e00c..6d574cfc47 100644
--- a/libgo/go/encoding/json/encode_test.go
+++ b/libgo/go/encoding/json/encode_test.go
@@ -6,8 +6,12 @@ package json
import (
"bytes"
+ "fmt"
+ "log"
"math"
"reflect"
+ "regexp"
+ "strconv"
"testing"
"unicode"
)
@@ -289,6 +293,44 @@ type BugX struct {
BugB
}
+// Issue 16042. Even if a nil interface value is passed in
+// as long as it implements MarshalJSON, it should be marshaled.
+type nilMarshaler string
+
+func (nm *nilMarshaler) MarshalJSON() ([]byte, error) {
+ if nm == nil {
+ return Marshal("0zenil0")
+ }
+ return Marshal("zenil:" + string(*nm))
+}
+
+// Issue 16042.
+func TestNilMarshal(t *testing.T) {
+ testCases := []struct {
+ v interface{}
+ want string
+ }{
+ {v: nil, want: `null`},
+ {v: new(float64), want: `0`},
+ {v: []interface{}(nil), want: `null`},
+ {v: []string(nil), want: `null`},
+ {v: map[string]string(nil), want: `null`},
+ {v: []byte(nil), want: `null`},
+ {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`},
+ {v: struct{ M Marshaler }{}, want: `{"M":null}`},
+ {v: struct{ M Marshaler }{(*nilMarshaler)(nil)}, want: `{"M":"0zenil0"}`},
+ {v: struct{ M interface{} }{(*nilMarshaler)(nil)}, want: `{"M":null}`},
+ }
+
+ for _, tt := range testCases {
+ out, err := Marshal(tt.v)
+ if err != nil || string(out) != tt.want {
+ t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want)
+ continue
+ }
+ }
+}
+
// Issue 5245.
func TestEmbeddedBug(t *testing.T) {
v := BugB{
@@ -374,66 +416,47 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
}
func TestStringBytes(t *testing.T) {
+ t.Parallel()
// Test that encodeState.stringBytes and encodeState.string use the same encoding.
- es := &encodeState{}
var r []rune
for i := '\u0000'; i <= unicode.MaxRune; i++ {
r = append(r, i)
}
s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too
- es.string(s)
- esBytes := &encodeState{}
- esBytes.stringBytes([]byte(s))
+ for _, escapeHTML := range []bool{true, false} {
+ es := &encodeState{}
+ es.string(s, escapeHTML)
- enc := es.Buffer.String()
- encBytes := esBytes.Buffer.String()
- if enc != encBytes {
- i := 0
- for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
- i++
- }
- enc = enc[i:]
- encBytes = encBytes[i:]
- i = 0
- for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
- i++
- }
- enc = enc[:len(enc)-i]
- encBytes = encBytes[:len(encBytes)-i]
-
- if len(enc) > 20 {
- enc = enc[:20] + "..."
- }
- if len(encBytes) > 20 {
- encBytes = encBytes[:20] + "..."
- }
-
- t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
- }
-}
-
-func TestIssue6458(t *testing.T) {
- type Foo struct {
- M RawMessage
- }
- x := Foo{RawMessage(`"foo"`)}
+ esBytes := &encodeState{}
+ esBytes.stringBytes([]byte(s), escapeHTML)
- b, err := Marshal(&x)
- if err != nil {
- t.Fatal(err)
- }
- if want := `{"M":"foo"}`; string(b) != want {
- t.Errorf("Marshal(&x) = %#q; want %#q", b, want)
- }
+ enc := es.Buffer.String()
+ encBytes := esBytes.Buffer.String()
+ if enc != encBytes {
+ i := 0
+ for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
+ i++
+ }
+ enc = enc[i:]
+ encBytes = encBytes[i:]
+ i = 0
+ for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
+ i++
+ }
+ enc = enc[:len(enc)-i]
+ encBytes = encBytes[:len(encBytes)-i]
- b, err = Marshal(x)
- if err != nil {
- t.Fatal(err)
- }
+ if len(enc) > 20 {
+ enc = enc[:20] + "..."
+ }
+ if len(encBytes) > 20 {
+ encBytes = encBytes[:20] + "..."
+ }
- if want := `{"M":"ImZvbyI="}`; string(b) != want {
- t.Errorf("Marshal(x) = %#q; want %#q", b, want)
+ t.Errorf("with escapeHTML=%t, encodings differ at %#q vs %#q",
+ escapeHTML, enc, encBytes)
+ }
}
}
@@ -478,7 +501,7 @@ func TestEncodePointerString(t *testing.T) {
t.Fatalf("Unmarshal: %v", err)
}
if back.N == nil {
- t.Fatalf("Unmarshalled nil N field")
+ t.Fatalf("Unmarshaled nil N field")
}
if *back.N != 42 {
t.Fatalf("*N = %d; want 42", *back.N)
@@ -536,3 +559,278 @@ func TestEncodeString(t *testing.T) {
}
}
}
+
+type jsonbyte byte
+
+func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) }
+
+type textbyte byte
+
+func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) }
+
+type jsonint int
+
+func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) }
+
+type textint int
+
+func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) }
+
+func tenc(format string, a ...interface{}) ([]byte, error) {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, format, a...)
+ return buf.Bytes(), nil
+}
+
+// Issue 13783
+func TestEncodeBytekind(t *testing.T) {
+ testdata := []struct {
+ data interface{}
+ want string
+ }{
+ {byte(7), "7"},
+ {jsonbyte(7), `{"JB":7}`},
+ {textbyte(4), `"TB:4"`},
+ {jsonint(5), `{"JI":5}`},
+ {textint(1), `"TI:1"`},
+ {[]byte{0, 1}, `"AAE="`},
+ {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`},
+ {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`},
+ {[]textbyte{2, 3}, `["TB:2","TB:3"]`},
+ {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`},
+ {[]textint{9, 3}, `["TI:9","TI:3"]`},
+ {[]int{9, 3}, `[9,3]`},
+ }
+ for _, d := range testdata {
+ js, err := Marshal(d.data)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ got, want := string(js), d.want
+ if got != want {
+ t.Errorf("got %s, want %s", got, want)
+ }
+ }
+}
+
+func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
+ b, err := Marshal(map[unmarshalerText]int{
+ {"x", "y"}: 1,
+ {"y", "x"}: 2,
+ {"a", "z"}: 3,
+ {"z", "a"}: 4,
+ })
+ if err != nil {
+ t.Fatalf("Failed to Marshal text.Marshaler: %v", err)
+ }
+ const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}`
+ if string(b) != want {
+ t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
+ }
+}
+
+var re = regexp.MustCompile
+
+// syntactic checks on form of marshaled floating point numbers.
+var badFloatREs = []*regexp.Regexp{
+ re(`p`), // no binary exponential notation
+ re(`^\+`), // no leading + sign
+ re(`^-?0[^.]`), // no unnecessary leading zeros
+ re(`^-?\.`), // leading zero required before decimal point
+ re(`\.(e|$)`), // no trailing decimal
+ re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction
+ re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa
+ re(`e[0-9]`), // positive exponent must be signed
+ re(`e[+-]0`), // exponent must not have leading zeros
+ re(`e-[1-6]$`), // not tiny enough for exponential notation
+ re(`e+(.|1.|20)$`), // not big enough for exponential notation
+ re(`^-?0\.0000000`), // too tiny, should use exponential notation
+ re(`^-?[0-9]{22}`), // too big, should use exponential notation
+ re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer
+ re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal
+ // below here for float32 only
+ re(`[1-9][0-9]{8}[1-9]`), // too many significant digits in integer
+ re(`[1-9][0-9.]{9}[1-9]`), // too many significant digits in decimal
+}
+
+func TestMarshalFloat(t *testing.T) {
+ t.Parallel()
+ nfail := 0
+ test := func(f float64, bits int) {
+ vf := interface{}(f)
+ if bits == 32 {
+ f = float64(float32(f)) // round
+ vf = float32(f)
+ }
+ bout, err := Marshal(vf)
+ if err != nil {
+ t.Errorf("Marshal(%T(%g)): %v", vf, vf, err)
+ nfail++
+ return
+ }
+ out := string(bout)
+
+ // result must convert back to the same float
+ g, err := strconv.ParseFloat(out, bits)
+ if err != nil {
+ t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err)
+ nfail++
+ return
+ }
+ if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0
+ t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf)
+ nfail++
+ return
+ }
+
+ bad := badFloatREs
+ if bits == 64 {
+ bad = bad[:len(bad)-2]
+ }
+ for _, re := range bad {
+ if re.MatchString(out) {
+ t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re)
+ nfail++
+ return
+ }
+ }
+ }
+
+ var (
+ bigger = math.Inf(+1)
+ smaller = math.Inf(-1)
+ )
+
+ var digits = "1.2345678901234567890123"
+ for i := len(digits); i >= 2; i-- {
+ for exp := -30; exp <= 30; exp++ {
+ for _, sign := range "+-" {
+ for bits := 32; bits <= 64; bits += 32 {
+ s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp)
+ f, err := strconv.ParseFloat(s, bits)
+ if err != nil {
+ log.Fatal(err)
+ }
+ next := math.Nextafter
+ if bits == 32 {
+ next = func(g, h float64) float64 {
+ return float64(math.Nextafter32(float32(g), float32(h)))
+ }
+ }
+ test(f, bits)
+ test(next(f, bigger), bits)
+ test(next(f, smaller), bits)
+ if nfail > 50 {
+ t.Fatalf("stopping test early")
+ }
+ }
+ }
+ }
+ }
+ test(0, 64)
+ test(math.Copysign(0, -1), 64)
+ test(0, 32)
+ test(math.Copysign(0, -1), 32)
+}
+
+func TestMarshalRawMessageValue(t *testing.T) {
+ type (
+ T1 struct {
+ M RawMessage `json:",omitempty"`
+ }
+ T2 struct {
+ M *RawMessage `json:",omitempty"`
+ }
+ )
+
+ var (
+ rawNil = RawMessage(nil)
+ rawEmpty = RawMessage([]byte{})
+ rawText = RawMessage([]byte(`"foo"`))
+ )
+
+ tests := []struct {
+ in interface{}
+ want string
+ ok bool
+ }{
+ // Test with nil RawMessage.
+ {rawNil, "null", true},
+ {&rawNil, "null", true},
+ {[]interface{}{rawNil}, "[null]", true},
+ {&[]interface{}{rawNil}, "[null]", true},
+ {[]interface{}{&rawNil}, "[null]", true},
+ {&[]interface{}{&rawNil}, "[null]", true},
+ {struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
+ {&struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
+ {struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
+ {&struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
+ {map[string]interface{}{"M": rawNil}, `{"M":null}`, true},
+ {&map[string]interface{}{"M": rawNil}, `{"M":null}`, true},
+ {map[string]interface{}{"M": &rawNil}, `{"M":null}`, true},
+ {&map[string]interface{}{"M": &rawNil}, `{"M":null}`, true},
+ {T1{rawNil}, "{}", true},
+ {T2{&rawNil}, `{"M":null}`, true},
+ {&T1{rawNil}, "{}", true},
+ {&T2{&rawNil}, `{"M":null}`, true},
+
+ // Test with empty, but non-nil, RawMessage.
+ {rawEmpty, "", false},
+ {&rawEmpty, "", false},
+ {[]interface{}{rawEmpty}, "", false},
+ {&[]interface{}{rawEmpty}, "", false},
+ {[]interface{}{&rawEmpty}, "", false},
+ {&[]interface{}{&rawEmpty}, "", false},
+ {struct{ X RawMessage }{rawEmpty}, "", false},
+ {&struct{ X RawMessage }{rawEmpty}, "", false},
+ {struct{ X *RawMessage }{&rawEmpty}, "", false},
+ {&struct{ X *RawMessage }{&rawEmpty}, "", false},
+ {map[string]interface{}{"nil": rawEmpty}, "", false},
+ {&map[string]interface{}{"nil": rawEmpty}, "", false},
+ {map[string]interface{}{"nil": &rawEmpty}, "", false},
+ {&map[string]interface{}{"nil": &rawEmpty}, "", false},
+ {T1{rawEmpty}, "{}", true},
+ {T2{&rawEmpty}, "", false},
+ {&T1{rawEmpty}, "{}", true},
+ {&T2{&rawEmpty}, "", false},
+
+ // Test with RawMessage with some text.
+ //
+ // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo".
+ // This behavior was intentionally changed in Go 1.8.
+ // See https://github.com/golang/go/issues/14493#issuecomment-255857318
+ {rawText, `"foo"`, true}, // Issue6458
+ {&rawText, `"foo"`, true},
+ {[]interface{}{rawText}, `["foo"]`, true}, // Issue6458
+ {&[]interface{}{rawText}, `["foo"]`, true}, // Issue6458
+ {[]interface{}{&rawText}, `["foo"]`, true},
+ {&[]interface{}{&rawText}, `["foo"]`, true},
+ {struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {&struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true},
+ {struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
+ {&struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
+ {map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {&map[string]interface{}{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true},
+ {&map[string]interface{}{"M": &rawText}, `{"M":"foo"}`, true},
+ {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458
+ {T2{&rawText}, `{"M":"foo"}`, true},
+ {&T1{rawText}, `{"M":"foo"}`, true},
+ {&T2{&rawText}, `{"M":"foo"}`, true},
+ }
+
+ for i, tt := range tests {
+ b, err := Marshal(tt.in)
+ if ok := (err == nil); ok != tt.ok {
+ if err != nil {
+ t.Errorf("test %d, unexpected failure: %v", i, err)
+ } else {
+ t.Errorf("test %d, unexpected success", i)
+ }
+ }
+ if got := string(b); got != tt.want {
+ t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want)
+ }
+ }
+}
diff --git a/libgo/go/encoding/json/example_marshaling_test.go b/libgo/go/encoding/json/example_marshaling_test.go
new file mode 100644
index 0000000000..1c4f783a69
--- /dev/null
+++ b/libgo/go/encoding/json/example_marshaling_test.go
@@ -0,0 +1,75 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package json_test
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "strings"
+)
+
+type Animal int
+
+const (
+ Unknown Animal = iota
+ Gopher
+ Zebra
+)
+
+func (a *Animal) UnmarshalJSON(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ switch strings.ToLower(s) {
+ default:
+ *a = Unknown
+ case "gopher":
+ *a = Gopher
+ case "zebra":
+ *a = Zebra
+ }
+
+ return nil
+}
+
+func (a Animal) MarshalJSON() ([]byte, error) {
+ var s string
+ switch a {
+ default:
+ s = "unknown"
+ case Gopher:
+ s = "gopher"
+ case Zebra:
+ s = "zebra"
+ }
+
+ return json.Marshal(s)
+}
+
+func Example_customMarshalJSON() {
+ blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]`
+ var zoo []Animal
+ if err := json.Unmarshal([]byte(blob), &zoo); err != nil {
+ log.Fatal(err)
+ }
+
+ census := make(map[Animal]int)
+ for _, animal := range zoo {
+ census[animal] += 1
+ }
+
+ fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras: %d\n* Unknown: %d\n",
+ census[Gopher], census[Zebra], census[Unknown])
+
+ // Output:
+ // Zoo Census:
+ // * Gophers: 3
+ // * Zebras: 2
+ // * Unknown: 3
+}
diff --git a/libgo/go/encoding/json/indent.go b/libgo/go/encoding/json/indent.go
index 7cd9f4db18..fba19548c9 100644
--- a/libgo/go/encoding/json/indent.go
+++ b/libgo/go/encoding/json/indent.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/libgo/go/encoding/json/number_test.go b/libgo/go/encoding/json/number_test.go
index 4e63cf9c74..4b86999638 100644
--- a/libgo/go/encoding/json/number_test.go
+++ b/libgo/go/encoding/json/number_test.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/libgo/go/encoding/json/scanner.go b/libgo/go/encoding/json/scanner.go
index ee6622e8cf..a6d8706c73 100644
--- a/libgo/go/encoding/json/scanner.go
+++ b/libgo/go/encoding/json/scanner.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -132,7 +132,7 @@ const (
// These values are stored in the parseState stack.
// They give the current state of a composite value
-// being scanned. If the parser is inside a nested value
+// being scanned. If the parser is inside a nested value
// the parseState describes the nested state, outermost at entry 0.
const (
parseObjectKey = iota // parsing object key (before colon)
diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go
index 66383ef0ef..c5c1be31f1 100644
--- a/libgo/go/encoding/json/scanner_test.go
+++ b/libgo/go/encoding/json/scanner_test.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -119,6 +119,7 @@ func TestCompactBig(t *testing.T) {
}
func TestIndentBig(t *testing.T) {
+ t.Parallel()
initBig()
var buf bytes.Buffer
if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
diff --git a/libgo/go/encoding/json/stream.go b/libgo/go/encoding/json/stream.go
index 8ddcf4d279..95e30ce36d 100644
--- a/libgo/go/encoding/json/stream.go
+++ b/libgo/go/encoding/json/stream.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -10,7 +10,7 @@ import (
"io"
)
-// A Decoder reads and decodes JSON objects from an input stream.
+// A Decoder reads and decodes JSON values from an input stream.
type Decoder struct {
r io.Reader
buf []byte
@@ -148,7 +148,7 @@ func (dec *Decoder) refill() error {
dec.buf = newBuf
}
- // Read. Delay error for next iteration (after scan).
+ // Read. Delay error for next iteration (after scan).
n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
dec.buf = dec.buf[0 : len(dec.buf)+n]
@@ -164,15 +164,20 @@ func nonSpace(b []byte) bool {
return false
}
-// An Encoder writes JSON objects to an output stream.
+// An Encoder writes JSON values to an output stream.
type Encoder struct {
- w io.Writer
- err error
+ w io.Writer
+ err error
+ escapeHTML bool
+
+ indentBuf *bytes.Buffer
+ indentPrefix string
+ indentValue string
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{w: w}
+ return &Encoder{w: w, escapeHTML: true}
}
// Encode writes the JSON encoding of v to the stream,
@@ -185,7 +190,7 @@ func (enc *Encoder) Encode(v interface{}) error {
return enc.err
}
e := newEncodeState()
- err := e.marshal(v)
+ err := e.marshal(v, encOpts{escapeHTML: enc.escapeHTML})
if err != nil {
return err
}
@@ -198,21 +203,55 @@ func (enc *Encoder) Encode(v interface{}) error {
// digits coming.
e.WriteByte('\n')
- if _, err = enc.w.Write(e.Bytes()); err != nil {
+ b := e.Bytes()
+ if enc.indentPrefix != "" || enc.indentValue != "" {
+ if enc.indentBuf == nil {
+ enc.indentBuf = new(bytes.Buffer)
+ }
+ enc.indentBuf.Reset()
+ err = Indent(enc.indentBuf, b, enc.indentPrefix, enc.indentValue)
+ if err != nil {
+ return err
+ }
+ b = enc.indentBuf.Bytes()
+ }
+ if _, err = enc.w.Write(b); err != nil {
enc.err = err
}
encodeStatePool.Put(e)
return err
}
-// RawMessage is a raw encoded JSON object.
+// SetIndent instructs the encoder to format each subsequent encoded
+// value as if indented by the package-level function Indent(dst, src, prefix, indent).
+// Calling SetIndent("", "") disables indentation.
+func (enc *Encoder) SetIndent(prefix, indent string) {
+ enc.indentPrefix = prefix
+ enc.indentValue = indent
+}
+
+// SetEscapeHTML specifies whether problematic HTML characters
+// should be escaped inside JSON quoted strings.
+// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
+// to avoid certain safety problems that can arise when embedding JSON in HTML.
+//
+// In non-HTML settings where the escaping interferes with the readability
+// of the output, SetEscapeHTML(false) disables this behavior.
+func (enc *Encoder) SetEscapeHTML(on bool) {
+ enc.escapeHTML = on
+}
+
+// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte
-// MarshalJSON returns *m as the JSON encoding of m.
-func (m *RawMessage) MarshalJSON() ([]byte, error) {
- return *m, nil
+// MarshalJSON returns m as the JSON encoding of m.
+func (m RawMessage) MarshalJSON() ([]byte, error) {
+ if m == nil {
+ return []byte("null"), nil
+ }
+ return m, nil
}
// UnmarshalJSON sets *m to a copy of data.
diff --git a/libgo/go/encoding/json/stream_test.go b/libgo/go/encoding/json/stream_test.go
index c2e30408cd..84edeb187c 100644
--- a/libgo/go/encoding/json/stream_test.go
+++ b/libgo/go/encoding/json/stream_test.go
@@ -1,4 +1,4 @@
-// Copyright 2010 The Go Authors. All rights reserved.
+// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -44,6 +44,9 @@ func TestEncoder(t *testing.T) {
for i := 0; i <= len(streamTest); i++ {
var buf bytes.Buffer
enc := NewEncoder(&buf)
+ // Check that enc.SetIndent("", "") turns off indentation.
+ enc.SetIndent(">", ".")
+ enc.SetIndent("", "")
for j, v := range streamTest[0:i] {
if err := enc.Encode(v); err != nil {
t.Fatalf("encode #%d: %v", j, err)
@@ -57,6 +60,69 @@ func TestEncoder(t *testing.T) {
}
}
+var streamEncodedIndent = `0.1
+"hello"
+null
+true
+false
+[
+>."a",
+>."b",
+>."c"
+>]
+{
+>."ß": "long s",
+>."K": "Kelvin"
+>}
+3.14
+`
+
+func TestEncoderIndent(t *testing.T) {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ enc.SetIndent(">", ".")
+ for _, v := range streamTest {
+ enc.Encode(v)
+ }
+ if have, want := buf.String(), streamEncodedIndent; have != want {
+ t.Error("indented encoding mismatch")
+ diff(t, []byte(have), []byte(want))
+ }
+}
+
+func TestEncoderSetEscapeHTML(t *testing.T) {
+ var c C
+ var ct CText
+ for _, tt := range []struct {
+ name string
+ v interface{}
+ wantEscape string
+ want string
+ }{
+ {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`},
+ {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`},
+ {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`},
+ } {
+ var buf bytes.Buffer
+ enc := NewEncoder(&buf)
+ if err := enc.Encode(tt.v); err != nil {
+ t.Fatalf("Encode(%s): %s", tt.name, err)
+ }
+ if got := strings.TrimSpace(buf.String()); got != tt.wantEscape {
+ t.Errorf("Encode(%s) = %#q, want %#q", tt.name, got, tt.wantEscape)
+ }
+ buf.Reset()
+ enc.SetEscapeHTML(false)
+ if err := enc.Encode(tt.v); err != nil {
+ t.Fatalf("SetEscapeHTML(false) Encode(%s): %s", tt.name, err)
+ }
+ if got := strings.TrimSpace(buf.String()); got != tt.want {
+ t.Errorf("SetEscapeHTML(false) Encode(%s) = %#q, want %#q",
+ tt.name, got, tt.want)
+ }
+ }
+}
+
func TestDecoder(t *testing.T) {
for i := 0; i <= len(streamTest); i++ {
// Use stream without newlines as input,
diff --git a/libgo/go/encoding/json/tables.go b/libgo/go/encoding/json/tables.go
new file mode 100644
index 0000000000..10acdc18c6
--- /dev/null
+++ b/libgo/go/encoding/json/tables.go
@@ -0,0 +1,218 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import "unicode/utf8"
+
+// safeSet holds the value true if the ASCII character with the given array
+// position can be represented inside a JSON string without any further
+// escaping.
+//
+// All values are true except for the ASCII control characters (0-31), the
+// double quote ("), and the backslash character ("\").
+var safeSet = [utf8.RuneSelf]bool{
+ ' ': true,
+ '!': true,
+ '"': false,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': true,
+ '\'': true,
+ '(': true,
+ ')': true,
+ '*': true,
+ '+': true,
+ ',': true,
+ '-': true,
+ '.': true,
+ '/': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ ':': true,
+ ';': true,
+ '<': true,
+ '=': true,
+ '>': true,
+ '?': true,
+ '@': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'V': true,
+ 'W': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '[': true,
+ '\\': false,
+ ']': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '{': true,
+ '|': true,
+ '}': true,
+ '~': true,
+ '\u007f': true,
+}
+
+// htmlSafeSet holds the value true if the ASCII character with the given
+// array position can be safely represented inside a JSON string, embedded
+// inside of HTML <script> tags, without any additional escaping.
+//
+// All values are true except for the ASCII control characters (0-31), the
+// double quote ("), the backslash character ("\"), HTML opening and closing
+// tags ("<" and ">"), and the ampersand ("&").
+var htmlSafeSet = [utf8.RuneSelf]bool{
+ ' ': true,
+ '!': true,
+ '"': false,
+ '#': true,
+ '$': true,
+ '%': true,
+ '&': false,
+ '\'': true,
+ '(': true,
+ ')': true,
+ '*': true,
+ '+': true,
+ ',': true,
+ '-': true,
+ '.': true,
+ '/': true,
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ ':': true,
+ ';': true,
+ '<': false,
+ '=': true,
+ '>': false,
+ '?': true,
+ '@': true,
+ 'A': true,
+ 'B': true,
+ 'C': true,
+ 'D': true,
+ 'E': true,
+ 'F': true,
+ 'G': true,
+ 'H': true,
+ 'I': true,
+ 'J': true,
+ 'K': true,
+ 'L': true,
+ 'M': true,
+ 'N': true,
+ 'O': true,
+ 'P': true,
+ 'Q': true,
+ 'R': true,
+ 'S': true,
+ 'T': true,
+ 'U': true,
+ 'V': true,
+ 'W': true,
+ 'X': true,
+ 'Y': true,
+ 'Z': true,
+ '[': true,
+ '\\': false,
+ ']': true,
+ '^': true,
+ '_': true,
+ '`': true,
+ 'a': true,
+ 'b': true,
+ 'c': true,
+ 'd': true,
+ 'e': true,
+ 'f': true,
+ 'g': true,
+ 'h': true,
+ 'i': true,
+ 'j': true,
+ 'k': true,
+ 'l': true,
+ 'm': true,
+ 'n': true,
+ 'o': true,
+ 'p': true,
+ 'q': true,
+ 'r': true,
+ 's': true,
+ 't': true,
+ 'u': true,
+ 'v': true,
+ 'w': true,
+ 'x': true,
+ 'y': true,
+ 'z': true,
+ '{': true,
+ '|': true,
+ '}': true,
+ '~': true,
+ '\u007f': true,
+}
diff --git a/libgo/go/encoding/json/tagkey_test.go b/libgo/go/encoding/json/tagkey_test.go
index 85bb4ba837..f77c49c764 100644
--- a/libgo/go/encoding/json/tagkey_test.go
+++ b/libgo/go/encoding/json/tagkey_test.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -44,6 +44,10 @@ type punctuationTag struct {
V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546
}
+type dashTag struct {
+ V string `json:"-,"`
+}
+
type emptyTag struct {
W string
}
@@ -80,6 +84,7 @@ var structTagObjectKeyTests = []struct {
{basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"},
{basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"},
{miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"},
+ {dashTag{"foo"}, "foo", "-"},
{emptyTag{"Pour Moi"}, "Pour Moi", "W"},
{misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"},
{badFormatTag{"Orfevre"}, "Orfevre", "Y"},
diff --git a/libgo/go/encoding/json/tags_test.go b/libgo/go/encoding/json/tags_test.go
index 91fb18831e..8ba8ddd5f8 100644
--- a/libgo/go/encoding/json/tags_test.go
+++ b/libgo/go/encoding/json/tags_test.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
diff --git a/libgo/go/encoding/json/testdata/code.json.gz b/libgo/go/encoding/json/testdata/code.json.gz
index 0e2895b53a..1572a92bfb 100644
--- a/libgo/go/encoding/json/testdata/code.json.gz
+++ b/libgo/go/encoding/json/testdata/code.json.gz
Binary files differ