抛砖引玉:拷贝对象,CopyObject

比如有这样的需求:把调用第三方的SDK拿到的用户数据录入我们自己的数据库中,但是两边的用户数据结构类似,但并不一样.

比如第三方的用户数据结构是这样的:

type User2 struct {
    Id       int
    Username string
    Age      int
    Address  string
}

而我们存储的用户数据结构是这样的:

type User1 struct {
    Id       int
    Username string
    Age      int
}

我之前傻傻的做法是这样的:

var u2 = User2{
    Username: "这是用户名",
    Id:       1,
    Age:      100,
    Address:  "这是地址",
}
var u1 = User1{
    Id:u2.Id,
    Username:u2.Username,
    Age:u2.Age,
}

也就是逐个字段对比,然后赋值。但是后来发现,如果我们之前不需要的Address字段,现在又需要了,那么除了改User1的结构之外,还要再修改赋值的地方。字段少的时候,这个还好处理,但是很多时候,这类的字段很多,就容易出差错了,比如遗漏了某个字段。

后来我想了个办法,使用reflect进行赋值。代码如下:

package main

import (
    "fmt"
    "reflect"
)

type User1 struct {
    Id       int
    Username string
    Age      int
}

type User2 struct {
    Id       int
    Username string
    Age      int
    Address  string
}

func main() {
    var u2 = User2{
        Username: "这是用户名",
        Id:       1,
        Age:      100,
        Address:  "这是地址",
    }
    var u1 = User1{}
    CopyObject(&u2, &u1)
    fmt.Printf("%+v\n", u1)
}

type ReflectVal struct {
    T reflect.Type
    V reflect.Value
}

func CopyObject(src, dst interface{}) {
    var srcMap = make(map[string]ReflectVal)

    vs := reflect.ValueOf(src)
    ts := reflect.TypeOf(src)
    vd := reflect.ValueOf(dst)
    td := reflect.TypeOf(dst)

    ls := vs.Elem().NumField()
    for i := 0; i < ls; i++ {
        fmt.Println(ts.Elem().Field(i).Name, vs.Elem().Field(i), vs.Elem().Field(i).Type())
        srcMap[ts.Elem().Field(i).Name] = ReflectVal{
            T: vs.Elem().Field(i).Type(),
            V: vs.Elem().Field(i),
        }
    }

    ld := vd.Elem().NumField()
    for i := 0; i < ld; i++ {
        n := td.Elem().Field(i).Name
        t := vd.Elem().Field(i).Type()
        if v, ok := srcMap[n]; ok && v.T == t && vd.Elem().Field(i).CanSet() {
            vd.Elem().Field(i).Set(v.V)
        }
    }
}

这样看起来,好像可以了。但是又有新的问题了,比如第三方的用户数据中的Username,其实对应我们的RealName,也就是字段名不同,这个就需要通过structtag去进行映射处理。那么还有新问题,如果都是Username,但是第三方的是*string类型,而我们的是string类型呢?

我把砖头抛出来了,希望大家把自己的玉献出来观摩参考参考。感觉这样的需求还是经常遇到

1 个评论

参考一下这个,http://legendtkl.com/2016/08/03/go-reflect-value/
对于指针类型,可能要单独处理

要回复文章请先登录注册