1
0
mirror of https://github.com/golang/go synced 2024-11-26 03:47:57 -07:00

json: Decode into native Go data structures

This patch adds an ability to convert JSON-encoded data into
a hierarchy of Go's native data types.

R=rsc
CC=golang-dev
https://golang.org/cl/161060
This commit is contained in:
Sergey 'SnakE' Gromov 2009-11-30 13:55:09 -08:00 committed by Russ Cox
parent c1767dbdda
commit 9d50b468a1
7 changed files with 271 additions and 427 deletions

View File

@ -61,18 +61,20 @@ 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, ok, errtok := json.StringToJson(s); j, err := json.Decode(s);
if !ok { if err != nil {
t.Errorf("colours.String() isn't valid JSON: %v", errtok) t.Errorf("colours.String() isn't valid JSON: %v", err)
} }
if j.Kind() != json.MapKind { m, ok := j.(map[string]interface{});
if !ok {
t.Error("colours.String() didn't produce a map.") t.Error("colours.String() didn't produce a map.")
} }
red := j.Get("red"); red := m["red"];
if red.Kind() != json.NumberKind { x, ok := red.(float64);
t.Error("red.Kind() is not a NumberKind.") if !ok {
t.Error("red.Kind() is not a number.")
} }
if x := red.Number(); x != 3 { if x != 3 {
t.Error("red = %v, want 3", x) t.Error("red = %v, want 3", x)
} }
} }

View File

@ -6,7 +6,8 @@ include ../../Make.$(GOARCH)
TARG=json TARG=json
GOFILES=\ GOFILES=\
generic.go\ decode.go\
error.go\
parse.go\ parse.go\
struct.go\ struct.go\

107
src/pkg/json/decode.go Normal file
View File

@ -0,0 +1,107 @@
// 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.
// Represents JSON data structure using native Go types: booleans, floats,
// strings, arrays, and maps.
package json
import (
"container/vector";
"os";
)
// Decode a JSON string
// Decode parses the string s as a JSON-syntax string and returns the
// generic JSON object representation. The object representation is a tree
// of Go data types. The data return value may be one of float64, string,
// bool, nil, []interface{} or map[string]interface{}. The array and map
// elements may in turn contain any of the types listed above and so on.
// If Decode encounters a syntax error, it returns with err set to an
// instance of ParseError. See ParseError documentation for details.
func Decode(s string) (data interface{}, err os.Error) {
jb := newDecoder(nil, nil);
ok, errPos, errTok := Parse(s, jb);
if ok {
data = jb.Data()
} else {
err = &ParseError{Index: errPos, Token: errTok}
}
return;
}
type decoder struct {
// A value being constructed.
value interface{};
// Container entity to flush into. Can be either vector.Vector or
// map[string]interface{}.
container interface{};
// The index into the container interface. Either int or string.
index interface{};
}
func newDecoder(container interface{}, key interface{}) *decoder {
return &decoder{container: container, index: key}
}
func (j *decoder) Int64(i int64) { j.value = float64(i) }
func (j *decoder) Uint64(i uint64) { j.value = float64(i) }
func (j *decoder) Float64(f float64) {
j.value = float64(f)
}
func (j *decoder) String(s string) { j.value = s }
func (j *decoder) Bool(b bool) { j.value = b }
func (j *decoder) Null() { j.value = nil }
func (j *decoder) Array() { j.value = new(vector.Vector) }
func (j *decoder) Map() { j.value = make(map[string]interface{}) }
func (j *decoder) Elem(i int) Builder {
v, ok := j.value.(*vector.Vector);
if !ok {
v = new(vector.Vector);
j.value = v;
}
if v.Len() <= i {
v.Resize(i+1, (i+1)*2)
}
return newDecoder(v, i);
}
func (j *decoder) Key(s string) Builder {
m, ok := j.value.(map[string]interface{});
if !ok {
m = make(map[string]interface{});
j.value = m;
}
return newDecoder(m, s);
}
func (j *decoder) Flush() {
switch c := j.container.(type) {
case *vector.Vector:
index := j.index.(int);
c.Set(index, j.Data());
case map[string]interface{}:
index := j.index.(string);
c[index] = j.Data();
}
}
// Get the value built by this builder.
func (j *decoder) Data() interface{} {
switch v := j.value.(type) {
case *vector.Vector:
return v.Data()
}
return j.value;
}

133
src/pkg/json/decode_test.go Normal file
View File

