summaryrefslogtreecommitdiff
path: root/src/pkg/json/encode.go
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2010-04-21 16:40:53 -0700
committerRuss Cox <rsc@golang.org>2010-04-21 16:40:53 -0700
commitb5c7ab099f6f6712a4f95da09d71d0ab378f6431 (patch)
tree658ed98c6804a2d8e11b7ea023ac07d7820299ae /src/pkg/json/encode.go
parent857111428bbbeef6a845399f49c9f113db6bbfde (diff)
downloadgolang-b5c7ab099f6f6712a4f95da09d71d0ab378f6431.tar.gz
json: Marshal, Unmarshal using new scanner
R=r CC=golang-dev http://codereview.appspot.com/953041
Diffstat (limited to 'src/pkg/json/encode.go')
-rw-r--r--src/pkg/json/encode.go282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go
new file mode 100644
index 000000000..1de22086d
--- /dev/null
+++ b/src/pkg/json/encode.go
@@ -0,0 +1,282 @@
+// 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('"')
+}