mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
encoding/json: allow non-string type keys for (un-)marshal
This CL allows JSON-encoding & -decoding maps whose keys are types that implement encoding.TextMarshaler / TextUnmarshaler. During encode, the map keys are marshaled upfront so that they can be sorted. Fixes #12146 Change-Id: I43809750a7ad82a3603662f095c7baf75fd172da Reviewed-on: https://go-review.googlesource.com/20356 Run-TryBot: Caleb Spare <cespare@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
acefcb732c
commit
ffbd31e9f7
4 changed files with 124 additions and 41 deletions
|
|
@ -61,10 +61,11 @@ import (
|
|||
// If the JSON array is smaller than the Go array,
|
||||
// the additional Go array elements are set to zero values.
|
||||
//
|
||||
// To unmarshal a JSON object into a string-keyed map, Unmarshal first
|
||||
// establishes a map to use, If the map is nil, Unmarshal allocates a new map.
|
||||
// Otherwise Unmarshal reuses the existing map, keeping existing entries.
|
||||
// Unmarshal then stores key-value pairs from the JSON object into the map.
|
||||
// To unmarshal a JSON object into a map, Unmarshal first establishes a map to
|
||||
// use, If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal
|
||||
// reuses the existing map, keeping existing entries. Unmarshal then stores key-
|
||||
// value pairs from the JSON object into the map. The map's key type must
|
||||
// either be a string or implement encoding.TextUnmarshaler.
|
||||
//
|
||||
// If a JSON value is not appropriate for a given target type,
|
||||
// or if a JSON number overflows the target type, Unmarshal
|
||||
|
|
@ -549,6 +550,7 @@ func (d *decodeState) array(v reflect.Value) {
|
|||
}
|
||||
|
||||
var nullLiteral = []byte("null")
|
||||
var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem()
|
||||
|
||||
// object consumes an object from d.data[d.off-1:], decoding into the value v.
|
||||
// the first byte ('{') of the object has been read already.
|
||||
|
|
@ -577,12 +579,15 @@ func (d *decodeState) object(v reflect.Value) {
|
|||
return
|
||||
}
|
||||
|
||||
// Check type of target: struct or map[string]T
|
||||
// Check type of target:
|
||||
// struct or
|
||||
// map[string]T or map[encoding.TextUnmarshaler]T
|
||||
switch v.Kind() {
|
||||
case reflect.Map:
|
||||
// map must have string kind
|
||||
// Map key must either have string kind or be an encoding.TextUnmarshaler.
|
||||
t := v.Type()
|
||||
if t.Key().Kind() != reflect.String {
|
||||
if t.Key().Kind() != reflect.String &&
|
||||
!reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) {
|
||||
d.saveError(&UnmarshalTypeError{"object", v.Type(), int64(d.off)})
|
||||
d.off--
|
||||
d.next() // skip over { } in input
|
||||
|
|
@ -687,7 +692,18 @@ 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())
|
||||
kt := v.Type().Key()
|
||||
var kv reflect.Value
|
||||
switch {
|
||||
case kt.Kind() == reflect.String:
|
||||
kv = reflect.ValueOf(key).Convert(v.Type().Key())
|
||||
case reflect.PtrTo(kt).Implements(textUnmarshalerType):
|
||||
kv = reflect.New(v.Type().Key())
|
||||
d.literalStore(item, kv, true)
|
||||
kv = kv.Elem()
|
||||
default:
|
||||
panic("json: Unexpected key type") // should never occur
|
||||
}
|
||||
v.SetMapIndex(kv, subv)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue