I'm trying to use the Unmarshal
method from the frontmatter package to parse front matter from a markdown file.
The type signature for the function is as follows
func Unmarshal(data []byte, v interface{}) (err error)
I've got the byte data and I understand that I need to pass an interface/struct with the appropriate fields as the second argument—however I don't know what the fields are going to be in the files that I parse and it's important that I don't lose data.
Internally this package uses yaml.v2 which provides a more comprehensive example for defining the interface before unmarshaling.
type T struct {
A string
B struct {
RenamedC int `yaml:"c"`
D []int `yaml:",flow"`
}
}
Then create an instance of the struct t
and pass a pointer to t
through to Unmarshal
.
t := T{}
err := yaml.Unmarshal([]byte(data), &t)
As I understand, this is only going to work if the YAML looks like this:
a: Easy!
b:
c: 2
d: [3, 4]
The second example looks closer to what I need. Rather than creating a struct, it seems to use a map of interface{} -> interface{}
.
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
I'm relatively new to Go and to me this looks like a generic map, which would be ideal for reading in unknown values.
I've adapted the example for my own project and ended up with the following code.
m := make(map[interface{}]interface{})
err := frontmatter.Unmarshal(data, &m)
But this results in a runtime error
panic: reflect: NumField of non-struct type
Full stacktrace here.
Am I heading in the right direction? If so, what's going wrong?
Turns out that the raw yaml.Unmarshal
method is robust enough to only pick up the front matter, even when presented with the entire file.
The solution I ended up using looks like this.
// read file made up of front matter and content
data, err := ioutil.ReadFile(file)
if err != nil {
log.Fatal(err)
}
meta := make(map[interface{}]interface{})
yaml.Unmarshal([]byte(data), &meta)
This means dropping the frontmatter
package and using the yaml
package directly.