You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/08/03 00:35:32 UTC
[dubbo-go] branch 1.5 updated: Fix: struct2MapAll logics (#1356)
This is an automated email from the ASF dual-hosted git repository.
alexstocks pushed a commit to branch 1.5
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git
The following commit(s) were added to refs/heads/1.5 by this push:
new 2dca192 Fix: struct2MapAll logics (#1356)
2dca192 is described below
commit 2dca19267ae3286cb6ae7fcff329fee4a26903fe
Author: XavierNiu <a...@nxw.name>
AuthorDate: Tue Aug 3 08:35:24 2021 +0800
Fix: struct2MapAll logics (#1356)
* fix struct2MapAll logics
* fix size computation for allocation may overflow
---
cluster/router/tag/router_rule.go | 2 +-
filter/filter_impl/generic_filter.go | 97 ++++++++++++++++++++--------
filter/filter_impl/generic_filter_test.go | 22 ++++++-
filter/filter_impl/generic_service_filter.go | 2 +-
4 files changed, 91 insertions(+), 32 deletions(-)
diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go
index 512d8f1..8993a6c 100644
--- a/cluster/router/tag/router_rule.go
+++ b/cluster/router/tag/router_rule.go
@@ -58,7 +58,7 @@ func getRule(rawRule string) (*RouterRule, error) {
// parseTags use for flattening tags data to @addressToTagNames and @tagNameToAddresses
func (t *RouterRule) parseTags() {
- t.AddressToTagNames = make(map[string][]string, 2*len(t.Tags))
+ t.AddressToTagNames = make(map[string][]string, len(t.Tags))
t.TagNameToAddresses = make(map[string][]string, len(t.Tags))
for _, tag := range t.Tags {
for _, address := range tag.Addresses {
diff --git a/filter/filter_impl/generic_filter.go b/filter/filter_impl/generic_filter.go
index cf307d0..86f8cfd 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -31,6 +31,7 @@ import (
import (
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
+ "github.com/apache/dubbo-go/common/logger"
"github.com/apache/dubbo-go/filter"
"github.com/apache/dubbo-go/protocol"
invocation2 "github.com/apache/dubbo-go/protocol/invocation"
@@ -59,7 +60,7 @@ func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i
if oldParams, ok := oldArguments[2].([]interface{}); ok {
newParams := make([]hessian.Object, 0, len(oldParams))
for i := range oldParams {
- newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
+ newParams = append(newParams, hessian.Object(objToMap(oldParams[i])))
}
newArguments := []interface{}{
oldArguments[0],
@@ -85,44 +86,70 @@ func GetGenericFilter() filter.Filter {
return &GenericFilter{}
}
-func struct2MapAll(obj interface{}) interface{} {
+func objToMap(obj interface{}) interface{} {
if obj == nil {
return obj
}
+
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
- if t.Kind() == reflect.Struct {
+
+ // if obj is a POJO, get the struct from the pointer (if it is a pointer)
+ pojo, isPojo := obj.(hessian.POJO)
+ if isPojo {
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ v = v.Elem()
+ }
+ }
+
+ switch t.Kind() {
+ case reflect.Struct:
result := make(map[string]interface{}, t.NumField())
+ if isPojo {
+ result["class"] = pojo.JavaClassName()
+ }
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
kind := value.Kind()
- if kind == reflect.Struct || kind == reflect.Slice || kind == reflect.Map {
- if value.CanInterface() {
- tmp := value.Interface()
- if _, ok := tmp.(time.Time); ok {
- setInMap(result, field, tmp)
- continue
- }
- setInMap(result, field, struct2MapAll(tmp))
+ if !value.CanInterface() {
+ logger.Debugf("objToMap for %v is skipped because it couldn't be converted to interface", field)
+ continue
+ }
+ valueIface := value.Interface()
+ switch kind {
+ case reflect.Ptr:
+ if value.IsNil() {
+ setInMap(result, field, nil)
+ continue
}
- } else {
- if value.CanInterface() {
- setInMap(result, field, value.Interface())
+ setInMap(result, field, objToMap(valueIface))
+ case reflect.Struct, reflect.Slice, reflect.Map:
+ if isPrimitive(valueIface) {
+ logger.Warnf("\"%s\" is primitive. The application may crash if it's transferred between "+
+ "systems implemented by different languages, e.g. dubbo-go <-> dubbo-java. We recommend "+
+ "you represent the object by basic types, like string.", value.Type())
+ setInMap(result, field, valueIface)
+ continue
}
+
+ setInMap(result, field, objToMap(valueIface))
+ default:
+ setInMap(result, field, valueIface)
}
}
return result
- } else if t.Kind() == reflect.Slice {
+ case reflect.Array, reflect.Slice:
value := reflect.ValueOf(obj)
- var newTemps = make([]interface{}, 0, value.Len())
+ newTemps := make([]interface{}, 0, value.Len())
for i := 0; i < value.Len(); i++ {
- newTemp := struct2MapAll(value.Index(i).Interface())
+ newTemp := objToMap(value.Index(i).Interface())
newTemps = append(newTemps, newTemp)
}
return newTemps
- } else if t.Kind() == reflect.Map {
- var newTempMap = make(map[interface{}]interface{}, v.Len())
+ case reflect.Map:
+ newTempMap := make(map[interface{}]interface{}, v.Len())
iter := v.MapRange()
for iter.Next() {
if !iter.Value().CanInterface() {
@@ -130,15 +157,18 @@ func struct2MapAll(obj interface{}) interface{} {
}
key := iter.Key()
mapV := iter.Value().Interface()
- newTempMap[convertMapKey(key)] = struct2MapAll(mapV)
+ newTempMap[mapKey(key)] = objToMap(mapV)
}
return newTempMap
- } else {
+ case reflect.Ptr:
+ return objToMap(v.Elem().Interface())
+ default:
return obj
}
}
-func convertMapKey(key reflect.Value) interface{} {
+// mapKey converts the map key to interface type
+func mapKey(key reflect.Value) interface{} {
switch key.Kind() {
case reflect.Bool, reflect.Int, reflect.Int8,
reflect.Int16, reflect.Int32, reflect.Int64,
@@ -147,21 +177,34 @@ func convertMapKey(key reflect.Value) interface{} {
reflect.Float64, reflect.String:
return key.Interface()
default:
- return key.String()
+ name := key.String()
+ if name == "class" {
+ panic(`"class" is a reserved keyword`)
+ }
+ return name
}
}
+// setInMap sets the struct into the map using the tag or the name of the struct as the key
func setInMap(m map[string]interface{}, structField reflect.StructField, value interface{}) (result map[string]interface{}) {
result = m
if tagName := structField.Tag.Get("m"); tagName == "" {
- result[headerAtoa(structField.Name)] = value
+ result[toUnexport(structField.Name)] = value
} else {
result[tagName] = value
}
return
}
-func headerAtoa(a string) (b string) {
- b = strings.ToLower(a[:1]) + a[1:]
- return
+// toUnexport is to lower the first letter
+func toUnexport(a string) string {
+ return strings.ToLower(a[:1]) + a[1:]
+}
+
+// isPrimitive determines if the object is primitive
+func isPrimitive(obj interface{}) bool {
+ if _, ok := obj.(time.Time); ok {
+ return true
+ }
+ return false
}
diff --git a/filter/filter_impl/generic_filter_test.go b/filter/filter_impl/generic_filter_test.go
index 4203dd6..4a1f12e 100644
--- a/filter/filter_impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_filter_test.go
@@ -50,7 +50,7 @@ func TestStruct2MapAll(t *testing.T) {
testData.CaCa.XxYy.Xx = "3"
testData.DaDa = time.Date(2020, 10, 29, 2, 34, 0, 0, time.Local)
testData.EeEe = 100
- m := struct2MapAll(testData).(map[string]interface{})
+ m := objToMap(testData).(map[string]interface{})
assert.Equal(t, "1", m["aaAa"].(string))
assert.Equal(t, "1", m["baBa"].(string))
assert.Equal(t, "2", m["caCa"].(map[string]interface{})["aaAa"].(string))
@@ -85,7 +85,7 @@ func TestStruct2MapAllSlice(t *testing.T) {
tmp.XxYy.xxXx = "3"
tmp.XxYy.Xx = "3"
testData.CaCa = append(testData.CaCa, tmp)
- m := struct2MapAll(testData).(map[string]interface{})
+ m := objToMap(testData).(map[string]interface{})
assert.Equal(t, "1", m["aaAa"].(string))
assert.Equal(t, "1", m["baBa"].(string))
@@ -120,7 +120,7 @@ func TestStruct2MapAllMap(t *testing.T) {
testData.CaCa["k1"] = "v1"
testData.CaCa["kv2"] = "v2"
testData.IntMap[1] = 1
- m := struct2MapAll(testData)
+ m := objToMap(testData)
assert.Equal(t, reflect.Map, reflect.TypeOf(m).Kind())
mappedStruct := m.(map[string]interface{})
@@ -135,3 +135,19 @@ func TestStruct2MapAllMap(t *testing.T) {
assert.Equal(t, reflect.Map, reflect.TypeOf(intMap).Kind())
assert.Equal(t, 1, intMap.(map[interface{}]interface{})[1])
}
+
+type mockParent struct {
+ Children []*mockChild
+}
+
+func (p *mockParent) JavaClassName() string {
+ return "org.apache.dubbo.mockParent"
+}
+
+type mockChild struct {
+ Name string
+}
+
+func (p *mockChild) JavaClassName() string {
+ return "org.apache.dubbo.mockChild"
+}
diff --git a/filter/filter_impl/generic_service_filter.go b/filter/filter_impl/generic_service_filter.go
index e843f2e..6056224 100644
--- a/filter/filter_impl/generic_service_filter.go
+++ b/filter/filter_impl/generic_service_filter.go
@@ -121,7 +121,7 @@ func (ef *GenericServiceFilter) OnResponse(ctx context.Context, result protocol.
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
- result.SetResult(struct2MapAll(v.Interface()))
+ result.SetResult(objToMap(v.Interface()))
}
return result
}