json: add struct tag option to wrap literals in strings

Since JavaScript doesn't have [u]int64 types, some JSON APIs
encode such types as strings to avoid losing precision.

This adds a new struct tag option ",string" to cause
fields to be wrapped in JSON strings on encoding
and unwrapped from strings when decoding.

R=rsc, gustavo
CC=golang-dev
https://golang.org/cl/4918051
This commit is contained in:
Brad Fitzpatrick 2011-08-29 12:46:32 -07:00
parent 2afc46f2b2
commit 596bf61d6c
7 changed files with 175 additions and 32 deletions

View file

@ -140,6 +140,7 @@ type decodeState struct {
scan scanner
nextscan scanner // for calls to nextValue
savedError os.Error
tempstr string // scratch space to avoid some allocations
}
// errPhase is used for errors that should not happen unless
@ -470,6 +471,8 @@ func (d *decodeState) object(v reflect.Value) {
// Figure out field corresponding to key.
var subv reflect.Value
destring := false // whether the value is wrapped in a string to be decoded first
if mv.IsValid() {
elemType := mv.Type().Elem()
if !mapElem.IsValid() {
@ -486,7 +489,8 @@ func (d *decodeState) object(v reflect.Value) {
if isValidTag(key) {
for i := 0; i < sv.NumField(); i++ {
f = st.Field(i)
if tagName(f.Tag.Get("json")) == key {
tagName, _ := parseTag(f.Tag.Get("json"))
if tagName == key {
ok = true
break
}
@ -508,6 +512,8 @@ func (d *decodeState) object(v reflect.Value) {
} else {
subv = sv.FieldByIndex(f.Index)
}
_, opts := parseTag(f.Tag.Get("json"))
destring = opts.Contains("string")
}
}
@ -520,8 +526,12 @@ func (d *decodeState) object(v reflect.Value) {
}
// Read value.
d.value(subv)
if destring {
d.value(reflect.ValueOf(&d.tempstr))
d.literalStore([]byte(d.tempstr), subv)
} else {
d.value(subv)
}
// Write value back to map;
// if using struct, subv points into struct already.
if mv.IsValid() {
@ -550,8 +560,12 @@ func (d *decodeState) literal(v reflect.Value) {
// Scan read one byte too far; back up.
d.off--
d.scan.undo(op)
item := d.data[start:d.off]
d.literalStore(d.data[start:d.off], v)
}
// literalStore decodes a literal stored in item into v.
func (d *decodeState) literalStore(item []byte, v reflect.Value) {
// Check for unmarshaler.
wantptr := item[0] == 'n' // null
unmarshaler, pv := d.indirect(v, wantptr)
@ -918,13 +932,3 @@ func unquoteBytes(s []byte) (t []byte, ok bool) {
}
return b[0:w], true
}
// tagName extracts the field name part out of the "json" struct tag
// value. The json struct tag format is an optional name, followed by
// zero or more ",option" values.
func tagName(v string) string {
if idx := strings.Index(v, ","); idx != -1 {
return v[:idx]
}
return v
}