diff options
Diffstat (limited to 'src/pkg/json/encode.go')
-rw-r--r-- | src/pkg/json/encode.go | 62 |
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 } |