summaryrefslogtreecommitdiff
path: root/src/pkg/encoding/json
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2013-12-03 09:43:15 +0100
committerMichael Stapelberg <stapelberg@debian.org>2013-12-03 09:43:15 +0100
commit64d2a7c8945ba05af859901f5e248f1befdd8621 (patch)
tree013fcb7e9e3296ecdda876012252c36bd6bcb063 /src/pkg/encoding/json
parentb901efe83e212f0c34c769c079e41373da12d723 (diff)
downloadgolang-64d2a7c8945ba05af859901f5e248f1befdd8621.tar.gz
Imported Upstream version 1.2upstream/1.2
Diffstat (limited to 'src/pkg/encoding/json')
-rw-r--r--src/pkg/encoding/json/decode.go70
-rw-r--r--src/pkg/encoding/json/decode_test.go167
-rw-r--r--src/pkg/encoding/json/encode.go714
-rw-r--r--src/pkg/encoding/json/encode_test.go123
-rw-r--r--src/pkg/encoding/json/example_test.go46
-rw-r--r--src/pkg/encoding/json/indent.go9
-rw-r--r--src/pkg/encoding/json/scanner.go2
-rw-r--r--src/pkg/encoding/json/scanner_test.go19
-rw-r--r--src/pkg/encoding/json/stream.go11
-rw-r--r--src/pkg/encoding/json/stream_test.go13
-rw-r--r--src/pkg/encoding/json/tags.go2
11 files changed, 969 insertions, 207 deletions
diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go
index 62ac294b8..458fb39ec 100644
--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -8,6 +8,7 @@
package json
import (
+ "encoding"
"encoding/base64"
"errors"
"fmt"
@@ -37,9 +38,7 @@ import (
// keys to the keys used by Marshal (either the struct field name or its tag),
// preferring an exact match but also accepting a case-insensitive match.
//
-// To unmarshal JSON into an interface value, Unmarshal unmarshals
-// the JSON into the concrete value contained in the interface value.
-// If the interface value is nil, that is, has no concrete value stored in it,
+// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
// bool, for JSON booleans
@@ -293,7 +292,7 @@ func (d *decodeState) value(v reflect.Value) {
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
-func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) {
+func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
// we find them.
@@ -322,28 +321,38 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 {
- if unmarshaler, ok := v.Interface().(Unmarshaler); ok {
- return unmarshaler, reflect.Value{}
+ 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{}
}
}
v = v.Elem()
}
- return nil, v
+ return nil, nil, v
}
// array consumes an array from d.data[d.off-1:], decoding into the value v.
// the first byte of the array ('[') has been read already.
func (d *decodeState) array(v reflect.Value) {
// Check for unmarshaler.
- unmarshaler, pv := d.indirect(v, false)
- if unmarshaler != nil {
+ u, ut, pv := d.indirect(v, false)
+ if u != nil {
d.off--
- err := unmarshaler.UnmarshalJSON(d.next())
+ err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
+ if ut != nil {
+ d.saveError(&UnmarshalTypeError{"array", v.Type()})
+ d.off--
+ d.next()
+ return
+ }
+
v = pv
// Check type of target.
@@ -434,15 +443,21 @@ func (d *decodeState) array(v reflect.Value) {
// the first byte of the object ('{') has been read already.
func (d *decodeState) object(v reflect.Value) {
// Check for unmarshaler.
- unmarshaler, pv := d.indirect(v, false)
- if unmarshaler != nil {
+ u, ut, pv := d.indirect(v, false)
+ if u != nil {
d.off--
- err := unmarshaler.UnmarshalJSON(d.next())
+ err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
+ if ut != nil {
+ d.saveError(&UnmarshalTypeError{"object", v.Type()})
+ d.off--
+ d.next() // skip over { } in input
+ return
+ }
v = pv
// Decoding into nil interface? Switch to non-reflect code.
@@ -611,14 +626,37 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
return
}
wantptr := item[0] == 'n' // null
- unmarshaler, pv := d.indirect(v, wantptr)
- if unmarshaler != nil {
- err := unmarshaler.UnmarshalJSON(item)
+ u, ut, pv := d.indirect(v, wantptr)
+ if u != nil {
+ err := u.UnmarshalJSON(item)
+ if err != nil {
+ d.error(err)
+ }
+ return
+ }
+ if ut != nil {
+ if item[0] != '"' {
+ 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()})
+ }
+ }
+ s, ok := unquoteBytes(item)
+ if !ok {
+ 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(errPhase)
+ }
+ }
+ err := ut.UnmarshalText(s)
if err != nil {
d.error(err)
}
return
}
+
v = pv
switch c := item[0]; c {
diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go
index f845f69ab..22c5f89f7 100644
--- a/src/pkg/encoding/json/decode_test.go
+++ b/src/pkg/encoding/json/decode_test.go
@@ -6,6 +6,7 @@ package json
import (
"bytes"
+ "encoding"
"fmt"
"image"
"reflect"
@@ -50,8 +51,6 @@ type tx struct {
x int
}
-var txType = reflect.TypeOf((*tx)(nil)).Elem()
-
// A type that can unmarshal itself.
type unmarshaler struct {
@@ -59,7 +58,7 @@ type unmarshaler struct {
}
func (u *unmarshaler) UnmarshalJSON(b []byte) error {
- *u = unmarshaler{true} // All we need to see that UnmarshalJson is called.
+ *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called.
return nil
}
@@ -67,6 +66,26 @@ type ustruct struct {
M unmarshaler
}
+type unmarshalerText struct {
+ T bool
+}
+
+// needed for re-marshaling tests
+func (u *unmarshalerText) MarshalText() ([]byte, error) {
+ return []byte(""), nil
+}
+
+func (u *unmarshalerText) UnmarshalText(b []byte) error {
+ *u = unmarshalerText{true} // All we need to see that UnmarshalText is called.
+ return nil
+}
+
+var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)
+
+type ustructText struct {
+ M unmarshalerText
+}
+
var (
um0, um1 unmarshaler // target2 of unmarshaling
ump = &um1
@@ -74,6 +93,13 @@ var (
umslice = []unmarshaler{{true}}
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}}
)
// Test data structures for anonymous fields.
@@ -184,6 +210,12 @@ type Ambig struct {
Second int `json:"Hello"`
}
+type XYZ struct {
+ X interface{}
+ Y interface{}
+ Z interface{}
+}
+
var unmarshalTests = []unmarshalTest{
// basic types
{in: `true`, ptr: new(bool), out: true},
@@ -263,6 +295,13 @@ var unmarshalTests = []unmarshalTest{
{in: `[{"T":false}]`, ptr: &umslicep, out: &umslice},
{in: `{"M":{"T":false}}`, 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: `{
"Level0": 1,
@@ -391,17 +430,23 @@ func TestMarshal(t *testing.T) {
}
}
+var badUTF8 = []struct {
+ in, out string
+}{
+ {"hello\xffworld", `"hello\ufffdworld"`},
+ {"", `""`},
+ {"\xff", `"\ufffd"`},
+ {"\xff\xff", `"\ufffd\ufffd"`},
+ {"a\xffb", `"a\ufffdb"`},
+ {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
+}
+
func TestMarshalBadUTF8(t *testing.T) {
- s := "hello\xffworld"
- b, err := Marshal(s)
- if err == nil {
- t.Fatal("Marshal bad UTF8: no error")
- }
- if len(b) != 0 {
- t.Fatal("Marshal returned data")
- }
- if _, ok := err.(*InvalidUTF8Error); !ok {
- t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err)
+ for _, tt := range badUTF8 {
+ b, err := Marshal(tt.in)
+ if string(b) != tt.out || err != nil {
+ t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out)
+ }
}
}
@@ -417,6 +462,45 @@ func TestMarshalNumberZeroVal(t *testing.T) {
}
}
+func TestMarshalEmbeds(t *testing.T) {
+ top := &Top{
+ Level0: 1,
+ Embed0: Embed0{
+ Level1b: 2,
+ Level1c: 3,
+ },
+ Embed0a: &Embed0a{
+ Level1a: 5,
+ Level1b: 6,
+ },
+ Embed0b: &Embed0b{
+ Level1a: 8,
+ Level1b: 9,
+ Level1c: 10,
+ Level1d: 11,
+ Level1e: 12,
+ },
+ Loop: Loop{
+ Loop1: 13,
+ Loop2: 14,
+ },
+ Embed0p: Embed0p{
+ Point: image.Point{X: 15, Y: 16},
+ },
+ Embed0q: Embed0q{
+ Point: Point{Z: 17},
+ },
+ }
+ b, err := Marshal(top)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17}"
+ if string(b) != want {
+ t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
+ }
+}
+
func TestUnmarshal(t *testing.T) {
for i, tt := range unmarshalTests {
var scan scanner
@@ -432,7 +516,7 @@ func TestUnmarshal(t *testing.T) {
}
// v = new(right-type)
v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- dec := NewDecoder(bytes.NewBuffer(in))
+ dec := NewDecoder(bytes.NewReader(in))
if tt.useNumber {
dec.UseNumber()
}
@@ -457,16 +541,18 @@ func TestUnmarshal(t *testing.T) {
continue
}
vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- dec = NewDecoder(bytes.NewBuffer(enc))
+ dec = NewDecoder(bytes.NewReader(enc))
if tt.useNumber {
dec.UseNumber()
}
if err := dec.Decode(vv.Interface()); err != nil {
- t.Errorf("#%d: error re-unmarshaling: %v", i, err)
+ t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
continue
}
if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
+ t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
+ t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
continue
}
}
@@ -568,14 +654,14 @@ func TestUnmarshalPtrPtr(t *testing.T) {
}
func TestEscape(t *testing.T) {
- const input = `"foobar"<html>`
- const expected = `"\"foobar\"\u003chtml\u003e"`
+ const input = `"foobar"<html>` + " [\u2028 \u2029]"
+ const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
b, err := Marshal(input)
if err != nil {
t.Fatalf("Marshal error: %v", err)
}
if s := string(b); s != expected {
- t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected)
+ t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected)
}
}
@@ -934,15 +1020,20 @@ func TestRefUnmarshal(t *testing.T) {
// Ref is defined in encode_test.go.
R0 Ref
R1 *Ref
+ R2 RefText
+ R3 *RefText
}
want := S{
R0: 12,
R1: new(Ref),
+ R2: 13,
+ R3: new(RefText),
}
*want.R1 = 12
+ *want.R3 = 13
var got S
- if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil {
+ if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !reflect.DeepEqual(got, want) {
@@ -1064,7 +1155,6 @@ func TestUnmarshalNulls(t *testing.T) {
func TestStringKind(t *testing.T) {
type stringKind string
- type aMap map[stringKind]int
var m1, m2 map[stringKind]int
m1 = map[stringKind]int{
@@ -1191,3 +1281,38 @@ func TestSkipArrayObjects(t *testing.T) {
t.Errorf("got error %q, want nil", err)
}
}
+
+// Test semantics of pre-filled struct fields and pre-filled map fields.
+// Issue 4900.
+func TestPrefilled(t *testing.T) {
+ ptrToMap := func(m map[string]interface{}) *map[string]interface{} { return &m }
+
+ // Values here change, cannot reuse table across runs.
+ var prefillTests = []struct {
+ in string
+ ptr interface{}
+ out interface{}
+ }{
+ {
+ in: `{"X": 1, "Y": 2}`,
+ ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
+ out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
+ },
+ {
+ in: `{"X": 1, "Y": 2}`,
+ ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}),
+ out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}),
+ },
+ }
+
+ for _, tt := range prefillTests {
+ ptrstr := fmt.Sprintf("%v", tt.ptr)
+ err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
+ if err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+ if !reflect.DeepEqual(tt.ptr, tt.out) {
+ t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out)
+ }
+ }
+}
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index 85727ba61..7d6c71d7a 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -12,6 +12,7 @@ package json
import (
"bytes"
+ "encoding"
"encoding/base64"
"math"
"reflect"
@@ -149,14 +150,14 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return buf.Bytes(), nil
}
-// HTMLEscape appends to dst the JSON-encoded src with <, >, and &
-// characters inside string literals changed to \u003c, \u003e, \u0026
+// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
+// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
// so that the JSON will be safe to embed inside HTML <script> tags.
// For historical reasons, web browsers don't honor standard HTML
// escaping within <script> tags, so an alternative JSON encoding must
// be used.
func HTMLEscape(dst *bytes.Buffer, src []byte) {
- // < > & can only appear in string literals,
+ // The characters can only appear in string literals,
// so just scan the string one byte at a time.
start := 0
for i, c := range src {
@@ -169,6 +170,15 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
dst.WriteByte(hex[c&0xF])
start = i + 1
}
+ // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
+ if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u202`)
+ dst.WriteByte(hex[src[i+2]&0xF])
+ start = i + 3
+ }
}
if start < len(src) {
dst.Write(src[start:])
@@ -200,8 +210,12 @@ func (e *UnsupportedValueError) Error() string {
return "json: unsupported value: " + e.Str
}
-// An InvalidUTF8Error is returned by Marshal when attempting
-// to encode a string value with invalid UTF-8 sequences.
+// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when
+// attempting to encode a string value with invalid UTF-8 sequences.
+// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by
+// replacing invalid bytes with the Unicode replacement rune U+FFFD.
+// This error is no longer generated but is kept for backwards compatibility
+// with programs that might mention it.
type InvalidUTF8Error struct {
S string // the whole string value that caused the error
}
@@ -227,12 +241,35 @@ type encodeState struct {
scratch [64]byte
}
+// TODO(bradfitz): use a sync.Cache here
+var encodeStatePool = make(chan *encodeState, 8)
+
+func newEncodeState() *encodeState {
+ select {
+ case e := <-encodeStatePool:
+ e.Reset()
+ return e
+ default:
+ return new(encodeState)
+ }
+}
+
+func putEncodeState(e *encodeState) {
+ select {
+ case encodeStatePool <- e:
+ default:
+ }
+}
+
func (e *encodeState) marshal(v interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
+ if s, ok := r.(string); ok {
+ panic(s)
+ }
err = r.(error)
}
}()
@@ -265,186 +302,438 @@ func isEmptyValue(v reflect.Value) bool {
}
func (e *encodeState) reflectValue(v reflect.Value) {
- e.reflectValueQuoted(v, false)
+ valueEncoder(v)(e, v, false)
+}
+
+type encoderFunc func(e *encodeState, v reflect.Value, quoted bool)
+
+var encoderCache struct {
+ sync.RWMutex
+ m map[reflect.Type]encoderFunc
}
-// reflectValueQuoted writes the value in v to the output.
-// If quoted is true, the serialization is wrapped in a JSON string.
-func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
+func valueEncoder(v reflect.Value) encoderFunc {
if !v.IsValid() {
- e.WriteString("null")
- return
+ return invalidValueEncoder
}
+ return typeEncoder(v.Type())
+}
- m, ok := v.Interface().(Marshaler)
- if !ok {
- // T doesn't match the interface. Check against *T too.
- if v.Kind() != reflect.Ptr && v.CanAddr() {
- m, ok = v.Addr().Interface().(Marshaler)
- if ok {
- v = v.Addr()
- }
- }
+func typeEncoder(t reflect.Type) encoderFunc {
+ encoderCache.RLock()
+ f := encoderCache.m[t]
+ encoderCache.RUnlock()
+ if f != nil {
+ return f
+ }
+
+ // 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
+ // func is only used for recursive types.
+ encoderCache.Lock()
+ if encoderCache.m == nil {
+ encoderCache.m = make(map[reflect.Type]encoderFunc)
+ }
+ var wg sync.WaitGroup
+ wg.Add(1)
+ encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) {
+ wg.Wait()
+ f(e, v, quoted)
+ }
+ encoderCache.Unlock()
+
+ // Compute fields without lock.
+ // Might duplicate effort but won't hold other computations back.
+ f = newTypeEncoder(t, true)
+ wg.Done()
+ encoderCache.Lock()
+ encoderCache.m[t] = f
+ encoderCache.Unlock()
+ return f
+}
+
+var (
+ marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
+ textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
+)
+
+// newTypeEncoder constructs an encoderFunc for a type.
+// The returned encoder only checks CanAddr when allowAddr is true.
+func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
+ if t.Implements(marshalerType) {
+ return marshalerEncoder
}
- if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
- b, err := m.MarshalJSON()
- if err == nil {
- // copy JSON into buffer, checking validity.
- err = compact(&e.Buffer, b, true)
+ if t.Kind() != reflect.Ptr && allowAddr {
+ if reflect.PtrTo(t).Implements(marshalerType) {
+ return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
}
- if err != nil {
- e.error(&MarshalerError{v.Type(), err})
+ }
+
+ if t.Implements(textMarshalerType) {
+ return textMarshalerEncoder
+ }
+ if t.Kind() != reflect.Ptr && allowAddr {
+ if reflect.PtrTo(t).Implements(textMarshalerType) {
+ return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
}
+ }
+
+ switch t.Kind() {
+ case reflect.Bool:
+ return boolEncoder
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return intEncoder
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return uintEncoder
+ case reflect.Float32:
+ return float32Encoder
+ case reflect.Float64:
+ return float64Encoder
+ case reflect.String:
+ return stringEncoder
+ case reflect.Interface:
+ return interfaceEncoder
+ case reflect.Struct:
+ return newStructEncoder(t)
+ case reflect.Map:
+ return newMapEncoder(t)
+ case reflect.Slice:
+ return newSliceEncoder(t)
+ case reflect.Array:
+ return newArrayEncoder(t)
+ case reflect.Ptr:
+ return newPtrEncoder(t)
+ default:
+ return unsupportedTypeEncoder
+ }
+}
+
+func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ e.WriteString("null")
+}
+
+func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ m := v.Interface().(Marshaler)
+ b, err := m.MarshalJSON()
+ if err == nil {
+ // copy JSON into buffer, checking validity.
+ err = compact(&e.Buffer, b, true)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
+
+func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ va := v.Addr()
+ if va.IsNil() {
+ e.WriteString("null")
return
}
+ m := va.Interface().(Marshaler)
+ b, err := m.MarshalJSON()
+ if err == nil {
+ // copy JSON into buffer, checking validity.
+ err = compact(&e.Buffer, b, true)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
- writeString := (*encodeState).WriteString
+func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.Kind() == reflect.Ptr && v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ m := v.Interface().(encoding.TextMarshaler)
+ b, err := m.MarshalText()
+ if err == nil {
+ _, err = e.stringBytes(b)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
+
+func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ va := v.Addr()
+ if va.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ m := va.Interface().(encoding.TextMarshaler)
+ b, err := m.MarshalText()
+ if err == nil {
+ _, err = e.stringBytes(b)
+ }
+ if err != nil {
+ e.error(&MarshalerError{v.Type(), err})
+ }
+}
+
+func boolEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if quoted {
+ e.WriteByte('"')
+ }
+ if v.Bool() {
+ e.WriteString("true")
+ } else {
+ e.WriteString("false")
+ }
+ if quoted {
+ e.WriteByte('"')
+ }
+}
+
+func intEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
+ if quoted {
+ e.WriteByte('"')
+ }
+ e.Write(b)
if quoted {
- writeString = (*encodeState).string
+ e.WriteByte('"')
}
+}
- switch v.Kind() {
- case reflect.Bool:
- x := v.Bool()
- if x {
- writeString(e, "true")
- } else {
- writeString(e, "false")
- }
+func uintEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
+ if quoted {
+ e.WriteByte('"')
+ }
+ e.Write(b)
+ if quoted {
+ e.WriteByte('"')
+ }
+}
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
- if quoted {
- writeString(e, string(b))
- } else {
- e.Write(b)
+type floatEncoder int // number of bits
+
+func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+ f := v.Float()
+ if math.IsInf(f, 0) || math.IsNaN(f) {
+ e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
+ }
+ b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
+ if quoted {
+ e.WriteByte('"')
+ }
+ e.Write(b)
+ if quoted {
+ e.WriteByte('"')
+ }
+}
+
+var (
+ float32Encoder = (floatEncoder(32)).encode
+ float64Encoder = (floatEncoder(64)).encode
+)
+
+func stringEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.Type() == numberType {
+ numStr := v.String()
+ if numStr == "" {
+ numStr = "0" // Number's zero-val
}
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
- if quoted {
- writeString(e, string(b))
- } else {
- e.Write(b)
+ e.WriteString(numStr)
+ return
+ }
+ if quoted {
+ sb, err := Marshal(v.String())
+ if err != nil {
+ e.error(err)
}
- case reflect.Float32, reflect.Float64:
- f := v.Float()
- if math.IsInf(f, 0) || math.IsNaN(f) {
- e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, v.Type().Bits())})
+ e.string(string(sb))
+ } else {
+ e.string(v.String())
+ }
+}
+
+func interfaceEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ e.reflectValue(v.Elem())
+}
+
+func unsupportedTypeEncoder(e *encodeState, v reflect.Value, quoted bool) {
+ e.error(&UnsupportedTypeError{v.Type()})
+}
+
+type structEncoder struct {
+ fields []field
+ fieldEncs []encoderFunc
+}
+
+func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+ e.WriteByte('{')
+ first := true
+ for i, f := range se.fields {
+ fv := fieldByIndex(v, f.index)
+ if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
+ continue
}
- b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, v.Type().Bits())
- if quoted {
- writeString(e, string(b))
+ if first {
+ first = false
} else {
- e.Write(b)
- }
- case reflect.String:
- if v.Type() == numberType {
- numStr := v.String()
- if numStr == "" {
- numStr = "0" // Number's zero-val
- }
- e.WriteString(numStr)
- break
- }
- if quoted {
- sb, err := Marshal(v.String())
- if err != nil {
- e.error(err)
- }
- e.string(string(sb))
- } else {
- e.string(v.String())
+ e.WriteByte(',')
}
+ e.string(f.name)
+ e.WriteByte(':')
+ se.fieldEncs[i](e, fv, f.quoted)
+ }
+ e.WriteByte('}')
+}
- case reflect.Struct:
- e.WriteByte('{')
- first := true
- for _, f := range cachedTypeFields(v.Type()) {
- fv := fieldByIndex(v, f.index)
- if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
- continue
- }
- if first {
- first = false
- } else {
- e.WriteByte(',')
- }
- e.string(f.name)
- e.WriteByte(':')
- e.reflectValueQuoted(fv, f.quoted)
- }
- e.WriteByte('}')
+func newStructEncoder(t reflect.Type) encoderFunc {
+ fields := cachedTypeFields(t)
+ se := &structEncoder{
+ fields: fields,
+ fieldEncs: make([]encoderFunc, len(fields)),
+ }
+ for i, f := range fields {
+ se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
+ }
+ return se.encode
+}
- case reflect.Map:
- if v.Type().Key().Kind() != reflect.String {
- e.error(&UnsupportedTypeError{v.Type()})
- }
- if v.IsNil() {
- e.WriteString("null")
- break
- }
- e.WriteByte('{')
- var sv stringValues = v.MapKeys()
- sort.Sort(sv)
- for i, k := range sv {
- if i > 0 {
- e.WriteByte(',')
- }
- e.string(k.String())
- e.WriteByte(':')
- e.reflectValue(v.MapIndex(k))
- }
- e.WriteByte('}')
+type mapEncoder struct {
+ elemEnc encoderFunc
+}
- case reflect.Slice:
- if v.IsNil() {
- e.WriteString("null")
- break
- }
- if v.Type().Elem().Kind() == reflect.Uint8 {
- // Byte slices get special treatment; arrays don't.
- s := v.Bytes()
- e.WriteByte('"')
- if len(s) < 1024 {
- // for small buffers, using Encode directly is much faster.
- dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
- base64.StdEncoding.Encode(dst, s)
- e.Write(dst)
- } else {
- // for large buffers, avoid unnecessary extra temporary
- // buffer space.
- enc := base64.NewEncoder(base64.StdEncoding, e)
- enc.Write(s)
- enc.Close()
- }
- e.WriteByte('"')
- break
- }
- // Slices can be marshalled as nil, but otherwise are handled
- // as arrays.
- fallthrough
- case reflect.Array:
- e.WriteByte('[')
- n := v.Len()
- for i := 0; i < n; i++ {
- if i > 0 {
- e.WriteByte(',')
- }
- e.reflectValue(v.Index(i))
+func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ e.WriteByte('{')
+ var sv stringValues = v.MapKeys()
+ sort.Sort(sv)
+ for i, k := range sv {
+ if i > 0 {
+ e.WriteByte(',')
}
- e.WriteByte(']')
+ e.string(k.String())
+ e.WriteByte(':')
+ me.elemEnc(e, v.MapIndex(k), false)
+ }
+ e.WriteByte('}')
+}
- case reflect.Interface, reflect.Ptr:
- if v.IsNil() {
- e.WriteString("null")
- return
+func newMapEncoder(t reflect.Type) encoderFunc {
+ if t.Key().Kind() != reflect.String {
+ return unsupportedTypeEncoder
+ }
+ me := &mapEncoder{typeEncoder(t.Elem())}
+ return me.encode
+}
+
+func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ s := v.Bytes()
+ e.WriteByte('"')
+ if len(s) < 1024 {
+ // for small buffers, using Encode directly is much faster.
+ dst := make([]byte, base64.StdEncoding.EncodedLen(len(s)))
+ base64.StdEncoding.Encode(dst, s)
+ e.Write(dst)
+ } else {
+ // for large buffers, avoid unnecessary extra temporary
+ // buffer space.
+ enc := base64.NewEncoder(base64.StdEncoding, e)
+ enc.Write(s)
+ enc.Close()
+ }
+ e.WriteByte('"')
+}
+
+// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.
+type sliceEncoder struct {
+ arrayEnc encoderFunc
+}
+
+func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ se.arrayEnc(e, v, false)
+}
+
+func newSliceEncoder(t reflect.Type) encoderFunc {
+ // Byte slices get special treatment; arrays don't.
+ if t.Elem().Kind() == reflect.Uint8 {
+ return encodeByteSlice
+ }
+ enc := &sliceEncoder{newArrayEncoder(t)}
+ return enc.encode
+}
+
+type arrayEncoder struct {
+ elemEnc encoderFunc
+}
+
+func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ e.WriteByte('[')
+ n := v.Len()
+ for i := 0; i < n; i++ {
+ if i > 0 {
+ e.WriteByte(',')
}
- e.reflectValue(v.Elem())
+ ae.elemEnc(e, v.Index(i), false)
+ }
+ e.WriteByte(']')
+}
- default:
- e.error(&UnsupportedTypeError{v.Type()})
+func newArrayEncoder(t reflect.Type) encoderFunc {
+ enc := &arrayEncoder{typeEncoder(t.Elem())}
+ return enc.encode
+}
+
+type ptrEncoder struct {
+ elemEnc encoderFunc
+}
+
+func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, _ bool) {
+ if v.IsNil() {
+ e.WriteString("null")
+ return
+ }
+ pe.elemEnc(e, v.Elem(), false)
+}
+
+func newPtrEncoder(t reflect.Type) encoderFunc {
+ enc := &ptrEncoder{typeEncoder(t.Elem())}
+ return enc.encode
+}
+
+type condAddrEncoder struct {
+ canAddrEnc, elseEnc encoderFunc
+}
+
+func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
+ if v.CanAddr() {
+ ce.canAddrEnc(e, v, quoted)
+ } else {
+ ce.elseEnc(e, v, quoted)
}
- return
+}
+
+// newCondAddrEncoder returns an encoder that checks whether its value
+// CanAddr and delegates to canAddrEnc if so, else to elseEnc.
+func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc {
+ enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}
+ return enc.encode
}
func isValidTag(s string) bool {
@@ -479,6 +768,16 @@ func fieldByIndex(v reflect.Value, index []int) reflect.Value {
return v
}
+func typeByIndex(t reflect.Type, index []int) reflect.Type {
+ for _, i := range index {
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ t = t.Field(i).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
@@ -488,13 +787,14 @@ 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() }
+// NOTE: keep in sync with stringBytes below.
func (e *encodeState) string(s string) (int, error) {
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 != '>' {
+ if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
i++
continue
}
@@ -526,7 +826,30 @@ func (e *encodeState) string(s string) (int, error) {
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
- e.error(&InvalidUTF8Error{s})
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ e.WriteString(`\ufffd`)
+ i += size
+ start = i
+ continue
+ }
+ // U+2028 is LINE SEPARATOR.
+ // U+2029 is PARAGRAPH SEPARATOR.
+ // They are both technically valid characters in JSON strings,
+ // but don't work in JSONP, which has to be evaluated as JavaScript,
+ // and can lead to security holes there. It is valid JSON to
+ // escape them, so we do so unconditionally.
+ // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
+ if c == '\u2028' || c == '\u2029' {
+ if start < i {
+ e.WriteString(s[start:i])
+ }
+ e.WriteString(`\u202`)
+ e.WriteByte(hex[c&0xF])
+ i += size
+ start = i
+ continue
}
i += size
}
@@ -537,6 +860,79 @@ func (e *encodeState) string(s string) (int, error) {
return e.Len() - len0, nil
}
+// NOTE: keep in sync with string above.
+func (e *encodeState) stringBytes(s []byte) (int, error) {
+ 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 != '&' {
+ i++
+ continue
+ }
+ if start < i {
+ e.Write(s[start:i])
+ }
+ switch b {
+ case '\\', '"':
+ e.WriteByte('\\')
+ e.WriteByte(b)
+ case '\n':
+ e.WriteByte('\\')
+ e.WriteByte('n')
+ case '\r':
+ e.WriteByte('\\')
+ e.WriteByte('r')
+ default:
+ // This encodes bytes < 0x20 except for \n and \r,
+ // as well as < and >. The latter are escaped because they
+ // can lead to security holes when user-controlled strings
+ // are rendered into JSON and served to some browsers.
+ e.WriteString(`\u00`)
+ e.WriteByte(hex[b>>4])
+ e.WriteByte(hex[b&0xF])
+ }
+ i++
+ start = i
+ continue
+ }
+ c, size := utf8.DecodeRune(s[i:])
+ if c == utf8.RuneError && size == 1 {
+ if start < i {
+ e.Write(s[start:i])
+ }
+ e.WriteString(`\ufffd`)
+ i += size
+ start = i
+ continue
+ }
+ // U+2028 is LINE SEPARATOR.
+ // U+2029 is PARAGRAPH SEPARATOR.
+ // They are both technically valid characters in JSON strings,
+ // but don't work in JSONP, which has to be evaluated as JavaScript,
+ // and can lead to security holes there. It is valid JSON to
+ // escape them, so we do so unconditionally.
+ // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
+ if c == '\u2028' || c == '\u2029' {
+ if start < i {
+ e.Write(s[start:i])
+ }
+ e.WriteString(`\u202`)
+ e.WriteByte(hex[c&0xF])
+ i += size
+ start = i
+ continue
+ }
+ i += size
+ }
+ if start < len(s) {
+ e.Write(s[start:])
+ }
+ e.WriteByte('"')
+ return e.Len() - len0, nil
+}
+
// A field represents a single field found in a struct.
type field struct {
name string
diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go
index 5be0a992e..9395db7cb 100644
--- a/src/pkg/encoding/json/encode_test.go
+++ b/src/pkg/encoding/json/encode_test.go
@@ -9,6 +9,7 @@ import (
"math"
"reflect"
"testing"
+ "unicode"
)
type Optionals struct {
@@ -146,19 +147,46 @@ func (Val) MarshalJSON() ([]byte, error) {
return []byte(`"val"`), nil
}
+// RefText has Marshaler and Unmarshaler methods with pointer receiver.
+type RefText int
+
+func (*RefText) MarshalText() ([]byte, error) {
+ return []byte(`"ref"`), nil
+}
+
+func (r *RefText) UnmarshalText([]byte) error {
+ *r = 13
+ return nil
+}
+
+// ValText has Marshaler methods with value receiver.
+type ValText int
+
+func (ValText) MarshalText() ([]byte, error) {
+ return []byte(`"val"`), nil
+}
+
func TestRefValMarshal(t *testing.T) {
var s = struct {
R0 Ref
R1 *Ref
+ R2 RefText
+ R3 *RefText
V0 Val
V1 *Val
+ V2 ValText
+ V3 *ValText
}{
R0: 12,
R1: new(Ref),
+ R2: 14,
+ R3: new(RefText),
V0: 13,
V1: new(Val),
+ V2: 15,
+ V3: new(ValText),
}
- const want = `{"R0":"ref","R1":"ref","V0":"val","V1":"val"}`
+ const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}`
b, err := Marshal(&s)
if err != nil {
t.Fatalf("Marshal: %v", err)
@@ -175,15 +203,32 @@ func (C) MarshalJSON() ([]byte, error) {
return []byte(`"<&>"`), nil
}
+// CText implements Marshaler and returns unescaped text.
+type CText int
+
+func (CText) MarshalText() ([]byte, error) {
+ return []byte(`"<&>"`), nil
+}
+
func TestMarshalerEscaping(t *testing.T) {
var c C
- const want = `"\u003c\u0026\u003e"`
+ want := `"\u003c\u0026\u003e"`
b, err := Marshal(c)
if err != nil {
- t.Fatalf("Marshal: %v", err)
+ t.Fatalf("Marshal(c): %v", err)
}
if got := string(b); got != want {
- t.Errorf("got %q, want %q", got, want)
+ t.Errorf("Marshal(c) = %#q, want %#q", got, want)
+ }
+
+ var ct CText
+ want = `"\"\u003c\u0026\u003e\""`
+ b, err = Marshal(ct)
+ if err != nil {
+ t.Fatalf("Marshal(ct): %v", err)
+ }
+ if got := string(b); got != want {
+ t.Errorf("Marshal(ct) = %#q, want %#q", got, want)
}
}
@@ -310,3 +355,73 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
t.Fatalf("Marshal: got %s want %s", got, want)
}
}
+
+func TestStringBytes(t *testing.T) {
+ // 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
+ _, err := es.string(s)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ esBytes := &encodeState{}
+ _, err = esBytes.stringBytes([]byte(s))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ 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"`)}
+
+ 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)
+ }
+
+ b, err = Marshal(x)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if want := `{"M":"ImZvbyI="}`; string(b) != want {
+ t.Errorf("Marshal(x) = %#q; want %#q", b, want)
+ }
+}
diff --git a/src/pkg/encoding/json/example_test.go b/src/pkg/encoding/json/example_test.go
index b8d150eda..ea0bc149c 100644
--- a/src/pkg/encoding/json/example_test.go
+++ b/src/pkg/encoding/json/example_test.go
@@ -81,3 +81,49 @@ func ExampleDecoder() {
// Sam: Go fmt who?
// Ed: Go fmt yourself!
}
+
+// This example uses RawMessage to delay parsing part of a JSON message.
+func ExampleRawMessage() {
+ type Color struct {
+ Space string
+ Point json.RawMessage // delay parsing until we know the color space
+ }
+ type RGB struct {
+ R uint8
+ G uint8
+ B uint8
+ }
+ type YCbCr struct {
+ Y uint8
+ Cb int8
+ Cr int8
+ }
+
+ var j = []byte(`[
+ {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}},
+ {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}}
+ ]`)
+ var colors []Color
+ err := json.Unmarshal(j, &colors)
+ if err != nil {
+ log.Fatalln("error:", err)
+ }
+
+ for _, c := range colors {
+ var dst interface{}
+ switch c.Space {
+ case "RGB":
+ dst = new(RGB)
+ case "YCbCr":
+ dst = new(YCbCr)
+ }
+ err := json.Unmarshal(c.Point, dst)
+ if err != nil {
+ log.Fatalln("error:", err)
+ }
+ fmt.Println(c.Space, dst)
+ }
+ // Output:
+ // YCbCr &{255 0 -10}
+ // RGB &{98 218 255}
+}
diff --git a/src/pkg/encoding/json/indent.go b/src/pkg/encoding/json/indent.go
index e8dfa4ec4..11ef709cc 100644
--- a/src/pkg/encoding/json/indent.go
+++ b/src/pkg/encoding/json/indent.go
@@ -27,6 +27,15 @@ func compact(dst *bytes.Buffer, src []byte, escape bool) error {
dst.WriteByte(hex[c&0xF])
start = i + 1
}
+ // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
+ if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
+ if start < i {
+ dst.Write(src[start:i])
+ }
+ dst.WriteString(`\u202`)
+ dst.WriteByte(hex[src[i+2]&0xF])
+ start = i + 3
+ }
v := scan.step(&scan, int(c))
if v >= scanSkipSpace {
if v == scanError {
diff --git a/src/pkg/encoding/json/scanner.go b/src/pkg/encoding/json/scanner.go
index 054b6b3d5..a4609c895 100644
--- a/src/pkg/encoding/json/scanner.go
+++ b/src/pkg/encoding/json/scanner.go
@@ -390,7 +390,7 @@ func stateInStringEscU123(s *scanner, c int) int {
return s.error(c, "in \\u hexadecimal character escape")
}
-// stateInStringEscU123 is the state after reading `-` during a number.
+// stateNeg is the state after reading `-` during a number.
func stateNeg(s *scanner, c int) int {
if c == '0' {
s.step = state0
diff --git a/src/pkg/encoding/json/scanner_test.go b/src/pkg/encoding/json/scanner_test.go
index 77d3455d3..90e45ff03 100644
--- a/src/pkg/encoding/json/scanner_test.go
+++ b/src/pkg/encoding/json/scanner_test.go
@@ -63,6 +63,25 @@ func TestCompact(t *testing.T) {
}
}
+func TestCompactSeparators(t *testing.T) {
+ // U+2028 and U+2029 should be escaped inside strings.
+ // They should not appear outside strings.
+ tests := []struct {
+ in, compact string
+ }{
+ {"{\"\u2028\": 1}", `{"\u2028":1}`},
+ {"{\"\u2029\" :2}", `{"\u2029":2}`},
+ }
+ for _, tt := range tests {
+ var buf bytes.Buffer
+ if err := Compact(&buf, []byte(tt.in)); err != nil {
+ t.Errorf("Compact(%q): %v", tt.in, err)
+ } else if s := buf.String(); s != tt.compact {
+ t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
+ }
+ }
+}
+
func TestIndent(t *testing.T) {
var buf bytes.Buffer
for _, tt := range examples {
diff --git a/src/pkg/encoding/json/stream.go b/src/pkg/encoding/json/stream.go
index 00f4726cf..1928abadb 100644
--- a/src/pkg/encoding/json/stream.go
+++ b/src/pkg/encoding/json/stream.go
@@ -148,7 +148,7 @@ func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
-// Encode writes the JSON encoding of v to the connection.
+// Encode writes the JSON encoding of v to the stream.
//
// See the documentation for Marshal for details about the
// conversion of Go values to JSON.
@@ -156,8 +156,8 @@ func (enc *Encoder) Encode(v interface{}) error {
if enc.err != nil {
return enc.err
}
- enc.e.Reset()
- err := enc.e.marshal(v)
+ e := newEncodeState()
+ err := e.marshal(v)
if err != nil {
return err
}
@@ -168,11 +168,12 @@ func (enc *Encoder) Encode(v interface{}) error {
// is required if the encoded value was a number,
// so that the reader knows there aren't more
// digits coming.
- enc.e.WriteByte('\n')
+ e.WriteByte('\n')
- if _, err = enc.w.Write(enc.e.Bytes()); err != nil {
+ if _, err = enc.w.Write(e.Bytes()); err != nil {
enc.err = err
}
+ putEncodeState(e)
return err
}
diff --git a/src/pkg/encoding/json/stream_test.go b/src/pkg/encoding/json/stream_test.go
index 07c9e1d39..b562e8769 100644
--- a/src/pkg/encoding/json/stream_test.go
+++ b/src/pkg/encoding/json/stream_test.go
@@ -191,3 +191,16 @@ func TestBlocking(t *testing.T) {
w.Close()
}
}
+
+func BenchmarkEncoderEncode(b *testing.B) {
+ b.ReportAllocs()
+ type T struct {
+ X, Y string
+ }
+ v := &T{"foo", "bar"}
+ for i := 0; i < b.N; i++ {
+ if err := NewEncoder(ioutil.Discard).Encode(v); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/src/pkg/encoding/json/tags.go b/src/pkg/encoding/json/tags.go
index 58cda2027..c38fd5102 100644
--- a/src/pkg/encoding/json/tags.go
+++ b/src/pkg/encoding/json/tags.go
@@ -21,7 +21,7 @@ func parseTag(tag string) (string, tagOptions) {
return tag, tagOptions("")
}
-// Contains returns whether checks that a comma-separated list of options
+// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {