前言

今天介绍一个 go 库 - mergo, mergo 用来方便的合并 struct 和 map ,可以将结构体的字段赋值到map中,可以将map的值赋值给结构体的字段.

Mergo 通过在零值字段中设置默认值来合并同类型的struct 和 map。Mergo 不会合并未导出(私有)字段。它会递归合并任何已导出的字段。它也不会合并map中的结构体(因为它们无法使用 Go 反射寻址)。

Mergo 在很多知名项目中被使用,如 containerd、k8s、loki等。

安装

使用以下命令安装 mergo : go get -u dario.cat/mergo

使用

接下来介绍 mergo 的基础使用和高级用法。

基础使用

mergo 提供了2个主要函数: Merge和Map, Mergo 用来合并2个相同结构的 struct 和 map, Map 用来在结构和map之间赋值。

例子:

package main

import (
	"fmt"
	"log"

	"github.com/imdario/mergo"
)

type redisConfig struct {
	Address  string
	Port     int
	DB       int
	UserName string
	PassWord string
}

var defaultConfig = redisConfig{
	Address:  "127.0.0.1",
	Port:     6379,
	DB:       1,
	UserName: "123",
	PassWord: "123",
}

func main() {
	var config redisConfig

	if err := mergo.Merge(&config, defaultConfig); err != nil {
		log.Fatal(err)
	}

	fmt.Println("redis address: ", config.Address)
	fmt.Println("redis port: ", config.Port)
	fmt.Println("redis db: ", config.DB)
	fmt.Println("redis username: ", config.UserName)
	fmt.Println("redis password: ", config.PassWord)

	var m = make(map[string]interface{})
	if err := mergo.Map(&m, defaultConfig); err != nil {
		log.Fatal(err)
	}

	fmt.Println(m)
}

接下来介绍一些高级用法:

覆盖

如果目标结构已经有初始值的情况,Merge/Map 函数不会覆盖已经有的值,如果想要覆盖原有的值,需要使用 WithOverride 参数。

	var config redisConfig
	config.UserName = "overstarry"
	if err := mergo.Merge(&config, defaultConfig, mergo.WithOverride); err != nil {
		log.Fatal(err)
	}

如果需要检查合并的对象类型是否一致,可以使用 mergo.WithTypeCheck 参数。如果需要覆盖指针,则需使用 WithoutDereference 参数。

slice

如果结构体中的字段是切片类型,覆盖时想要合并2个切片,则使用 WithAppendSlice 参数。

  if err := mergo.Merge(&config, defaultConfig, mergo.WithAppendSlice); err != nil {
    log.Fatal(err)
  }

小结

本文简单的介绍了 mergo 库和常用的一些参数用法,mergo 还提供了一些复杂的参数,有兴趣的读者可以自行研究。

参考