summaryrefslogtreecommitdiff
path: root/src/pkg/encoding/json
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/encoding/json')
-rw-r--r--src/pkg/encoding/json/Makefile16
-rw-r--r--src/pkg/encoding/json/decode.go14
-rw-r--r--src/pkg/encoding/json/decode_test.go50
-rw-r--r--src/pkg/encoding/json/encode.go19
-rw-r--r--src/pkg/encoding/json/encode_test.go41
-rw-r--r--src/pkg/encoding/json/example_test.go39
-rw-r--r--src/pkg/encoding/json/scanner.go19
-rw-r--r--src/pkg/encoding/json/stream.go3
8 files changed, 167 insertions, 34 deletions
diff --git a/src/pkg/encoding/json/Makefile b/src/pkg/encoding/json/Makefile
deleted file mode 100644
index 37223e75e..000000000
--- a/src/pkg/encoding/json/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2009 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.
-
-include ../../../Make.inc
-
-TARG=encoding/json
-GOFILES=\
- decode.go\
- encode.go\
- indent.go\
- scanner.go\
- stream.go\
- tags.go\
-
-include ../../../Make.pkg
diff --git a/src/pkg/encoding/json/decode.go b/src/pkg/encoding/json/decode.go
index 87076b53d..110c6fd62 100644
--- a/src/pkg/encoding/json/decode.go
+++ b/src/pkg/encoding/json/decode.go
@@ -496,6 +496,12 @@ func (d *decodeState) object(v reflect.Value) {
// Pretend this field doesn't exist.
continue
}
+ if sf.Anonymous {
+ // Pretend this field doesn't exist,
+ // so that we can do a good job with
+ // these in a later version.
+ continue
+ }
// First, tag match
tagName, _ := parseTag(tag)
if tagName == key {
@@ -963,3 +969,11 @@ func unquoteBytes(s []byte) (t []byte, ok bool) {
}
return b[0:w], true
}
+
+// The following is issue 3069.
+
+// BUG(rsc): This package ignores anonymous (embedded) struct fields
+// during encoding and decoding. A future version may assign meaning
+// to them. To force an anonymous field to be ignored in all future
+// versions of this package, use an explicit `json:"-"` tag in the struct
+// definition.
diff --git a/src/pkg/encoding/json/decode_test.go b/src/pkg/encoding/json/decode_test.go
index cc3103f03..0eec586a9 100644
--- a/src/pkg/encoding/json/decode_test.go
+++ b/src/pkg/encoding/json/decode_test.go
@@ -598,3 +598,53 @@ var pallValueIndent = `{
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
+
+func TestRefUnmarshal(t *testing.T) {
+ type S struct {
+ // Ref is defined in encode_test.go.
+ R0 Ref
+ R1 *Ref
+ }
+ want := S{
+ R0: 12,
+ R1: new(Ref),
+ }
+ *want.R1 = 12
+
+ var got S
+ if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("got %+v, want %+v", got, want)
+ }
+}
+
+// Test that anonymous fields are ignored.
+// We may assign meaning to them later.
+func TestAnonymous(t *testing.T) {
+ type S struct {
+ T
+ N int
+ }
+
+ data, err := Marshal(new(S))
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ want := `{"N":0}`
+ if string(data) != want {
+ t.Fatalf("Marshal = %#q, want %#q", string(data), want)
+ }
+
+ var s S
+ if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ if s.N != 2 {
+ t.Fatal("Unmarshal: did not set N")
+ }
+ if s.T.Y != 0 {
+ t.Fatal("Unmarshal: did set T.Y")
+ }
+}
diff --git a/src/pkg/encoding/json/encode.go b/src/pkg/encoding/json/encode.go
index eac14a47e..8a794b79b 100644
--- a/src/pkg/encoding/json/encode.go
+++ b/src/pkg/encoding/json/encode.go
@@ -262,8 +262,18 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
return
}
- if j, ok := v.Interface().(Marshaler); ok && (v.Kind() != reflect.Ptr || !v.IsNil()) {
- b, err := j.MarshalJSON()
+ 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()
+ }
+ }
+ }
+ 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)
@@ -528,6 +538,11 @@ func encodeFields(t reflect.Type) []encodeField {
if f.PkgPath != "" {
continue
}
+ if f.Anonymous {
+ // We want to do a better job with these later,
+ // so for now pretend they don't exist.
+ continue
+ }
var ef encodeField
ef.i = i
ef.tag = f.Name
diff --git a/src/pkg/encoding/json/encode_test.go b/src/pkg/encoding/json/encode_test.go
index 0e39559a4..7a726a91c 100644
--- a/src/pkg/encoding/json/encode_test.go
+++ b/src/pkg/encoding/json/encode_test.go
@@ -126,3 +126,44 @@ func TestUnsupportedValues(t *testing.T) {
}
}
}
+
+// Ref has Marshaler and Unmarshaler methods with pointer receiver.
+type Ref int
+
+func (*Ref) MarshalJSON() ([]byte, error) {
+ return []byte(`"ref"`), nil
+}
+
+func (r *Ref) UnmarshalJSON([]byte) error {
+ *r = 12
+ return nil
+}
+
+// Val has Marshaler methods with value receiver.
+type Val int
+
+func (Val) MarshalJSON() ([]byte, error) {
+ return []byte(`"val"`), nil
+}
+
+func TestRefValMarshal(t *testing.T) {
+ var s = struct {
+ R0 Ref
+ R1 *Ref
+ V0 Val
+ V1 *Val
+ }{
+ R0: 12,
+ R1: new(Ref),
+ V0: 13,
+ V1: new(Val),
+ }
+ const want = `{"R0":"ref","R1":"ref","V0":"val","V1":"val"}`
+ b, err := Marshal(&s)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if got := string(b); got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
diff --git a/src/pkg/encoding/json/example_test.go b/src/pkg/encoding/json/example_test.go
index 7f4a78c31..b8d150eda 100644
--- a/src/pkg/encoding/json/example_test.go
+++ b/src/pkg/encoding/json/example_test.go
@@ -7,10 +7,12 @@ package json_test
import (
"encoding/json"
"fmt"
+ "io"
+ "log"
"os"
+ "strings"
)
-// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
func ExampleMarshal() {
type ColorGroup struct {
ID int
@@ -27,9 +29,10 @@ func ExampleMarshal() {
fmt.Println("error:", err)
}
os.Stdout.Write(b)
+ // Output:
+ // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
}
-// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
func ExampleUnmarshal() {
var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
@@ -45,4 +48,36 @@ func ExampleUnmarshal() {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
+ // Output:
+ // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
+}
+
+// This example uses a Decoder to decode a stream of distinct JSON values.
+func ExampleDecoder() {
+ const jsonStream = `
+ {"Name": "Ed", "Text": "Knock knock."}
+ {"Name": "Sam", "Text": "Who's there?"}
+ {"Name": "Ed", "Text": "Go fmt."}
+ {"Name": "Sam", "Text": "Go fmt who?"}
+ {"Name": "Ed", "Text": "Go fmt yourself!"}
+ `
+ type Message struct {
+ Name, Text string
+ }
+ dec := json.NewDecoder(strings.NewReader(jsonStream))
+ for {
+ var m Message
+ if err := dec.Decode(&m); err == io.EOF {
+ break
+ } else if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("%s: %s\n", m.Name, m.Text)
+ }
+ // Output:
+ // Ed: Knock knock.
+ // Sam: Who's there?
+ // Ed: Go fmt.
+ // Sam: Go fmt who?
+ // Ed: Go fmt yourself!
}
diff --git a/src/pkg/encoding/json/scanner.go b/src/pkg/encoding/json/scanner.go
index 2661f410e..054b6b3d5 100644
--- a/src/pkg/encoding/json/scanner.go
+++ b/src/pkg/encoding/json/scanner.go
@@ -185,18 +185,9 @@ func isSpace(c rune) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
-// NOTE(rsc): The various instances of
-//
-// if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n')
-//
-// below should all be if c <= ' ' && isSpace(c), but inlining
-// the checks makes a significant difference (>10%) in tight loops
-// such as nextValue. These should be rewritten with the clearer
-// function call once 6g knows to inline the call.
-
// stateBeginValueOrEmpty is the state after reading `[`.
func stateBeginValueOrEmpty(s *scanner, c int) int {
- if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
if c == ']' {
@@ -207,7 +198,7 @@ func stateBeginValueOrEmpty(s *scanner, c int) int {
// stateBeginValue is the state at the beginning of the input.
func stateBeginValue(s *scanner, c int) int {
- if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
switch c {
@@ -247,7 +238,7 @@ func stateBeginValue(s *scanner, c int) int {
// stateBeginStringOrEmpty is the state after reading `{`.
func stateBeginStringOrEmpty(s *scanner, c int) int {
- if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
if c == '}' {
@@ -260,7 +251,7 @@ func stateBeginStringOrEmpty(s *scanner, c int) int {
// stateBeginString is the state after reading `{"key": value,`.
func stateBeginString(s *scanner, c int) int {
- if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
if c == '"' {
@@ -280,7 +271,7 @@ func stateEndValue(s *scanner, c int) int {
s.endTop = true
return stateEndTop(s, c)
}
- if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ if c <= ' ' && isSpace(rune(c)) {
s.step = stateEndValue
return scanSkipSpace
}
diff --git a/src/pkg/encoding/json/stream.go b/src/pkg/encoding/json/stream.go
index f24763950..7d1cc5f11 100644
--- a/src/pkg/encoding/json/stream.go
+++ b/src/pkg/encoding/json/stream.go
@@ -19,6 +19,9 @@ type Decoder struct {
}
// NewDecoder returns a new decoder that reads from r.
+//
+// The decoder introduces its own buffering and may
+// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}