“合并"字段两个相同类型的结构 [英] "Merge" fields two structs of same type
问题描述
看看这个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"}`
文件配置覆盖 S2
、S3
和 S5
字段.
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 zeroS2
was zero in default, was given in file, result is the file valueS3
was given in config, was overriden to be zero in file, result is zeroS4
was given in config, was missing in file, result is the default valueS5
was given in config, was given in file, result is the file value
这篇关于“合并"字段两个相同类型的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!