• golang 对不同结构体中数据进行相互转换的几种常用方法


    常用的不同结构体中的数据相互转换的方法

    1. 利用json包的marshal和unmarshal

    要求:json标签的值必须一致
    示例:

    package main
    import (
    	"encoding/json"
    	"fmt"
    )
    type A struct {
    	Name string `json:"name"`
    	Age int `json:"age"`
    	Gender string `json:"gender"`
    }
    type B struct {
    	Name string `json:"name"`
    	Age int `json:"age"`
    	Weight string `json:"weight"`
    }
    
    func main() {
    	a:=A{
    		Name:   "小可",
    		Age:    121,
    		Gender: "男",
    	}
    	var b B
    	jsonBytes,_:=json.Marshal(a)
    	err:=json.Unmarshal(jsonBytes,&b)
    	if err!=nil{
    		fmt.Println(err.Error())
    	}else{
    		fmt.Printf("%+v",b)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出:

    {Name:小可 Age:121 Weight:}
    
    • 1

    2. 使用第三方包 copier 进行数据转换

    要求:结构体的数据结构和字段名必须一致

    go get github.com/jinzhu/copier
    
    • 1

    使用示例

    import (
    	"fmt"
    	"github.com/jinzhu/copier"
    )
    
    func main() {
    	a:=A{
    		Name:   "小可",
    		Age:    121,
    		Gender: "男",
    	}
    	var b B
    	err := copier.Copy(&a, &b)
    	if err!=nil{
    		fmt.Println(err.Error())
    	}else{
    		fmt.Printf("%+v",b)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    对字段名相同,但是数据类型不同的字段,可以像这样额外添加option去转换

    // time.Time 和 string 之间相互转换的方法
    var CopierProtoOptions = copier.Option{
    	IgnoreEmpty: true,
    	DeepCopy:    true,
    	Converters: []copier.TypeConverter{
    		{
    			SrcType: time.Time{},
    			DstType: copier.String,
    			Fn: func(src interface{}) (interface{}, error) {
    				s, ok := src.(time.Time)
    				if !ok {
    					return nil, errors.New("src type :time.Time not matching")
    				}
    				return s.Format("2006-01-02 15:04:05"), nil
    			},
    		},
    		{
    			SrcType: copier.String,
    			DstType: time.Time{},
    			Fn: func(src interface{}) (interface{}, error) {
    				s, ok := src.(string)
    				if !ok {
    					return nil, errors.New("src type :time.Time not matching")
    				}
    				tt, err := time.ParseInLocation(s, "2006-01-02 15:04:05", shanghai)
    				return tt, err
    			},
    		},
    	},
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    使用

    type A struct {
    	Name string 
    	Age int
    	Gender string 
    	BirthDay string 
    }
    type B struct {
    	Name string 
    	Age int
    	Weight string
    	BirthDay time.time
    }
    
    func main() {
    	a:=A{
    		Name:   "小可",
    		Age:    121,
    		Gender: "男",
    		BirthDay :"1997-05-08 11:20:11",
    	}
    	var b B
    	err := copier.CopyWithOption(&a, &b,CopierProtoOptions)
    	if err!=nil{
    		fmt.Println(err.Error())
    	}else{
    		fmt.Printf("%+v",b)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3.对结构不同的结构体进行转换

    举例有如下结构体,需要把DataRequest中的数据转换到ProtoRequest 中。特点是,ProtoRequest 中,定义了一个Query字段来继承*CommonQuery类型。

    type CommonQuery struct {
    	Id   int
    	Name string
    }
    type DataRequest struct {
    	CommonQuery
    	Page     int64 `json:"page"`
    	PageSize int64 `json:"pageSize"`
    }
    type ProtoRequest struct {
    	Query    *CommonQuery `json:"query"`
    	Page     int64        `json:"page"`
    	PageSize int64        `json:"pageSize"`
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    此时需要自定义一个方法,示例如下,大家可以参考下面的代码进行适当修改。

    var shanghai, _ = time.LoadLocation("Asia/Shanghai")
    func ConvertData(from interface{}, to interface{}) {
    	var proxyField = "Query"
    	fromValue := reflect.ValueOf(from)
    	toValue := reflect.ValueOf(to)
    	toType := reflect.TypeOf(to)
    
    	// 获取From结构体的字段信息
    	fromType := fromValue.Type().Elem()
    	for i := 0; i < fromType.NumField(); i++ {
    		// 获取字段名和字段值
    		fieldName := fromType.Field(i).Name
    		fieldValue := fromValue.Elem().FieldByName(fieldName)
    		if fieldName != proxyField {
    			_, exists := toType.Elem().FieldByName(fieldName)
    			if exists {
    				// 设置To结构体中相应字段的值
    				toValue.Elem().FieldByName(fieldName).Set(fieldValue)
    			}
    		}
    	}
    	queryField, exists := toType.Elem().FieldByName(proxyField)
    	if exists {
    		var queryFieldTypeName string
    		// 指针类型额外处理,拿到真实的数据类型
    		if queryField.Type.Kind() == reflect.Ptr {
    			queryFieldTypeName = queryField.Type.Elem().String()
    		} else {
    			queryFieldTypeName = queryField.Type.Kind().String()
    		}
    		//处理拿到的结构体类型如 utils.xxxx的类型,去掉utils.这部分
    		if strings.Contains(queryFieldTypeName, ".") {
    			queryFieldTypeName = strings.Split(queryFieldTypeName, ".")[1]
    		}
    		fromQueryValue := fromValue.Elem().FieldByName(queryFieldTypeName)
    		if fromQueryValue.IsValid() && fromQueryValue.CanAddr() {
    			toValue.Elem().FieldByName(proxyField).Set(fromQueryValue.Addr())
    		}
    	}
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    202109 CSP认证 | 脉冲神经网络
    SaulLM-7B: A pioneering Large Language Model for Law
    曲线任意里程中边桩坐标正反算4800P计算序
    ORACLE日期数据类型和转换
    05 CNN 猴子类别检测
    Bra12同态加密方案初步学习
    Django常见面试题总结(二)
    vue全局事件总线是什么?有什么用?解决了什么问题,与pinia有什么区别?
    三面阿里,被Java面试官虐哭!现场还原真实的“被虐”场景
    CentOS7源码安装 lldpd 并附查询脚本
  • 原文地址:https://blog.csdn.net/dorlolo/article/details/133353461