github.com/gogf/gf@v1.16.9/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  	"github.com/gogf/gf/errors/gcode"
    11  	"github.com/gogf/gf/errors/gerror"
    12  	"github.com/gogf/gf/internal/empty"
    13  	"github.com/gogf/gf/internal/json"
    14  	"github.com/gogf/gf/internal/structs"
    15  	"reflect"
    16  	"strings"
    17  
    18  	"github.com/gogf/gf/internal/utils"
    19  )
    20  
    21  // Struct maps the params key-value pairs to the corresponding struct object's attributes.
    22  // The third parameter `mapping` is unnecessary, indicating the mapping rules between the
    23  // custom key name and the attribute name(case sensitive).
    24  //
    25  // Note:
    26  // 1. The `params` can be any type of map/struct, usually a map.
    27  // 2. The `pointer` should be type of *struct/**struct, which is a pointer to struct object
    28  //    or struct pointer.
    29  // 3. Only the public attributes of struct object can be mapped.
    30  // 4. If `params` is a map, the key of the map `params` can be lowercase.
    31  //    It will automatically convert the first letter of the key to uppercase
    32  //    in mapping procedure to do the matching.
    33  //    It ignores the map key, if it does not match.
    34  func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
    35  	return Scan(params, pointer, mapping...)
    36  }
    37  
    38  // StructTag acts as Struct but also with support for priority tag feature, which retrieves the
    39  // specified tags for `params` key-value items to struct attribute names mapping.
    40  // The parameter `priorityTag` supports multiple tags that can be joined with char ','.
    41  func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
    42  	return doStruct(params, pointer, nil, priorityTag)
    43  }
    44  
    45  // StructDeep do Struct function recursively.
    46  // Deprecated, use Struct instead.
    47  func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
    48  	var keyToAttributeNameMapping map[string]string
    49  	if len(mapping) > 0 {
    50  		keyToAttributeNameMapping = mapping[0]
    51  	}
    52  	return doStruct(params, pointer, keyToAttributeNameMapping, "")
    53  }
    54  
    55  // doStruct is the core internal converting function for any data to struct.
    56  func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
    57  	if params == nil {
    58  		// If `params` is nil, no conversion.
    59  		return nil
    60  	}
    61  	if pointer == nil {
    62  		return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
    63  	}
    64  
    65  	defer func() {
    66  		// Catch the panic, especially the reflect operation panics.
    67  		if exception := recover(); exception != nil {
    68  			if e, ok := exception.(errorStack); ok {
    69  				err = e
    70  			} else {
    71  				err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
    72  			}
    73  		}
    74  	}()
    75  
    76  	// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
    77  	switch r := params.(type) {
    78  	case []byte:
    79  		if json.Valid(r) {
    80  			if rv, ok := pointer.(reflect.Value); ok {
    81  				if rv.Kind() == reflect.Ptr {
    82  					return json.UnmarshalUseNumber(r, rv.Interface())
    83  				} else if rv.CanAddr() {
    84  					return json.UnmarshalUseNumber(r, rv.Addr().Interface())
    85  				}
    86  			} else {
    87  				return json.UnmarshalUseNumber(r, pointer)
    88  			}
    89  		}
    90  	case string:
    91  		if paramsBytes := []byte(r); json.Valid(paramsBytes) {
    92  			if rv, ok := pointer.(reflect.Value); ok {
    93  				if rv.Kind() == reflect.Ptr {
    94  					return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
    95  				} else if rv.CanAddr() {
    96  					return json.UnmarshalUseNumber(paramsBytes, rv.Addr().Interface())
    97  				}
    98  			} else {
    99  				return json.UnmarshalUseNumber(paramsBytes, pointer)
   100  			}
   101  		}
   102  	}
   103  
   104  	var (
   105  		paramsReflectValue      reflect.Value
   106  		paramsInterface         interface{} // DO NOT use `params` directly as it might be type of `reflect.Value`
   107  		pointerReflectValue     reflect.Value
   108  		pointerReflectKind      reflect.Kind
   109  		pointerElemReflectValue reflect.Value // The pointed element.
   110  	)
   111  	if v, ok := params.(reflect.Value); ok {
   112  		paramsReflectValue = v
   113  	} else {
   114  		paramsReflectValue = reflect.ValueOf(params)
   115  	}
   116  	paramsInterface = paramsReflectValue.Interface()
   117  	if v, ok := pointer.(reflect.Value); ok {
   118  		pointerReflectValue = v
   119  		pointerElemReflectValue = v
   120  	} else {
   121  		pointerReflectValue = reflect.ValueOf(pointer)
   122  		pointerReflectKind = pointerReflectValue.Kind()
   123  		if pointerReflectKind != reflect.Ptr {
   124  			return gerror.NewCodef(gcode.CodeInvalidParameter, "object pointer should be type of '*struct', but got '%v'", pointerReflectKind)
   125  		}
   126  		// Using IsNil on reflect.Ptr variable is OK.
   127  		if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() {
   128  			return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
   129  		}
   130  		pointerElemReflectValue = pointerReflectValue.Elem()
   131  	}
   132  	// If `params` and `pointer` are the same type, the do directly assignment.
   133  	// For performance enhancement purpose.
   134  	if pointerElemReflectValue.IsValid() && pointerElemReflectValue.Type() == paramsReflectValue.Type() {
   135  		pointerElemReflectValue.Set(paramsReflectValue)
   136  		return nil
   137  	}
   138  
   139  	// Normal unmarshalling interfaces checks.
   140  	if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
   141  		return err
   142  	}
   143  
   144  	// It automatically creates struct object if necessary.
   145  	// For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User.
   146  	if pointerElemReflectValue.Kind() == reflect.Ptr {
   147  		if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
   148  			e := reflect.New(pointerElemReflectValue.Type().Elem()).Elem()
   149  			pointerElemReflectValue.Set(e.Addr())
   150  		}
   151  		//if v, ok := pointerElemReflectValue.Interface().(apiUnmarshalValue); ok {
   152  		//	return v.UnmarshalValue(params)
   153  		//}
   154  		// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
   155  		if err, ok := bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
   156  			return err
   157  		}
   158  		// Retrieve its element, may be struct at last.
   159  		pointerElemReflectValue = pointerElemReflectValue.Elem()
   160  	}
   161  
   162  	// paramsMap is the map[string]interface{} type variable for params.
   163  	// DO NOT use MapDeep here.
   164  	paramsMap := Map(paramsInterface)
   165  	if paramsMap == nil {
   166  		return gerror.NewCodef(gcode.CodeInvalidParameter, "convert params to map failed: %v", params)
   167  	}
   168  
   169  	// It only performs one converting to the same attribute.
   170  	// doneMap is used to check repeated converting, its key is the real attribute name
   171  	// of the struct.
   172  	doneMap := make(map[string]struct{})
   173  
   174  	// The key of the attrMap is the attribute name of the struct,
   175  	// and the value is its replaced name for later comparison to improve performance.
   176  	var (
   177  		tempName       string
   178  		elemFieldType  reflect.StructField
   179  		elemFieldValue reflect.Value
   180  		elemType       = pointerElemReflectValue.Type()
   181  		attrMap        = make(map[string]string)
   182  	)
   183  	for i := 0; i < pointerElemReflectValue.NumField(); i++ {
   184  		elemFieldType = elemType.Field(i)
   185  		// Only do converting to public attributes.
   186  		if !utils.IsLetterUpper(elemFieldType.Name[0]) {
   187  			continue
   188  		}
   189  		// Maybe it's struct/*struct embedded.
   190  		if elemFieldType.Anonymous {
   191  			elemFieldValue = pointerElemReflectValue.Field(i)
   192  			// Ignore the interface attribute if it's nil.
   193  			if elemFieldValue.Kind() == reflect.Interface {
   194  				elemFieldValue = elemFieldValue.Elem()
   195  				if !elemFieldValue.IsValid() {
   196  					continue
   197  				}
   198  			}
   199  			if err = doStruct(paramsMap, elemFieldValue, mapping, priorityTag); err != nil {
   200  				return err
   201  			}
   202  		} else {
   203  			tempName = elemFieldType.Name
   204  			attrMap[tempName] = utils.RemoveSymbols(tempName)
   205  		}
   206  	}
   207  	if len(attrMap) == 0 {
   208  		return nil
   209  	}
   210  
   211  	// The key of the tagMap is the attribute name of the struct,
   212  	// and the value is its replaced tag name for later comparison to improve performance.
   213  	var (
   214  		tagMap           = make(map[string]string)
   215  		priorityTagArray []string
   216  	)
   217  	if priorityTag != "" {
   218  		priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), StructTagPriority...)
   219  	} else {
   220  		priorityTagArray = StructTagPriority
   221  	}
   222  	tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, priorityTagArray)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	for tagName, attributeName := range tagToNameMap {
   227  		// If there's something else in the tag string,
   228  		// it uses the first part which is split using char ','.
   229  		// Eg:
   230  		// orm:"id, priority"
   231  		// orm:"name, with:uid=id"
   232  		tagMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0])
   233  	}
   234  
   235  	var (
   236  		attrName  string
   237  		checkName string
   238  	)
   239  	for mapK, mapV := range paramsMap {
   240  		attrName = ""
   241  		// It firstly checks the passed mapping rules.
   242  		if len(mapping) > 0 {
   243  			if passedAttrKey, ok := mapping[mapK]; ok {
   244  				attrName = passedAttrKey
   245  			}
   246  		}
   247  		// It secondly checks the predefined tags and matching rules.
   248  		if attrName == "" {
   249  			checkName = utils.RemoveSymbols(mapK)
   250  			// Loop to find the matched attribute name with or without
   251  			// string cases and chars like '-'/'_'/'.'/' '.
   252  
   253  			// Matching the parameters to struct tag names.
   254  			// The `tagV` is the attribute name of the struct.
   255  			for attrKey, cmpKey := range tagMap {
   256  				if strings.EqualFold(checkName, cmpKey) {
   257  					attrName = attrKey
   258  					break
   259  				}
   260  			}
   261  			// Matching the parameters to struct attributes.
   262  			if attrName == "" {
   263  				for attrKey, cmpKey := range attrMap {
   264  					// Eg:
   265  					// UserName  eq user_name
   266  					// User-Name eq username
   267  					// username  eq userName
   268  					// etc.
   269  					if strings.EqualFold(checkName, cmpKey) {
   270  						attrName = attrKey
   271  						break
   272  					}
   273  				}
   274  			}
   275  		}
   276  
   277  		// No matching, it gives up this attribute converting.
   278  		if attrName == "" {
   279  			continue
   280  		}
   281  		// If the attribute name is already checked converting, then skip it.
   282  		if _, ok := doneMap[attrName]; ok {
   283  			continue
   284  		}
   285  		// Mark it done.
   286  		doneMap[attrName] = struct{}{}
   287  		if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping, priorityTag); err != nil {
   288  			return err
   289  		}
   290  	}
   291  	return nil
   292  }
   293  
   294  // bindVarToStructAttr sets value to struct object attribute by name.
   295  func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping map[string]string, priorityTag string) (err error) {
   296  	structFieldValue := elem.FieldByName(name)
   297  	if !structFieldValue.IsValid() {
   298  		return nil
   299  	}
   300  	// CanSet checks whether attribute is public accessible.
   301  	if !structFieldValue.CanSet() {
   302  		return nil
   303  	}
   304  	defer func() {
   305  		if exception := recover(); exception != nil {
   306  			if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil {
   307  				err = gerror.WrapCodef(gcode.CodeInternalError, err, `error binding value to attribute "%s"`, name)
   308  			}
   309  		}
   310  	}()
   311  	// Directly converting.
   312  	if empty.IsNil(value) {
   313  		structFieldValue.Set(reflect.Zero(structFieldValue.Type()))
   314  	} else {
   315  		structFieldValue.Set(reflect.ValueOf(doConvert(
   316  			doConvertInput{
   317  				FromValue:  value,
   318  				ToTypeName: structFieldValue.Type().String(),
   319  				ReferValue: structFieldValue,
   320  			},
   321  		)))
   322  	}
   323  	return nil
   324  }
   325  
   326  // bindVarToReflectValueWithInterfaceCheck does binding using common interfaces checks.
   327  func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (err error, ok bool) {
   328  	var pointer interface{}
   329  	if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
   330  		reflectValueAddr := reflectValue.Addr()
   331  		if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
   332  			return nil, false
   333  		}
   334  		// Not a pointer, but can token address, that makes it can be unmarshalled.
   335  		pointer = reflectValue.Addr().Interface()
   336  	} else {
   337  		if reflectValue.IsNil() || !reflectValue.IsValid() {
   338  			return nil, false
   339  		}
   340  		pointer = reflectValue.Interface()
   341  	}
   342  	// UnmarshalValue.
   343  	if v, ok := pointer.(apiUnmarshalValue); ok {
   344  		return v.UnmarshalValue(value), ok
   345  	}
   346  	// UnmarshalText.
   347  	if v, ok := pointer.(apiUnmarshalText); ok {
   348  		var valueBytes []byte
   349  		if b, ok := value.([]byte); ok {
   350  			valueBytes = b
   351  		} else if s, ok := value.(string); ok {
   352  			valueBytes = []byte(s)
   353  		}
   354  		if len(valueBytes) > 0 {
   355  			return v.UnmarshalText(valueBytes), ok
   356  		}
   357  	}
   358  	// UnmarshalJSON.
   359  	if v, ok := pointer.(apiUnmarshalJSON); ok {
   360  		var valueBytes []byte
   361  		if b, ok := value.([]byte); ok {
   362  			valueBytes = b
   363  		} else if s, ok := value.(string); ok {
   364  			valueBytes = []byte(s)
   365  		}
   366  
   367  		if len(valueBytes) > 0 {
   368  			// If it is not a valid JSON string, it then adds char `"` on its both sides to make it is.
   369  			if !json.Valid(valueBytes) {
   370  				newValueBytes := make([]byte, len(valueBytes)+2)
   371  				newValueBytes[0] = '"'
   372  				newValueBytes[len(newValueBytes)-1] = '"'
   373  				copy(newValueBytes[1:], valueBytes)
   374  				valueBytes = newValueBytes
   375  			}
   376  			return v.UnmarshalJSON(valueBytes), ok
   377  		}
   378  	}
   379  	if v, ok := pointer.(apiSet); ok {
   380  		v.Set(value)
   381  		return nil, ok
   382  	}
   383  	return nil, false
   384  }
   385  
   386  // bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
   387  func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) {
   388  	if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok {
   389  		return err
   390  	}
   391  	kind := structFieldValue.Kind()
   392  	// Converting using interface, for some kinds.
   393  	switch kind {
   394  	case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface:
   395  		if !structFieldValue.IsNil() {
   396  			if v, ok := structFieldValue.Interface().(apiSet); ok {
   397  				v.Set(value)
   398  				return nil
   399  			}
   400  		}
   401  	}
   402  
   403  	// Converting by kind.
   404  	switch kind {
   405  	case reflect.Map:
   406  		return doMapToMap(value, structFieldValue, mapping)
   407  
   408  	case reflect.Struct:
   409  		// Recursively converting for struct attribute.
   410  		if err := doStruct(value, structFieldValue, nil, ""); err != nil {
   411  			// Note there's reflect conversion mechanism here.
   412  			structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
   413  		}
   414  
   415  	// Note that the slice element might be type of struct,
   416  	// so it uses Struct function doing the converting internally.
   417  	case reflect.Slice, reflect.Array:
   418  		a := reflect.Value{}
   419  		v := reflect.ValueOf(value)
   420  		if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
   421  			a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len())
   422  			if v.Len() > 0 {
   423  				t := a.Index(0).Type()
   424  				for i := 0; i < v.Len(); i++ {
   425  					if t.Kind() == reflect.Ptr {
   426  						e := reflect.New(t.Elem()).Elem()
   427  						if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
   428  							// Note there's reflect conversion mechanism here.
   429  							e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
   430  						}
   431  						a.Index(i).Set(e.Addr())
   432  					} else {
   433  						e := reflect.New(t).Elem()
   434  						if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
   435  							// Note there's reflect conversion mechanism here.
   436  							e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
   437  						}
   438  						a.Index(i).Set(e)
   439  					}
   440  				}
   441  			}
   442  		} else {
   443  			a = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
   444  			t := a.Index(0).Type()
   445  			if t.Kind() == reflect.Ptr {
   446  				e := reflect.New(t.Elem()).Elem()
   447  				if err := doStruct(value, e, nil, ""); err != nil {
   448  					// Note there's reflect conversion mechanism here.
   449  					e.Set(reflect.ValueOf(value).Convert(t))
   450  				}
   451  				a.Index(0).Set(e.Addr())
   452  			} else {
   453  				e := reflect.New(t).Elem()
   454  				if err := doStruct(value, e, nil, ""); err != nil {
   455  					// Note there's reflect conversion mechanism here.
   456  					e.Set(reflect.ValueOf(value).Convert(t))
   457  				}
   458  				a.Index(0).Set(e)
   459  			}
   460  		}
   461  		structFieldValue.Set(a)
   462  
   463  	case reflect.Ptr:
   464  		item := reflect.New(structFieldValue.Type().Elem())
   465  		if err, ok := bindVarToReflectValueWithInterfaceCheck(item, value); ok {
   466  			structFieldValue.Set(item)
   467  			return err
   468  		}
   469  		elem := item.Elem()
   470  		if err = bindVarToReflectValue(elem, value, mapping, priorityTag); err == nil {
   471  			structFieldValue.Set(elem.Addr())
   472  		}
   473  
   474  	// It mainly and specially handles the interface of nil value.
   475  	case reflect.Interface:
   476  		if value == nil {
   477  			// Specially.
   478  			structFieldValue.Set(reflect.ValueOf((*interface{})(nil)))
   479  		} else {
   480  			// Note there's reflect conversion mechanism here.
   481  			structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
   482  		}
   483  
   484  	default:
   485  		defer func() {
   486  			if exception := recover(); exception != nil {
   487  				err = gerror.NewCodef(
   488  					gcode.CodeInternalError,
   489  					`cannot convert value "%+v" to type "%s":%+v`,
   490  					value,
   491  					structFieldValue.Type().String(),
   492  					exception,
   493  				)
   494  			}
   495  		}()
   496  		// It here uses reflect converting `value` to type of the attribute and assigns
   497  		// the result value to the attribute. It might fail and panic if the usual Go
   498  		// conversion rules do not allow conversion.
   499  		structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
   500  	}
   501  	return nil
   502  }