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