github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_map.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/wangyougui/gf.
     6  
     7  package gconv
     8  
     9  import (
    10  	"reflect"
    11  	"strings"
    12  
    13  	"github.com/wangyougui/gf/v2/internal/empty"
    14  	"github.com/wangyougui/gf/v2/internal/json"
    15  	"github.com/wangyougui/gf/v2/internal/utils"
    16  	"github.com/wangyougui/gf/v2/util/gtag"
    17  )
    18  
    19  type recursiveType string
    20  
    21  const (
    22  	recursiveTypeAuto recursiveType = "auto"
    23  	recursiveTypeTrue recursiveType = "true"
    24  )
    25  
    26  // MapOption specifies the option for map converting.
    27  type MapOption struct {
    28  	// Deep marks doing Map function recursively, which means if the attribute of given converting value
    29  	// is also a struct/*struct, it automatically calls Map function on this attribute converting it to
    30  	// a map[string]interface{} type variable.
    31  	Deep bool
    32  
    33  	// OmitEmpty ignores the attributes that has json `omitempty` tag.
    34  	OmitEmpty bool
    35  
    36  	// Tags specifies the converted map key name by struct tag name.
    37  	Tags []string
    38  }
    39  
    40  // Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
    41  // map/struct/*struct type, then the conversion will fail and returns nil.
    42  //
    43  // If `value` is a struct/*struct object, the second parameter `tags` specifies the most priority
    44  // tags that will be detected, otherwise it detects the tags in order of:
    45  // gconv, json, field name.
    46  func Map(value interface{}, option ...MapOption) map[string]interface{} {
    47  	return doMapConvert(value, recursiveTypeAuto, false, option...)
    48  }
    49  
    50  // MapDeep does Map function recursively, which means if the attribute of `value`
    51  // is also a struct/*struct, calls Map function on this attribute converting it to
    52  // a map[string]interface{} type variable.
    53  // Deprecated: used Map instead.
    54  func MapDeep(value interface{}, tags ...string) map[string]interface{} {
    55  	return doMapConvert(value, recursiveTypeTrue, false, MapOption{
    56  		Deep: true,
    57  		Tags: tags,
    58  	})
    59  }
    60  
    61  // doMapConvert implements the map converting.
    62  // It automatically checks and converts json string to map if `value` is string/[]byte.
    63  //
    64  // TODO completely implement the recursive converting for all types, especially the map.
    65  func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, option ...MapOption) map[string]interface{} {
    66  	if value == nil {
    67  		return nil
    68  	}
    69  	// It redirects to its underlying value if it has implemented interface iVal.
    70  	if v, ok := value.(iVal); ok {
    71  		value = v.Val()
    72  	}
    73  
    74  	var (
    75  		usedOption = getUsedMapOption(option...)
    76  		newTags    = gtag.StructTagPriority
    77  	)
    78  	if usedOption.Deep {
    79  		recursive = recursiveTypeTrue
    80  	}
    81  	switch len(usedOption.Tags) {
    82  	case 0:
    83  		// No need handling.
    84  	case 1:
    85  		newTags = append(strings.Split(usedOption.Tags[0], ","), gtag.StructTagPriority...)
    86  	default:
    87  		newTags = append(usedOption.Tags, gtag.StructTagPriority...)
    88  	}
    89  	// Assert the common combination of types, and finally it uses reflection.
    90  	dataMap := make(map[string]interface{})
    91  	switch r := value.(type) {
    92  	case string:
    93  		// If it is a JSON string, automatically unmarshal it!
    94  		if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
    95  			if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil {
    96  				return nil
    97  			}
    98  		} else {
    99  			return nil
   100  		}
   101  	case []byte:
   102  		// If it is a JSON string, automatically unmarshal it!
   103  		if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
   104  			if err := json.UnmarshalUseNumber(r, &dataMap); err != nil {
   105  				return nil
   106  			}
   107  		} else {
   108  			return nil
   109  		}
   110  	case map[interface{}]interface{}:
   111  		recursiveOption := usedOption
   112  		recursiveOption.Tags = newTags
   113  		for k, v := range r {
   114  			dataMap[String(k)] = doMapConvertForMapOrStructValue(
   115  				doMapConvertForMapOrStructValueInput{
   116  					IsRoot:          false,
   117  					Value:           v,
   118  					RecursiveType:   recursive,
   119  					RecursiveOption: recursive == recursiveTypeTrue,
   120  					Option:          recursiveOption,
   121  				},
   122  			)
   123  		}
   124  	case map[interface{}]string:
   125  		for k, v := range r {
   126  			dataMap[String(k)] = v
   127  		}
   128  	case map[interface{}]int:
   129  		for k, v := range r {
   130  			dataMap[String(k)] = v
   131  		}
   132  	case map[interface{}]uint:
   133  		for k, v := range r {
   134  			dataMap[String(k)] = v
   135  		}
   136  	case map[interface{}]float32:
   137  		for k, v := range r {
   138  			dataMap[String(k)] = v
   139  		}
   140  	case map[interface{}]float64:
   141  		for k, v := range r {
   142  			dataMap[String(k)] = v
   143  		}
   144  	case map[string]bool:
   145  		for k, v := range r {
   146  			dataMap[k] = v
   147  		}
   148  	case map[string]int:
   149  		for k, v := range r {
   150  			dataMap[k] = v
   151  		}
   152  	case map[string]uint:
   153  		for k, v := range r {
   154  			dataMap[k] = v
   155  		}
   156  	case map[string]float32:
   157  		for k, v := range r {
   158  			dataMap[k] = v
   159  		}
   160  	case map[string]float64:
   161  		for k, v := range r {
   162  			dataMap[k] = v
   163  		}
   164  	case map[string]string:
   165  		for k, v := range r {
   166  			dataMap[k] = v
   167  		}
   168  	case map[string]interface{}:
   169  		if recursive == recursiveTypeTrue {
   170  			recursiveOption := usedOption
   171  			recursiveOption.Tags = newTags
   172  			// A copy of current map.
   173  			for k, v := range r {
   174  				dataMap[k] = doMapConvertForMapOrStructValue(
   175  					doMapConvertForMapOrStructValueInput{
   176  						IsRoot:          false,
   177  						Value:           v,
   178  						RecursiveType:   recursive,
   179  						RecursiveOption: recursive == recursiveTypeTrue,
   180  						Option:          recursiveOption,
   181  					},
   182  				)
   183  			}
   184  		} else {
   185  			// It returns the map directly without any changing.
   186  			return r
   187  		}
   188  	case map[int]interface{}:
   189  		recursiveOption := usedOption
   190  		recursiveOption.Tags = newTags
   191  		for k, v := range r {
   192  			dataMap[String(k)] = doMapConvertForMapOrStructValue(
   193  				doMapConvertForMapOrStructValueInput{
   194  					IsRoot:          false,
   195  					Value:           v,
   196  					RecursiveType:   recursive,
   197  					RecursiveOption: recursive == recursiveTypeTrue,
   198  					Option:          recursiveOption,
   199  				},
   200  			)
   201  		}
   202  	case map[int]string:
   203  		for k, v := range r {
   204  			dataMap[String(k)] = v
   205  		}
   206  	case map[uint]string:
   207  		for k, v := range r {
   208  			dataMap[String(k)] = v
   209  		}
   210  
   211  	default:
   212  		// Not a common type, it then uses reflection for conversion.
   213  		var reflectValue reflect.Value
   214  		if v, ok := value.(reflect.Value); ok {
   215  			reflectValue = v
   216  		} else {
   217  			reflectValue = reflect.ValueOf(value)
   218  		}
   219  		reflectKind := reflectValue.Kind()
   220  		// If it is a pointer, we should find its real data type.
   221  		for reflectKind == reflect.Ptr {
   222  			reflectValue = reflectValue.Elem()
   223  			reflectKind = reflectValue.Kind()
   224  		}
   225  		switch reflectKind {
   226  		// If `value` is type of array, it converts the value of even number index as its key and
   227  		// the value of odd number index as its corresponding value, for example:
   228  		// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
   229  		// []string{"k1","v1","k2"}      => map[string]interface{}{"k1":"v1", "k2":nil}
   230  		case reflect.Slice, reflect.Array:
   231  			length := reflectValue.Len()
   232  			for i := 0; i < length; i += 2 {
   233  				if i+1 < length {
   234  					dataMap[String(reflectValue.Index(i).Interface())] = reflectValue.Index(i + 1).Interface()
   235  				} else {
   236  					dataMap[String(reflectValue.Index(i).Interface())] = nil
   237  				}
   238  			}
   239  		case reflect.Map, reflect.Struct, reflect.Interface:
   240  			recursiveOption := usedOption
   241  			recursiveOption.Tags = newTags
   242  			convertedValue := doMapConvertForMapOrStructValue(
   243  				doMapConvertForMapOrStructValueInput{
   244  					IsRoot:          true,
   245  					Value:           value,
   246  					RecursiveType:   recursive,
   247  					RecursiveOption: recursive == recursiveTypeTrue,
   248  					Option:          recursiveOption,
   249  					MustMapReturn:   mustMapReturn,
   250  				},
   251  			)
   252  			if m, ok := convertedValue.(map[string]interface{}); ok {
   253  				return m
   254  			}
   255  			return nil
   256  		default:
   257  			return nil
   258  		}
   259  	}
   260  	return dataMap
   261  }
   262  
   263  func getUsedMapOption(option ...MapOption) MapOption {
   264  	var usedOption MapOption
   265  	if len(option) > 0 {
   266  		usedOption = option[0]
   267  	}
   268  	return usedOption
   269  }
   270  
   271  type doMapConvertForMapOrStructValueInput struct {
   272  	IsRoot          bool          // It returns directly if it is not root and with no recursive converting.
   273  	Value           interface{}   // Current operation value.
   274  	RecursiveType   recursiveType // The type from top function entry.
   275  	RecursiveOption bool          // Whether convert recursively for `current` operation.
   276  	Option          MapOption     // Map converting option.
   277  	MustMapReturn   bool          // Must return map instead of Value when empty.
   278  }
   279  
   280  func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} {
   281  	if !in.IsRoot && !in.RecursiveOption {
   282  		return in.Value
   283  	}
   284  
   285  	var reflectValue reflect.Value
   286  	if v, ok := in.Value.(reflect.Value); ok {
   287  		reflectValue = v
   288  		in.Value = v.Interface()
   289  	} else {
   290  		reflectValue = reflect.ValueOf(in.Value)
   291  	}
   292  	reflectKind := reflectValue.Kind()
   293  	// If it is a pointer, we should find its real data type.
   294  	for reflectKind == reflect.Ptr {
   295  		reflectValue = reflectValue.Elem()
   296  		reflectKind = reflectValue.Kind()
   297  	}
   298  	switch reflectKind {
   299  	case reflect.Map:
   300  		var (
   301  			mapKeys = reflectValue.MapKeys()
   302  			dataMap = make(map[string]interface{})
   303  		)
   304  		for _, k := range mapKeys {
   305  			var (
   306  				mapKeyValue = reflectValue.MapIndex(k)
   307  				mapValue    interface{}
   308  			)
   309  			switch {
   310  			case mapKeyValue.IsZero():
   311  				if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() {
   312  					// quick check for nil value.
   313  					mapValue = nil
   314  				} else {
   315  					// in case of:
   316  					// exception recovered: reflect: call of reflect.Value.Interface on zero Value
   317  					mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface()
   318  				}
   319  			default:
   320  				mapValue = mapKeyValue.Interface()
   321  			}
   322  			dataMap[String(k.Interface())] = doMapConvertForMapOrStructValue(
   323  				doMapConvertForMapOrStructValueInput{
   324  					IsRoot:          false,
   325  					Value:           mapValue,
   326  					RecursiveType:   in.RecursiveType,
   327  					RecursiveOption: in.RecursiveType == recursiveTypeTrue,
   328  					Option:          in.Option,
   329  				},
   330  			)
   331  		}
   332  		return dataMap
   333  
   334  	case reflect.Struct:
   335  		var dataMap = make(map[string]interface{})
   336  		// Map converting interface check.
   337  		if v, ok := in.Value.(iMapStrAny); ok {
   338  			// Value copy, in case of concurrent safety.
   339  			for mapK, mapV := range v.MapStrAny() {
   340  				if in.RecursiveOption {
   341  					dataMap[mapK] = doMapConvertForMapOrStructValue(
   342  						doMapConvertForMapOrStructValueInput{
   343  							IsRoot:          false,
   344  							Value:           mapV,
   345  							RecursiveType:   in.RecursiveType,
   346  							RecursiveOption: in.RecursiveType == recursiveTypeTrue,
   347  							Option:          in.Option,
   348  						},
   349  					)
   350  				} else {
   351  					dataMap[mapK] = mapV
   352  				}
   353  			}
   354  			if len(dataMap) > 0 {
   355  				return dataMap
   356  			}
   357  		}
   358  		// Using reflect for converting.
   359  		var (
   360  			rtField     reflect.StructField
   361  			rvField     reflect.Value
   362  			reflectType = reflectValue.Type() // attribute value type.
   363  			mapKey      = ""                  // mapKey may be the tag name or the struct attribute name.
   364  		)
   365  		for i := 0; i < reflectValue.NumField(); i++ {
   366  			rtField = reflectType.Field(i)
   367  			rvField = reflectValue.Field(i)
   368  			// Only convert the public attributes.
   369  			fieldName := rtField.Name
   370  			if !utils.IsLetterUpper(fieldName[0]) {
   371  				continue
   372  			}
   373  			mapKey = ""
   374  			fieldTag := rtField.Tag
   375  			for _, tag := range in.Option.Tags {
   376  				if mapKey = fieldTag.Get(tag); mapKey != "" {
   377  					break
   378  				}
   379  			}
   380  			if mapKey == "" {
   381  				mapKey = fieldName
   382  			} else {
   383  				// Support json tag feature: -, omitempty
   384  				mapKey = strings.TrimSpace(mapKey)
   385  				if mapKey == "-" {
   386  					continue
   387  				}
   388  				array := strings.Split(mapKey, ",")
   389  				if len(array) > 1 {
   390  					switch strings.TrimSpace(array[1]) {
   391  					case "omitempty":
   392  						if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
   393  							continue
   394  						} else {
   395  							mapKey = strings.TrimSpace(array[0])
   396  						}
   397  					default:
   398  						mapKey = strings.TrimSpace(array[0])
   399  					}
   400  				}
   401  				if mapKey == "" {
   402  					mapKey = fieldName
   403  				}
   404  			}
   405  			if in.RecursiveOption || rtField.Anonymous {
   406  				// Do map converting recursively.
   407  				var (
   408  					rvAttrField = rvField
   409  					rvAttrKind  = rvField.Kind()
   410  				)
   411  				if rvAttrKind == reflect.Ptr {
   412  					rvAttrField = rvField.Elem()
   413  					rvAttrKind = rvAttrField.Kind()
   414  				}
   415  				switch rvAttrKind {
   416  				case reflect.Struct:
   417  					// Embedded struct and has no fields, just ignores it.
   418  					// Eg: gmeta.Meta
   419  					if rvAttrField.Type().NumField() == 0 {
   420  						continue
   421  					}
   422  					var (
   423  						hasNoTag = mapKey == fieldName
   424  						// DO NOT use rvAttrField.Interface() here,
   425  						// as it might be changed from pointer to struct.
   426  						rvInterface = rvField.Interface()
   427  					)
   428  					switch {
   429  					case hasNoTag && rtField.Anonymous:
   430  						// It means this attribute field has no tag.
   431  						// Overwrite the attribute with sub-struct attribute fields.
   432  						anonymousValue := doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
   433  							IsRoot:          false,
   434  							Value:           rvInterface,
   435  							RecursiveType:   in.RecursiveType,
   436  							RecursiveOption: true,
   437  							Option:          in.Option,
   438  						})
   439  						if m, ok := anonymousValue.(map[string]interface{}); ok {
   440  							for k, v := range m {
   441  								dataMap[k] = v
   442  							}
   443  						} else {
   444  							dataMap[mapKey] = rvInterface
   445  						}
   446  
   447  					// It means this attribute field has desired tag.
   448  					case !hasNoTag && rtField.Anonymous:
   449  						dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
   450  							IsRoot:          false,
   451  							Value:           rvInterface,
   452  							RecursiveType:   in.RecursiveType,
   453  							RecursiveOption: true,
   454  							Option:          in.Option,
   455  						})
   456  
   457  					default:
   458  						dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
   459  							IsRoot:          false,
   460  							Value:           rvInterface,
   461  							RecursiveType:   in.RecursiveType,
   462  							RecursiveOption: in.RecursiveType == recursiveTypeTrue,
   463  							Option:          in.Option,
   464  						})
   465  					}
   466  
   467  				// The struct attribute is type of slice.
   468  				case reflect.Array, reflect.Slice:
   469  					length := rvAttrField.Len()
   470  					if length == 0 {
   471  						dataMap[mapKey] = rvAttrField.Interface()
   472  						break
   473  					}
   474  					array := make([]interface{}, length)
   475  					for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
   476  						array[arrayIndex] = doMapConvertForMapOrStructValue(
   477  							doMapConvertForMapOrStructValueInput{
   478  								IsRoot:          false,
   479  								Value:           rvAttrField.Index(arrayIndex).Interface(),
   480  								RecursiveType:   in.RecursiveType,
   481  								RecursiveOption: in.RecursiveType == recursiveTypeTrue,
   482  								Option:          in.Option,
   483  							},
   484  						)
   485  					}
   486  					dataMap[mapKey] = array
   487  				case reflect.Map:
   488  					var (
   489  						mapKeys   = rvAttrField.MapKeys()
   490  						nestedMap = make(map[string]interface{})
   491  					)
   492  					for _, k := range mapKeys {
   493  						nestedMap[String(k.Interface())] = doMapConvertForMapOrStructValue(
   494  							doMapConvertForMapOrStructValueInput{
   495  								IsRoot:          false,
   496  								Value:           rvAttrField.MapIndex(k).Interface(),
   497  								RecursiveType:   in.RecursiveType,
   498  								RecursiveOption: in.RecursiveType == recursiveTypeTrue,
   499  								Option:          in.Option,
   500  							},
   501  						)
   502  					}
   503  					dataMap[mapKey] = nestedMap
   504  				default:
   505  					if rvField.IsValid() {
   506  						dataMap[mapKey] = reflectValue.Field(i).Interface()
   507  					} else {
   508  						dataMap[mapKey] = nil
   509  					}
   510  				}
   511  			} else {
   512  				// No recursive map value converting
   513  				if rvField.IsValid() {
   514  					dataMap[mapKey] = reflectValue.Field(i).Interface()
   515  				} else {
   516  					dataMap[mapKey] = nil
   517  				}
   518  			}
   519  		}
   520  		if !in.MustMapReturn && len(dataMap) == 0 {
   521  			return in.Value
   522  		}
   523  		return dataMap
   524  
   525  	// The given value is type of slice.
   526  	case reflect.Array, reflect.Slice:
   527  		length := reflectValue.Len()
   528  		if length == 0 {
   529  			break
   530  		}
   531  		array := make([]interface{}, reflectValue.Len())
   532  		for i := 0; i < length; i++ {
   533  			array[i] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
   534  				IsRoot:          false,
   535  				Value:           reflectValue.Index(i).Interface(),
   536  				RecursiveType:   in.RecursiveType,
   537  				RecursiveOption: in.RecursiveType == recursiveTypeTrue,
   538  				Option:          in.Option,
   539  			})
   540  		}
   541  		return array
   542  	}
   543  	return in.Value
   544  }
   545  
   546  // MapStrStr converts `value` to map[string]string.
   547  // Note that there might be data copy for this map type converting.
   548  func MapStrStr(value interface{}, option ...MapOption) map[string]string {
   549  	if r, ok := value.(map[string]string); ok {
   550  		return r
   551  	}
   552  	m := Map(value, option...)
   553  	if len(m) > 0 {
   554  		vMap := make(map[string]string, len(m))
   555  		for k, v := range m {
   556  			vMap[k] = String(v)
   557  		}
   558  		return vMap
   559  	}
   560  	return nil
   561  }
   562  
   563  // MapStrStrDeep converts `value` to map[string]string recursively.
   564  // Note that there might be data copy for this map type converting.
   565  // Deprecated: used MapStrStr instead.
   566  func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
   567  	if r, ok := value.(map[string]string); ok {
   568  		return r
   569  	}
   570  	m := MapDeep(value, tags...)
   571  	if len(m) > 0 {
   572  		vMap := make(map[string]string, len(m))
   573  		for k, v := range m {
   574  			vMap[k] = String(v)
   575  		}
   576  		return vMap
   577  	}
   578  	return nil
   579  }