Skip to content

Commit 466001d

Browse files
committed
encoding/json: add "overflow" struct tag option
Fixes #6213. R=golang-dev, dsymonds, bradfitz CC=golang-dev https://golang.org/cl/13180043
1 parent 9169372 commit 466001d

File tree

5 files changed

+240
-49
lines changed

5 files changed

+240
-49
lines changed

src/pkg/encoding/json/decode.go

Lines changed: 59 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ import (
6161
// Instead, they are replaced by the Unicode replacement
6262
// character U+FFFD.
6363
//
64+
// When unmarshalling into struct values, a struct field of map type with the
65+
// "overflow" option will store values whose keys do not match the other struct
66+
// fields.
67+
//
6468
func Unmarshal(data []byte, v interface{}) error {
6569
// Check for well-formedness.
6670
// Avoids filling out half a data structure
@@ -489,8 +493,6 @@ func (d *decodeState) object(v reflect.Value) {
489493
return
490494
}
491495

492-
var mapElem reflect.Value
493-
494496
for {
495497
// Read opening " of string key or closing }.
496498
op := d.scanWhile(scanSkipSpace)
@@ -512,44 +514,7 @@ func (d *decodeState) object(v reflect.Value) {
512514
}
513515

514516
// Figure out field corresponding to key.
515-
var subv reflect.Value
516-
destring := false // whether the value is wrapped in a string to be decoded first
517-
518-
if v.Kind() == reflect.Map {
519-
elemType := v.Type().Elem()
520-
if !mapElem.IsValid() {
521-
mapElem = reflect.New(elemType).Elem()
522-
} else {
523-
mapElem.Set(reflect.Zero(elemType))
524-
}
525-
subv = mapElem
526-
} else {
527-
var f *field
528-
fields := cachedTypeFields(v.Type())
529-
for i := range fields {
530-
ff := &fields[i]
531-
if ff.name == key {
532-
f = ff
533-
break
534-
}
535-
if f == nil && strings.EqualFold(ff.name, key) {
536-
f = ff
537-
}
538-
}
539-
if f != nil {
540-
subv = v
541-
destring = f.quoted
542-
for _, i := range f.index {
543-
if subv.Kind() == reflect.Ptr {
544-
if subv.IsNil() {
545-
subv.Set(reflect.New(subv.Type().Elem()))
546-
}
547-
subv = subv.Elem()
548-
}
549-
subv = subv.Field(i)
550-
}
551-
}
552-
}
517+
subv, mapv, destring := subValue(v, key)
553518

554519
// Read : before value.
555520
if op == scanSkipSpace {
@@ -569,9 +534,9 @@ func (d *decodeState) object(v reflect.Value) {
569534

570535
// Write value back to map;
571536
// if using struct, subv points into struct already.
572-
if v.Kind() == reflect.Map {
573-
kv := reflect.ValueOf(key).Convert(v.Type().Key())
574-
v.SetMapIndex(kv, subv)
537+
if mapv.IsValid() {
538+
kv := reflect.ValueOf(key).Convert(mapv.Type().Key())
539+
mapv.SetMapIndex(kv, subv)
575540
}
576541

577542
// Next token must be , or }.
@@ -585,6 +550,57 @@ func (d *decodeState) object(v reflect.Value) {
585550
}
586551
}
587552

553+
// subValue returns (and allocates, if necessary) the field in the struct or
554+
// map v whose name matches key.
555+
func subValue(v reflect.Value, key string) (subv, mapv reflect.Value, destring bool) {
556+
// Create new map element.
557+
if v.Kind() == reflect.Map {
558+
subv = reflect.New(v.Type().Elem()).Elem()
559+
mapv = v
560+
return
561+
}
562+
563+
// Get struct field.
564+
var f *field
565+
fields := cachedTypeFields(v.Type())
566+
for i := range fields {
567+
ff := &fields[i]
568+
if ff.name == key {
569+
f = ff
570+
break
571+
}
572+
if f == nil && strings.EqualFold(ff.name, key) {
573+
f = ff
574+
}
575+
}
576+
if f != nil {
577+
subv = fieldByIndex(v, f.index, true)
578+
destring = f.quoted
579+
return
580+
}
581+
582+
// Decode into overflow field if present.
583+
for _, f := range fields {
584+
if f.overflow {
585+
// Find overflow field.
586+
mapv = fieldByIndex(v, f.index, true)
587+
if k := mapv.Kind(); k != reflect.Map {
588+
panic("unsupported overflow field kind: " + k.String())
589+
}
590+
// Make map if necessary.
591+
if mapv.IsNil() {
592+
mapv.Set(reflect.MakeMap(mapv.Type()))
593+
}
594+
// Create new map element.
595+
subv = reflect.New(mapv.Type().Elem()).Elem()
596+
return
597+
}
598+
}
599+
600+
// Not found.
601+
return
602+
}
603+
588604
// literal consumes a literal from d.data[d.off-1:], decoding into the value v.
589605
// The first byte of the literal has been read already
590606
// (that's how the caller knows it's a literal).

src/pkg/encoding/json/decode_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,3 +1275,23 @@ func TestSkipArrayObjects(t *testing.T) {
12751275
t.Errorf("got error %q, want nil", err)
12761276
}
12771277
}
1278+
1279+
func TestDecodeOverflow(t *testing.T) {
1280+
json := `{"A":1,"B":2,"C":3}`
1281+
type S struct {
1282+
A int
1283+
E map[string]interface{} `json:",overflow"`
1284+
C int
1285+
}
1286+
var (
1287+
want = S{1, map[string]interface{}{"B": float64(2)}, 3}
1288+
dest S
1289+
)
1290+
err := Unmarshal([]byte(json), &dest)
1291+
if err != nil {
1292+
t.Errorf("got error %q, want nil", err)
1293+
}
1294+
if !reflect.DeepEqual(dest, want) {
1295+
t.Errorf("Got %+v; want %+v", dest, want)
1296+
}
1297+
}

src/pkg/encoding/json/encode.go

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ import (
109109
// an anonymous struct field in both current and earlier versions, give the field
110110
// a JSON tag of "-".
111111
//
112+
// The "overflow" option may be used with a struct field of a map type to
113+
// indicate that the map contents should be marshalled as if the keys are part
114+
// of the struct object itself.
115+
//
112116
// Map values encode as JSON objects.
113117
// The map's key type must be string; the object keys are used directly
114118
// as map keys.
@@ -239,6 +243,32 @@ var hex = "0123456789abcdef"
239243
type encodeState struct {
240244
bytes.Buffer // accumulated output
241245
scratch [64]byte
246+
overflow int
247+
}
248+
249+
func (e *encodeState) startOverflow() {
250+
e.overflow = e.Len()
251+
}
252+
253+
func (e *encodeState) endOverflow() {
254+
if e.overflow == 0 {
255+
panic("endOverflow called before startOverflow")
256+
}
257+
start, end := e.overflow, e.Len()
258+
b := e.Bytes()
259+
if b[start] == '{' && b[end-1] == '}' {
260+
// Remove surrounding { and }.
261+
copy(b[start:], b[start+1:])
262+
e.Truncate(end - 2)
263+
} else if bytes.Equal(b[start:end], []byte("null")) {
264+
// Drop "null".
265+
e.Truncate(start)
266+
}
267+
// Remove trailing comma if overflow value was null or {}.
268+
if start > 0 && e.Len() == start && b[start-1] == ',' {
269+
e.Truncate(start - 1)
270+
}
271+
e.overflow = 0
242272
}
243273

244274
// TODO(bradfitz): use a sync.Cache here
@@ -582,7 +612,7 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
582612
e.WriteByte('{')
583613
first := true
584614
for i, f := range se.fields {
585-
fv := fieldByIndex(v, f.index)
615+
fv := fieldByIndex(v, f.index, false)
586616
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
587617
continue
588618
}
@@ -591,6 +621,16 @@ func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
591621
} else {
592622
e.WriteByte(',')
593623
}
624+
if f.overflow {
625+
if tenc := se.fieldEncs[i]; tenc != nil {
626+
e.startOverflow()
627+
tenc(e, fv, f.quoted)
628+
e.endOverflow()
629+
} else {
630+
panic("no encoder for " + fv.String())
631+
}
632+
continue
633+
}
594634
e.string(f.name)
595635
e.WriteByte(':')
596636
if tenc := se.fieldEncs[i]; tenc != nil {
@@ -610,7 +650,7 @@ func newStructEncoder(t reflect.Type, vx reflect.Value) encoderFunc {
610650
fieldEncs: make([]encoderFunc, len(fields)),
611651
}
612652
for i, f := range fields {
613-
vxf := fieldByIndex(vx, f.index)
653+
vxf := fieldByIndex(vx, f.index, false)
614654
if vxf.IsValid() {
615655
se.fieldEncs[i] = typeEncoder(vxf.Type(), vxf)
616656
}
@@ -750,11 +790,16 @@ func isValidTag(s string) bool {
750790
return true
751791
}
752792

753-
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
793+
// fieldByIndex fetches (and allocates, if create is true) the field in v
794+
// indentified by index.
795+
func fieldByIndex(v reflect.Value, index []int, create bool) reflect.Value {
754796
for _, i := range index {
755797
if v.Kind() == reflect.Ptr {
756798
if v.IsNil() {
757-
return reflect.Value{}
799+
if !create {
800+
return reflect.Value{}
801+
}
802+
v.Set(reflect.New(v.Type().Elem()))
758803
}
759804
v = v.Elem()
760805
}
@@ -926,6 +971,7 @@ type field struct {
926971
typ reflect.Type
927972
omitEmpty bool
928973
quoted bool
974+
overflow bool
929975
}
930976

931977
// byName sorts field by name, breaking ties with depth,
@@ -1027,8 +1073,15 @@ func typeFields(t reflect.Type) []field {
10271073
if name == "" {
10281074
name = sf.Name
10291075
}
1030-
fields = append(fields, field{name, tagged, index, ft,
1031-
opts.Contains("omitempty"), opts.Contains("string")})
1076+
fields = append(fields, field{
1077+
name: name,
1078+
tag: tagged,
1079+
index: index,
1080+
typ: ft,
1081+
omitEmpty: opts.Contains("omitempty"),
1082+
quoted: opts.Contains("string"),
1083+
overflow: opts.Contains("overflow")},
1084+
)
10321085
if count[f.typ] > 1 {
10331086
// If there were multiple instances, add a second,
10341087
// so that the annihilation code will see a duplicate.

src/pkg/encoding/json/encode_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,3 +401,59 @@ func TestStringBytes(t *testing.T) {
401401
t.Errorf("encodings differ at %#q vs %#q", enc, encBytes)
402402
}
403403
}
404+
405+
func TestEncodeOverflow(t *testing.T) {
406+
for _, c := range []struct {
407+
in interface{}
408+
want string
409+
}{
410+
{
411+
struct {
412+
A int
413+
E map[string]interface{} `json:",overflow"`
414+
C int
415+
}{
416+
A: 12,
417+
E: map[string]interface{}{"B": 42},
418+
C: 64,
419+
},
420+
`{"A":12,"B":42,"C":64}`,
421+
},
422+
{
423+
struct {
424+
E map[string]interface{} `json:",overflow"`
425+
}{
426+
E: map[string]interface{}{"B": 42},
427+
},
428+
`{"B":42}`,
429+
},
430+
{
431+
struct {
432+
A int
433+
E map[string]interface{} `json:",overflow"`
434+
}{
435+
A: 12,
436+
},
437+
`{"A":12}`,
438+
},
439+
{
440+
struct {
441+
A int
442+
E map[string]interface{} `json:",overflow"`
443+
}{
444+
A: 12,
445+
E: map[string]interface{}{},
446+
},
447+
`{"A":12}`,
448+
},
449+
} {
450+
b, err := Marshal(c.in)
451+
if err != nil {
452+
t.Error(err)
453+
continue
454+
}
455+
if got := string(b); got != c.want {
456+
t.Errorf("Marshal(%q) = %s, want %s", c.in, got, c.want)
457+
}
458+
}
459+
}

0 commit comments

Comments
 (0)