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

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

问题描述

看看这个 struct

  type Config struct {
路径字符串
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 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

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

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