@ -0,0 +1,133 @@
// 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 (
"container/vector";
"reflect";
"testing";
)
func TestDecodeInt64(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Int64(-15);
assertResult(t, nb.Data(), float64(-15));
}
func TestDecodeUint64(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Uint64(15);
assertResult(t, nb.Data(), float64(15));
}
func TestDecodeFloat64(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Float64(3.14159);
assertResult(t, nb.Data(), float64(3.14159));
}
func TestDecodeString(t *testing.T) {
nb := newDecoder(nil, nil);
nb.String("Some string");
assertResult(t, nb.Data(), "Some string");
}
func TestDecodeBool(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Bool(true);
assertResult(t, nb.Data(), true);
}
func TestDecodeNull(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Null();
assertResult(t, nb.Data(), nil);
}
func TestDecodeEmptyArray(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Array();
assertResult(t, nb.Data(), []interface{}{});
}
func TestDecodeEmptyMap(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Map();
assertResult(t, nb.Data(), map[string]interface{}{});
}
func TestDecodeFlushElem(t *testing.T) {
testVec := new(vector.Vector).Resize(2, 2);
nb := newDecoder(testVec, 1);
nb.Float64(3.14159);
nb.Flush();
assertResult(t, testVec.Data(), []interface{}{nil, float64(3.14159)});
}
func TestDecodeFlushKey(t *testing.T) {
testMap := make(map[string]interface{});
nb := newDecoder(testMap, "key");
nb.Float64(3.14159);
nb.Flush();
assertResult(t, testMap, map[string]interface{}{"key": float64(3.14159)});
}
// Elem() and Key() are hard to test in isolation because all they do
// is create a new, properly initialized, decoder, and modify state of
// the underlying decoder. I'm testing them through already tested
// Array(), String(), and Flush().
func TestDecodeElem(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Array();
var b Builder = nb.Elem(0);
b.String("0");
b.Flush();
assertResult(t, nb.Data(), []interface{}{"0"});
}
func TestDecodeKey(t *testing.T) {
nb := newDecoder(nil, nil);
nb.Map();
var b Builder = nb.Key("a");
b.String("0");
b.Flush();
assertResult(t, nb.Data(), map[string]interface{}{"a": "0"});
}
func assertResult(t *testing.T, results, expected interface{}) {
if !reflect.DeepEqual(results, expected) {
t.Fatalf("have %T(%#v) want %T(%#v)", results, results, expected, expected)
}
}
type decodeTest struct {
s string;
r interface{};
}
var tests = []decodeTest{
decodeTest{`null`, nil},
decodeTest{`true`, true},
decodeTest{`false`, false},
decodeTest{`"abc"`, "abc"},
decodeTest{`123`, float64(123)},
decodeTest{`0.1`, float64(0.1)},
decodeTest{`1e-10`, float64(1e-10)},
decodeTest{`[]`, []interface{}{}},
decodeTest{`[1,2,3,4]`, []interface{}{float64(1), float64(2), float64(3), float64(4)}},
decodeTest{`[1,2,"abc",null,true,false]`, []interface{}{float64(1), float64(2), "abc", nil, true, false}},
decodeTest{`{}`, map[string]interface{}{}},
decodeTest{`{"a":1}`, map[string]interface{}{"a": float64(1)}},
decodeTest{`"q\u0302"`, "q\u0302"},
}
func TestDecode(t *testing.T) {
for _, test := range tests {
if val, err := Decode(test.s); err != nil || !reflect.DeepEqual(val, test.r) {
t.Errorf("Decode(%#q) = %v, %v want %v, nil", test.s, val, err, test.r)
}
}
}

19
src/pkg/json/error.go Normal file
View File

@ -0,0 +1,19 @@
// 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 "fmt"
// ParseError aggregates information about a JSON parse error. It is
// compatible with the os.Error interface.
type ParseError struct {
Index int; // A byte index in JSON string where the error occurred
Token string; // An offending token
}
// Produce a string representation of this ParseError.
func (pe *ParseError) String() string {
return fmt.Sprintf("Unexpected JSON token at position %d: %q.", pe.Index, pe.Token)
}

View File

