Let’s say you have the following structs that you need to unmarshal using Go’s Viper library (Go configuration with fangs).
// Level 2; Struct for the top-level service configuration
type ServiceConfig struct {
BaseURL string `yaml:"base_url"`
APIKey string `yaml:"api_key"`
}
// Level 2; Struct for the providers configuration, with nested structs
type ProvidersConfig struct {
Payment struct {
Dwolla struct {
BaseURL string `yaml:"base_url"`
APIKey string `yaml:"api_key"`
} `yaml:"dwolla"`
} `yaml:"payment"`
}
// Level 1; struct to hold the entire config
type Config struct {
Service ServiceConfig `yaml:"service"`
Providers ProvidersConfig `yaml:"providers"`
}
It’s intended to parse the configuration file you have found on config.yaml
on your root folder, which looks something like this:
service:
base_url: https://api.yourwebsite.com/v1
api_key: some_api_key
providers:
payment:
dwolla:
base_url: https://sandbox.api.dwolla.com
api_key: some_api_key
Symptom or Problem
Sometimes you encounter unmarshalling errors when using viper.Unmarshal
, or sometimes there is no error or warning message at all- the struct simply does not get its field values parsed.
Root cause
When encoding or decoding data, Go uses metadata found on the struct tags to determine how to unmarshal or marshal fields within a struct (e.g. using encoding formats like JSON or YAML). The yaml
struct tag is useful when the config file you are parsing is being parsed with only a single level (e.g. all of your fields are parsed into a single struct found on Level 1, as labeled above, without any Level 2 structs). However, sometimes you already have existing struct definitions that you want to reuse instead of introducing redundancies through duplicate fields.
Solution
If your config file is a single level struct
, then there won’t be any problems using the yaml
Golang structural flag. However, if your config file uses multiple-level structs as in the case of providers
, then you need to use mapstructure
instead of yaml
when defining your structs.
As shown in the example above, there are multiple levels of structs under providers
(i.e. payment contains Dwolla). The different with service
is that its fields are set only in one level.
Reproducible example
- Replit example (Go 1.21)
- You can find
valid/main.go
for the correct implementation using mapstructure. - You can find
invalid/main.go
for the implementation that doesn’t throw an error, but proceeds execution without properly parsing the config file (leaving it empty).
- You can find
References
- See this stack overflow resource for more information.
Keywords
- Go struct not unmarshalling YAML
- Nested YAML not parsing in Go
- Go struct parsing issues with Viper
- YAML config file not working in Go
- Viper mapstructure nested structs
- Viper cannot unmarshal nested YAML
- Go Viper config unmarshal error
- YAML nested structs in Go not working
- Go Viper not parsing nested config
- Viper not loading nested YAML
- “Go struct not being parsed correctly”
- “Unmarshal Go YAML config file not working”
- “Viper config not populating Go struct”
- “Go mapstructure nested config issue”
- “YAML nested structure error in Go”
- “Viper struct unmarshal nested error”
- “Nested structs not loading in Go”
- “Viper cannot parse multiple-level YAML”
- “Mapstructure not decoding nested YAML”
- “Viper not decoding YAML into struct”
- “Error unmarshaling Go struct with nested YAML”
- “Go YAML config file nested struct parsing issue”
- “Cannot unmarshal nested YAML into Go struct”
- “Why isn’t my YAML file populating Go struct?”
- “Viper failing to unmarshal nested YAML structure”
- “Go struct unmarshalling failure in nested config”
- “Incorrect field mapping in Go nested YAML”
- “Mapstructure decoding error with nested structs”
- “Go Viper YAML parsing not working”
- “Go nested struct configuration errors in YAML”
Leave a Reply