Skip to content

Instantly share code, notes, and snippets.

@rkspx
Last active November 14, 2023 22:24
Show Gist options
  • Save rkspx/b5516deacab6899fc6e251cc6fd2f6fa to your computer and use it in GitHub Desktop.
Save rkspx/b5516deacab6899fc6e251cc6fd2f6fa to your computer and use it in GitHub Desktop.
Custom marshal YAML file in golang
package config
import (
"errors"
"io/ioutil"
"gopkg.in/yaml.v2"
)
type Config struct {
Values []string `yaml:"value"`
}
var (
ErrNoValue = errors.New("no value for field 'value'")
ErrInvalidValue = errors.New("invalid value for field 'value'")
)
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
mstr := make(map[string]string)
if err := unmarshal(&mstr); err == nil {
if str, ok := mstr["value"]; ok {
c.Values = []string{str}
return nil
}
return ErrNoValue
}
miface := make(map[interface{}]interface{})
if err := unmarshal(&miface); err == nil {
sstr := make([]string, 0)
if val, ok := miface["value"]; ok {
for _, v := range val.([]interface{}) {
if str, ok := v.(string); ok {
sstr = append(sstr, str)
}
}
c.Values = sstr
return nil
}
return ErrNoValue
}
return ErrInvalidValue
}
func FromYAML(f string) (*Config, error) {
var c Config
b, err := ioutil.ReadFile(f)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(b, &c)
if err != nil {
return nil, err
}
return &c, nil
}
package config
import (
"path"
"testing"
)
const testdir = "testdata"
func TestUnmarshal(t *testing.T) {
val, err := FromYAML(path.Join(testdir, "yaml1.yaml"))
if err != nil {
t.Fatalf(err.Error())
}
if len(val.Values) != 1 {
t.Fatalf("expected 1 value, but got %d value", len(val.Values))
}
if val.Values[0] != "foo" {
t.Errorf("expected value to be 'foo' but got '%s'", val.Values[0])
}
val, err = FromYAML(path.Join(testdir, "yaml2.yaml"))
if err != nil {
t.Fatalf(err.Error())
}
if len(val.Values) != 2 {
t.Fatalf("expected 2 values, but got %d", len(val.Values))
}
if val.Values[0] != "foo" {
t.Errorf("expected value to be 'foo' but got '%s'", val.Values[0])
}
if val.Values[1] != "bar" {
t.Errorf("expected value to be 'bar', but got '%s'", val.Values[1])
}
}
name: test
value: foo
name: test
value:
- foo
- bar
@AyalaJuan
Copy link

On line 33 within the for loop there is an issue. The YAML config won't be parsed because the type assertion fails.
The value "v" is of type interface{}, and the check is for v.(string) which evaluates to false so the YAML config won't be filled.

To fix it I did this:

		for _, v := range val.([]interface{}) {
			sstr = append(sstr, fmt.Sprintf("%s", v))
		}

While less error checking, it worked for me. Wanted to point it out if anybody else runs into this wonderful snippet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment