encoding/json: support encoding.TextMarshaler, encoding.TextUnmarshaler

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12703043
This commit is contained in:
Russ Cox 2013-08-14 14:56:07 -04:00
parent 5822e7848a
commit 7e886740d1
4 changed files with 315 additions and 26 deletions

View file

@ -8,6 +8,7 @@
package json
import (
"encoding"
"encoding/base64"
"errors"
"fmt"
@ -293,7 +294,7 @@ func (d *decodeState) value(v reflect.Value) {
// until it gets to a non-pointer.
// if it encounters an Unmarshaler, indirect stops and returns that.
// if decodingNull is true, indirect stops at the last pointer so it can be set to nil.
func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) {
func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) {
// If v is a named type and is addressable,
// start with its address, so that if the type has pointer methods,
// we find them.
@ -322,28 +323,38 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
v.Set(reflect.New(v.Type().Elem()))
}
if v.Type().NumMethod() > 0 {
if unmarshaler, ok := v.Interface().(Unmarshaler); ok {
return unmarshaler, reflect.Value{}
if u, ok := v.Interface().(Unmarshaler); ok {
return u, nil, reflect.Value{}
}
if u, ok := v.Interface().(encoding.TextUnmarshaler); ok {
return nil, u, reflect.Value{}
}
}
v = v.Elem()
}
return nil, v
return nil, nil, v
}
// array consumes an array from d.data[d.off-1:], decoding into the value v.
// the first byte of the array ('[') has been read already.
func (d *decodeState) array(v reflect.Value) {
// Check for unmarshaler.
unmarshaler, pv := d.indirect(v, false)
if unmarshaler != nil {
u, ut, pv := d.indirect(v, false)
if u != nil {
d.off--
err := unmarshaler.UnmarshalJSON(d.next())
err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
if ut != nil {
d.saveError(&UnmarshalTypeError{"array", v.Type()})
d.off--
d.next()
return
}
v = pv
// Check type of target.
@ -434,15 +445,21 @@ func (d *decodeState) array(v reflect.Value) {
// the first byte of the object ('{') has been read already.
func (d *decodeState) object(v reflect.Value) {
// Check for unmarshaler.
unmarshaler, pv := d.indirect(v, false)
if unmarshaler != nil {
u, ut, pv := d.indirect(v, false)
if u != nil {
d.off--
err := unmarshaler.UnmarshalJSON(d.next())
err := u.UnmarshalJSON(d.next())
if err != nil {
d.error(err)
}
return
}
if ut != nil {
d.saveError(&UnmarshalTypeError{"object", v.Type()})
d.off--
d.next() // skip over { } in input
return
}
v = pv
// Decoding into nil interface? Switch to non-reflect code.
@ -611,14 +628,37 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
return
}
wantptr := item[0] == 'n' // null
unmarshaler, pv := d.indirect(v, wantptr)
if unmarshaler != nil {
err := unmarshaler.UnmarshalJSON(item)
u, ut, pv := d.indirect(v, wantptr)
if u != nil {
err := u.UnmarshalJSON(item)
if err != nil {
d.error(err)
}
return
}
if ut != nil {
if item[0] != '"' {
if fromQuoted {
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.saveError(&UnmarshalTypeError{"string", v.Type()})
}
}
s, ok := unquoteBytes(item)
if !ok {
if fromQuoted {
d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
} else {
d.error(errPhase)
}
}
err := ut.UnmarshalText(s)
if err != nil {
d.error(err)
}
return
}
v = pv
switch c := item[0]; c {