安装

go get github.com/imdario/mergo

使用

package main

import (
    "fmt"
    "github.com/imdario/mergo"
    "log"
)

type person struct {
    Age int
    Name string
    Hobby string
}

var tomPerson = person{
    Age:   18,
    Name:  "tom",
    Hobby: "basketball",
}

func main()  {
    var demoPerson person

    if err := mergo.Merge(&demoPerson, tomPerson); err != nil {
        log.Fatalln(err)
    }

    fmt.Println("demoPerson age:", demoPerson.Age)
    fmt.Println("demoPerson name:", demoPerson.Name)
    fmt.Println("demoPerson hobby:", demoPerson.Hobby)

    var p = make(map[string]interface{})

    if err := mergo.Map(&p, tomPerson); err != nil {
        log.Fatal(err)
    }

    fmt.Println(p)
}

使用非常简单,mergo提供了两个接口:

  • Mergo:合并两个相同类型的structmap
  • Map:在structmap之间赋值

参数1是目标对象,参数2是源对象,这两个函数的功能就是将源对象中的字段复制到目标对象的对应字段上

运行上面的代码结果为:

demoPerson age: 18
demoPerson name: tom
demoPerson hobby: basketball
map[age:18 hobby:basketball name:tom]

高级选项

如果仅仅是复制结构体,为啥不直接写demoPerson = tomPerson呢?mergo提供了很多选项

覆盖

默认情况下,如果目标对象的字段已经设置了,那么Mergo/Map不会用源对象中的字段替换它,我们在上面的代码var demoPerson person下加一行

demoPerson.Age = 20

再看看运行结果:

demoPerson age: 20
demoPerson name: tom
demoPerson hobby: basketball
map[age:18 hobby:basketball name:tom]

demoPersonAge为20,我们会发现源对象的Age并未覆盖demoPersonAge

我们可以通过选项来改变这个行为,调用Mergo/Map时,传入WithOverride,那么就会覆盖

if err := mergo.Merge(&demoPerson, tomPerson, mergo.WithOverride); err != nil {
        log.Fatalln(err)
}

此时的输出结果为:

demoPerson age: 18
demoPerson name: tom
demoPerson hobby: basketball
map[age:18 hobby:basketball name:tom]

切片

如果某个字段是一个切片,使用覆盖和不覆盖都不合适,此时我们就需要使用WithAppendSlice选项将源对象切片中的值添加到目标对象的字段中

修改上面的代码

package main

import (
    "fmt"
    "github.com/imdario/mergo"
    "log"
)

type person struct {
    Age int
    Name string
    Hobbies []string
}

var tomPerson = person{
    Age:   18,
    Name:  "tom",
    Hobbies: []string{"basketball"},
}

func main()  {
    var demoPerson person

    demoPerson.Hobbies = []string{"football"}

    if err := mergo.Merge(&demoPerson, tomPerson, mergo.WithAppendSlice); err != nil {
        log.Fatalln(err)
    }

    fmt.Println("demoPerson age:", demoPerson.Age)
    fmt.Println("demoPerson name:", demoPerson.Name)
    fmt.Println("demoPerson hobbies:", demoPerson.Hobbies)
}

此时的输出结果为:

demoPerson age: 18
demoPerson name: tom
demoPerson hobbies: [football basketball]

可以看到,源对象中的值添加到了目标对象中了

空值覆盖

默认情况下,如果源对象中的字段为空值(数字为0,字符串为”“等),即使我们使用了WithOverride选项也不会覆盖,下面这两个选项是强制这种情况下也覆盖:

  • WithOverrideEmptySlice:源对象的空切片覆盖目标对象的对应字段
  • WithOverwriteWithEmptyValue:源对象的空值会覆盖目标对象的对应字段,这个对切片也有效果

这两个选项必须和WithOverride一起使用

package main

import (
    "fmt"
    "github.com/imdario/mergo"
    "log"
)

type person struct {
    Age int
    Name string
    Hobbies []string
}

var tomPerson = person{
    Age:   18,
    Name:  "tom",
}

func main()  {
    var demoPerson person

    demoPerson.Hobbies = []string{"football"}

    if err := mergo.Merge(&demoPerson, tomPerson, mergo.WithOverwriteWithEmptyValue); err != nil {
        log.Fatalln(err)
    }

    fmt.Println("demoPerson age:", demoPerson.Age)
    fmt.Println("demoPerson name:", demoPerson.Name)
    fmt.Println("demoPerson hobbies:", demoPerson.Hobbies)
}

以上代码输出:

demoPerson age: 18
demoPerson name: tom
demoPerson hobbies: []

类型检查

这个主要用在map之间的赋值,因为mergo在两个结构体之间赋值必须要求两个结构体的类型相同,没有检查的必要

func main()  {
    m1 := make(map[string]interface{})
    m1["key"] = []int64{1, 2}

    m2 := make(map[string]interface{})
    m2["key"] = []int{2, 3}

    if err := mergo.Map(&m1, &m2, mergo.WithOverride); err != nil {
        log.Fatalln(err)
    }
    fmt.Println(m1)
}

以上代码输出为:

map[key:[2 3]]

以上代码中的map类型为map[string]interface{},所以默认情况下,map切片类型不一致也是可以赋值的

如果添加mergo.WithTypeCheck选项,则会抛出错误

cannot override two slices with different type ([]int, []int64)

注意事项

  • mergo不会赋值非导出字段
  • map中对应的键名首字母为转为小写
  • mergo可以嵌套赋值

版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:https://www.ltfred.com/article/go-mergo/