blob: 9fa1ae550527b5e352fe4ad192c7a83b3a2cad3e [file] [log] [blame]
// 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)
}
// 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 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.