diff options
Diffstat (limited to 'src/pkg/encoding/json')
-rw-r--r-- | src/pkg/encoding/json/Makefile | 16 | ||||
-rw-r--r-- | src/pkg/encoding/json/decode.go | 14 | ||||
-rw-r--r-- | src/pkg/encoding/json/decode_test.go | 50 | ||||
-rw-r--r-- | src/pkg/encoding/json/encode.go | 19 | ||||
-rw-r--r-- | src/pkg/encoding/json/encode_test.go | 41 | ||||
-rw-r--r-- | src/pkg/encoding/json/example_test.go | 39 | ||||
-rw-r--r-- | src/pkg/encoding/json/scanner.go | 19 | ||||
-rw-r--r-- | src/pkg/encoding/json/stream.go | 3 |
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} } |