github.com/zhongdalu/gf@v1.0.0/g/util/gconv/gconv_struct.go (about)

     1  // Copyright 2017-2019 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf.
     6  
     7  package gconv
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"reflect"
    13  	"strings"
    14  
    15  	"github.com/zhongdalu/gf/g/internal/structs"
    16  	"github.com/zhongdalu/gf/g/internal/strutils"
    17  )
    18  
    19  // Struct maps the params key-value pairs to the corresponding struct object's properties.
    20  // The third parameter <mapping> is unnecessary, indicating the mapping rules between the custom key name
    21  // and the attribute name(case sensitive).
    22  //
    23  // Note:
    24  // 1. The <params> can be any type of map/struct, usually a map.
    25  // 2. The second parameter <pointer> should be a pointer to the struct object.
    26  // 3. Only the public attributes of struct object can be mapped.
    27  // 4. If <params> is a map, the key of the map <params> can be lowercase.
    28  //    It will automatically convert the first letter of the key to uppercase
    29  //    in mapping procedure to do the matching.
    30  //    It ignores the map key, if it does not match.
    31  func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) error {
    32  	if params == nil {
    33  		return errors.New("params cannot be nil")
    34  	}
    35  	if pointer == nil {
    36  		return errors.New("object pointer cannot be nil")
    37  	}
    38  	paramsMap := Map(params)
    39  	if paramsMap == nil {
    40  		return fmt.Errorf("invalid params: %v", params)
    41  	}
    42  	// Using reflect to do the converting,
    43  	// it also supports type of reflect.Value for <pointer>(always in internal usage).
    44  	elem, ok := pointer.(reflect.Value)
    45  	if !ok {
    46  		rv := reflect.ValueOf(pointer)
    47  		if kind := rv.Kind(); kind != reflect.Ptr {
    48  			return fmt.Errorf("object pointer should be type of: %v", kind)
    49  		}
    50  		// Using IsNil on reflect.Ptr variable is OK.
    51  		if !rv.IsValid() || rv.IsNil() {
    52  			return errors.New("object pointer cannot be nil")
    53  		}
    54  		elem = rv.Elem()
    55  		// Auto create struct object.
    56  		// For example, if <pointer> is **User, then <elem> is *User, which is a pointer to User.
    57  		if elem.Type().Kind() == reflect.Ptr && (!elem.IsValid() || elem.IsNil()) {
    58  			e := reflect.New(elem.Type().Elem()).Elem()
    59  			elem.Set(e.Addr())
    60  			elem = e
    61  		}
    62  	}
    63  	// It only performs one converting to the same attribute.
    64  	// doneMap is used to check repeated converting.
    65  	doneMap := make(map[string]bool)
    66  	// It first checks the passed mapping rules.
    67  	if len(mapping) > 0 && len(mapping[0]) > 0 {
    68  		for mapK, mapV := range mapping[0] {
    69  			if v, ok := paramsMap[mapK]; ok {
    70  				doneMap[mapV] = true
    71  				if err := bindVarToStructAttr(elem, mapV, v); err != nil {
    72  					return err
    73  				}
    74  			}
    75  		}
    76  	}
    77  	// It secondly checks the tags of attributes.
    78  	tagMap := structs.TagMapName(pointer, structTagPriority, true)
    79  	for tagK, tagV := range tagMap {
    80  		if _, ok := doneMap[tagV]; ok {
    81  			continue
    82  		}
    83  		if v, ok := paramsMap[tagK]; ok {
    84  			doneMap[tagV] = true
    85  			if err := bindVarToStructAttr(elem, tagV, v); err != nil {
    86  				return err
    87  			}
    88  		}
    89  	}
    90  	// It finally do the converting with default rules.
    91  	attrMap := make(map[string]struct{})
    92  	elemType := elem.Type()
    93  	for i := 0; i < elem.NumField(); i++ {
    94  		// Only do converting to public attributes.
    95  		if !strutils.IsLetterUpper(elemType.Field(i).Name[0]) {
    96  			continue
    97  		}
    98  		attrMap[elemType.Field(i).Name] = struct{}{}
    99  	}
   100  	for mapK, mapV := range paramsMap {
   101  		name := ""
   102  		for _, checkName := range []string{
   103  			strutils.UcFirst(mapK),
   104  			strutils.ReplaceByMap(mapK, map[string]string{
   105  				"_": "",
   106  				"-": "",
   107  				" ": "",
   108  			})} {
   109  			if _, ok := doneMap[checkName]; ok {
   110  				continue
   111  			}
   112  			if _, ok := tagMap[checkName]; ok {
   113  				continue
   114  			}
   115  			// Loop to find the matched attribute name.
   116  			for value, _ := range attrMap {
   117  				if strings.EqualFold(checkName, value) {
   118  					name = value
   119  					break
   120  				}
   121  				if strings.EqualFold(checkName, strings.Replace(value, "_", "", -1)) {
   122  					name = value
   123  					break
   124  				}
   125  			}
   126  			doneMap[checkName] = true
   127  			if name != "" {
   128  				break
   129  			}
   130  		}
   131  		// No matching, give up this attribute converting.
   132  		if name == "" {
   133  			continue
   134  		}
   135  		if err := bindVarToStructAttr(elem, name, mapV); err != nil {
   136  			return err
   137  		}
   138  	}
   139  	return nil
   140  }
   141  
   142  // StructDeep do Struct function recursively.
   143  // See Struct.
   144  func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
   145  	if err := Struct(params, pointer, mapping...); err != nil {
   146  		return err
   147  	} else {
   148  		rv, ok := pointer.(reflect.Value)
   149  		if !ok {
   150  			rv = reflect.ValueOf(pointer)
   151  		}
   152  		kind := rv.Kind()
   153  		if kind == reflect.Ptr {
   154  			rv = rv.Elem()
   155  			kind = rv.Kind()
   156  		}
   157  		switch kind {
   158  		case reflect.Struct:
   159  			rt := rv.Type()
   160  			for i := 0; i < rv.NumField(); i++ {
   161  				// Only do converting to public attributes.
   162  				if !strutils.IsLetterUpper(rt.Field(i).Name[0]) {
   163  					continue
   164  				}
   165  				trv := rv.Field(i)
   166  				switch trv.Kind() {
   167  				case reflect.Struct:
   168  					if err := StructDeep(params, trv, mapping...); err != nil {
   169  						return err
   170  					}
   171  				}
   172  			}
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  // 将参数值绑定到对象指定名称的属性上
   179  func bindVarToStructAttr(elem reflect.Value, name string, value interface{}) (err error) {
   180  	structFieldValue := elem.FieldByName(name)
   181  	// 键名与对象属性匹配检测,map中如果有struct不存在的属性,那么不做处理,直接return
   182  	if !structFieldValue.IsValid() {
   183  		return nil
   184  	}
   185  	// CanSet的属性必须为公开属性(首字母大写)
   186  	if !structFieldValue.CanSet() {
   187  		return nil
   188  	}
   189  	// 必须将value转换为struct属性的数据类型,这里必须用到gconv包
   190  	defer func() {
   191  		// 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换
   192  		if recover() != nil {
   193  			err = bindVarToReflectValue(structFieldValue, value)
   194  		}
   195  	}()
   196  	structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
   197  	return nil
   198  }
   199  
   200  // 将参数值绑定到对象指定索引位置的属性上
   201  func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (err error) {
   202  	structFieldValue := elem.FieldByIndex([]int{index})
   203  	// 键名与对象属性匹配检测
   204  	if !structFieldValue.IsValid() {
   205  		return nil
   206  	}
   207  	// CanSet的属性必须为公开属性(首字母大写)
   208  	if !structFieldValue.CanSet() {
   209  		return nil
   210  	}
   211  	// 必须将value转换为struct属性的数据类型,这里必须用到gconv包
   212  	defer func() {
   213  		// 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换
   214  		if recover() != nil {
   215  			err = bindVarToReflectValue(structFieldValue, value)
   216  		}
   217  	}()
   218  	structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
   219  	return nil
   220  }
   221  
   222  // 当默认的基本类型转换失败时,通过recover判断后执行反射类型转换(处理复杂类型)
   223  func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}) error {
   224  	switch structFieldValue.Kind() {
   225  	// 属性为结构体
   226  	case reflect.Struct:
   227  		if err := Struct(value, structFieldValue); err != nil {
   228  			structFieldValue.Set(reflect.ValueOf(value))
   229  		}
   230  
   231  	// 属性为数组类型
   232  	case reflect.Slice:
   233  		fallthrough
   234  	case reflect.Array:
   235  		a := reflect.Value{}
   236  		v := reflect.ValueOf(value)
   237  		if v.Kind() == reflect.Slice || v.Kind() == reflect.Array {
   238  			if v.Len() > 0 {
   239  				a = reflect.MakeSlice(structFieldValue.Type(), v.Len(), v.Len())
   240  				t := a.Index(0).Type()
   241  				for i := 0; i < v.Len(); i++ {
   242  					if t.Kind() == reflect.Ptr {
   243  						e := reflect.New(t.Elem()).Elem()
   244  						if err := Struct(v.Index(i).Interface(), e); err != nil {
   245  							e.Set(reflect.ValueOf(v.Index(i).Interface()))
   246  						}
   247  						a.Index(i).Set(e.Addr())
   248  					} else {
   249  						e := reflect.New(t).Elem()
   250  						if err := Struct(v.Index(i).Interface(), e); err != nil {
   251  							e.Set(reflect.ValueOf(v.Index(i).Interface()))
   252  						}
   253  						a.Index(i).Set(e)
   254  					}
   255  				}
   256  			}
   257  		} else {
   258  			a = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
   259  			t := a.Index(0).Type()
   260  			if t.Kind() == reflect.Ptr {
   261  				e := reflect.New(t.Elem()).Elem()
   262  				if err := Struct(value, e); err != nil {
   263  					e.Set(reflect.ValueOf(value))
   264  				}
   265  				a.Index(0).Set(e.Addr())
   266  			} else {
   267  				e := reflect.New(t).Elem()
   268  				if err := Struct(value, e); err != nil {
   269  					e.Set(reflect.ValueOf(value))
   270  				}
   271  				a.Index(0).Set(e)
   272  			}
   273  		}
   274  		structFieldValue.Set(a)
   275  
   276  	// 属性为指针类型
   277  	case reflect.Ptr:
   278  		e := reflect.New(structFieldValue.Type().Elem()).Elem()
   279  		if err := Struct(value, e); err != nil {
   280  			e.Set(reflect.ValueOf(value))
   281  		}
   282  		structFieldValue.Set(e.Addr())
   283  
   284  	default:
   285  		return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String()))
   286  	}
   287  	return nil
   288  }