aboutsummaryrefslogtreecommitdiff
path: root/src/encoding/json/decode.go
diff options
context:
space:
mode:
authorAugusto Roman <aroman@gmail.com>2016-03-08 12:41:35 -0800
committerBrad Fitzpatrick <bradfitz@golang.org>2016-04-05 15:08:04 +0000
commitffbd31e9f79ad8b6aaeceac1397678e237581064 (patch)
tree1f6a77560f5d80df8b1198d496156a5c5f47a703 /src/encoding/json/decode.go
parentacefcb732cae4daa59a621cb102793860b564a12 (diff)
downloadgo-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.go32
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)
}