"合并"字段两个相同类型的结构 [英] "Merge" fields two structs of same type
问题描述
看看这个 struct
:
type Config struct {
$ p $现在我有一个
路径字符串
id字符串
键字符串
地址字符串
大小uint64
}
DefaultConfig
用一些值初始化,一个从一个文件加载,比如说>
。
FileConfig
我希望将这两个结构合并到一起,以便使用两个结构的内容获得Config
。FileConfig
应该覆盖DefaultConfig
中设置的任何内容,而FileConfig
可能没有设置所有字段。
(为什么这么做?因为潜在用户可能不知道默认值,因此删除该条目将等同于设置默认值 - 我认为)
I我认为我需要反思:
func合并(默认*配置,文件*配置)(*配置){
b:= reflect.ValueOf(default).Elem()
o:= reflect.ValueOf(file).Elem()
for i:= 0;我< 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
}
这里我不确定:
- 如果需要反思
- 可能有更简单的方法可以做到这一点
我在这里看到的另一个问题是,检查零值可能会很棘手:如果重写的struct 意图用零值覆盖会怎么样?幸运的是,我不认为它适用于我的情况 - 但是这变成了一个函数,它可能会在稍后成为问题。 解决方案
前言: encoding / json
包使用反射(包 反映
)来读/写值,包括结构。其他使用反射的库(例如TOML和YAML的实现)可能以类似的方式运行(或者以相同的方式),因此这里介绍的原理也可能适用于这些库。您需要用您使用的库进行测试。
为简单起见,此处介绍的解决方案使用标准库的 encoding / json
$ hr
$ b
一个优雅的零工作解决方案是使用 encoding / json
package和 unmarshal为一个值准备,默认配置。
这可以处理您需要的一切:
类型配置结构{
S1字符串
S2字符串
S3字符串
S4字符串
S5字符串
}
以及默认配置:
var defConfig =& Config {
S1:,//零值
S2:,//零值
S3:abc,
S4:def,
S5:ghi,
}
假设文件包含以下配置:
const fileContent =`{S2:file-s2, S3:,S5:file-s5}`
覆盖 S2
, S3
和 S5
字段。
加载配置的代码:
conf:= new(Config) //新配置
* conf = * defConfig //用默认值初始化
err:= json.NewDecoder(strings.NewReader(fileContent))。Decode(& conf)
如果err!= nil {
panic(err)
}
fmt.Printf(%+ v,conf)
然后输出(在 Go Playground ):
& {S1:S2:file-s2 S3 :S4:def S5:file-s5}
分析结果:
-
S1
默认为零,文件丢失,结果为零 - S2 在默认情况下为零,在文件中给出,结果为文件值
-
S3
在config中给出,被覆盖为文件中的零,结果为零 -
S4
在config中,缺少文件,结果是默认值。
-
S5在配置中给出,在文件中给出,结果是文件值
Looking at this struct
:
type Config struct {
path string
id string
key string
addr string
size uint64
}
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
}
Here I am not sure:
- If reflection is needed at all
- There may be easier ways to do this
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
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.
For simplicity, the solution presented here uses the standard lib's 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:
- missing values in config file: default applies
- a value given in file overrides default config (whatever it was)
- explicit overrides to zero values in the file takes precedence (overwrites non-zero default config)
To demonstrate, we'll use this config struct:
type Config struct {
S1 string
S2 string
S3 string
S4 string
S5 string
}
And the default configuration:
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"}`
The file config overrides S2
, S3
and the S5
fields.
Code to load the configuration:
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)
And the output (try it on the Go Playground):
&{S1: S2:file-s2 S3: S4:def S5:file-s5}
Analyzing the results:
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屋!