@ -1,338 +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.
// Generic representation of JSON objects.
package json
import (
"container/vector";
"fmt";
"math";
"strconv";
"strings";
)
// Integers identifying the data type in the Json interface.
const (
StringKind = iota;
NumberKind;
MapKind; // JSON term is "Object", but in Go, it's a map
ArrayKind;
BoolKind;
NullKind;
)
// The Json interface is implemented by all JSON objects.
type Json interface {
Kind() int; // StringKind, NumberKind, etc.
String() string; // a string form (any kind)
Number() float64; // numeric form (NumberKind)
Bool() bool; // boolean (BoolKind)
Get(s string) Json; // field lookup (MapKind)
Elem(i int) Json; // element lookup (ArrayKind)
Len() int; // length (ArrayKind, MapKind)
Map() map[string]Json; // map form (MapKind)
}
// JsonToString returns the textual JSON syntax representation
// for the JSON object j.
//
// JsonToString differs from j.String() in the handling
// of string objects. If j represents the string abc,
// j.String() == `abc`, but JsonToString(j) == `"abc"`.
func JsonToString(j Json) string {
if j == nil {
return "null"
}
if j.Kind() == StringKind {
return Quote(j.String())
}
return j.String();
}
type _Null struct{}
// Null is the JSON object representing the null data object.
var Null Json = &_Null{}
func (*_Null) Kind() int { return NullKind }
func (*_Null) String() string { return "null" }
func (*_Null) Number() float64 { return 0 }
func (*_Null) Bool() bool { return false }
func (*_Null) Get(s string) Json { return Null }
func (*_Null) Elem(int) Json { return Null }
func (*_Null) Len() int { return 0 }
func (*_Null) Map() map[string]Json { return nil }
type _String struct {
s string;
_Null;
}
func (j *_String) Kind() int { return StringKind }
func (j *_String) String() string { return j.s }
type _Number struct {
f float64;
_Null;
}
func (j *_Number) Kind() int { return NumberKind }
func (j *_Number) Number() float64 { return j.f }
func (j *_Number) String() string {
if math.Floor(j.f) == j.f {
return fmt.Sprintf("%.0f", j.f)
}
return fmt.Sprintf("%g", j.f);
}
type _Array struct {
a *vector.Vector;
_Null;
}
func (j *_Array) Kind() int { return ArrayKind }
func (j *_Array) Len() int { return j.a.Len() }
func (j *_Array) Elem(i int) Json {
if i < 0 || i >= j.a.Len() {
return Null
}
return j.a.At(i).(Json);
}
func (j *_Array) String() string {
s := "[";
for i := 0; i < j.a.Len(); i++ {
if i > 0 {
s += ","
}
s += JsonToString(j.a.At(i).(Json));
}
s += "]";
return s;
}
type _Bool struct {
b bool;
_Null;
}
func (j *_Bool) Kind() int { return BoolKind }
func (j *_Bool) Bool() bool { return j.b }
func (j *_Bool) String() string {
if j.b {
return "true"
}
return "false";
}
type _Map struct {
m map[string]Json;
_Null;
}
func (j *_Map) Kind() int { return MapKind }
func (j *_Map) Len() int { return len(j.m) }
func (j *_Map) Get(s string) Json {
if j.m == nil {
return Null
}
v, ok := j.m[s];
if !ok {
return Null
}
return v;
}
func (j *_Map) String() string {
s := "{";
first := true;
for k, v := range j.m {
if first {
first = false
} else {
s += ","
}
s += Quote(k);
s += ":";
s += JsonToString(v);
}
s += "}";
return s;
}
func (j *_Map) Map() map[string]Json { return j.m }
// Walk evaluates path relative to the JSON object j.
// Path is taken as a sequence of slash-separated field names
// or numbers that can be used to index into JSON map and
// array objects.
//
// For example, if j is the JSON object for
// {"abc": [true, false]}, then Walk(j, "abc/1") returns the
// JSON object for true.
func Walk(j Json, path string) Json {
for len(path) > 0 {
var elem string;
if i := strings.Index(path, "/"); i >= 0 {
elem = path[0:i];
path = path[i+1:];
} else {
elem = path;
path = "";
}
switch j.Kind() {
case ArrayKind:
indx, err := strconv.Atoi(elem);
if err != nil {
return Null
}
j = j.Elem(indx);
case MapKind:
j = j.Get(elem)
default:
return Null
}
}
return j;
}
// Equal returns whether a and b are indistinguishable JSON objects.
func Equal(a, b Json) bool {
switch {
case a == nil && b == nil:
return true
case a == nil || b == nil:
return false
case a.Kind() != b.Kind():
return false
}
switch a.Kind() {
case NullKind:
return true
case StringKind:
return a.String() == b.String()
case NumberKind:
return a.Number() == b.Number()
case BoolKind:
return a.Bool() == b.Bool()
case ArrayKind:
if a.Len() != b.Len() {
return false
}
for i := 0; i < a.Len(); i++ {
if !Equal(a.Elem(i), b.Elem(i)) {
return false
}
}
return true;
case MapKind:
m := a.(*_Map).m;
if len(m) != len(b.(*_Map).m) {
return false
}
for k, v := range m {
if !Equal(v, b.Get(k)) {
return false
}
}
return true;
}
// invalid kind
return false;
}
// Parse builder for JSON objects.
type _JsonBuilder struct {
// either writing to *ptr
ptr *Json;
// or to a[i] (can't set ptr = &a[i])
a *vector.Vector;
i int;
// or to m[k] (can't set ptr = &m[k])
m map[string]Json;
k string;
}
func (b *_JsonBuilder) Put(j Json) {
switch {
case b.ptr != nil:
*b.ptr = j
case b.a != nil:
b.a.Set(b.i, j)
case b.m != nil:
b.m[b.k] = j
}
}
func (b *_JsonBuilder) Get() Json {
switch {
case b.ptr != nil:
return *b.ptr
case b.a != nil:
return b.a.At(b.i).(Json)
case b.m != nil:
return b.m[b.k]
}
return nil;
}
func (b *_JsonBuilder) Float64(f float64) { b.Put(&_Number{f, _Null{}}) }
func (b *_JsonBuilder) Int64(i int64) { b.Float64(float64(i)) }
func (b *_JsonBuilder) Uint64(i uint64) { b.Float64(float64(i)) }
func (b *_JsonBuilder) Bool(tf bool) { b.Put(&_Bool{tf, _Null{}}) }
func (b *_JsonBuilder) Null() { b.Put(Null) }
func (b *_JsonBuilder) String(s string) { b.Put(&_String{s, _Null{}}) }
func (b *_JsonBuilder) Array() { b.Put(&_Array{new(vector.Vector), _Null{}}) }
func (b *_JsonBuilder) Map() { b.Put(&_Map{make(map[string]Json), _Null{}}) }
func (b *_JsonBuilder) Elem(i int) Builder {
bb := new(_JsonBuilder);
bb.a = b.Get().(*_Array).a;
bb.i = i;
for i >= bb.a.Len() {
bb.a.Push(Null)
}
return bb;
}
func (b *_JsonBuilder) Key(k string) Builder {
bb := new(_JsonBuilder);
bb.m = b.Get().(*_Map).m;
bb.k = k;
bb.m[k] = Null;
return bb;
}
func (b *_JsonBuilder) Flush() {}
// StringToJson parses the string s as a JSON-syntax string
// and returns the generic JSON object representation.
// On success, StringToJson returns with ok set to true and errtok empty.
// If StringToJson encounters a syntax error, it returns with
// ok set to false and errtok set to a fragment of the offending syntax.
func StringToJson(s string) (json Json, ok bool, errtok string) {
var j Json;
b := new(_JsonBuilder);
b.ptr = &j;
ok, _, errtok = Parse(s, b);
if !ok {
return nil, false, errtok
}
return j, true, "";
}
// BUG(rsc): StringToJson should return an os.Error instead of a bool.

