dubbo.apache.org/dubbo-go/v3@v3.1.1/filter/generic/generalizer/map.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package generalizer
    19  
    20  import (
    21  	"reflect"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  import (
    28  	hessian "github.com/apache/dubbo-go-hessian2"
    29  
    30  	"github.com/dubbogo/gost/log/logger"
    31  
    32  	"github.com/mitchellh/mapstructure"
    33  
    34  	perrors "github.com/pkg/errors"
    35  )
    36  
    37  import (
    38  	"dubbo.apache.org/dubbo-go/v3/protocol/dubbo/hessian2"
    39  )
    40  
    41  var (
    42  	mapGeneralizer     Generalizer
    43  	mapGeneralizerOnce sync.Once
    44  )
    45  
    46  func GetMapGeneralizer() Generalizer {
    47  	mapGeneralizerOnce.Do(func() {
    48  		mapGeneralizer = &MapGeneralizer{}
    49  	})
    50  	return mapGeneralizer
    51  }
    52  
    53  type MapGeneralizer struct{}
    54  
    55  func (g *MapGeneralizer) Generalize(obj interface{}) (gobj interface{}, err error) {
    56  	gobj = objToMap(obj)
    57  	return
    58  }
    59  
    60  func (g *MapGeneralizer) Realize(obj interface{}, typ reflect.Type) (interface{}, error) {
    61  	newobj := reflect.New(typ).Interface()
    62  	err := mapstructure.Decode(obj, newobj)
    63  	if err != nil {
    64  		return nil, perrors.Errorf("realizing map failed, %v", err)
    65  	}
    66  
    67  	return reflect.ValueOf(newobj).Elem().Interface(), nil
    68  }
    69  
    70  func (g *MapGeneralizer) GetType(obj interface{}) (typ string, err error) {
    71  	typ, err = hessian2.GetJavaName(obj)
    72  	// no error or error is not NilError
    73  	if err == nil || err != hessian2.NilError {
    74  		return
    75  	}
    76  
    77  	typ = "java.lang.Object"
    78  	if err == hessian2.NilError {
    79  		logger.Debugf("the type of nil object couldn't be inferred, use the default value(\"%s\")", typ)
    80  		return
    81  	}
    82  
    83  	logger.Debugf("the type of object(=%T) couldn't be recognized as a POJO, use the default value(\"%s\")", obj, typ)
    84  	return
    85  }
    86  
    87  // objToMap converts an object(interface{}) to a map
    88  func objToMap(obj interface{}) interface{} {
    89  	if obj == nil {
    90  		return obj
    91  	}
    92  
    93  	t := reflect.TypeOf(obj)
    94  	v := reflect.ValueOf(obj)
    95  
    96  	// if obj is a POJO, get the struct from the pointer (if it is a pointer)
    97  	pojo, isPojo := obj.(hessian.POJO)
    98  	if isPojo {
    99  		for t.Kind() == reflect.Ptr {
   100  			t = t.Elem()
   101  			v = v.Elem()
   102  		}
   103  	}
   104  
   105  	switch t.Kind() {
   106  	case reflect.Struct:
   107  		result := make(map[string]interface{}, t.NumField())
   108  		if isPojo {
   109  			result["class"] = pojo.JavaClassName()
   110  		}
   111  		for i := 0; i < t.NumField(); i++ {
   112  			field := t.Field(i)
   113  			value := v.Field(i)
   114  			kind := value.Kind()
   115  			if !value.CanInterface() {
   116  				logger.Debugf("objToMap for %v is skipped because it couldn't be converted to interface", field)
   117  				continue
   118  			}
   119  			valueIface := value.Interface()
   120  			switch kind {
   121  			case reflect.Ptr:
   122  				if value.IsNil() {
   123  					setInMap(result, field, nil)
   124  					continue
   125  				}
   126  				setInMap(result, field, objToMap(valueIface))
   127  			case reflect.Struct, reflect.Slice, reflect.Map:
   128  				if isPrimitive(valueIface) {
   129  					logger.Warnf("\"%s\" is primitive. The application may crash if it's transferred between "+
   130  						"systems implemented by different languages, e.g. dubbo-go <-> dubbo-java. We recommend "+
   131  						"you represent the object by basic types, like string.", value.Type())
   132  					setInMap(result, field, valueIface)
   133  					continue
   134  				}
   135  
   136  				setInMap(result, field, objToMap(valueIface))
   137  			default:
   138  				setInMap(result, field, valueIface)
   139  			}
   140  		}
   141  		return result
   142  	case reflect.Array, reflect.Slice:
   143  		value := reflect.ValueOf(obj)
   144  		newTemps := make([]interface{}, 0, value.Len())
   145  		for i := 0; i < value.Len(); i++ {
   146  			newTemp := objToMap(value.Index(i).Interface())
   147  			newTemps = append(newTemps, newTemp)
   148  		}
   149  		return newTemps
   150  	case reflect.Map:
   151  		newTempMap := make(map[interface{}]interface{}, v.Len())
   152  		iter := v.MapRange()
   153  		for iter.Next() {
   154  			if !iter.Value().CanInterface() {
   155  				continue
   156  			}
   157  			key := iter.Key()
   158  			mapV := iter.Value().Interface()
   159  			newTempMap[mapKey(key)] = objToMap(mapV)
   160  		}
   161  		return newTempMap
   162  	case reflect.Ptr:
   163  		return objToMap(v.Elem().Interface())
   164  	default:
   165  		return obj
   166  	}
   167  }
   168  
   169  // mapKey converts the map key to interface type
   170  func mapKey(key reflect.Value) interface{} {
   171  	switch key.Kind() {
   172  	case reflect.Bool, reflect.Int, reflect.Int8,
   173  		reflect.Int16, reflect.Int32, reflect.Int64,
   174  		reflect.Uint, reflect.Uint8, reflect.Uint16,
   175  		reflect.Uint32, reflect.Uint64, reflect.Float32,
   176  		reflect.Float64, reflect.String:
   177  		return key.Interface()
   178  	default:
   179  		name := key.String()
   180  		if name == "class" {
   181  			panic(`"class" is a reserved keyword`)
   182  		}
   183  		return name
   184  	}
   185  }
   186  
   187  // setInMap sets the struct into the map using the tag or the name of the struct as the key
   188  func setInMap(m map[string]interface{}, structField reflect.StructField, value interface{}) (result map[string]interface{}) {
   189  	result = m
   190  	if tagName := structField.Tag.Get("m"); tagName == "" {
   191  		result[toUnexport(structField.Name)] = value
   192  	} else {
   193  		result[tagName] = value
   194  	}
   195  	return
   196  }
   197  
   198  // toUnexport is to lower the first letter
   199  func toUnexport(a string) string {
   200  	return strings.ToLower(a[:1]) + a[1:]
   201  }
   202  
   203  // isPrimitive determines if the object is primitive
   204  func isPrimitive(obj interface{}) bool {
   205  	if _, ok := obj.(time.Time); ok {
   206  		return true
   207  	}
   208  	return false
   209  }