mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
encoding/json: add "overflow" struct tag option
Fixes #6213. R=golang-dev, dsymonds, bradfitz CC=golang-dev https://golang.org/cl/13180043
This commit is contained in:
parent
9169372749
commit
466001d05d
5 changed files with 240 additions and 49 deletions
|
|
@ -61,6 +61,10 @@ import (
|
|||
// Instead, they are replaced by the Unicode replacement
|
||||
// character U+FFFD.
|
||||
//
|
||||
// When unmarshalling into struct values, a struct field of map type with the
|
||||
// "overflow" option will store values whose keys do not match the other struct
|
||||
// fields.
|
||||
//
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
// Check for well-formedness.
|
||||
// Avoids filling out half a data structure
|
||||
|
|
@ -489,8 +493,6 @@ func (d *decodeState) object(v reflect.Value) {
|
|||
return
|
||||
}
|
||||
|
||||
var mapElem reflect.Value
|
||||
|
||||
for {
|
||||
// Read opening " of string key or closing }.
|
||||
op := d.scanWhile(scanSkipSpace)
|
||||
|
|
@ -512,44 +514,7 @@ 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 v.Kind() == reflect.Map {
|
||||
elemType := v.Type().Elem()
|
||||
if !mapElem.IsValid() {
|
||||
mapElem = reflect.New(elemType).Elem()
|
||||
} else {
|
||||
mapElem.Set(reflect.Zero(elemType))
|
||||
}
|
||||
subv = mapElem
|
||||
} else {
|
||||
var f *field
|
||||
fields := cachedTypeFields(v.Type())
|
||||
for i := range fields {
|
||||
ff := &fields[i]
|
||||
if ff.name == key {
|
||||
f = ff
|
||||
break
|
||||
}
|
||||
if f == nil && strings.EqualFold(ff.name, key) {
|
||||
f = ff
|
||||
}
|
||||
}
|
||||
if f != nil {
|
||||
subv = v
|
||||
destring = f.quoted
|
||||
for _, i := range f.index {
|
||||
if subv.Kind() == reflect.Ptr {
|
||||
if subv.IsNil() {
|
||||
subv.Set(reflect.New(subv.Type().Elem()))
|
||||
}
|
||||
subv = subv.Elem()
|
||||
}
|
||||
subv = subv.Field(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
subv, mapv, destring := subValue(v, key)
|
||||
|
||||
// Read : before value.
|
||||
if op == scanSkipSpace {
|
||||
|
|
@ -569,9 +534,9 @@ func (d *decodeState) object(v reflect.Value) {
|
|||
|
||||
// Write value back to map;
|
||||
// if using struct, subv points into struct already.
|
||||
if v.Kind() == reflect.Map {
|
||||
kv := reflect.ValueOf(key).Convert(v.Type().Key())
|
||||
v.SetMapIndex(kv, subv)
|
||||
if mapv.IsValid() {
|
||||
kv := reflect.ValueOf(key).Convert(mapv.Type().Key())
|
||||
mapv.SetMapIndex(kv, subv)
|
||||
}
|
||||
|
||||
// Next token must be , or }.
|
||||
|
|
@ -585,6 +550,57 @@ func (d *decodeState) object(v reflect.Value) {
|
|||
}
|
||||
}
|
||||
|
||||
// subValue returns (and allocates, if necessary) the field in the struct or
|
||||
// map v whose name matches key.
|
||||
func subValue(v reflect.Value, key string) (subv, mapv reflect.Value, destring bool) {
|
||||
// Create new map element.
|
||||
if v.Kind() == reflect.Map {
|
||||
subv = reflect.New(v.Type().Elem()).Elem()
|
||||
mapv = v
|
||||
return
|
||||
}
|
||||
|
||||
// Get struct field.
|
||||
var f *field
|
||||
fields := cachedTypeFields(v.Type())
|
||||
for i := range fields {
|
||||
ff := &fields[i]
|
||||
if ff.name == key {
|
||||
f = ff
|
||||
break
|
||||
}
|
||||
if f == nil && strings.EqualFold(ff.name, key) {
|
||||
f = ff
|
||||
}
|
||||
}
|
||||
if f != nil {
|
||||
subv = fieldByIndex(v, f.index, true)
|
||||
destring = f.quoted
|
||||
return
|
||||
}
|
||||
|
||||
// Decode into overflow field if present.
|
||||
for _, f := range fields {
|
||||
if f.overflow {
|
||||
// Find overflow field.
|
||||
mapv = fieldByIndex(v, f.index, true)
|
||||
if k := mapv.Kind(); k != reflect.Map {
|
||||
panic("unsupported overflow field kind: " + k.String())
|
||||
}
|
||||
// Make map if necessary.
|
||||
if mapv.IsNil() {
|
||||
mapv.Set(reflect.MakeMap(mapv.Type()))
|
||||
}
|
||||
// Create new map element.
|
||||
subv = reflect.New(mapv.Type().Elem()).Elem()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Not found.
|
||||
return
|
||||
}
|
||||
|
||||
// literal consumes a literal from d.data[d.off-1:], decoding into the value v.
|
||||
// The first byte of the literal has been read already
|
||||
// (that's how the caller knows it's a literal).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue