mirror of
https://github.com/golang/go
synced 2024-11-25 00:28:00 -07:00
undo CL 13180043 / 318540e7857f
Accidentally submitted. ««« original CL description encoding/json: add "overflow" struct tag option Fixes #6213. R=golang-dev, dsymonds, bradfitz CC=golang-dev https://golang.org/cl/13180043 »»» R=golang-dev CC=golang-dev https://golang.org/cl/13234045
This commit is contained in:
parent
10e2ffdf2c
commit
27f4166e37
@ -61,10 +61,6 @@ import (
|
|||||||
// Instead, they are replaced by the Unicode replacement
|
// Instead, they are replaced by the Unicode replacement
|
||||||
// character U+FFFD.
|
// 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 {
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
// Check for well-formedness.
|
// Check for well-formedness.
|
||||||
// Avoids filling out half a data structure
|
// Avoids filling out half a data structure
|
||||||
@ -493,6 +489,8 @@ func (d *decodeState) object(v reflect.Value) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mapElem reflect.Value
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Read opening " of string key or closing }.
|
// Read opening " of string key or closing }.
|
||||||
op := d.scanWhile(scanSkipSpace)
|
op := d.scanWhile(scanSkipSpace)
|
||||||
@ -514,7 +512,44 @@ func (d *decodeState) object(v reflect.Value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Figure out field corresponding to key.
|
// Figure out field corresponding to key.
|
||||||
subv, mapv, destring := subValue(v, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Read : before value.
|
// Read : before value.
|
||||||
if op == scanSkipSpace {
|
if op == scanSkipSpace {
|
||||||
@ -534,9 +569,9 @@ func (d *decodeState) object(v reflect.Value) {
|
|||||||
|
|
||||||
// Write value back to map;
|
// Write value back to map;
|
||||||
// if using struct, subv points into struct already.
|
// if using struct, subv points into struct already.
|
||||||
if mapv.IsValid() {
|
if v.Kind() == reflect.Map {
|
||||||
kv := reflect.ValueOf(key).Convert(mapv.Type().Key())
|
kv := reflect.ValueOf(key).Convert(v.Type().Key())
|
||||||
mapv.SetMapIndex(kv, subv)
|
v.SetMapIndex(kv, subv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next token must be , or }.
|
// Next token must be , or }.
|
||||||
@ -550,57 +585,6 @@ 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.
|
// 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
|
// The first byte of the literal has been read already
|
||||||
// (that's how the caller knows it's a literal).
|
// (that's how the caller knows it's a literal).
|
||||||
|
@ -1275,23 +1275,3 @@ func TestSkipArrayObjects(t *testing.T) {
|
|||||||
t.Errorf("got error %q, want nil", err)
|
t.Errorf("got error %q, want nil", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeOverflow(t *testing.T) {
|
|
||||||
json := `{"A":1,"B":2,"C":3}`
|
|
||||||
type S struct {
|
|
||||||
A int
|
|
||||||
E map[string]interface{} `json:",overflow"`
|
|
||||||
C int
|
|
||||||
}
|
|
||||||
var (
|
|
||||||
want = S{1, map[string]interface{}{"B": float64(2)}, 3}
|
|
||||||
dest S
|
|
||||||
)
|
|
||||||
err := Unmarshal([]byte(json), &dest)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("got error %q, want nil", err)
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(dest, want) {
|
|
||||||
t.Errorf("Got %+v; want %+v", dest, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -109,10 +109,6 @@ import (
|
|||||||
// an anonymous struct field in both current and earlier versions, give the field
|
// an anonymous struct field in both current and earlier versions, give the field
|
||||||
// a JSON tag of "-".
|
// a JSON tag of "-".
|
||||||
//
|
//
|
||||||
// The "overflow" option may be used with a struct field of a map type to
|
|
||||||
// indicate that the map contents should be marshalled as if the keys are part
|
|
||||||
// of the struct object itself.
|
|
||||||
//
|
|
||||||
// Map values encode as JSON objects.
|
// Map values encode as JSON objects.
|
||||||
// The map's key type must be string; the object keys are used directly
|
// The map's key type must be string; the object keys are used directly
|
||||||
// as map keys.
|
// as map keys.
|
||||||
@ -243,32 +239,6 @@ var hex = "0123456789abcdef"
|
|||||||
type encodeState struct {
|
type encodeState struct {
|
||||||
bytes.Buffer // accumulated output
|
bytes.Buffer // accumulated output
|
||||||
scratch [64]byte
|
scratch [64]byte
|
||||||
overflow int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encodeState) startOverflow() {
|
|
||||||
e.overflow = e.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *encodeState) endOverflow() {
|
|
||||||
if e.overflow == 0 {
|
|
||||||
panic("endOverflow called before startOverflow")
|
|
||||||
}
|
|
||||||
start, end := e.overflow, e.Len()
|
|
||||||
b := e.Bytes()
|
|
||||||
if b[start] == '{' && b[end-1] == '}' {
|
|
||||||
// Remove surrounding { and }.
|
|
||||||
copy(b[start:], b[start+1:])
|
|
||||||
e.Truncate(end - 2)
|
|
||||||
} else if bytes.Equal(b[start:end], []byte("null")) {
|
|
||||||
// Drop "null".
|
|
||||||
e.Truncate(start)
|
|
||||||
}
|
|
||||||
// Remove trailing comma if overflow value was null or {}.
|
|
||||||
if start > 0 && e.Len() == start && b[start-1] == ',' {
|
|
||||||
e.Truncate(start - 1)
|
|
||||||
}
|
|
||||||
e.overflow = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bradfitz): use a sync.Cache here
|
// TODO(bradfitz): use a sync.Cache here
|
||||||
@ -612,7 +582,7 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
|
|||||||
e.WriteByte('{')
|
e.WriteByte('{')
|
||||||
first := true
|
first := true
|
||||||
for i, f := range se.fields {
|
for i, f := range se.fields {
|
||||||
fv := fieldByIndex(v, f.index, false)
|
fv := fieldByIndex(v, f.index)
|
||||||
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
|
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -621,16 +591,6 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
|
|||||||
} else {
|
} else {
|
||||||
e.WriteByte(',')
|
e.WriteByte(',')
|
||||||
}
|
}
|
||||||
if f.overflow {
|
|
||||||
if tenc := se.fieldEncs[i]; tenc != nil {
|
|
||||||
e.startOverflow()
|
|
||||||
tenc(e, fv, f.quoted)
|
|
||||||
e.endOverflow()
|
|
||||||
} else {
|
|
||||||
panic("no encoder for " + fv.String())
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
e.string(f.name)
|
e.string(f.name)
|
||||||
e.WriteByte(':')
|
e.WriteByte(':')
|
||||||
if tenc := se.fieldEncs[i]; tenc != nil {
|
if tenc := se.fieldEncs[i]; tenc != nil {
|
||||||
@ -650,7 +610,7 @@ func newStructEncoder(t reflect.Type, vx reflect.Value) encoderFunc {
|
|||||||
fieldEncs: make([]encoderFunc, len(fields)),
|
fieldEncs: make([]encoderFunc, len(fields)),
|
||||||
}
|
}
|
||||||
for i, f := range fields {
|
for i, f := range fields {
|
||||||
vxf := fieldByIndex(vx, f.index, false)
|
vxf := fieldByIndex(vx, f.index)
|
||||||
if vxf.IsValid() {
|
if vxf.IsValid() {
|
||||||
se.fieldEncs[i] = typeEncoder(vxf.Type(), vxf)
|
se.fieldEncs[i] = typeEncoder(vxf.Type(), vxf)
|
||||||
}
|
}
|
||||||
@ -790,16 +750,11 @@ func isValidTag(s string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// fieldByIndex fetches (and allocates, if create is true) the field in v
|
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
||||||
// indentified by index.
|
|
||||||
func fieldByIndex(v reflect.Value, index []int, create bool) reflect.Value {
|
|
||||||
for _, i := range index {
|
for _, i := range index {
|
||||||
if v.Kind() == reflect.Ptr {
|
if v.Kind() == reflect.Ptr {
|
||||||
if v.IsNil() {
|
if v.IsNil() {
|
||||||
if !create {
|
return reflect.Value{}
|
||||||
return reflect.Value{}
|
|
||||||
}
|
|
||||||
v.Set(reflect.New(v.Type().Elem()))
|
|
||||||
}
|
}
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
}
|
}
|
||||||
@ -971,7 +926,6 @@ type field struct {
|
|||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
omitEmpty bool
|
omitEmpty bool
|
||||||
quoted bool
|
quoted bool
|
||||||
overflow bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// byName sorts field by name, breaking ties with depth,
|
// byName sorts field by name, breaking ties with depth,
|
||||||
@ -1073,15 +1027,8 @@ func typeFields(t reflect.Type) []field {
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
name = sf.Name
|
name = sf.Name
|
||||||
}
|
}
|
||||||
fields = append(fields, field{
|
fields = append(fields, field{name, tagged, index, ft,
|
||||||
name: name,
|
opts.Contains("omitempty"), opts.Contains("string")})
|
||||||
tag: tagged,
|
|
||||||
index: index,
|
|
||||||
typ: ft,
|
|
||||||
omitEmpty: opts.Contains("omitempty"),
|
|
||||||
quoted: opts.Contains("string"),
|
|
||||||
overflow: opts.Contains("overflow")},
|
|
||||||
)
|
|
||||||
if count[f.typ] > 1 {
|
if count[f.typ] > 1 {
|
||||||
// If there were multiple instances, add a second,
|
// If there were multiple instances, add a second,
|
||||||
// so that the annihilation code will see a duplicate.
|
// so that the annihilation code will see a duplicate.
|
||||||
|
@ -401,59 +401,3 @@ func TestStringBytes(t *testing.T) {
|
|||||||
t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
|
t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeOverflow(t *testing.T) {
|
|
||||||
for _, c := range []struct {
|
|
||||||
in interface{}
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
struct {
|
|
||||||
A int
|
|
||||||
E map[string]interface{} `json:",overflow"`
|
|
||||||
C int
|
|
||||||
}{
|
|
||||||
A: 12,
|
|
||||||
E: map[string]interface{}{"B": 42},
|
|
||||||
C: 64,
|
|
||||||
},
|
|
||||||
`{"A":12,"B":42,"C":64}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
struct {
|
|
||||||
E map[string]interface{} `json:",overflow"`
|
|
||||||
}{
|
|
||||||
E: map[string]interface{}{"B": 42},
|
|
||||||
},
|
|
||||||
`{"B":42}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
struct {
|
|
||||||
A int
|
|
||||||
E map[string]interface{} `json:",overflow"`
|
|
||||||
}{
|
|
||||||
A: 12,
|
|
||||||
},
|
|
||||||
`{"A":12}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
struct {
|
|
||||||
A int
|
|
||||||
E map[string]interface{} `json:",overflow"`
|
|
||||||
}{
|
|
||||||
A: 12,
|
|
||||||
E: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
`{"A":12}`,
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
b, err := Marshal(c.in)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if got := string(b); got != c.want {
|
|
||||||
t.Errorf("Marshal(%q) = %s, want %s", c.in, got, c.want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -33,29 +33,6 @@ func ExampleMarshal() {
|
|||||||
// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
|
// {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMarshal_overflow() {
|
|
||||||
type Record struct {
|
|
||||||
ID string
|
|
||||||
Seq int
|
|
||||||
Meta map[string]string `json:",overflow"`
|
|
||||||
}
|
|
||||||
r := Record{
|
|
||||||
ID: "CheeseWhiz",
|
|
||||||
Seq: 42,
|
|
||||||
Meta: map[string]string{
|
|
||||||
"Created": "1980-06-20",
|
|
||||||
"Destroyed": "1998-02-06",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b, err := json.Marshal(r)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("error:", err)
|
|
||||||
}
|
|
||||||
os.Stdout.Write(b)
|
|
||||||
// Output:
|
|
||||||
// {"ID":"CheeseWhiz","Seq":42,"Created":"1980-06-20","Destroyed":"1998-02-06"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExampleUnmarshal() {
|
func ExampleUnmarshal() {
|
||||||
var jsonBlob = []byte(`[
|
var jsonBlob = []byte(`[
|
||||||
{"Name": "Platypus", "Order": "Monotremata"},
|
{"Name": "Platypus", "Order": "Monotremata"},
|
||||||
@ -75,29 +52,6 @@ func ExampleUnmarshal() {
|
|||||||
// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
|
// [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}]
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleUnmarshal_overflow() {
|
|
||||||
var jsonBlob = []byte(`
|
|
||||||
{
|
|
||||||
"Token": "Kaip4uM1ieng6Eiw",
|
|
||||||
"User": "bimmler",
|
|
||||||
"Animal": "rabbit"
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
type Auth struct {
|
|
||||||
Token string
|
|
||||||
User string
|
|
||||||
Extra map[string]string `json:",overflow"`
|
|
||||||
}
|
|
||||||
var auth Auth
|
|
||||||
err := json.Unmarshal(jsonBlob, &auth)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("error:", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("%+v", auth)
|
|
||||||
// Output:
|
|
||||||
// {Token:Kaip4uM1ieng6Eiw User:bimmler Extra:map[Animal:rabbit]}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This example uses a Decoder to decode a stream of distinct JSON values.
|
// This example uses a Decoder to decode a stream of distinct JSON values.
|
||||||
func ExampleDecoder() {
|
func ExampleDecoder() {
|
||||||
const jsonStream = `
|
const jsonStream = `
|
||||||
|
Loading…
Reference in New Issue
Block a user