diff options
| author | Augusto Roman <aroman@gmail.com> | 2016-03-08 12:41:35 -0800 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2016-04-05 15:08:04 +0000 |
| commit | ffbd31e9f79ad8b6aaeceac1397678e237581064 (patch) | |
| tree | 1f6a77560f5d80df8b1198d496156a5c5f47a703 /src/encoding/json/decode.go | |
| parent | acefcb732cae4daa59a621cb102793860b564a12 (diff) | |
| download | go-ffbd31e9f79ad8b6aaeceac1397678e237581064.tar.xz | |
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>
Diffstat (limited to 'src/encoding/json/decode.go')
| -rw-r--r-- | src/encoding/json/decode.go | 32 |
1 files changed, 24 insertions, 8 deletions
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 3e4b16e410..a7ff8cf3dc 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -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) } |
