将多个复杂的映射键作为结构体的 YAML 翻译为:

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

yaml multiple complex map keys as a struct

问题

我想将复杂的映射键解组为结构体:

YAML

unf:
    item_meta:
      source:
        ? id: "aa"
          name: "bb"
        : "some value"

结构体

type Source struct {
	ID     string `yaml:"id"`
	Name   string `yaml:"name"`
}

一切都按预期工作,直到我添加另一个键:

YAML 2

unf:
    item_meta:
      source:
        ? id: "012"
          name: "Bill"
        : "some value"
        ? id: "066"
          name: "Bob"
        : "another value"

我收到一个错误

"line xxx: mapping key "" already defined at line xxx"

我决定使用别名:

YAML 3

unf:
    item_meta:
      aliases:
        - bill: &alias_bill
          id: "012"
          name: "Bill"
        - bob: &alias_bob
          id: "066"
          name: "Bob"
      source:
        ? *alias_bill
        : "some value"
        ? *alias_bob
          name: "Bob"
        : "another value"

它解决了问题!但是我们在堆栈中使用hiera server,而hiera返回已经替换的配置文件内容,所以我最终得到YAML 2版本。

有没有办法解决这个问题?配置hiera server不是一个选项。

前往playground

英文:

I want to unmarshal complex map key into struct:

YAML

unf:
    item_meta:
      source:
        ? id: "aa"
          name: "bb"
        : "some value"

Struct

type Source struct {
	ID     string `yaml:"id"`
	Name   string `yaml:"name"`
}

Everything works as expected until I add another key:

YAML 2

unf:
    item_meta:
      source:
        ? id: "012"
          name: "Bill"
        : "some value"
        ? id: "066"
          name: "Bob"
        : "another value"

I got an error

> "line xxx: mapping key "" already defined at line xxx"

I decided to use aliases:

YAML 3

unf:
    item_meta:
      aliases:
        - bill: &alias_bill
          id: "012"
          name: "Bill"
        - bob: &alias_bob
          id: "066"
          name: "Bob"
      source:
        ? *alias_bill
        : "some value"
        ? *alias_bob
          name: "Bob"
        : "another value"

It fixed the problem! BUT we use hiera server in our stack AND hiera returns contents of a config file already substituted so I end up with YAML 2 version.

Any ideas on how to fix the problem? To config the hiera server is not an option.

Go playground

答案1

得分: 1

我的解决方案主要基于larsks的这个问题

想法是找到具有重复映射键的节点,并从映射节点的值节点创建自定义值。

func fixYamlNode(node *yaml.Node) {
    if node.Kind == yaml.MappingNode {
        length := len(node.Content)

        for i := 0; i < length; i += 2 {
            nodes := make([]*yaml.Node, 0)
            nodes = append(nodes, node.Content[i])
            for j := i + 2; j < length; j += 2 {
                nj := node.Content[j]
                if nodes[0].Kind == nj.Kind && nodes[0].Value == nj.Value {
                    nodes = append(nodes, nj)
                }
            }
            if len(nodes) == 1 {
                continue
            }

            fillMapValue(nodes)
        }

        for i := 1; i < length; i += 2 {
            valueNode := node.Content[i]
            fixYamlNode(valueNode)
        }
    }
}

func fillMapValue(nodes []*yaml.Node) {
    for _, node := range nodes {
        length := len(node.Content)
        for i := 0; i < length; i += 2 {
            node.Value = fmt.Sprintf("%s %s", node.Value, node.Content[i+1].Value)
        }
    }
}
英文:

My solution mostly was based on this issue @larsks

The idea was to find nodes with duplicate map keys and to create a custom value from value nodes of THE map node.

func fixYamlNode(node *yaml.Node) {
	if node.Kind == yaml.MappingNode {
		length := len(node.Content)

		for i := 0; i &lt; length; i += 2 {
			nodes := make([]*yaml.Node, 0)
			nodes = append(nodes, node.Content[i])
			for j := i + 2; j &lt; length; j += 2 {
				nj := node.Content[j]
				if nodes[0].Kind == nj.Kind &amp;&amp; nodes[0].Value == nj.Value {
					nodes = append(nodes, nj)
				}
			}
			if len(nodes) == 1 {
				continue
			}

			fillMapValue(nodes)
		}

		for i := 1; i &lt; length; i += 2 {
			valueNode := node.Content[i]
			fixYamlNode(valueNode)
		}
	}
}

func fillMapValue(nodes []*yaml.Node) {
	for _, node := range nodes {
		length := len(node.Content)
		for i := 0; i &lt; length; i += 2 {
			node.Value = fmt.Sprintf(&quot;%s %s&quot;, node.Value, node.Content[i+1].Value)
		}
	}
}

答案2

得分: 0

这是go-yaml中的一个错误。请参考这些行

			ni := n.Content[i]
			for j := i + 2; j < l; j += 2 {
				nj := n.Content[j]
				if ni.Kind == nj.Kind && ni.Value == nj.Value {
					d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
				}
			}

这段代码用于检查重复的键。对于映射中的每个键(ni),它会遍历所有后续的键(nj),并检查它们是否具有相同的Kind和相同的Value。这是错误的,因为Value仅针对标量键设置,所以一旦你有两个键是映射类型,即使键是不同的,这个错误也会触发,因为代码不会比较键的内容,只会比较它们的值(对于非标量节点,值将为空字符串)。

我建议你在go-yaml上提一个问题。

英文:

This is an error in go-yaml. See these lines:

			ni := n.Content[i]
			for j := i + 2; j &lt; l; j += 2 {
				nj := n.Content[j]
				if ni.Kind == nj.Kind &amp;&amp; ni.Value == nj.Value {
					d.terrors = append(d.terrors, fmt.Sprintf(&quot;line %d: mapping key %#v already defined at line %d&quot;, nj.Line, nj.Value, ni.Line))
				}
			}

This code checks for duplicate keys. For each key in the mapping (ni), it goes through all succeeding keys (nj) and checks whether they have the same Kind and the same Value. This is wrong, since Value is only set for scalar keys, so as soon as you have two keys that are mappings, this error fires even though the keys are different, because the code does not compare the key's content, only their Values (which will be the empty strings for non-scalar nodes).

I suggest you open an issue with go-yaml.

huangapple
  • 本文由 发表于 2023年1月30日 22:58:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75286145.html
匿名

发表评论

匿名网友

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

确定