首页 > 其他分享 >Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib

Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib

时间:2023-03-19 09:44:32浏览次数:51  
标签:选项 map GitHub lib err fmt darjun mergo config

简介

今天我们介绍一个合并结构体字段的库mergomergo可以在相同的结构体或map之间赋值,可以将结构体的字段赋值到map中,可以将map的值赋值给结构体的字段。感谢@thinkgos推荐。

快速使用

先安装:

$ go get github.com/imdario/mergo

后使用:

  1.   package main
  2.    
  3.   import (
  4.   "fmt"
  5.   "log"
  6.    
  7.   "github.com/imdario/mergo"
  8.   )
  9.    
  10.   type redisConfig struct {
  11.   Address string
  12.   Port int
  13.   DB int
  14.   }
  15.    
  16.   var defaultConfig = redisConfig{
  17.   Address: "127.0.0.1",
  18.   Port: 6381,
  19.   DB: 1,
  20.   }
  21.    
  22.   func main() {
  23.   var config redisConfig
  24.    
  25.   if err := mergo.Merge(&config, defaultConfig); err != nil {
  26.   log.Fatal(err)
  27.   }
  28.    
  29.   fmt.Println("redis address: ", config.Address)
  30.   fmt.Println("redis port: ", config.Port)
  31.   fmt.Println("redis db: ", config.DB)
  32.    
  33.   var m = make(map[string]interface{})
  34.   if err := mergo.Map(&m, defaultConfig); err != nil {
  35.   log.Fatal(err)
  36.   }
  37.    
  38.   fmt.Println(m)
  39.   }

使用非常简单。mergo提供了两组接口(其实就是两个,*WithOverwrite已经废弃了,可使用WithOverride选项代替):

  • Merge:合并两个相同类型的结构或map
  • Map:在结构和map之间赋值。

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

高级选项

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

覆盖

默认情况下,如果目标对象的字段已经设置了,那么Merge/Map不会用源对象中的字段替换它。我们在上面程序的var config redisConfig定义下添加一行:

config.DB = 2

再看看运行结果,发现输出的db是 2,而非 1。

可以通过选项来改变这个行为,调用Merge/Map时,传入WithOverride参数,那么目标对象中已经设置的字段也会被覆盖:

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

只需要修改这一行调用。结果输出db是 1,覆盖了!

这里用到了 Go 中的选项模式。在参数比较多,且大部分有默认值的情况下,我们可以在函数最后添加一个可变的选项参数,通过传入选项来改变函数的行为,不传入的选项就使用默认值。选项模式在 Go 语言中使用非常广泛,能大大提高代码的可扩展性,使用可变参数也能使函数更易用。mergo中的选项都是这种形式。想要深入了解一下?看这里https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis。

mergo老的接口MergeWithOverrideMapWithOverride都使用选项模式重构了。

切片

如果某个字段是一个切片,不覆盖就保留目标对象的值,或者用源对象的值覆盖都不合适。我们可能想将源对象中切片的值对添加到目标对象的字段中,这时可以使用WithAppendSlice选项。

  1.   package main
  2.    
  3.   import (
  4.   "fmt"
  5.   "log"
  6.    
  7.   "github.com/imdario/mergo"
  8.   )
  9.    
  10.   type redisConfig struct {
  11.   Address string
  12.   Port int
  13.   DBs []int
  14.   }
  15.    
  16.   var defaultConfig = redisConfig{
  17.   Address: "127.0.0.1",
  18.   Port: 6381,
  19.   DBs: []int{1},
  20.   }
  21.    
  22.   func main() {
  23.   var config redisConfig
  24.   config.DBs = []int{2, 3}
  25.    
  26.   if err := mergo.Merge(&config, defaultConfig, mergo.WithAppendSlice); err != nil {
  27.   log.Fatal(err)
  28.   }
  29.    
  30.   fmt.Println("redis address: ", config.Address)
  31.   fmt.Println("redis port: ", config.Port)
  32.   fmt.Println("redis dbs: ", config.DBs)
  33.   }

我们将DB字段改为[]int类型的DBs,使用WithAppendSliec选项,最后输出的DBs[2 3 1]

空值覆盖

默认情况下,如果源对象中的字段为空值(数组、切片长度为 0 ,指针为nil,数字为 0,字符串为""等),即使我们使用了WithOverride选项也是不会覆盖的。下面两个选项就是强制这种情况下也覆盖:

  • WithOverrideEmptySlice:源对象的空切片覆盖目标对象的对应字段;
  • WithOverwriteWithEmptyValue:源对象中的空值覆盖目标对象的对应字段,其实这个对切片也有效。

文档中这两个选项的介绍比较混乱,我通过看源码和自己试验下来发现:

  • 这两个选项都必须和WithOverride一起使用;
  • WithOverwriteWithEmptyValue这个选项也可以处理切片类型的值。

