首页 > 其他分享 >Go每日一库之19:mergo

Go每日一库之19:mergo

时间:2023-09-22 09:03:32浏览次数:22  
标签:选项 map err fmt 一库 Go mergo config

简介

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

快速使用

先安装:

$ go get github.com/imdario/mergo

后使用:

package main

import (
  "fmt"
  "log"

  "github.com/imdario/mergo"
)

type redisConfig struct {
  Address string
  Port    int
  DB      int
}

var defaultConfig = redisConfig{
  Address: "127.0.0.1",
  Port:    6381,
  DB:      1,
}

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)

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

  fmt.Println(m)
}

使用非常简单。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参数,那么目标对象中已经设置的字段也会被覆盖:

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

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

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

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

切片

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

package main

import (
  "fmt"
  "log"

  "github.com/imdario/mergo"
)

type redisConfig struct {
  Address string
  Port    int
  DBs     []int
}

var defaultConfig = redisConfig{
  Address: "127.0.0.1",
  Port:    6381,
  DBs:     []int{1},
}

func main() {
  var config redisConfig
  config.DBs = []int{2, 3}

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

  fmt.Println("redis address: ", config.Address)
  fmt.Println("redis port: ", config.Port)
  fmt.Println("redis dbs: ", config.DBs)
}

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

空值覆盖

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

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

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

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

看下面代码:

type redisConfig struct {
  Address string
  Port    int
  DBs     []int
}

var defaultConfig = redisConfig{
  Address: "127.0.0.1",
  Port:    6381,
}

func main() {
  var config redisConfig
  config.DBs = []int{2, 3}

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

  fmt.Println("redis address: ", config.Address)
  fmt.Println("redis port: ", config.Port)
  fmt.Println("redis dbs: ", config.DBs)
}

最终会输出空的DBs

类型检查

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

func main() {
  m1 := make(map[string]interface{})
  m1["dbs"] = []uint32{2, 3}

  m2 := make(map[string]interface{})
  m2["dbs"] = []int{1}

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

  fmt.Println(m1)
}

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

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

输出:

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

注意事项

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

总结

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

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

标签:选项,map,err,fmt,一库,Go,mergo,config
From: https://www.cnblogs.com/arena/p/17721475.html

相关文章

  • django- 实现模型字段 每天 -1
    fromdjango.dbimportmodelsfromdatetimeimportdatetimeclassMyModel(models.Model):count=models.IntegerField()created_at=models.DateTimeField(auto_now_add=True)defsave(self,*args,**kwargs):ifnotself.created_at:......
  • python: Essential Algorithms
     #encoding:utf-8#版权所有2023涂聚文有限公司#许可信息查看:#描述:#Author:geovindu,GeovinDu涂聚文.#IDE:PyCharm2023.1python311#Datetime:2023/9/2121:28#User:geovindu#Product:PyCharm#Project:EssentialAlgor......
  • golang 有没有 类似 typescript 的 联合类型?
    Go语言(Golang)不像TypeScript那样直接支持联合类型(UnionTypes)。在TypeScript中,联合类型允许一个变量具有多个不同的数据类型,而在Go中,通常使用接口(interfaces)和具体类型来处理类似的情况。以下是在Go中处理联合类型的一些方法:使用接口:Go中的接口可以用于定义一组方法的契约,而不是特......
  • 关于hive中的com.google.common.base.Preconditions.checkArgument(ZLjava/lang/Strin
    com.google.common.base.Preconditions.checkArgument(ZLjava/lang/String;Ljava/lang/Object;)V这个报错是因为Hive 3.1.3guava19.jar和hadoop3.2.4不兼容导致 解决方法—— 之后hive就可以正常初始化了  参考博客——https://blog.csdn.net/happyfreeangel/ar......
  • Python Web开发:Django框架入门
    Django是一个高级的PythonWeb框架,它鼓励快速开发和干净,务实的设计。它旨在使开发人员能够快速地构建和维护高质量的Web应用程序。在本博文中,我们将介绍Django框架的基础知识,并通过具体案例说明其功能。Django安装首先,我们需要在计算机上安装Django。你可以使用pip(Python包管理器)来......
  • go开发之基于个微的机器人开发
    简要描述:删除联系人请求URL:http://域名地址/delContact请求方式:POST请求头Headers:Content-Type:application/jsonAuthorization:login接口返回参数:参数名必选类型说明wId是String微信实列IDwcId是String需删除的微信id返回数据:参数名类型说......
  • 最简单的方式实现 Golang的级别日志
    lo.gopackageloimport("log""os")const(TRACEint=iotaDEBUGINFOWARNERRORFATAL)typeLevelLoggerstruct{levelintlogger*log.Logger}varl*LevelLoggerfuncinit(){......
  • 全栈性能测试工具:RunnerGo
    随着自动化测试技术的不断进步,自动化测试已成为企业级应用的重要组成部分。然而,传统的性能测试工具往往复杂、繁琐,让企业陷入了两难的境地。软件测试正逐渐从手动测试向自动化测试转变,各种自动化测试工具和框架层出不穷,极大地提高了测试效率和质量。近年来出现的敏捷开发方法使得......
  • 成功入选 2023 谷歌出海创业加速器,Tapdata 乘势远航Tapdata Connector 实用指南:如何将
    9月6日,2023Google开发者大会的收官之行于上海拉开帷幕。会间,官方正式公布了最新一期谷歌出海创业加速器入营名单,Tapdata成功入选:长期以来,Google开发者大会为开发者提供了一个独一无二的学习和合作机会,这是一场汇聚全球创新者的聚会,鼓励创新思维。从中能够深入了解最新的......
  • 全栈性能测试工具:RunnerGo
    随着自动化测试技术的不断进步,自动化测试已成为企业级应用的重要组成部分。然而,传统的性能测试工具往往复杂、繁琐,让企业陷入了两难的境地。软件测试正逐渐从手动测试向自动化测试转变,各种自动化测试工具和框架层出不穷,极大地提高了测试效率和质量。近年来出现的敏捷开发方法使得软......