I am trying to unmarshall the following yaml into the below structs, how do I convert the List field into a map field and access it using struct?

huangapple go评论87阅读模式
英文:

I am trying to unmarshall the following yaml into the below structs, how do I convert the List field into a map field and access it using struct?

问题

YAML文件:

namespaces:
  - namespace: default
    aliasname: k8s
    components:
      - component: comp1
        replicas: 1
        port: 8080
      - component: comp2
        replicas: 1
        port: 9999
  - namespace: ns2
    components:
      - component: comp1
        replicas: 1

根据上面的YAML文件,我想创建以下结构体:

type Namespaces struct {
	NamespaceName    string       `yaml:"namespace"`
	Aliasname     string       `yaml:"aliasname,omitempty"`
	ListOfComponents []Components `yaml:"components"`
    ComponentMap     map[string]Components

}

type Components struct {
	ComponentName string `yaml:"component"`
	NumReplicas   int    `yaml:"replicas"`
	Port          int    `yaml:"port"`
}

type Config struct {
	ListOfNamespaces []Namespaces `yaml:"namespaces"`
	NamespaceMap map[string]Namespaces
}

在访问confignamespace对象时,应该能够检索到NamespacemapComponentmap字段。我创建了一个方法将命名空间和组件的列表转换为映射,但是当我调用config.NamespacemapNamespace.ComponentMap时,它返回一个空映射。

基本上我想知道:如何向类型结构体添加额外的字段?我想从config结构体中访问新的变量,比如一个映射。

更新:
感谢blami的指导,但是当我尝试为Components编写相同的代码时,它没有给我整个包含componentmap的namespacemap:

type Components struct {
	ComponentName string `yaml:"component"`
	NumReplicas   int    `yaml:"replicas"`
	Port          int    `yaml:"port"`

}

type Namespaces struct {
	NamespaceName string                `yaml:"namespace"`
	Aliasname  string                `yaml:"aliasname"`
	ComponentMap  map[string]Components `yaml:"components"`
}

func (n *Namespaces) UnmarshalYAML(unmarshal func(interface{}) error) error {

	type origNamespace struct {
		ListOfComponents []Components `yaml:"components"`
	}

	var on origNamespace
	err1 := unmarshal(&on)
	if err1 != nil {
		return err1
	}
	n.ComponentMap = make(map[string]Components)
	for _, i := range on.ListOfComponents {
		n.ComponentMap[i.ComponentName] = i
	}

	return nil
}

当我运行config.NamespaceMap时,它给出以下结果:

map[:{NamespaceName: K8sNamespace: ComponentMap:map[comp1:{ComponentName:comp1 NumShards:0 NumReplicas:1 Port:0 EpochLength:0}]}]
英文:

The YAML file:

namespaces:
  - namespace: default
    aliasname: k8s
    components:
      - component: comp1
        replicas: 1
        port: 8080
      - component: comp2
        replicas: 1
        port: 9999
  - namespace: ns2
    components:
      - component: comp1
        replicas: 1

From the YAML file above, I want to create structs like the following:

type Namespaces struct {
	NamespaceName    string       `yaml:"namespace"`
	Aliasname     string       `yaml:"aliasname,omitempty"`
	ListOfComponents []Components `yaml:"components"`
    ComponentMap     map[string]Components

}

type Components struct {
	ComponentName string `yaml:"component"`
	NumReplicas   int    `yaml:"replicas"`
	Port          int    `yaml:"port"`
}

type Config struct {
	ListOfNamespaces []Namespaces `yaml:"namespaces"`
	NamespaceMap map[string]Namespaces
}

The fields Namespacemap and Componentmap should be able to be retrieved when accessing the config and namespace object respectively. I created a method to convert the list of the namespaces and components into maps, but when I call config.Namespacemap or Namespace.ComponentMap, it returns an empty map.

Basically I would like to know: How do we add extra fields to type structs? I would like to access new variables like a map from the config struct.

Update:
Thanks blami for guiding me, but when I try to write the same for Components, it doesn't give me the whole namespacemap with the componentmap included:

type Components struct {
	ComponentName string `yaml:"component"`
	NumReplicas   int    `yaml:"replicas"`
	Port          int    `yaml:"port"`

}

type Namespaces struct {
	NamespaceName string                `yaml:"namespace"`
	Aliasname  string                `yaml:"aliasname"`
	ComponentMap  map[string]Components `yaml:"components"`
}

func (n *Namespaces) UnmarshalYAML(unmarshal func(interface{}) error) error {

	type origNamespace struct {
		ListOfComponents []Components `yaml:"components"`
	}

	var on origNamespace
	err1 := unmarshal(&on)
	if err1 != nil {
		return err1
	}
	n.ComponentMap = make(map[string]Components)
	for _, i := range on.ListOfComponents {
		n.ComponentMap[i.ComponentName] = i
	}

	return nil
}

When I run the config.NamespaceMap it gives the following

map[:{NamespaceName: K8sNamespace: ComponentMap:map[comp1:{ComponentName:comp1 NumShards:0 NumReplicas:1 Port:0 EpochLength:0}]}]

答案1

得分: 1

如果您想进行这样的转换,您需要在ConfigNamespace类型上编写一个自定义的UnmarshalYAML()接收器。以下是一个基本的工作示例(仅适用于"namespaces"):

type Config struct {
	Namespaces map[string]Namespace `yaml:"namespaces"`
}

// 用自定义的解码器替换 go-yaml 的内置解码器,以便将列表转换为映射。
// 请注意,此代码仅作为演示!
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type origConfig struct {
		Namespaces []Namespace `yaml:"namespaces"`
	}
	var o origConfig
	err := unmarshal(&o)
	if err != nil {
		return err
	}

    // 将列表中的命名空间分配给映射;具有相同名称的命名空间将被覆盖
	c.Namespaces = make(map[string]Namespace)
	for _, n := range o.Namespaces {
		c.Namespaces[n.Namespace] = n
	}
	return nil
}

// 您可以像往常一样使用 "Config" 类型
func main() {
	var config Config
	err := yaml.Unmarshal(<your_yaml>, &config)
	if err != nil {
		log.Panic(err)
	}
	fmt.Println(config.Namespaces)
    // map[default:{default k8s} ns2:{ns2 }]
	fmt.Printf("%v\n", config.Namespaces["default"])
    // {default k8s}
}

正如代码示例中所指出的,这会导致一些问题(例如,如果命名空间名称相同该怎么办?)。

Playground 链接:https://go.dev/play/p/IKg8kmRnknq

英文:

If you want to do such transformation you will need to write a customized UnmarshalYAML() receiver on types Config and Namespace. Here is basic working example of doing so (only for "namespaces"):

type Config struct {
	Namespaces map[string]Namespace `yaml:&quot;namespaces&quot;`
}

// Replace go-yaml built-in unmarshaller with custom that will transform list to map.
// Note this code is meant only as demonstration!
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type origConfig struct {
		Namespaces []Namespace `yaml:&quot;namespaces&quot;`
	}
	var o origConfig
	err := unmarshal(&amp;o)
	if err != nil {
		return err
	}

    // Assign namespaces in list to map; namespaces with same name will be overwritten
	c.Namespaces = make(map[string]Namespace)
	for _, n := range o.Namespaces {
		c.Namespaces[n.Namespace] = n
	}
	return nil
}

// You can use &quot;Config&quot; type as usual
func main() {
	var config Config
	err := yaml.Unmarshal(&lt;your_yaml&gt;, &amp;config)
	if err != nil {
		log.Panic(err)
	}
	fmt.Println(config.Namespaces)
    // map[default:{default k8s} ns2:{ns2 }]
	fmt.Printf(&quot;%v\n&quot;, config.Namespaces[&quot;default&quot;])
    // {default k8s}
}

As noted in code example this will cause some problems (e.g. what to do if namespace names are same?).

Playground link: https://go.dev/play/p/IKg8kmRnknq

huangapple
  • 本文由 发表于 2022年1月4日 10:23:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/70573464.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定