• Go语言不同结构体相同字段名,进行值转换


    问题:下面定义Student和Teacher两个结构体,如何让他们的数据值转换呢?

    type Student struct {
        Id   int    `json:"id"`
        Name string `json:"name"`
        Slic []int  `json:"slic"`
        S    struct {
            Id   int    `json:"id"`
            Name string `json:"name"`
        } `json:"s"`
    }
    
    type Teacher struct {
        Id   int    `json:"id"`
        Name string `json:"name"`
        Slic []int  `json:"slic"`
        S    struct {
            Id   int    `json:"id"`
            Name string `json:"name"`
        } `json:"s"`
    }

    小问题,还可以很优雅:

    // StructToStruct 结构体转结构体
    func StructToStruct(sourceStruct, targetStruct interface{}, cover bool) (err error) {
    	sourceType := reflect.TypeOf(sourceStruct)
    	targetType := reflect.TypeOf(targetStruct)
    	if targetType.Kind() != reflect.Ptr {
    		err = errors.New("转换失败,目标结构体不是指针")
    		return
    	}
    	for sourceType.Kind() == reflect.Ptr {
    		sourceType = sourceType.Elem()
    	}
    	for targetType.Kind() == reflect.Ptr {
    		targetType = targetType.Elem()
    	}
    	if sourceType.Kind() != reflect.Struct || targetType.Kind() != reflect.Struct {
    		err = errors.New("转换失败,只支持转结构体")
    		return
    	}
    	sourceValue := reflect.ValueOf(sourceStruct)
    	targetValue := reflect.ValueOf(targetStruct)
    	for sourceValue.Kind() == reflect.Ptr {
    		sourceValue = sourceValue.Elem()
    	}
    	for targetValue.Kind() == reflect.Ptr {
    		targetValue = targetValue.Elem()
    	}
    	ok := transformation(sourceType, targetType, sourceValue, targetValue, cover)
    	if !ok {
    		err = errors.New("未转换")
    	}
    	return
    }
    
    func transformation(sourceType, targetType reflect.Type, sourceValue, targetValue reflect.Value, cover bool) (isSet bool) {
    	for sourceType.Kind() == reflect.Ptr {
    		sourceType = sourceType.Elem()
    	}
    	for targetType.Kind() == reflect.Ptr {
    		targetType = targetType.Elem()
    	}
    	for sourceValue.Kind() == reflect.Ptr {
    		sourceValue = sourceValue.Elem()
    	}
    	for targetValue.Kind() == reflect.Ptr {
    		targetValue = targetValue.Elem()
    	}
    	// 获取目标字段,只获取不为0,且可设置的字段
    	var targetMap = make(map[string]int, targetValue.NumField())
    	for i := 0; i < targetValue.NumField(); i++ {
    		if !targetValue.Field(i).CanSet() {
    			continue
    		}
    		if !cover && !isBlank(targetValue.Field(i)) {
    			continue
    		}
    		targetMap[getFieldName(targetType.Field(i))] = i
    	}
    	if len(targetMap) == 0 {
    		return
    	}
    	// 获取源数据字段
    	var sourceMap = make(map[string]int, sourceType.NumField())
    	for i := 0; i < sourceType.NumField(); i++ {
    		name := getFieldName(sourceType.Field(i))
    		if _, ok := targetMap[name]; !ok {
    			continue
    		}
    		sourceMap[getFieldName(sourceType.Field(i))] = i
    	}
    	for name, source := range sourceMap {
    		target, ok := targetMap[name]
    		if !ok {
    			continue
    		}
    		// 不支持往下层走,后面再补充
    		if sourceValue.Field(source).Kind() == reflect.Ptr || sourceValue.Field(source).Kind() == reflect.Struct {
    			ok = transformation(sourceType.Field(source).Type, targetType.Field(target).Type, sourceValue.Field(source), targetValue.Field(target), cover)
    			if ok {
    				if !isSet {
    					isSet = true
    				}
    				continue
    			}
    		}
    		if sourceType.Field(source).Type != targetType.Field(target).Type {
    			continue
    		}
    		targetValue.Field(target).Set(sourceValue.Field(source))
    		isSet = true
    	}
    	return
    }
    
    // 获取字段tag或者name
    func getFieldName(t reflect.StructField) string {
    	s := t.Tag.Get("gorm")
    	if s != "" {
    		s1 := strings.Split(s, ";")
    		for i := range s1 {
    			if strings.HasPrefix(s1[i], "column:") {
    				s = strings.TrimSpace(s1[i][6:])
    				break
    			}
    		}
    		if s != "" {
    			return s
    		}
    	}
    	s = t.Tag.Get("json")
    	if s != "" {
    		return s
    	}
    	return t.Name
    }
    
    // 判断反射的值是否为0
    func isBlank(value reflect.Value) bool {
    	switch value.Kind() {
    	case reflect.String:
    		return value.Len() == 0
    	case reflect.Bool:
    		return !value.Bool()
    	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
    		return value.Int() == 0
    	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
    		return value.Uint() == 0
    	case reflect.Float32, reflect.Float64:
    		return value.Float() == 0
    	case reflect.Interface, reflect.Ptr:
    		return value.IsNil()
    	}
    	return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
    }
    

      

  • 相关阅读:
    hadoop集群的搭建
    EclipseAndroid打包签名发布安装失败闪退运行不了
    [目录]C#学习笔记
    [目录]搭建一个简单的WebGIS应用程序
    实现DataTables搜索框查询结果高亮显示
    解决将Excel表导入到SQL Server数据库时出现Text was truncated or one or more characters had no match in the target code错误
    将展示内容(div、iframe)放在Expand控件中
    [C#学习笔记1]用csc.exe和记事本写一个C#应用程序
    选中FeatureLayer元素并高亮显示
    在地图中调用显示FeatureLayer并进行render、popupTemplate、添加图例等相关内容的设置
  • 原文地址:https://www.cnblogs.com/hardykay/p/16177265.html
Copyright © 2020-2023  润新知