github.com/gogf/gf@v1.16.9/util/gconv/gconv_structs.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/json"
    13  	"reflect"
    14  )
    15  
    16  // Structs converts any slice to given struct slice.
    17  // Also see Scan, Struct.
    18  func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
    19  	return Scan(params, pointer, mapping...)
    20  }
    21  
    22  // StructsTag acts as Structs but also with support for priority tag feature, which retrieves the
    23  // specified tags for `params` key-value items to struct attribute names mapping.
    24  // The parameter `priorityTag` supports multiple tags that can be joined with char ','.
    25  func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
    26  	return doStructs(params, pointer, nil, priorityTag)
    27  }
    28  
    29  // StructsDeep converts any slice to given struct slice recursively.
    30  // Deprecated, use Structs instead.
    31  func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
    32  	var keyToAttributeNameMapping map[string]string
    33  	if len(mapping) > 0 {
    34  		keyToAttributeNameMapping = mapping[0]
    35  	}
    36  	return doStructs(params, pointer, keyToAttributeNameMapping, "")
    37  }
    38  
    39  // doStructs converts any slice to given struct slice.
    40  //
    41  // It automatically checks and converts json string to []map if `params` is string/[]byte.
    42  //
    43  // The parameter `pointer` should be type of pointer to slice of struct.
    44  // Note that if `pointer` is a pointer to another pointer of type of slice of struct,
    45  // it will create the struct/pointer internally.
    46  func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
    47  	if params == nil {
    48  		// If `params` is nil, no conversion.
    49  		return nil
    50  	}
    51  	if pointer == nil {
    52  		return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
    53  	}
    54  
    55  	if doStructsByDirectReflectSet(params, pointer) {
    56  		return nil
    57  	}
    58  
    59  	defer func() {
    60  		// Catch the panic, especially the reflect operation panics.
    61  		if exception := recover(); exception != nil {
    62  			if e, ok := exception.(errorStack); ok {
    63  				err = e
    64  			} else {
    65  				err = gerror.NewCodeSkipf(gcode.CodeInternalError, 1, "%v", exception)
    66  			}
    67  		}
    68  	}()
    69  	// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
    70  	switch r := params.(type) {
    71  	case []byte:
    72  		if json.Valid(r) {
    73  			if rv, ok := pointer.(reflect.Value); ok {
    74  				if rv.Kind() == reflect.Ptr {
    75  					return json.UnmarshalUseNumber(r, rv.Interface())
    76  				}
    77  			} else {
    78  				return json.UnmarshalUseNumber(r, pointer)
    79  			}
    80  		}
    81  	case string:
    82  		if paramsBytes := []byte(r); json.Valid(paramsBytes) {
    83  			if rv, ok := pointer.(reflect.Value); ok {
    84  				if rv.Kind() == reflect.Ptr {
    85  					return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
    86  				}
    87  			} else {
    88  				return json.UnmarshalUseNumber(paramsBytes, pointer)
    89  			}
    90  		}
    91  	}
    92  	// Pointer type check.
    93  	pointerRv, ok := pointer.(reflect.Value)
    94  	if !ok {
    95  		pointerRv = reflect.ValueOf(pointer)
    96  		if kind := pointerRv.Kind(); kind != reflect.Ptr {
    97  			return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of pointer, but got: %v", kind)
    98  		}
    99  	}
   100  	// Converting `params` to map slice.
   101  	var (
   102  		paramsList []interface{}
   103  		paramsRv   = reflect.ValueOf(params)
   104  		paramsKind = paramsRv.Kind()
   105  	)
   106  	for paramsKind == reflect.Ptr {
   107  		paramsRv = paramsRv.Elem()
   108  		paramsKind = paramsRv.Kind()
   109  	}
   110  	switch paramsKind {
   111  	case reflect.Slice, reflect.Array:
   112  		paramsList = make([]interface{}, paramsRv.Len())
   113  		for i := 0; i < paramsRv.Len(); i++ {
   114  			paramsList[i] = paramsRv.Index(i)
   115  		}
   116  	default:
   117  		var paramsMaps = Maps(params)
   118  		paramsList = make([]interface{}, len(paramsMaps))
   119  		for i := 0; i < len(paramsMaps); i++ {
   120  			paramsList[i] = paramsMaps[i]
   121  		}
   122  	}
   123  	// If `params` is an empty slice, no conversion.
   124  	if len(paramsList) == 0 {
   125  		return nil
   126  	}
   127  	var (
   128  		reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList))
   129  		itemType         = reflectElemArray.Index(0).Type()
   130  		itemTypeKind     = itemType.Kind()
   131  		pointerRvElem    = pointerRv.Elem()
   132  		pointerRvLength  = pointerRvElem.Len()
   133  	)
   134  	if itemTypeKind == reflect.Ptr {
   135  		// Pointer element.
   136  		for i := 0; i < len(paramsList); i++ {
   137  			var tempReflectValue reflect.Value
   138  			if i < pointerRvLength {
   139  				// Might be nil.
   140  				tempReflectValue = pointerRvElem.Index(i).Elem()
   141  			}
   142  			if !tempReflectValue.IsValid() {
   143  				tempReflectValue = reflect.New(itemType.Elem()).Elem()
   144  			}
   145  			if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil {
   146  				return err
   147  			}
   148  			reflectElemArray.Index(i).Set(tempReflectValue.Addr())
   149  		}
   150  	} else {
   151  		// Struct element.
   152  		for i := 0; i < len(paramsList); i++ {
   153  			var tempReflectValue reflect.Value
   154  			if i < pointerRvLength {
   155  				tempReflectValue = pointerRvElem.Index(i)
   156  			} else {
   157  				tempReflectValue = reflect.New(itemType).Elem()
   158  			}
   159  			if err = doStruct(paramsList[i], tempReflectValue, mapping, priorityTag); err != nil {
   160  				return err
   161  			}
   162  			reflectElemArray.Index(i).Set(tempReflectValue)
   163  		}
   164  	}
   165  	pointerRv.Elem().Set(reflectElemArray)
   166  	return nil
   167  }
   168  
   169  // doStructsByDirectReflectSet do the converting directly using reflect Set.
   170  // It returns true if success, or else false.
   171  func doStructsByDirectReflectSet(params interface{}, pointer interface{}) (ok bool) {
   172  	v1 := reflect.ValueOf(pointer)
   173  	v2 := reflect.ValueOf(params)
   174  	if v1.Kind() == reflect.Ptr {
   175  		if elem := v1.Elem(); elem.IsValid() && elem.Type() == v2.Type() {
   176  			elem.Set(v2)
   177  			ok = true
   178  		}
   179  	}
   180  	return ok
   181  }