github.com/wangyougui/gf/v2@v2.6.5/util/gconv/gconv_scan.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  
    12  	"github.com/wangyougui/gf/v2/errors/gcode"
    13  	"github.com/wangyougui/gf/v2/errors/gerror"
    14  	"github.com/wangyougui/gf/v2/internal/json"
    15  )
    16  
    17  // Scan automatically checks the type of `pointer` and converts `params` to `pointer`.
    18  // It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
    19  //
    20  // TODO change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`.
    21  func Scan(srcValue interface{}, dstPointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
    22  	if srcValue == nil {
    23  		// If `srcValue` is nil, no conversion.
    24  		return nil
    25  	}
    26  	if dstPointer == nil {
    27  		return gerror.NewCode(
    28  			gcode.CodeInvalidParameter,
    29  			`destination pointer should not be nil`,
    30  		)
    31  	}
    32  
    33  	// json converting check.
    34  	ok, err := doConvertWithJsonCheck(srcValue, dstPointer)
    35  	if err != nil {
    36  		return err
    37  	}
    38  	if ok {
    39  		return nil
    40  	}
    41  
    42  	var (
    43  		dstPointerReflectType  reflect.Type
    44  		dstPointerReflectValue reflect.Value
    45  	)
    46  	if v, ok := dstPointer.(reflect.Value); ok {
    47  		dstPointerReflectValue = v
    48  		dstPointerReflectType = v.Type()
    49  	} else {
    50  		dstPointerReflectValue = reflect.ValueOf(dstPointer)
    51  		// do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero.
    52  		dstPointerReflectType = reflect.TypeOf(dstPointer)
    53  	}
    54  
    55  	// pointer kind validation.
    56  	var dstPointerReflectKind = dstPointerReflectType.Kind()
    57  	if dstPointerReflectKind != reflect.Ptr {
    58  		if dstPointerReflectValue.CanAddr() {
    59  			dstPointerReflectValue = dstPointerReflectValue.Addr()
    60  			dstPointerReflectType = dstPointerReflectValue.Type()
    61  			dstPointerReflectKind = dstPointerReflectType.Kind()
    62  		} else {
    63  			return gerror.NewCodef(
    64  				gcode.CodeInvalidParameter,
    65  				`destination pointer should be type of pointer, but got type: %v`,
    66  				dstPointerReflectType,
    67  			)
    68  		}
    69  	}
    70  	// direct assignment checks!
    71  	var srcValueReflectValue reflect.Value
    72  	if v, ok := srcValue.(reflect.Value); ok {
    73  		srcValueReflectValue = v
    74  	} else {
    75  		srcValueReflectValue = reflect.ValueOf(srcValue)
    76  	}
    77  	// if `srcValue` and `dstPointer` are the same type, the do directly assignment.
    78  	// For performance enhancement purpose.
    79  	var dstPointerReflectValueElem = dstPointerReflectValue.Elem()
    80  	// if `srcValue` and `dstPointer` are the same type, the do directly assignment.
    81  	// for performance enhancement purpose.
    82  	if ok = doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok {
    83  		return nil
    84  	}
    85  
    86  	// do the converting.
    87  	var (
    88  		dstPointerReflectTypeElem     = dstPointerReflectType.Elem()
    89  		dstPointerReflectTypeElemKind = dstPointerReflectTypeElem.Kind()
    90  		keyToAttributeNameMapping     map[string]string
    91  	)
    92  	if len(paramKeyToAttrMap) > 0 {
    93  		keyToAttributeNameMapping = paramKeyToAttrMap[0]
    94  	}
    95  	switch dstPointerReflectTypeElemKind {
    96  	case reflect.Map:
    97  		return doMapToMap(srcValue, dstPointer, paramKeyToAttrMap...)
    98  
    99  	case reflect.Array, reflect.Slice:
   100  		var (
   101  			sliceElem     = dstPointerReflectTypeElem.Elem()
   102  			sliceElemKind = sliceElem.Kind()
   103  		)
   104  		for sliceElemKind == reflect.Ptr {
   105  			sliceElem = sliceElem.Elem()
   106  			sliceElemKind = sliceElem.Kind()
   107  		}
   108  		if sliceElemKind == reflect.Map {
   109  			return doMapToMaps(srcValue, dstPointer, paramKeyToAttrMap...)
   110  		}
   111  		return doStructs(srcValue, dstPointer, keyToAttributeNameMapping, "")
   112  
   113  	default:
   114  		return doStruct(srcValue, dstPointer, keyToAttributeNameMapping, "")
   115  	}
   116  }
   117  
   118  func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem reflect.Value) (ok bool) {
   119  	if !dstPointerReflectValueElem.IsValid() || !srcValueReflectValue.IsValid() {
   120  		return false
   121  	}
   122  	switch {
   123  	// Example:
   124  	// UploadFile    => UploadFile
   125  	// []UploadFile  => []UploadFile
   126  	// *UploadFile   => *UploadFile
   127  	// *[]UploadFile => *[]UploadFile
   128  	// map           => map
   129  	// []map         => []map
   130  	// *[]map        => *[]map
   131  	case dstPointerReflectValueElem.Type() == srcValueReflectValue.Type():
   132  		dstPointerReflectValueElem.Set(srcValueReflectValue)
   133  		return true
   134  
   135  	// Example:
   136  	// UploadFile    => *UploadFile
   137  	// []UploadFile  => *[]UploadFile
   138  	// map           => *map
   139  	// []map         => *[]map
   140  	case dstPointerReflectValueElem.Kind() == reflect.Ptr &&
   141  		dstPointerReflectValueElem.Elem().IsValid() &&
   142  		dstPointerReflectValueElem.Elem().Type() == srcValueReflectValue.Type():
   143  		dstPointerReflectValueElem.Elem().Set(srcValueReflectValue)
   144  		return true
   145  
   146  	// Example:
   147  	// *UploadFile    => UploadFile
   148  	// *[]UploadFile  => []UploadFile
   149  	// *map           => map
   150  	// *[]map         => []map
   151  	case srcValueReflectValue.Kind() == reflect.Ptr &&
   152  		srcValueReflectValue.Elem().IsValid() &&
   153  		dstPointerReflectValueElem.Type() == srcValueReflectValue.Elem().Type():
   154  		dstPointerReflectValueElem.Set(srcValueReflectValue.Elem())
   155  		return true
   156  
   157  	default:
   158  		return false
   159  	}
   160  }
   161  
   162  // doConvertWithJsonCheck does json converting check.
   163  // If given `params` is JSON, it then uses json.Unmarshal doing the converting.
   164  func doConvertWithJsonCheck(srcValue interface{}, dstPointer interface{}) (ok bool, err error) {
   165  	switch valueResult := srcValue.(type) {
   166  	case []byte:
   167  		if json.Valid(valueResult) {
   168  			if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
   169  				if dstPointerReflectType.Kind() == reflect.Ptr {
   170  					if dstPointerReflectType.IsNil() {
   171  						return false, nil
   172  					}
   173  					return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Interface())
   174  				} else if dstPointerReflectType.CanAddr() {
   175  					return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Addr().Interface())
   176  				}
   177  			} else {
   178  				return true, json.UnmarshalUseNumber(valueResult, dstPointer)
   179  			}
   180  		}
   181  
   182  	case string:
   183  		if valueBytes := []byte(valueResult); json.Valid(valueBytes) {
   184  			if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
   185  				if dstPointerReflectType.Kind() == reflect.Ptr {
   186  					if dstPointerReflectType.IsNil() {
   187  						return false, nil
   188  					}
   189  					return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Interface())
   190  				} else if dstPointerReflectType.CanAddr() {
   191  					return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Addr().Interface())
   192  				}
   193  			} else {
   194  				return true, json.UnmarshalUseNumber(valueBytes, dstPointer)
   195  			}
   196  		}
   197  
   198  	default:
   199  		// The `params` might be struct that implements interface function Interface, eg: gvar.Var.
   200  		if v, ok := srcValue.(iInterface); ok {
   201  			return doConvertWithJsonCheck(v.Interface(), dstPointer)
   202  		}
   203  	}
   204  	return false, nil
   205  }