拋磚引玉:拷貝物件,CopyObject

1272881215發表於2018-12-05

比如有這樣的需求:把呼叫第三方的 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型別呢?

我把磚頭丟擲來了,希望大家把自己的玉獻出來觀摩參考參考。感覺這樣的需求還是經常遇到

更多原創文章乾貨分享,請關注公眾號
  • 拋磚引玉:拷貝物件,CopyObject
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章