diff --git a/feature_reflect.go b/feature_reflect.go index 4912ae8..8b60d2b 100644 --- a/feature_reflect.go +++ b/feature_reflect.go @@ -510,7 +510,7 @@ func decoderOfMap(typ reflect.Type) (Decoder, error) { return nil, err } mapInterface := reflect.New(typ).Interface() - return &mapDecoder{typ, typ.Elem(), decoder, extractInterface(mapInterface)}, nil + return &mapDecoder{typ, typ.Key(), typ.Elem(), decoder, extractInterface(mapInterface)}, nil } func extractInterface(val interface{}) emptyInterface { diff --git a/feature_reflect_map.go b/feature_reflect_map.go index a2d62d8..98d2fad 100644 --- a/feature_reflect_map.go +++ b/feature_reflect_map.go @@ -5,10 +5,12 @@ import ( "reflect" "encoding/json" "encoding" + "strconv" ) type mapDecoder struct { mapType reflect.Type + keyType reflect.Type elemType reflect.Type elemDecoder Decoder mapInterface emptyInterface @@ -23,12 +25,43 @@ func (decoder *mapDecoder) decode(ptr unsafe.Pointer, iter *Iterator) { if realVal.IsNil() { realVal.Set(reflect.MakeMap(realVal.Type())) } - for field := iter.ReadObject(); field != ""; field = iter.ReadObject() { + iter.ReadObjectCB(func(iter *Iterator, keyStr string) bool{ elem := reflect.New(decoder.elemType) decoder.elemDecoder.decode(unsafe.Pointer(elem.Pointer()), iter) // to put into map, we have to use reflection - realVal.SetMapIndex(reflect.ValueOf(string([]byte(field))), elem.Elem()) + realVal.SetMapIndex(decodeMapKey(iter, keyStr, decoder.keyType), elem.Elem()) + return true + }) +} + +func decodeMapKey(iter *Iterator, keyStr string, keyType reflect.Type) reflect.Value { + switch { + case keyType.Kind() == reflect.String: + return reflect.ValueOf(keyStr) + //case reflect.PtrTo(kt).Implements(textUnmarshalerType): + // kv = reflect.New(v.Type().Key()) + // d.literalStore(item, kv, true) + // kv = kv.Elem() + default: + switch keyType.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + n, err := strconv.ParseInt(keyStr, 10, 64) + if err != nil || reflect.Zero(keyType).OverflowInt(n) { + iter.reportError("read map key as int64", "read int64 failed") + return reflect.ValueOf("") + } + return reflect.ValueOf(n).Convert(keyType) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + n, err := strconv.ParseUint(keyStr, 10, 64) + if err != nil || reflect.Zero(keyType).OverflowUint(n) { + iter.reportError("read map key as uint64", "read uint64 failed") + return reflect.ValueOf("") + } + return reflect.ValueOf(n).Convert(keyType) + } } + iter.reportError("read map key", "json: Unexpected key type") + return reflect.ValueOf("") } type mapEncoder struct { diff --git a/jsoniter_map_test.go b/jsoniter_map_test.go index 7faf82e..9d6dcbb 100644 --- a/jsoniter_map_test.go +++ b/jsoniter_map_test.go @@ -68,10 +68,17 @@ func Test_slice_of_map(t *testing.T) { should.Equal("2", val[0]["1"]) } -func Test_write_int_key_map(t *testing.T) { +func Test_encode_int_key_map(t *testing.T) { should := require.New(t) val := map[int]string{1: "2"} str, err := MarshalToString(val) should.Nil(err) should.Equal(`{"1":"2"}`, str) +} + +func Test_decode_int_key_map(t *testing.T) { + should := require.New(t) + var val map[int]string + should.Nil(UnmarshalFromString(`{"1":"2"}`, &val)) + should.Equal(map[int]string{1: "2"}, val) } \ No newline at end of file