Commit dba9d62b authored by Russ Cox's avatar Russ Cox

json: Marshal, Unmarshal using new scanner

R=r
CC=golang-dev
https://golang.org/cl/953041
parent 214a55b0
...@@ -225,16 +225,13 @@ func expvarHandler(c *http.Conn, req *http.Request) { ...@@ -225,16 +225,13 @@ func expvarHandler(c *http.Conn, req *http.Request) {
} }
func memstats() string { func memstats() string {
var buf bytes.Buffer b, _ := json.MarshalIndent(&runtime.MemStats, "", "\t")
json.MarshalIndent(&buf, &runtime.MemStats, " ") return string(b)
s := buf.String()
return s[0 : len(s)-1] // chop final \n
} }
func cmdline() string { func cmdline() string {
var buf bytes.Buffer b, _ := json.Marshal(os.Args)
json.Marshal(&buf, os.Args) return string(b)
return buf.String()
} }
func init() { func init() {
......
...@@ -61,7 +61,8 @@ func TestMapCounter(t *testing.T) { ...@@ -61,7 +61,8 @@ func TestMapCounter(t *testing.T) {
// colours.String() should be '{"red":3, "blue":4}', // colours.String() should be '{"red":3, "blue":4}',
// though the order of red and blue could vary. // though the order of red and blue could vary.
s := colours.String() s := colours.String()
j, err := json.Decode(s) var j interface{}
err := json.Unmarshal([]byte(s), &j)
if err != nil { if err != nil {
t.Errorf("colours.String() isn't valid JSON: %v", err) t.Errorf("colours.String() isn't valid JSON: %v", err)
} }
......
...@@ -7,10 +7,10 @@ include ../../Make.$(GOARCH) ...@@ -7,10 +7,10 @@ include ../../Make.$(GOARCH)
TARG=json TARG=json
GOFILES=\ GOFILES=\
decode.go\ decode.go\
encode.go\
error.go\ error.go\
indent.go\ indent.go\
parse.go\ parse.go\
scanner.go\ scanner.go\
struct.go\
include ../../Make.pkg include ../../Make.pkg
This diff is collapsed.
This diff is collapsed.
// 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
import (
"os"
"bytes"
"reflect"
"runtime"
"sort"
"strconv"
"strings"
)
// Marshal returns the JSON encoding of v.
//
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface,
// Marshal calls its MarshalJSON method to produce JSON.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as JSON booleans.
//
// Floating point and integer values encode as JSON numbers.
//
// String values encode as JSON strings, with each invalid UTF-8 sequence
// replaced by the encoding of the Unicode replacement character U+FFFD.
//
// Array and slice values encode as JSON arrays.
//
// Struct values encode as JSON objects. Each struct field becomes
// a member of the object. By default the object's key name is the
// struct field name converted to lower case. If the struct field
// has a tag, that tag will be used as the name instead.
//
// Map values encode as JSON objects.
// The map's key type must be string; the object keys are used directly
// as map keys.
//
// Pointer values encode as the value pointed at.
// A nil pointer encodes as the null JSON object.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the null JSON object.
//
// Channel, complex, and function values cannot be encoded in JSON.
// Attempting to encode such a value causes Marshal to return
// an InvalidTypeError.
//
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(v interface{}) ([]byte, os.Error) {
e := &encodeState{}
err := e.marshal(v)
if err != nil {
return nil, err
}
return e.Bytes(), nil
}
// MarshalIndent is like Marshal but applies Indent to format the output.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, os.Error) {
b, err := Marshal(v)
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = Indent(&buf, b, prefix, indent)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Marshaler is the interface implemented by objects that
// can marshal themselves into valid JSON.
type Marshaler interface {
MarshalJSON() ([]byte, os.Error)
}
type UnsupportedTypeError struct {
Type reflect.Type
}
func (e *UnsupportedTypeError) String() string {
return "json: unsupported type: " + e.Type.String()
}
type MarshalerError struct {
Type reflect.Type
Error os.Error
}
func (e *MarshalerError) String() string {
return "json: error calling MarshalJSON for type " + e.Type.String() + ": " + e.Error.String()
}
type interfaceOrPtrValue interface {
IsNil() bool
Elem() reflect.Value
}
var hex = "0123456789abcdef"
// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
bytes.Buffer // accumulated output
}
func (e *encodeState) marshal(v interface{}) (err os.Error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
err = r.(os.Error)
}
}()
e.reflectValue(reflect.NewValue(v))
return nil
}
func (e *encodeState) error(err os.Error) {
panic(err)
}
func (e *encodeState) reflectValue(v reflect.Value) {
if v == nil {
e.WriteString("null")
return
}
if j, ok := v.Interface().(Marshaler); ok {
b, err := j.MarshalJSON()
if err == nil {
// copy JSON into buffer, checking validity.
err = Compact(&e.Buffer, b)
}
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}
return
}
switch v := v.(type) {
case *reflect.BoolValue:
x := v.Get()
if x {
e.WriteString("true")
} else {
e.WriteString("false")
}
case *reflect.IntValue:
e.WriteString(strconv.Itoa(v.Get()))
case *reflect.Int8Value:
e.WriteString(strconv.Itoa(int(v.Get())))
case *reflect.Int16Value:
e.WriteString(strconv.Itoa(int(v.Get())))
case *reflect.Int32Value:
e.WriteString(strconv.Itoa(int(v.Get())))
case *reflect.Int64Value:
e.WriteString(strconv.Itoa64(v.Get()))
case *reflect.UintValue:
e.WriteString(strconv.Uitoa(v.Get()))
case *reflect.Uint8Value:
e.WriteString(strconv.Uitoa(uint(v.Get())))
case *reflect.Uint16Value:
e.WriteString(strconv.Uitoa(uint(v.Get())))
case *reflect.Uint32Value:
e.WriteString(strconv.Uitoa(uint(v.Get())))
case *reflect.Uint64Value:
e.WriteString(strconv.Uitoa64(v.Get()))
case *reflect.UintptrValue:
e.WriteString(strconv.Uitoa64(uint64(v.Get())))
case *reflect.FloatValue:
e.WriteString(strconv.Ftoa(v.Get(), 'g', -1))
case *reflect.Float32Value:
e.WriteString(strconv.Ftoa32(v.Get(), 'g', -1))
case *reflect.Float64Value:
e.WriteString(strconv.Ftoa64(v.Get(), 'g', -1))
case *reflect.StringValue:
e.string(v.Get())
case *reflect.StructValue:
e.WriteByte('{')
t := v.Type().(*reflect.StructType)
n := v.NumField()
for i := 0; i < n; i++ {
if i > 0 {
e.WriteByte(',')
}
f := t.Field(i)
if f.Tag != "" {
e.string(f.Tag)
} else {
e.string(strings.ToLower(f.Name))
}
e.WriteByte(':')
e.reflectValue(v.Field(i))
}
e.WriteByte('}')
case *reflect.MapValue:
if _, ok := v.Type().(*reflect.MapType).Key().(*reflect.StringType); !ok {
e.error(&UnsupportedTypeError{v.Type()})
}
if v.IsNil() {
e.WriteString("null")
break
}
e.WriteByte('{')
var sv stringValues = v.Keys()
sort.Sort(sv)
for i, k := range sv {
if i > 0 {
e.WriteByte(',')
}
e.string(k.(*reflect.StringValue).Get())
e.WriteByte(':')
e.reflectValue(v.Elem(k))
}
e.WriteByte('}')
case reflect.ArrayOrSliceValue:
e.WriteByte('[')
n := v.Len()
for i := 0; i < n; i++ {
if i > 0 {
e.WriteByte(',')
}
e.reflectValue(v.Elem(i))
}
e.WriteByte(']')
case interfaceOrPtrValue:
if v.IsNil() {
e.WriteString("null")
return
}
e.reflectValue(v.Elem())
default:
e.error(&UnsupportedTypeError{v.Type()})
}
return
}
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
// It implements the methods to sort by string.
type stringValues []reflect.Value
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].(*reflect.StringValue).Get() }
func (e *encodeState) string(s string) {
e.WriteByte('"')
for _, c := range s {
switch {
case c < 0x20:
e.WriteString(`\u00`)
e.WriteByte(hex[c>>4])
e.WriteByte(hex[c&0xF])
case c == '\\' || c == '"':
e.WriteByte('\\')
fallthrough
default:
e.WriteRune(c)
}
}
e.WriteByte('"')
}
...@@ -181,6 +181,10 @@ func (s *scanner) popParseState() { ...@@ -181,6 +181,10 @@ func (s *scanner) popParseState() {
} }
} }
func isSpace(c int) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
// NOTE(rsc): The various instances of // NOTE(rsc): The various instances of
// //
// if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') // if c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n')
...@@ -590,7 +594,7 @@ func stateError(s *scanner, c int) int { ...@@ -590,7 +594,7 @@ func stateError(s *scanner, c int) int {
// error records an error and switches to the error state. // error records an error and switches to the error state.
func (s *scanner) error(c int, context string) int { func (s *scanner) error(c int, context string) int {
s.step = stateError s.step = stateError
s.err = SyntaxError("invalid character '" + quoteChar(c) + "' " + context) s.err = SyntaxError("invalid character " + quoteChar(c) + " " + context)
return scanError return scanError
} }
......
...@@ -175,6 +175,7 @@ func diff(t *testing.T, a, b []byte) { ...@@ -175,6 +175,7 @@ func diff(t *testing.T, a, b []byte) {
j = 0 j = 0
} }
t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:])) t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
return
} }
} }
} }
...@@ -191,9 +192,11 @@ func trim(b []byte) []byte { ...@@ -191,9 +192,11 @@ func trim(b []byte) []byte {
var jsonBig []byte var jsonBig []byte
func init() { func init() {
var buf bytes.Buffer b, err := Marshal(genValue(10000))
Marshal(&buf, genValue(100000)) if err != nil {
jsonBig = buf.Bytes() panic(err)
}
jsonBig = b
} }
func genValue(n int) interface{} { func genValue(n int) interface{} {
......
This diff is collapsed.
// 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.
package json
import (
"bytes"
"reflect"
"strconv"
"testing"
)
type myStruct struct {
T bool
F bool
S string
I8 int8
I16 int16
I32 int32
I64 int64
U8 uint8
U16 uint16
U32 uint32
U64 uint64
I int
U uint
Fl float
Fl32 float32
Fl64 float64
A []string
My *myStruct
Map map[string][]int
MapStruct map[string]myStruct
MapPtrStruct map[string]*myStruct
}
const encoded = `{"t":true,"f":false,"s":"abc","i8":1,"i16":2,"i32":3,"i64":4,` +
` "u8":5,"u16":6,"u32":7,"u64":8,` +
` "i":-9,"u":10,"bogusfield":"should be ignored",` +
` "fl":11.5,"fl32":12.25,"fl64":13.75,` +
` "a":["x","y","z"],"my":{"s":"subguy"},` +
`"map":{"k1":[1,2,3],"k2":[],"k3":[3,4]},` +
`"mapstruct":{"m1":{"u8":8}},` +
`"mapptrstruct":{"m1":{"u8":8}}}`
var decodedMap = map[string][]int{
"k1": []int{1, 2, 3},
"k2": []int{},
"k3": []int{3, 4},
}
var decodedMapStruct = map[string]myStruct{
"m1": myStruct{U8: 8},
}
var decodedMapPtrStruct = map[string]*myStruct{
"m1": &myStruct{U8: 8},
}
func check(t *testing.T, ok bool, name string, v interface{}) {
if !ok {
t.Errorf("%s = %v (BAD)", name, v)
} else {
t.Logf("%s = %v (good)", name, v)
}
}
const whiteSpaceEncoded = " \t{\n\"s\"\r:\"string\"\v}"
func TestUnmarshalWhitespace(t *testing.T) {
var m myStruct
ok, errtok := Unmarshal(whiteSpaceEncoded, &m)
if !ok {
t.Fatalf("Unmarshal failed near %s", errtok)
}
check(t, m.S == "string", "string", m.S)
}
func TestUnmarshal(t *testing.T) {
var m myStruct
m.F = true
ok, errtok := Unmarshal(encoded, &m)
if !ok {
t.Fatalf("Unmarshal failed near %s", errtok)
}
check(t, m.T == true, "t", m.T)
check(t, m.F == false, "f", m.F)
check(t, m.S == "abc", "s", m.S)
check(t, m.I8 == 1, "i8", m.I8)
check(t, m.I16 == 2, "i16", m.I16)
check(t, m.I32 == 3, "i32", m.I32)
check(t, m.I64 == 4, "i64", m.I64)
check(t, m.U8 == 5, "u8", m.U8)
check(t, m.U16 == 6, "u16", m.U16)
check(t, m.U32 == 7, "u32", m.U32)
check(t, m.U64 == 8, "u64", m.U64)
check(t, m.I == -9, "i", m.I)
check(t, m.U == 10, "u", m.U)
check(t, m.Fl == 11.5, "fl", m.Fl)
check(t, m.Fl32 == 12.25, "fl32", m.Fl32)
check(t, m.Fl64 == 13.75, "fl64", m.Fl64)
check(t, m.A != nil, "a", m.A)
if m.A != nil {
check(t, m.A[0] == "x", "a[0]", m.A[0])
check(t, m.A[1] == "y", "a[1]", m.A[1])
check(t, m.A[2] == "z", "a[2]", m.A[2])
}
check(t, m.My != nil, "my", m.My)
if m.My != nil {
check(t, m.My.S == "subguy", "my.s", m.My.S)
}
check(t, reflect.DeepEqual(m.Map, decodedMap), "map", m.Map)
check(t, reflect.DeepEqual(m.MapStruct, decodedMapStruct), "mapstruct", m.MapStruct)
check(t, reflect.DeepEqual(m.MapPtrStruct, decodedMapPtrStruct), "mapptrstruct", m.MapPtrStruct)
}
type Issue147Text struct {
Text string
}
type Issue147 struct {
Test []Issue147Text
}
const issue147Input = `{"test": [{"text":"0"},{"text":"1"},{"text":"2"},
{"text":"3"},{"text":"4"},{"text":"5"},
{"text":"6"},{"text":"7"},{"text":"8"},
{"text":"9"},{"text":"10"},{"text":"11"},
{"text":"12"},{"text":"13"},{"text":"14"},
{"text":"15"},{"text":"16"},{"text":"17"},
{"text":"18"},{"text":"19"},{"text":"20"},
{"text":"21"},{"text":"22"},{"text":"23"},
{"text":"24"},{"text":"25"},{"text":"26"},
{"text":"27"},{"text":"28"},{"text":"29"}]}`
func TestIssue147(t *testing.T) {
var timeline Issue147
Unmarshal(issue147Input, &timeline)
if len(timeline.Test) != 30 {
t.Errorf("wrong length: got %d want 30", len(timeline.Test))
}
for i, e := range timeline.Test {
if e.Text != strconv.Itoa(i) {
t.Errorf("index: %d got: %s want: %d", i, e.Text, i)
}
}
}
type Issue114 struct {
Text string
}
const issue114Input = `[{"text" : "0"}, {"text" : "1"}, {"text" : "2"}, {"text" : "3"}]`
func TestIssue114(t *testing.T) {
var items []Issue114
Unmarshal(issue114Input, &items)
if len(items) != 4 {
t.Errorf("wrong length: got %d want 4", len(items))
}
for i, e := range items {
if e.Text != strconv.Itoa(i) {
t.Errorf("index: %d got: %s want: %d", i, e.Text, i)
}
}
}
type marshalTest struct {
val interface{}
out string
}
type MTE string
type OneField struct {
a int
}
type ScalarWithString int
const (
AA ScalarWithString = iota
BB
CC
)
var scalarStrings = []string{"AA", "BB", "CC"}
func (x ScalarWithString) String() string { return scalarStrings[x] }
var marshalTests = []marshalTest{
// basic string
marshalTest{nil, "null"},
marshalTest{true, "true"},
marshalTest{false, "false"},
marshalTest{123, "123"},
marshalTest{0.1, "0.1"},
marshalTest{1e-10, "1e-10"},
marshalTest{"teststring", `"teststring"`},
marshalTest{[4]int{1, 2, 3, 4}, "[1,2,3,4]"},
marshalTest{[]int{1, 2, 3, 4}, "[1,2,3,4]"},
marshalTest{[]interface{}{nil}, "[null]"},
marshalTest{[][]int{[]int{1, 2}, []int{3, 4}}, "[[1,2],[3,4]]"},
marshalTest{map[string]string{"one": "one"}, `{"one":"one"}`},
marshalTest{map[string]int{"one": 1}, `{"one":1}`},
marshalTest{map[string]interface{}{"null": nil}, `{"null":null}`},
marshalTest{struct{}{}, "{}"},
marshalTest{struct{ a int }{1}, `{"a":1}`},
marshalTest{struct{ a interface{} }{nil}, `{"a":null}`},
marshalTest{struct {
a int
b string
}{1, "hello"},
`{"a":1,"b":"hello"}`,
},
marshalTest{map[string][]int{"3": []int{1, 2, 3}}, `{"3":[1,2,3]}`},
marshalTest{map[string]*MTE{"hi": nil}, `{"hi":null}`},
marshalTest{map[string]interface{}{"hi": 3}, `{"hi":3}`},
marshalTest{&OneField{3}, `{"a":3}`},
marshalTest{"\x05\x06", `"\u0005\u0006"`},
marshalTest{uintptr(50000), "50000"},
marshalTest{uint64(50000), "50000"},
marshalTest{uint32(50000), "50000"},
marshalTest{uint16(50000), "50000"},
marshalTest{uint8(50), "50"},
marshalTest{int64(50000), "50000"},
marshalTest{int32(50000), "50000"},
marshalTest{int16(10000), "10000"},
marshalTest{int8(50), "50"},
marshalTest{BB, "1"},
}
func TestMarshal(t *testing.T) {
for _, tt := range marshalTests {
var buf bytes.Buffer
err := Marshal(&buf, tt.val)
if err != nil {
t.Fatalf("Marshal(%T): %s", tt.val, err)
}
s := buf.String()
if s != tt.out {
t.Errorf("Marshal(%T) = %q, want %q\n", tt.val, s, tt.out)
}
}
}
type marshalIndentTest struct {
val interface{}
indent string
out string
}
const marshalIndentTest1 = `[
1,
2,
3,
4
]
`
const marshalIndentTest2 = `[
[
1,
2
],
[
3,
4
]
]
`
const marshalIndentTest3 = `[
[
1,
2
],
[
3,
4
]
]
`
const marshalIndentTest4 = `[
[
1,
2
],
[
3,
4
]
]
`
const marshalIndentTest5 = `{
"a":1,
"b":"hello"
}
`
const marshalIndentTest6 = `{
"3":[
1,
2,
3
]
}
`
var marshalIndentTests = []marshalIndentTest{
marshalIndentTest{[]int{1, 2, 3, 4}, " ", marshalIndentTest1},
marshalIndentTest{[][]int{[]int{1, 2}, []int{3, 4}}, "", marshalIndentTest2},
marshalIndentTest{[][]int{[]int{1, 2}, []int{3, 4}}, " ", marshalIndentTest3},
marshalIndentTest{[][]int{[]int{1, 2}, []int{3, 4}}, " ", marshalIndentTest4},
marshalIndentTest{struct {
a int
b string
}{1, "hello"},
" ",
marshalIndentTest5,
},
marshalIndentTest{map[string][]int{"3": []int{1, 2, 3}}, " ", marshalIndentTest6},
}
func TestMarshalIndent(t *testing.T) {
for _, tt := range marshalIndentTests {
var buf bytes.Buffer
err := MarshalIndent(&buf, tt.val, tt.indent)
if err != nil {
t.Fatalf("MarshalIndent(%v): %s", tt.val, err)
}
s := buf.String()
if s != tt.out {
t.Errorf("MarshalIndent(%v) = %q, want %q\n", tt.val, s, tt.out)
}
}
}
type marshalErrorTest struct {
val interface{}
error string
}
type ChanVal struct {
C chan int
}
var marshalErrorTests = []marshalErrorTest{
marshalErrorTest{map[chan int]string{make(chan int): "one"}, "json cannot encode value of type map[chan int] string"},
marshalErrorTest{make(chan int, 100), "json cannot encode value of type chan int"},
marshalErrorTest{new(ChanVal), "json cannot encode value of type chan int"},
}
func TestMarshalError(t *testing.T) {
for _, tt := range marshalErrorTests {
var buf bytes.Buffer
err := Marshal(&buf, tt.val)
if err == nil {
t.Fatalf("Marshal(%T): no error, want error %s", tt.val, tt.error)
}
if err.String() != tt.error {
t.Fatalf("Marshal(%T) = error %s, want error %s", tt.val, err, tt.error)
}
}
}
...@@ -415,7 +415,7 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) { ...@@ -415,7 +415,7 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) {
s.false = false s.false = false
s.mp = make(map[string]string) s.mp = make(map[string]string)
s.mp["mapkey"] = "Ahoy!" s.mp["mapkey"] = "Ahoy!"
s.json, _ = json.Decode("{\"maps\":[{\"a\":1,\"b\":2},{\"a\":3,\"b\":4}]}") json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.json)
s.innermap.mp = make(map[string]int) s.innermap.mp = make(map[string]int)
s.innermap.mp["innerkey"] = 55 s.innermap.mp["innerkey"] = 55
s.stringmap = make(map[string]string) s.stringmap = make(map[string]string)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment