“合并"字段两个相同类型的结构 [英] "Merge" fields two structs of same type

查看:21
本文介绍了“合并"字段两个相同类型的结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看看这个struct:

type Config struct {
  path string
  id   string
  key  string
  addr string
  size uint64
}

现在我有一个 DefaultConfig 初始化了一些值和一个从文件加载的,比方说 FileConfig.我希望合并两个结构,以便我获得包含两个结构内容的 Config.FileConfig 应该覆盖 DefaultConfig 中设置的任何内容,而 FileConfig 可能没有设置所有字段.(这是为什么?因为潜在用户可能不知道默认值,所以删除该条目相当于设置默认值 - 我认为)

Now I have a DefaultConfig intialized with some values and one loaded from a file, let's say FileConfig. I want both structs to me merged, so that I get a Config with the content of both structs. FileConfig should override anything set in DefaultConfig, while FileConfig may not have all fields set. (Why this? Because a potential user may not know the default value, so removing that entry would be equivalent to setting the default - I think)

我认为我需要对此进行反思:

I thought I'd need reflection for this:

 func merge(default *Config, file *Config) (*Config) {
  b := reflect.ValueOf(default).Elem()
  o := reflect.ValueOf(file).Elem()

  for i := 0; i < b.NumField(); i++ {
    defaultField := b.Field(i)
    fileField := o.Field(i)
    if defaultField.Interface() != reflect.Zero(fileField.Type()).Interface() {
     defaultField.Set(reflect.ValueOf(fileField.Interface()))
    }
  }

  return default
 }

这里我不确定:

  • 如果完全需要反思
  • 可能有更简单的方法来做到这一点

我在这里看到的另一个问题是检查零值可能很棘手:如果覆盖结构打算覆盖零值怎么办?幸运的是,我不认为它适用于我的情况 - 但这变成了一个函数,以后可能会成为问题

Another issue I see here is that checking for zero values may be tricky: what if the overriding struct intends to override with a zero value? Luckily, I don't think it applies to my case - but this becomes a function, it may become a problem later

推荐答案

前言: encoding/json 包使用反射(包 reflect) 读取/写入值,包括结构.其他也使用反射的库(例如 TOML 和 YAML 的实现)可能以类似的方式(甚至以相同的方式)运行,因此这里介绍的原则也适用于这些库.您需要使用您使用的库对其进行测试.

Foreword: The encoding/json package uses reflection (package reflect) to read/write values, including structs. Other libraries also using reflection (such as implementations of TOML and YAML) may operate in a similar (or even in the same way), and thus the principle presented here may apply to those libraries as well. You need to test it with the library you use.

为简单起见,此处提供的解决方案使用标准库的 encoding/json.

For simplicity, the solution presented here uses the standard lib's encoding/json.

一种优雅且零努力"的解决方案是使用 encoding/json打包并解组为准备好的"值,默认配置.

An elegant and "zero-effort" solution is to use the encoding/json package and unmarshal into a value of the "prepared", default configuration.

这可以处理您需要的一切:

This handles everything you need:

  • 配置文件中的缺失值:默认适用
  • 文件中给出的值会覆盖默认配置(无论它是什么)
  • 显式覆盖文件中的零值优先(覆盖非零默认配置)

为了演示,我们将使用这个配置结构:

To demonstrate, we'll use this config struct:

type Config struct {
    S1 string
    S2 string
    S3 string
    S4 string
    S5 string
}

和默认配置:

var defConfig = &Config{
    S1: "", // Zero value
    S2: "", // Zero value
    S3: "abc",
    S4: "def",
    S5: "ghi",
}

假设文件包含以下配置:

And let's say the file contains the following configuration:

const fileContent = `{"S2":"file-s2","S3":"","S5":"file-s5"}`

文件配置覆盖 S2S3S5 字段.

The file config overrides S2, S3 and the S5 fields.

加载配置的代码:

conf := new(Config) // New config
*conf = *defConfig  // Initialize with defaults

err := json.NewDecoder(strings.NewReader(fileContent)).Decode(&conf)
if err != nil {
    panic(err)
}

fmt.Printf("%+v", conf)

和输出(在 Go Playground 上试试):

And the output (try it on the Go Playground):

&{S1: S2:file-s2 S3: S4:def S5:file-s5}

分析结果:

  • S1 默认为零,从文件中丢失,结果为零
  • S2 默认为零,在文件中给出,结果为文件值
  • S3 在配置中给出,在文件中被覆盖为零,结果为零
  • S4 在配置中给出,在文件中丢失,结果为默认值
  • S5 在配置中给出,在文件中给出,结果为文件值
  • S1 was zero in default, was missing from file, result is zero
  • S2 was zero in default, was given in file, result is the file value
  • S3 was given in config, was overriden to be zero in file, result is zero
  • S4 was given in config, was missing in file, result is the default value
  • S5 was given in config, was given in file, result is the file value

这篇关于“合并"字段两个相同类型的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