golang 对不同结构体中数据进行相互转换的几种常用方法
- 常用的不同结构体中的数据相互转换的方法
- 1. 利用json包的marshal和unmarshal
- 2. 使用第三方包 copier 进行数据转换
- 3.对结构不同的结构体进行转换
常用的不同结构体中的数据相互转换的方法
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 BjsonBytes,_:=json.Marshal(a)err:=json.Unmarshal(jsonBytes,&b)if err!=nil{fmt.Println(err.Error())}else{fmt.Printf("%+v",b)}
}
输出:
{Name:小可 Age:121 Weight:}
2. 使用第三方包 copier 进行数据转换
要求:结构体的数据结构和字段名必须一致
go get github.com/jinzhu/copier
使用示例
import ("fmt""github.com/jinzhu/copier"
)func main() {a:=A{Name: "小可",Age: 121,Gender: "男",}var b Berr := copier.Copy(&a, &b)if err!=nil{fmt.Println(err.Error())}else{fmt.Printf("%+v",b)}
}
对字段名相同,但是数据类型不同的字段,可以像这样额外添加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},},},
}
使用
type A struct {Name string Age intGender string BirthDay string
}
type B struct {Name string Age intWeight stringBirthDay time.time
}func main() {a:=A{Name: "小可",Age: 121,Gender: "男",BirthDay :"1997-05-08 11:20:11",}var b Berr := copier.CopyWithOption(&a, &b,CopierProtoOptions)if err!=nil{fmt.Println(err.Error())}else{fmt.Printf("%+v",b)}
}
3.对结构不同的结构体进行转换
举例有如下结构体,需要把DataRequest
中的数据转换到ProtoRequest
中。特点是,ProtoRequest
中,定义了一个Query字段来继承*CommonQuery
类型。
type CommonQuery struct {Id intName string
}
type DataRequest struct {CommonQueryPage int64 `json:"page"`PageSize int64 `json:"pageSize"`
}
type ProtoRequest struct {Query *CommonQuery `json:"query"`Page int64 `json:"page"`PageSize int64 `json:"pageSize"`
}
此时需要自定义一个方法,示例如下,大家可以参考下面的代码进行适当修改。
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).NamefieldValue := 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())}}
}