diff options
author | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2009-06-09 09:53:44 -0700 |
commit | 7249ea4df2b4f12a4e7ed446f270cea87e4ffd34 (patch) | |
tree | 7032a11d0cac2ae4d3e90f7a189b575b5a50f848 /src/pkg/json/generic.go | |
parent | acf6ef7a82b3fe61516a1bac4563706552bdf078 (diff) | |
download | golang-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.go | 331 |
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. |