github.com/gogf/gf/v2@v2.7.4/util/gconv/gconv_struct.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gconv
     8  
     9  import (
    10  	"reflect"
    11  	"strings"
    12  
    13  	"github.com/gogf/gf/v2/errors/gcode"
    14  	"github.com/gogf/gf/v2/errors/gerror"
    15  	"github.com/gogf/gf/v2/internal/empty"
    16  	"github.com/gogf/gf/v2/internal/json"
    17  	"github.com/gogf/gf/v2/internal/utils"
    18  	"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
    19  	"github.com/gogf/gf/v2/util/gconv/internal/structcache"
    20  )
    21  
    22  // Struct maps the params key-value pairs to the corresponding struct object's attributes.
    23  // The third parameter `mapping` is unnecessary, indicating the mapping rules between the
    24  // custom key name and the attribute name(case-sensitive).
    25  //
    26  // Note:
    27  //  1. The `params` can be any type of map/struct, usually a map.
    28  //  2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object
    29  //     or struct pointer.
    30  //  3. Only the public attributes of struct object can be mapped.
    31  //  4. If `params` is a map, the key of the map `params` can be lowercase.
    32  //     It will automatically convert the first letter of the key to uppercase
    33  //     in mapping procedure to do the matching.
    34  //     It ignores the map key, if it does not match.
    35  func Struct(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
    36  	return Scan(params, pointer, paramKeyToAttrMap...)
    37  }
    38  
    39  // StructTag acts as Struct but also with support for priority tag feature, which retrieves the
    40  // specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping.
    41  // The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','.
    42  func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
    43  	return doStruct(params, pointer, nil, priorityTag)
    44  }
    45  
    46  // doStruct is the core internal converting function for any data to struct.
    47  func doStruct(
    48  	params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
    49  ) (err error) {
    50  	if params == nil {
    51  		// If `params` is nil, no conversion.
    52  		return nil
    53  	}
    54  	if pointer == nil {
    55  		return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
    56  	}
    57  
    58  	// JSON content converting.
    59  	ok, err := doConvertWithJsonCheck(params, pointer)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	if ok {
    64  		return nil
    65  	}
    66  
    67  	defer func() {
    68  		// Catch the panic, especially the reflection operation panics.
    69  		if exception := recover(); exception != nil {
    70  			if v, ok := exception.(error); ok && gerror.HasStack(v) {
    71  				err = v
    72  			} else {
    73  				err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
    74  			}
    75  		}
    76  	}()
    77  
    78  	var (
    79  		paramsReflectValue      reflect.Value
    80  		paramsInterface         interface{} // DO NOT use `params` directly as it might be type `reflect.Value`
    81  		pointerReflectValue     reflect.Value
    82  		pointerReflectKind      reflect.Kind
    83  		pointerElemReflectValue reflect.Value // The reflection value to struct element.
    84  	)
    85  	if v, ok := params.(reflect.Value); ok {
    86  		paramsReflectValue = v
    87  	} else {
    88  		paramsReflectValue = reflect.ValueOf(params)
    89  	}
    90  	paramsInterface = paramsReflectValue.Interface()
    91  	if v, ok := pointer.(reflect.Value); ok {
    92  		pointerReflectValue = v
    93  		pointerElemReflectValue = v
    94  	} else {
    95  		pointerReflectValue = reflect.ValueOf(pointer)
    96  		pointerReflectKind = pointerReflectValue.Kind()
    97  		if pointerReflectKind != reflect.Ptr {
    98  			return gerror.NewCodef(
    99  				gcode.CodeInvalidParameter,
   100  				"destination pointer should be type of '*struct', but got '%v'",
   101  				pointerReflectKind,
   102  			)
   103  		}
   104  		// Using IsNil on reflect.Ptr variable is OK.
   105  		if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() {
   106  			return gerror.NewCode(
   107  				gcode.CodeInvalidParameter,
   108  				"destination pointer cannot be nil",
   109  			)
   110  		}
   111  		pointerElemReflectValue = pointerReflectValue.Elem()
   112  	}
   113  
   114  	// If `params` and `pointer` are the same type, the do directly assignment.
   115  	// For performance enhancement purpose.
   116  	if ok = doConvertWithTypeCheck(paramsReflectValue, pointerElemReflectValue); ok {
   117  		return nil
   118  	}
   119  
   120  	// custom convert.
   121  	if ok, err = callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
   122  		return err
   123  	}
   124  
   125  	// Normal unmarshalling interfaces checks.
   126  	if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
   127  		return err
   128  	}
   129  
   130  	// It automatically creates struct object if necessary.
   131  	// For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User.
   132  	if pointerElemReflectValue.Kind() == reflect.Ptr {
   133  		if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
   134  			e := reflect.New(pointerElemReflectValue.Type().Elem())
   135  			pointerElemReflectValue.Set(e)
   136  			defer func() {
   137  				if err != nil {
   138  					// If it is converted failed, it reset the `pointer` to nil.
   139  					pointerReflectValue.Elem().Set(reflect.Zero(pointerReflectValue.Type().Elem()))
   140  				}
   141  			}()
   142  		}
   143  		// if v, ok := pointerElemReflectValue.Interface().(localinterface.IUnmarshalValue); ok {
   144  		//	return v.UnmarshalValue(params)
   145  		// }
   146  		// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
   147  		if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
   148  			return err
   149  		}
   150  		// Retrieve its element, may be struct at last.
   151  		pointerElemReflectValue = pointerElemReflectValue.Elem()
   152  	}
   153  	paramsMap, ok := paramsInterface.(map[string]interface{})
   154  	if !ok {
   155  		// paramsMap is the map[string]interface{} type variable for params.
   156  		// DO NOT use MapDeep here.
   157  		paramsMap = doMapConvert(paramsInterface, recursiveTypeAuto, true)
   158  		if paramsMap == nil {
   159  			return gerror.NewCodef(
   160  				gcode.CodeInvalidParameter,
   161  				`convert params from "%#v" to "map[string]interface{}" failed`,
   162  				params,
   163  			)
   164  		}
   165  	}
   166  	// Nothing to be done as the parameters are empty.
   167  	if len(paramsMap) == 0 {
   168  		return nil
   169  	}
   170  	// Get struct info from cache or parse struct and cache the struct info.
   171  	cachedStructInfo := structcache.GetCachedStructInfo(
   172  		pointerElemReflectValue.Type(), priorityTag,
   173  	)
   174  	// Nothing to be converted.
   175  	if cachedStructInfo == nil {
   176  		return nil
   177  	}
   178  	// For the structure types of 0 tagOrFiledNameToFieldInfoMap,
   179  	// they also need to be cached to prevent invalid logic
   180  	if cachedStructInfo.HasNoFields() {
   181  		return nil
   182  	}
   183  	var (
   184  		// Indicates that those values have been used and cannot be reused.
   185  		usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
   186  		cachedFieldInfo           *structcache.CachedFieldInfo
   187  		paramsValue               interface{}
   188  	)
   189  	defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)
   190  
   191  	// Firstly, search according to custom mapping rules.
   192  	// If a possible direct assignment is found, reduce the number of subsequent map searches.
   193  	for paramKey, fieldName := range paramKeyToAttrMap {
   194  		paramsValue, ok = paramsMap[paramKey]
   195  		if !ok {
   196  			continue
   197  		}
   198  		cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName)
   199  		if cachedFieldInfo != nil {
   200  			fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
   201  			if err = bindVarToStructField(
   202  				fieldValue,
   203  				paramsValue,
   204  				cachedFieldInfo,
   205  				paramKeyToAttrMap,
   206  			); err != nil {
   207  				return err
   208  			}
   209  			if len(cachedFieldInfo.OtherSameNameField) > 0 {
   210  				if err = setOtherSameNameField(
   211  					cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
   212  				); err != nil {
   213  					return err
   214  				}
   215  			}
   216  			usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
   217  		}
   218  	}
   219  	// Already done converting for given `paramsMap`.
   220  	if len(usedParamsKeyOrTagNameMap) == len(paramsMap) {
   221  		return nil
   222  	}
   223  	// If the length of `paramsMap` is less than the number of fields, then loop based on `paramsMap`
   224  	if len(paramsMap) < len(cachedStructInfo.FieldConvertInfos) {
   225  		return bindStructWithLoopParamsMap(
   226  			paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
   227  		)
   228  	}
   229  	return bindStructWithLoopFieldInfos(
   230  		paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
   231  	)
   232  }
   233  
   234  func setOtherSameNameField(
   235  	cachedFieldInfo *structcache.CachedFieldInfo,
   236  	srcValue any,
   237  	structValue reflect.Value,
   238  	paramKeyToAttrMap map[string]string,
   239  ) (err error) {
   240  	// loop the same field name of all sub attributes.
   241  	for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField {
   242  		fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes)
   243  		if err = bindVarToStructField(fieldValue, srcValue, otherFieldInfo, paramKeyToAttrMap); err != nil {
   244  			return err
   245  		}
   246  	}
   247  	return nil
   248  }
   249  
   250  func bindStructWithLoopParamsMap(
   251  	paramsMap map[string]any,
   252  	structValue reflect.Value,
   253  	paramKeyToAttrMap map[string]string,
   254  	usedParamsKeyOrTagNameMap map[string]struct{},
   255  	cachedStructInfo *structcache.CachedStructInfo,
   256  ) (err error) {
   257  	var (
   258  		fieldName       string
   259  		cachedFieldInfo *structcache.CachedFieldInfo
   260  		fuzzLastKey     string
   261  		fieldValue      reflect.Value
   262  		paramKey        string
   263  		paramValue      any
   264  		ok              bool
   265  	)
   266  	for paramKey, paramValue = range paramsMap {
   267  		if _, ok = usedParamsKeyOrTagNameMap[paramKey]; ok {
   268  			continue
   269  		}
   270  		cachedFieldInfo = cachedStructInfo.GetFieldInfo(paramKey)
   271  		if cachedFieldInfo != nil {
   272  			fieldName = cachedFieldInfo.FieldName()
   273  			// already converted using its field name?
   274  			// the field name has the more priority than tag name.
   275  			_, ok = usedParamsKeyOrTagNameMap[fieldName]
   276  			if ok && cachedFieldInfo.IsField {
   277  				continue
   278  			}
   279  			fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
   280  			if err = bindVarToStructField(
   281  				fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
   282  			); err != nil {
   283  				return err
   284  			}
   285  			// handle same field name in nested struct.
   286  			if len(cachedFieldInfo.OtherSameNameField) > 0 {
   287  				if err = setOtherSameNameField(cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap); err != nil {
   288  					return err
   289  				}
   290  			}
   291  			usedParamsKeyOrTagNameMap[fieldName] = struct{}{}
   292  			continue
   293  		}
   294  
   295  		// fuzzy matching.
   296  		for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
   297  			fieldName = cachedFieldInfo.FieldName()
   298  			if _, ok = usedParamsKeyOrTagNameMap[fieldName]; ok {
   299  				continue
   300  			}
   301  			fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
   302  			paramValue, ok = paramsMap[fuzzLastKey]
   303  			if !ok {
   304  				if strings.EqualFold(
   305  					cachedFieldInfo.RemoveSymbolsFieldName, utils.RemoveSymbols(paramKey),
   306  				) {
   307  					paramValue, ok = paramsMap[paramKey]
   308  					// If it is found this time, update it based on what was not found last time.
   309  					cachedFieldInfo.LastFuzzyKey.Store(paramKey)
   310  				}
   311  			}
   312  			if ok {
   313  				fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
   314  				if paramValue != nil {
   315  					if err = bindVarToStructField(
   316  						fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
   317  					); err != nil {
   318  						return err
   319  					}
   320  					// handle same field name in nested struct.
   321  					if len(cachedFieldInfo.OtherSameNameField) > 0 {
   322  						if err = setOtherSameNameField(
   323  							cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
   324  						); err != nil {
   325  							return err
   326  						}
   327  					}
   328  				}
   329  				usedParamsKeyOrTagNameMap[cachedFieldInfo.FieldName()] = struct{}{}
   330  				break
   331  			}
   332  		}
   333  	}
   334  	return nil
   335  }
   336  
   337  func bindStructWithLoopFieldInfos(
   338  	paramsMap map[string]any,
   339  	structValue reflect.Value,
   340  	paramKeyToAttrMap map[string]string,
   341  	usedParamsKeyOrTagNameMap map[string]struct{},
   342  	cachedStructInfo *structcache.CachedStructInfo,
   343  ) (err error) {
   344  	var (
   345  		cachedFieldInfo *structcache.CachedFieldInfo
   346  		fuzzLastKey     string
   347  		fieldValue      reflect.Value
   348  		paramKey        string
   349  		paramValue      any
   350  		matched         bool
   351  		ok              bool
   352  	)
   353  	for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
   354  		for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
   355  			if paramValue, ok = paramsMap[fieldTag]; !ok {
   356  				continue
   357  			}
   358  			if _, ok = usedParamsKeyOrTagNameMap[fieldTag]; ok {
   359  				matched = true
   360  				break
   361  			}
   362  			fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
   363  			if err = bindVarToStructField(
   364  				fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
   365  			); err != nil {
   366  				return err
   367  			}
   368  			// handle same field name in nested struct.
   369  			if len(cachedFieldInfo.OtherSameNameField) > 0 {
   370  				if err = setOtherSameNameField(
   371  					cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
   372  				); err != nil {
   373  					return err
   374  				}
   375  			}
   376  			usedParamsKeyOrTagNameMap[fieldTag] = struct{}{}
   377  			matched = true
   378  			break
   379  		}
   380  		if matched {
   381  			matched = false
   382  			continue
   383  		}
   384  
   385  		fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
   386  		if paramValue, ok = paramsMap[fuzzLastKey]; !ok {
   387  			paramKey, paramValue = fuzzyMatchingFieldName(
   388  				cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
   389  			)
   390  			ok = paramKey != ""
   391  			cachedFieldInfo.LastFuzzyKey.Store(paramKey)
   392  		}
   393  		if ok {
   394  			fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
   395  			if paramValue != nil {
   396  				if err = bindVarToStructField(
   397  					fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
   398  				); err != nil {
   399  					return err
   400  				}
   401  				// handle same field name in nested struct.
   402  				if len(cachedFieldInfo.OtherSameNameField) > 0 {
   403  					if err = setOtherSameNameField(
   404  						cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
   405  					); err != nil {
   406  						return err
   407  					}
   408  				}
   409  			}
   410  			usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
   411  		}
   412  	}
   413  	return nil
   414  }
   415  
   416  // fuzzy matching rule:
   417  // to match field name and param key in case-insensitive and without symbols.
   418  func fuzzyMatchingFieldName(
   419  	fieldName string,
   420  	paramsMap map[string]any,
   421  	usedParamsKeyMap map[string]struct{},
   422  ) (string, any) {
   423  	for paramKey, paramValue := range paramsMap {
   424  		if _, ok := usedParamsKeyMap[paramKey]; ok {
   425  			continue
   426  		}
   427  		removeParamKeyUnderline := utils.RemoveSymbols(paramKey)
   428  		if strings.EqualFold(fieldName, removeParamKeyUnderline) {
   429  			return paramKey, paramValue
   430  		}
   431  	}
   432  	return "", nil
   433  }
   434  
   435  // bindVarToStructField sets value to struct object attribute by name.
   436  func bindVarToStructField(
   437  	fieldValue reflect.Value,
   438  	srcValue interface{},
   439  	cachedFieldInfo *structcache.CachedFieldInfo,
   440  	paramKeyToAttrMap map[string]string,
   441  ) (err error) {
   442  	if !fieldValue.IsValid() {
   443  		return nil
   444  	}
   445  	// CanSet checks whether attribute is public accessible.
   446  	if !fieldValue.CanSet() {
   447  		return nil
   448  	}
   449  	defer func() {
   450  		if exception := recover(); exception != nil {
   451  			if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil {
   452  				err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
   453  			}
   454  		}
   455  	}()
   456  	// Directly converting.
   457  	if empty.IsNil(srcValue) {
   458  		fieldValue.Set(reflect.Zero(fieldValue.Type()))
   459  		return nil
   460  	}
   461  	// Try to call custom converter.
   462  	// Issue: https://github.com/gogf/gf/issues/3099
   463  	var (
   464  		customConverterInput reflect.Value
   465  		ok                   bool
   466  	)
   467  	if cachedFieldInfo.IsCustomConvert {
   468  		if customConverterInput, ok = srcValue.(reflect.Value); !ok {
   469  			customConverterInput = reflect.ValueOf(srcValue)
   470  		}
   471  		if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
   472  			return
   473  		}
   474  	}
   475  	if cachedFieldInfo.IsCommonInterface {
   476  		if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil {
   477  			return
   478  		}
   479  	}
   480  	// Common types use fast assignment logic
   481  	if cachedFieldInfo.ConvertFunc != nil {
   482  		cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
   483  		return nil
   484  	}
   485  	doConvertWithReflectValueSet(fieldValue, doConvertInput{
   486  		FromValue:  srcValue,
   487  		ToTypeName: cachedFieldInfo.StructField.Type.String(),
   488  		ReferValue: fieldValue,
   489  	})
   490  	return nil
   491  }
   492  
   493  // bindVarToReflectValueWithInterfaceCheck does bind using common interfaces checks.
   494  func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (bool, error) {
   495  	var pointer interface{}
   496  	if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
   497  		reflectValueAddr := reflectValue.Addr()
   498  		if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
   499  			return false, nil
   500  		}
   501  		// Not a pointer, but can token address, that makes it can be unmarshalled.
   502  		pointer = reflectValue.Addr().Interface()
   503  	} else {
   504  		if reflectValue.IsNil() || !reflectValue.IsValid() {
   505  			return false, nil
   506  		}
   507  		pointer = reflectValue.Interface()
   508  	}
   509  	// UnmarshalValue.
   510  	if v, ok := pointer.(localinterface.IUnmarshalValue); ok {
   511  		return ok, v.UnmarshalValue(value)
   512  	}
   513  	// UnmarshalText.
   514  	if v, ok := pointer.(localinterface.IUnmarshalText); ok {
   515  		var valueBytes []byte
   516  		if b, ok := value.([]byte); ok {
   517  			valueBytes = b
   518  		} else if s, ok := value.(string); ok {
   519  			valueBytes = []byte(s)
   520  		} else if f, ok := value.(localinterface.IString); ok {
   521  			valueBytes = []byte(f.String())
   522  		}
   523  		if len(valueBytes) > 0 {
   524  			return ok, v.UnmarshalText(valueBytes)
   525  		}
   526  	}
   527  	// UnmarshalJSON.
   528  	if v, ok := pointer.(localinterface.IUnmarshalJSON); ok {
   529  		var valueBytes []byte
   530  		if b, ok := value.([]byte); ok {
   531  			valueBytes = b
   532  		} else if s, ok := value.(string); ok {
   533  			valueBytes = []byte(s)
   534  		} else if f, ok := value.(localinterface.IString); ok {
   535  			valueBytes = []byte(f.String())
   536  		}
   537  
   538  		if len(valueBytes) > 0 {
   539  			// If it is not a valid JSON string, it then adds char `"` on its both sides to make it is.
   540  			if !json.Valid(valueBytes) {
   541  				newValueBytes := make([]byte, len(valueBytes)+2)
   542  				newValueBytes[0] = '"'
   543  				newValueBytes[len(newValueBytes)-1] = '"'
   544  				copy(newValueBytes[1:], valueBytes)
   545  				valueBytes = newValueBytes
   546  			}
   547  			return ok, v.UnmarshalJSON(valueBytes)
   548  		}
   549  	}
   550  	if v, ok := pointer.(localinterface.ISet); ok {
   551  		v.Set(value)
   552  		return ok, nil
   553  	}
   554  	return false, nil
   555  }
   556  
   557  // bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
   558  func bindVarToReflectValue(
   559  	structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string,
   560  ) (err error) {
   561  	// JSON content converting.
   562  	ok, err := doConvertWithJsonCheck(value, structFieldValue)
   563  	if err != nil {
   564  		return err
   565  	}
   566  	if ok {
   567  		return nil
   568  	}
   569  
   570  	kind := structFieldValue.Kind()
   571  	// Converting using `Set` interface implements, for some types.
   572  	switch kind {
   573  	case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface:
   574  		if !structFieldValue.IsNil() {
   575  			if v, ok := structFieldValue.Interface().(localinterface.ISet); ok {
   576  				v.Set(value)
   577  				return nil
   578  			}
   579  		}
   580  	}
   581  
   582  	// Converting using reflection by kind.
   583  	switch kind {
   584  	case reflect.Map:
   585  		return doMapToMap(value, structFieldValue, paramKeyToAttrMap)
   586  
   587  	case reflect.Struct:
   588  		// Recursively converting for struct attribute.
   589  		if err = doStruct(value, structFieldValue, nil, ""); err != nil {
   590  			// Note there's reflect conversion mechanism here.
   591  			structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
   592  		}
   593  
   594  	// Note that the slice element might be type of struct,
   595  	// so it uses Struct function doing the converting internally.
   596  	case reflect.Slice, reflect.Array:
   597  		var (
   598  			reflectArray reflect.Value
   599  			reflectValue = reflect.ValueOf(value)
   600  		)
   601  		if reflectValue.Kind() == reflect.Slice || reflectValue.Kind() == reflect.Array {
   602  			reflectArray = reflect.MakeSlice(structFieldValue.Type(), reflectValue.Len(), reflectValue.Len())
   603  			if reflectValue.Len() > 0 {
   604  				var (
   605  					elemType     = reflectArray.Index(0).Type()
   606  					elemTypeName string
   607  					converted    bool
   608  				)
   609  				for i := 0; i < reflectValue.Len(); i++ {
   610  					converted = false
   611  					elemTypeName = elemType.Name()
   612  					if elemTypeName == "" {
   613  						elemTypeName = elemType.String()
   614  					}
   615  					var elem reflect.Value
   616  					if elemType.Kind() == reflect.Ptr {
   617  						elem = reflect.New(elemType.Elem()).Elem()
   618  					} else {
   619  						elem = reflect.New(elemType).Elem()
   620  					}
   621  					if elem.Kind() == reflect.Struct {
   622  						if err = doStruct(reflectValue.Index(i).Interface(), elem, nil, ""); err == nil {
   623  							converted = true
   624  						}
   625  					}
   626  					if !converted {
   627  						doConvertWithReflectValueSet(elem, doConvertInput{
   628  							FromValue:  reflectValue.Index(i).Interface(),
   629  							ToTypeName: elemTypeName,
   630  							ReferValue: elem,
   631  						})
   632  					}
   633  					if elemType.Kind() == reflect.Ptr {
   634  						// Before it sets the `elem` to array, do pointer converting if necessary.
   635  						elem = elem.Addr()
   636  					}
   637  					reflectArray.Index(i).Set(elem)
   638  				}
   639  			}
   640  		} else {
   641  			var (
   642  				elem         reflect.Value
   643  				elemType     = structFieldValue.Type().Elem()
   644  				elemTypeName = elemType.Name()
   645  				converted    bool
   646  			)
   647  			switch reflectValue.Kind() {
   648  			case reflect.String:
   649  				// Value is empty string.
   650  				if reflectValue.IsZero() {
   651  					var elemKind = elemType.Kind()
   652  					// Try to find the original type kind of the slice element.
   653  					if elemKind == reflect.Ptr {
   654  						elemKind = elemType.Elem().Kind()
   655  					}
   656  					switch elemKind {
   657  					case reflect.String:
   658  						// Empty string cannot be assigned to string slice.
   659  						return nil
   660  					}
   661  				}
   662  			}
   663  			if elemTypeName == "" {
   664  				elemTypeName = elemType.String()
   665  			}
   666  			if elemType.Kind() == reflect.Ptr {
   667  				elem = reflect.New(elemType.Elem()).Elem()
   668  			} else {
   669  				elem = reflect.New(elemType).Elem()
   670  			}
   671  			if elem.Kind() == reflect.Struct {
   672  				if err = doStruct(value, elem, nil, ""); err == nil {
   673  					converted = true
   674  				}
   675  			}
   676  			if !converted {
   677  				doConvertWithReflectValueSet(elem, doConvertInput{
   678  					FromValue:  value,
   679  					ToTypeName: elemTypeName,
   680  					ReferValue: elem,
   681  				})
   682  			}
   683  			if elemType.Kind() == reflect.Ptr {
   684  				// Before it sets the `elem` to array, do pointer converting if necessary.
   685  				elem = elem.Addr()
   686  			}
   687  			reflectArray = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
   688  			reflectArray.Index(0).Set(elem)
   689  		}
   690  		structFieldValue.Set(reflectArray)
   691  
   692  	case reflect.Ptr:
   693  		if structFieldValue.IsNil() || structFieldValue.IsZero() {
   694  			// Nil or empty pointer, it creates a new one.
   695  			item := reflect.New(structFieldValue.Type().Elem())
   696  			if ok, err = bindVarToReflectValueWithInterfaceCheck(item, value); ok {
   697  				structFieldValue.Set(item)
   698  				return err
   699  			}
   700  			elem := item.Elem()
   701  			if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil {
   702  				structFieldValue.Set(elem.Addr())
   703  			}
   704  		} else {
   705  			// Not empty pointer, it assigns values to it.
   706  			return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap)
   707  		}
   708  
   709  	// It mainly and specially handles the interface of nil value.
   710  	case reflect.Interface:
   711  		if value == nil {
   712  			// Specially.
   713  			structFieldValue.Set(reflect.ValueOf((*interface{})(nil)))
   714  		} else {
   715  			// Note there's reflect conversion mechanism here.
   716  			structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
   717  		}
   718  
   719  	default:
   720  		defer func() {
   721  			if exception := recover(); exception != nil {
   722  				err = gerror.NewCodef(
   723  					gcode.CodeInternalPanic,
   724  					`cannot convert value "%+v" to type "%s":%+v`,
   725  					value,
   726  					structFieldValue.Type().String(),
   727  					exception,
   728  				)
   729  			}
   730  		}()
   731  		// It here uses reflect converting `value` to type of the attribute and assigns
   732  		// the result value to the attribute. It might fail and panic if the usual Go
   733  		// conversion rules do not allow conversion.
   734  		structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
   735  	}
   736  	return nil
   737  }