Skip to content

Commit

Permalink
Merge pull request #261 from wongoo/fix-259
Browse files Browse the repository at this point in the history
Fix map decode error issue #259
  • Loading branch information
AlexStocks committed Mar 13, 2021
2 parents cbcfbc2 + ece0e49 commit f61ce42
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 68 deletions.
45 changes: 30 additions & 15 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ type Decoder struct {
reader *bufio.Reader
refs []interface{}
// record type refs, both list and map need it
// todo: map
typeRefs *TypeRefs
classInfoList []*classInfo
isSkip bool
Expand Down Expand Up @@ -162,36 +161,52 @@ func (d *Decoder) nextRune(s []rune) []rune {
}

// read the type of data, used to decode list or map
func (d *Decoder) decType() (string, error) {
func (d *Decoder) decMapType() (reflect.Type, error) {
var (
err error
arr [1]byte
buf []byte
tag byte
idx int32
typ reflect.Type
err error
arr [1]byte
buf []byte
tag byte
idx int32
typ reflect.Type
typName string
)

buf = arr[:1]
if _, err = io.ReadFull(d.reader, buf); err != nil {
return "", perrors.WithStack(err)
return nil, perrors.WithStack(err)
}
tag = buf[0]
if (tag >= BC_STRING_DIRECT && tag <= STRING_DIRECT_MAX) ||
(tag >= 0x30 && tag <= 0x33) || (tag == BC_STRING) || (tag == BC_STRING_CHUNK) {
return d.decString(int32(tag))
typName, err = d.decString(int32(tag))
if err != nil {
return nil, perrors.WithStack(err)
}

info, ok := getStructInfo(typName)
if ok {
typ = info.typ
} else {
typ = reflect.TypeOf(map[interface{}]interface{}{})
}

// add to type map
d.typeRefs.appendTypeRefs(typName, typ)

return typ, nil
}

if idx, err = d.decInt32(int32(tag)); err != nil {
return "", perrors.WithStack(err)
return nil, perrors.WithStack(err)
}

typ, _, err = d.getStructDefByIndex(int(idx))
if err == nil {
return typ.String(), nil
typ = d.typeRefs.Get(int(idx))
if typ == nil {
return nil, perrors.Errorf("the type ref index %d is out of range", idx)
}

return "", err
return typ, err
}

// Decode parse hessian data, and ensure the reflection value unpacked
Expand Down
2 changes: 1 addition & 1 deletion decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func testDecodeFrameworkWithSkip(t *testing.T, method string, expected interface
func testDecodeJavaData(t *testing.T, method, className string, skip bool, expected interface{}) {
r, e := decodeJavaResponse(method, className, skip)
if e != nil {
t.Errorf("%s: decode fail with error %v", method, e)
t.Errorf("%s: decode fail with error: %v", method, e)
return
}

Expand Down
85 changes: 33 additions & 52 deletions map.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,20 +217,20 @@ func (d *Decoder) decMapByValue(value reflect.Value) error {
return nil
}

// TODO to decode ref object in map
// decode map object
func (d *Decoder) decMap(flag int32) (interface{}, error) {
var (
err error
tag byte
ok bool
t string
m map[interface{}]interface{}
k interface{}
v interface{}
inst interface{}
instValue reflect.Value
fieldName string
fieldValue reflect.Value
typ reflect.Type
)

if flag != TAG_READ {
Expand All @@ -245,67 +245,48 @@ func (d *Decoder) decMap(flag int32) (interface{}, error) {
case tag == BC_REF:
return d.decRef(int32(tag))
case tag == BC_MAP:
if t, err = d.decType(); err != nil {
if typ, err = d.decMapType(); err != nil {
return nil, err
}

_, ok = checkPOJORegistry(t)
if ok {
inst = createInstance(t)
instValue = reflect.ValueOf(inst)
d.appendRefs(inst)
for d.peekByte() != BC_END {
k, err = d.Decode()
if err != nil {
return nil, err
}
v, err = d.Decode()
if err != nil {
return nil, err
}
if typ.Kind() == reflect.Map {
instValue = reflect.MakeMap(typ)
} else {
instValue = reflect.New(typ).Elem()
}

fieldName, ok = k.(string)
if !ok {
return nil, perrors.Errorf("the type of map key must be string, but get %v", k)
}
if instValue.Kind() == reflect.Map {
instValue.SetMapIndex(reflect.ValueOf(k), EnsureRawValue(v))
} else {
fieldValue = instValue.FieldByName(fieldName)
if fieldValue.IsValid() {
fieldValue.Set(EnsureRawValue(v))
}
}
inst = instValue.Interface()

d.appendRefs(inst)

for d.peekByte() != BC_END {
k, err = d.Decode()
if err != nil {
return nil, err
}
_, err = d.ReadByte()
v, err = d.Decode()
if err != nil {
return nil, perrors.WithStack(err)
return nil, err
}
return inst, nil
} else {
m = make(map[interface{}]interface{})
classIndex := RegisterPOJOMapping(t, m)
d.appendClsDef(pojoRegistry.classInfoList[classIndex])

d.appendRefs(m)
for d.peekByte() != BC_END {
k, err = d.Decode()
if err != nil {
return nil, err

if typ.Kind() == reflect.Map {
instValue.SetMapIndex(reflect.ValueOf(k), EnsureRawValue(v))
} else {
fieldName, ok = k.(string)
if !ok {
return nil, perrors.Errorf("the type of map key must be string, but get %v", k)
}
v, err = d.Decode()
if err != nil {
return nil, err
fieldValue = instValue.FieldByName(fieldName)
if fieldValue.IsValid() {
fieldValue.Set(EnsureRawValue(v))
}
m[k] = v
}
_, err = d.ReadByte()
if err != nil {
return nil, perrors.WithStack(err)
}
return m, nil
}

_, err = d.ReadByte()
if err != nil {
return nil, perrors.WithStack(err)
}
return inst, nil
case tag == BC_MAP_UNTYPED:
m = make(map[interface{}]interface{})
d.appendRefs(m)
Expand Down
20 changes: 20 additions & 0 deletions map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import (
"testing"
)

import (
big "github.com/dubbogo/gost/math/big"
)

func TestEncUntypedMap(t *testing.T) {
var (
m map[interface{}]interface{}
Expand Down Expand Up @@ -106,4 +110,20 @@ func TestCustomMap(t *testing.T) {
}
testDecodeFramework(t, "customReplyMapInMap", mapInMap)
testDecodeFramework(t, "customReplyMapInMapJsonObject", mapInMap)

b3 := &big.Decimal{}
_ = b3.FromString("33.33")
b3.Value = "33.33"

b5 := &big.Decimal{}
_ = b5.FromString("55.55")
b5.Value = "55.55"

multipleTypeMap := map[interface{}]interface{}{
"m1": map[interface{}]interface{}{"a": int32(1), "b": int32(2)},
"m2": map[interface{}]interface{}{int64(3): "c", int64(4): "d"},
"m3": map[interface{}]interface{}{int32(3): b3, int32(5): b5},
}

testDecodeFramework(t, "customReplyMultipleTypeMap", multipleTypeMap)
}
17 changes: 17 additions & 0 deletions serialize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,23 @@ func TestDecimalListGoDecode(t *testing.T) {
}
}

func TestCustomReplyObjectJsonObjectBigDecimalDecode(t *testing.T) {
decimal := &big.Decimal{}
_ = decimal.FromString("100")

out, err := decodeJavaResponse(`customReplyObjectJsonObjectBigDecimal`, ``, false)
if err != nil {
t.Error(err)
return
}

res := out.(map[interface{}]interface{})
assert.Equal(t, 1, len(res))

dec := res["test_BigDecimal"].(*big.Decimal)
assert.Equal(t, decimal.String(), dec.String())
}

func TestObjectListGoDecode(t *testing.T) {
data := []string{
"1234",
Expand Down
28 changes: 28 additions & 0 deletions test_hessian/src/main/java/test/TestCustomReply.java
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,14 @@ public void customReplyTypedFixedList_BigDecimal() throws Exception {
output.flush();
}

public void customReplyObjectJsonObjectBigDecimal() throws Exception {
JSONObject t = new JSONObject();
BigDecimal decimal = new BigDecimal("100");
t.put("test_BigDecimal",decimal);
output.writeObject(t);
output.flush();
}

public void customReplyTypedFixedDateNull() throws Exception {
DateDemo demo = new DateDemo("zhangshan", null, null);
output.writeObject(demo);
Expand Down Expand Up @@ -492,6 +500,25 @@ public void customReplyMap() throws Exception {
output.flush();
}

public void customReplyMultipleTypeMap() throws Exception {
Map<String, Integer> map1 = new HashMap<String, Integer>(4);
map1.put("a", 1);
map1.put("b", 2);
Map<Long, String> map2 = new HashMap<Long, String>(4);
map2.put(3L, "c");
map2.put(4L, "d");
Map<Integer, BigDecimal> map3 = new HashMap<Integer, BigDecimal>(4);
map3.put(5,new BigDecimal("55.55"));
map3.put(3,new BigDecimal("33.33"));
Map<String, Object> map = new HashMap<String, Object>(4);
map.put("m1", map1);
map.put("m2", map2);
map.put("m3", map3);

output.writeObject(map);
output.flush();
}

public Map<String, Object> mapInMap() throws Exception {
Map<String, Object> map1 = new HashMap<String, Object>();
map1.put("a", 1);
Expand Down Expand Up @@ -567,3 +594,4 @@ class InnerPerson implements Serializable {
public String name;
public Integer age;
}

0 comments on commit f61ce42

Please sign in to comment.