summaryrefslogtreecommitdiff
path: root/src/pkg/json/encode.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/json/encode.go')
-rw-r--r--src/pkg/json/encode.go62
1 files changed, 44 insertions, 18 deletions
diff --git a/src/pkg/json/encode.go b/src/pkg/json/encode.go
index 3e593fec1..16be5e2af 100644
--- a/src/pkg/json/encode.go
+++ b/src/pkg/json/encode.go
@@ -4,6 +4,9 @@
// Package json implements encoding and decoding of JSON objects as defined in
// RFC 4627.
+//
+// See "JSON and Go" for an introduction to this package:
+// http://blog.golang.org/2011/01/json-and-go.html
package json
import (
@@ -14,7 +17,6 @@ import (
"runtime"
"sort"
"strconv"
- "strings"
"unicode"
"utf8"
)
@@ -59,6 +61,12 @@ import (
// // Note the leading comma.
// Field int `json:",omitempty"`
//
+// The "string" option signals that a field is stored as JSON inside a
+// JSON-encoded string. This extra level of encoding is sometimes
+// used when communicating with JavaScript programs:
+//
+// Int64String int64 `json:",string"`
+//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, dollar signs, hyphens, and underscores.
//
@@ -221,6 +229,12 @@ func isEmptyValue(v reflect.Value) bool {
}
func (e *encodeState) reflectValue(v reflect.Value) {
+ e.reflectValueQuoted(v, false)
+}
+
+// reflectValueQuoted writes the value in v to the output.
+// If quoted is true, the serialization is wrapped in a JSON string.
+func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) {
if !v.IsValid() {
e.WriteString("null")
return
@@ -238,26 +252,39 @@ func (e *encodeState) reflectValue(v reflect.Value) {
return
}
+ writeString := (*encodeState).WriteString
+ if quoted {
+ writeString = (*encodeState).string
+ }
+
switch v.Kind() {
case reflect.Bool:
x := v.Bool()
if x {
- e.WriteString("true")
+ writeString(e, "true")
} else {
- e.WriteString("false")
+ writeString(e, "false")
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- e.WriteString(strconv.Itoa64(v.Int()))
+ writeString(e, strconv.Itoa64(v.Int()))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- e.WriteString(strconv.Uitoa64(v.Uint()))
+ writeString(e, strconv.Uitoa64(v.Uint()))
case reflect.Float32, reflect.Float64:
- e.WriteString(strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))
+ writeString(e, strconv.FtoaN(v.Float(), 'g', -1, v.Type().Bits()))
case reflect.String:
- e.string(v.String())
+ if quoted {
+ sb, err := Marshal(v.String())
+ if err != nil {
+ e.error(err)
+ }
+ e.string(string(sb))
+ } else {
+ e.string(v.String())
+ }
case reflect.Struct:
e.WriteByte('{')
@@ -269,17 +296,14 @@ func (e *encodeState) reflectValue(v reflect.Value) {
if f.PkgPath != "" {
continue
}
- tag, omitEmpty := f.Name, false
+ tag, omitEmpty, quoted := f.Name, false, false
if tv := f.Tag.Get("json"); tv != "" {
- ss := strings.SplitN(tv, ",", 2)
- if isValidTag(ss[0]) {
- tag = ss[0]
- }
- if len(ss) > 1 {
- // Currently the only option is omitempty,
- // so parsing is trivial.
- omitEmpty = ss[1] == "omitempty"
+ name, opts := parseTag(tv)
+ if isValidTag(name) {
+ tag = name
}
+ omitEmpty = opts.Contains("omitempty")
+ quoted = opts.Contains("string")
}
fieldValue := v.Field(i)
if omitEmpty && isEmptyValue(fieldValue) {
@@ -292,7 +316,7 @@ func (e *encodeState) reflectValue(v reflect.Value) {
}
e.string(tag)
e.WriteByte(':')
- e.reflectValue(fieldValue)
+ e.reflectValueQuoted(fieldValue, quoted)
}
e.WriteByte('}')
@@ -380,7 +404,8 @@ 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].String() }
-func (e *encodeState) string(s string) {
+func (e *encodeState) string(s string) (int, os.Error) {
+ len0 := e.Len()
e.WriteByte('"')
start := 0
for i := 0; i < len(s); {
@@ -425,4 +450,5 @@ func (e *encodeState) string(s string) {
e.WriteString(s[start:])
}
e.WriteByte('"')
+ return e.Len() - len0, nil
}