View File

@ -1,80 +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.
package json
import (
"reflect";
"testing";
)
var jsontests = []string{
`null`,
`true`,
`false`,
`"abc"`,
`123`,
`0.1`,
`1e-10`,
`[]`,
`[1,2,3,4]`,
`[1,2,"abc",null,true,false]`,
`{}`,
`{"a":1}`,
`"q\u0302"`,
}
func TestJson(t *testing.T) {
for i := 0; i < len(jsontests); i++ {
val, ok, errtok := StringToJson(jsontests[i]);
if !ok {
t.Errorf("StringToJson(%#q) => error near %v", jsontests[i], errtok);
continue;
}
str := JsonToString(val);
if str != jsontests[i] {
t.Errorf("JsonToString(StringToJson(%#q)) = %#q", jsontests[i], str);
continue;
}
}
}
func TestJsonMap(t *testing.T) {
values := make(map[string]Json);
mapstr := "{";
for i := 0; i < len(jsontests); i++ {
val, ok, errtok := StringToJson(jsontests[i]);
if !ok {
t.Errorf("StringToJson(%#q) => error near %v", jsontests[i], errtok)
}
if i > 0 {
mapstr += ","
}
values[jsontests[i]] = val;
mapstr += Quote(jsontests[i]);
mapstr += ":";
mapstr += JsonToString(val);
}
mapstr += "}";
mapv, ok, errtok := StringToJson(mapstr);
if !ok {
t.Fatalf("StringToJson(%#q) => error near %v", mapstr, errtok)
}
if mapv == nil {
t.Fatalf("StringToJson(%#q) => nil, %v, %v", mapstr, ok, errtok)
}
if cnt := mapv.Len(); cnt != len(jsontests) {
t.Errorf("StringToJson(%#q).Len() => %v, want %v", mapstr, cnt,
len(jsontests))
}
for k, v := range values {
if v1 := mapv.Get(k); !Equal(v1, v) {
t.Errorf("MapTest: Walk(%#q) => %v, want %v", k, v1, v)
}
}
if !reflect.DeepEqual(values, mapv.Map()) {
t.Errorf("DeepEqual(values, mapv.Map()) failed")
}
}