看下面代码:

  1.   type redisConfig struct {
  2.   Address string
  3.   Port int
  4.   DBs []int
  5.   }
  6.    
  7.   var defaultConfig = redisConfig{
  8.   Address: "127.0.0.1",
  9.   Port: 6381,
  10.   }
  11.    
  12.   func main() {
  13.   var config redisConfig
  14.   config.DBs = []int{2, 3}
  15.    
  16.   if err := mergo.Merge(&config, defaultConfig, mergo.WithOverride, mergo.WithOverrideEmptySlice); err != nil {
  17.   log.Fatal(err)
  18.   }
  19.    
  20.   fmt.Println("redis address: ", config.Address)
  21.   fmt.Println("redis port: ", config.Port)
  22.   fmt.Println("redis dbs: ", config.DBs)
  23.   }

最终会输出空的DBs

类型检查

这个主要用在map之间的切片字段的赋值,因为使用mergo在两个结构体之间赋值必须保证两个结构体类型相同,没有类型检查的必要。因为map类型为map[string]interface{},所以默认情况下,map切片类型不一致也是可以赋值的:

  1.   func main() {
  2.   m1 := make(map[string]interface{})
  3.   m1["dbs"] = []uint32{2, 3}
  4.    
  5.   m2 := make(map[string]interface{})
  6.   m2["dbs"] = []int{1}
  7.    
  8.   if err := mergo.Map(&m1, &m2, mergo.WithOverride); err != nil {
  9.   log.Fatal(err)
  10.   }
  11.    
  12.   fmt.Println(m1)
  13.   }

如果添加mergo.WithTypeCheck选项,则切片类型不一致会抛出错误:

  1.   if err := mergo.Map(&m1, &m2, mergo.WithOverride, mergo.WithTypeCheck); err != nil {
  2.   log.Fatal(err)
  3.   }

输出:

  1.   cannot override two slices with different type ([]int, []uint32)
  2.   exit status 1

注意事项

  1. mergo不会赋值非导出字段;
  2. map中对应的键名首字母会转为小写;
  3. mergo可嵌套赋值,我们演示的只有一层结构。

总结

mergo其实在很多知名项目中都有应用,如moby/kubernetes等。本文介绍了mergo的基本用法,感兴趣可以去 GitHub 上深入学习。关于选项模式,这里多说一句,我在实际项目中多次应用,能极大地提高可扩展性,方便今后添加新的功能。

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue

参考

  1. mergo GitHub:https://github.com/imdario/mergo
  2. Go 每日一库 GitHub:https://github.com/darjun/go-daily-lib

标签:选项,map,GitHub,lib,err,fmt,darjun,mergo,config
From: https://www.cnblogs.com/cheyunhua/p/17232486.html

相关文章

  • 爬虫urllib库(上)
    Urllib1.什么是互联网爬虫?如果我们把互联网比作一张大的蜘蛛网,那一台计算机上的数据便是蜘蛛网上的一个猎物,而爬虫程序就是一只小蜘蛛,沿着蜘蛛网抓取自己想要的数据。2......
  • Quick start | Libevent
    1.下载libevent源码$gitclonehttps://github.com/libevent/libevent.git2.创建本地安装目录并添加本地bin目录到环境变量PATH$exportMY_INSTALL_DIR=$HOME/.......
  • 哔哩哔哩(bilibili)如何下载视频
    一:哔哩哔哩bilibili下载视频名称推荐在线哔哩哔哩(bilibili)视频解析下载工具哔哩哔哩下载中心、唧唧、下载姬脚本插件工具NDM、哔哩哔哩助手、增强脚本......
  • configure: error: MySQL library not found 报错
     在CentOS系统中,安装zabbix进行configure时会遇到以下问题./configure--enable-server--enable-agent--with-mysql--with-net-snmp --with-jabber--with-libcurl......
  • linux下安装绿色版(glibc版)mysql-5.7.31
    安装依赖库libaio库#yuminstalllibaio-y上传软件包解压#tar-xfmysql-5.7.31-linux-glibc2.12-x86_64.tar.gz软件的安装第一步:创建一个数据库专用账号mysql(其所属组也......
  • [A40i-源码构建] fakeroot: preload library 'libfakeroot.so' not found, aborting.
    fakeroot:preloadlibrary`libfakeroot.so'notfound,aborting.​ 查看linux-3.10/scripts/build.sh第298行:../scripts/build_rootfs.she./$RAMFS_TARGET>/dev......
  • 2023-Q1 公众号内容全部整合到 Github 啦
    项目地址:https://github.com/duanbiaowu/go-examples-for-beginners......
  • GitHub删除项目步骤
    删除GitHub中项目步骤1、选择要删除项目2、点击“Setting”3、一直拉到最后,选择“Deletethisrepository”4、位置①就是你的项目名称,直接复制到位置②,点击位置③,......
  • github高级搜索和快速开发
    1.打开GitHub,进行学习或范围搜索          2.快捷搜索,在目标仓库下进行快捷搜索(按T键),如搜索button.js等  3.快速定位,当我们看一段代码(具体到......
  • 用TortoiseGit上传代码到Github的步骤
    下载安装两个软件:   官方网址下载:GitforWindowshttps://gitforwindows.org/Download–TortoiseGit–WindowsShellInterfacetoGithttps://tortoisegit.o......