Parsing YAML and return with line number

Viewed 580

I'm creating a document generator from YAML input in Go. It needs to specify which line of the YAML file each item/node is generated from. Is there a way to achieve it in Go?

For example here's a YAML file

- key1: item 1
  key2: item 2
- key1: another item 1
  key2: another item 2

and I'd like to see the following

[
     {'__line__': 1, 'key1': 'item 1', 'key2': 'item 2'},
     {'__line__': 3, 'key1': 'another item 1', 'key2': 'another item 2'},
]

I see a similar question answered for Python Parsing YAML, return with line number but I'm missing how to make use of https://pkg.go.dev/gopkg.in/yaml.v3

1 Answers

In Go this is possible by implementing a custom Unmarshaler and setting the line number manually.

So let's build the data structure we need for the file:

type ListItem struct {
    Line int

    ListItemData
}

type ListItemData struct {
    Key1 string `yaml:"key1"`
    Key2 string `yaml:"key2"`
}

Now we implement the Unmarshaler interface by creating a method on the ListItem type:

func (li *ListItem) UnmarshalYAML(value *yaml.Node) error {
    err := value.Decode(&li.ListItemData)
    if err != nil {
        return err
    }
     
    // Save the line number
    li.Line = value.Line

    return nil
}

You will notice that I created an inner struct and only decode the data to that struct. This is because if we were to call Decode on li itself, we would get a stack overflow, as decoding calls the UnmarshalYAML method repeatedly.

You can try this online and see that the line numbers are set correctly.

Related