Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: struct2MapAll logics #1356

Merged
merged 2 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cluster/router/tag/router_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
97 changes: 70 additions & 27 deletions filter/filter_impl/generic_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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],
Expand All @@ -85,60 +86,89 @@ 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() {
continue
}
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,
Expand All @@ -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
}
22 changes: 19 additions & 3 deletions filter/filter_impl/generic_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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{})
Expand All @@ -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"
}
2 changes: 1 addition & 1 deletion filter/filter_impl/generic_service_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down