Search code examples
goyaml

go-yaml v3 encoder writing tag of merge node ( !!merge) in output


I am trying to parse a Yaml file with go-yaml v3 and do some modifications on the YAML nodes but when I am writing back the content to a file, the yaml encoder adds an extra merge tag (!!merge) wherever i have used <<

func main() {

    sourceYaml := `
demo:
  - name: some_name
    location:
      country:
        meta_data: &meta_data
          name: some_place
          description: "name of a place"
      <<: *meta_data`

    node := yaml.Node{}
    err := yaml.Unmarshal([]byte(sourceYaml), &node)
    if err != nil {
        log.Fatalf(err.Error())
        os.Exit(1)
    }

    var b bytes.Buffer
    yamlEncoder := yaml.NewEncoder(&b)
    yamlEncoder.SetIndent(2)
    err = yamlEncoder.Encode(&node)
    if err != nil {
        log.Fatalf(err.Error())
        os.Exit(1)
    }

    fmt.Println(b.String())
    err = os.WriteFile("test.yaml", b.Bytes(), 0o664)
    if err != nil {
        log.Fatalf(err.Error())
        os.Exit(1)
    }
}

This will print

demo:
  - name: some_name
    location:
      country:
        meta_data: &meta_data
          name: some_place
          description: "name of a place"
      !!merge <<: *meta_data

I was expecting to get the same yaml printed but there was a difference in the output because of extra !!merge string.

How can i remove this !!merge string from my generated file? I would consider a string replacement as the last solution but I want to avoid that first because my YAML can contain comments as well and I don't want to change that.


Solution

  • The tag is automatically filled by go-yaml:

    When decoding, this field will always be set to the resolved tag, even when it wasn't explicitly provided in the YAML content.

    Technically go-yaml is not wrong to output the tag, however their docs state

    [the tag] will only be serialized into the representation if TaggedStyle is used or the implicit tag diverges from the provided one.

    „The implicit tag“ is !!merge (as your code shows) so go-yaml shouldn't output it. You could file a bug report for this.

    In the meantime, you can remove the tag after unmarshaling with a function like this:

    func removeMerge(node *yaml.Node) {
        if node.Tag == "!!merge" {
            node.Tag = ""
        }
        for _, child := range node.Content {
            removeMerge(child)
        }
    }
    

    Full example based on your code at Go playground