summaryrefslogtreecommitdiff
path: root/src/pkg/json/generic.go
diff options
context:
space:
mode:
authorRob Pike <r@golang.org>2009-06-09 09:53:44 -0700
committerRob Pike <r@golang.org>2009-06-09 09:53:44 -0700
commit7249ea4df2b4f12a4e7ed446f270cea87e4ffd34 (patch)
tree7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/pkg/json/generic.go
parentacf6ef7a82b3fe61516a1bac4563706552bdf078 (diff)
downloadgolang-7249ea4df2b4f12a4e7ed446f270cea87e4ffd34.tar.gz
mv src/lib to src/pkg
tests: all.bash passes, gobuild still works, godoc still works. R=rsc OCL=30096 CL=30102
Diffstat (limited to 'src/pkg/json/generic.go')
-rw-r--r--src/pkg/json/generic.go331
1 files changed, 331 insertions, 0 deletions
diff --git a/src/pkg/json/generic.go b/src/pkg/json/generic.go
new file mode 100644
index 000000000..e3194eb17
--- /dev/null
+++ b/src/pkg/json/generic.go
@@ -0,0 +1,331 @@
+// 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";
+ "json";
+ "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)
+}
+
+// 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 }
+
+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;
+}
+
+// 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:len(path)];
+ } 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{vector.New(0), _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
+}
+
+// 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 errindx int;
+ var j Json;
+ b := new(_JsonBuilder);
+ b.ptr = &j;
+ ok, errindx, 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.