要求: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)
}
}
输出:
{Name:小可 Age:121 Weight:}
要求:结构体的数据结构和字段名必须一致
go get github.com/jinzhu/copier
使用示例
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)
}
}
对字段名相同,但是数据类型不同的字段,可以像这样额外添加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 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)
}
}
举例有如下结构体,需要把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"`
}
此时需要自定义一个方法,示例如下,大家可以参考下面的代码进行适当修改。
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())
}
}
}