Reputation: 5182
I am trying to parse a YAML file with Go. The problem is that the keys in the YAML file might not always be the same. This is to do API versioning so the user can define the versions they support. For instance V1, V2, V3 etc. They do not need to be in order and can omit versions they don't support i.e, V0, V2, V5 etc.
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
var data = `
---
development:
skip-header-validation: true
V1:
current: "1.0.0"
mime_types:
- application/vnd.company.jk.identity+json;
- application/vnd.company.jk.user+json;
- application/vnd.company.jk.role+json;
- application/vnd.company.jk.scope+json;
- application/vnd.company.jk.test+json;
skip-mime-type-validation: true
skip-version-validation: true
V2:
current: "2.0.0"
mime_types:
- application/vnd.company.jk.identity+json;
- application/vnd.company.jk.user+json;
- application/vnd.company.jk.role+json;
- application/vnd.company.jk.scope+json;
- application/vnd.company.jk.test+json;
`
type MajorVersion struct {
Current string `yaml:"current"`
MimeTypes []string `yaml:"mime_types"`
SkipVersionValidation bool `yaml:"skip-version-validation"`
SkipMimeTypeValidation bool `yaml:"skip-mime-type-validation"`
}
type Environment struct {
SkipHeaderValidation bool `yaml:"skip-header-validation"`
Version map[string]MajorVersion
}
func main() {
e := Environment{}
yaml.Unmarshal([]byte(data), &e)
fmt.Println(e)
}
I saw a similar question asked here
This is at the top level and I haven't quite figured out how to do this from inside the struct.
Upvotes: 15
Views: 13220
Reputation: 179
It is not a solution! This is a suggestion.
If yaml structure coul'd be a little bit diffrent.
package main
import (
"fmt"
"github.com/kr/pretty"
"gopkg.in/yaml.v3"
)
var data = `
---
development:
skip-header-validation: true
versions:
V1:
current: "1.0.0"
mime_types:
- application/vnd.company.jk.identity+json;
- application/vnd.company.jk.user+json;
- application/vnd.company.jk.role+json;
- application/vnd.company.jk.scope+json;
- application/vnd.company.jk.test+json;
skip-mime-type-validation: true
V5:
current: "5.10.3"
mime_types:
- application/vnd.company.jk.identity+json;
- application/vnd.company.jk.user+json;
- application/vnd.company.jk.role+json;
- application/vnd.company.jk.scope+json;
- application/vnd.company.jk.test+json;
skip-version-validation: false
`
type MajorVersion struct {
Current string `yaml:"current"`
MimeTypes []string `yaml:"mime_types"`
SkipVersionValidation bool `yaml:"skip-version-validation,omitempty"`
SkipMimeTypeValidation bool `yaml:"skip-mime-type-validation,omitempty"`
}
type Environment struct {
SkipHeaderValidation bool `yaml:"skip_header_validation"`
Versions map[string]MajorVersion `yaml:"versions"`
}
type Development struct {
Development Environment `yaml:development`
}
func main() {
e := Development{}
if err := yaml.Unmarshal([]byte(data), &e); err != nil {
fmt.Println(err.Error())
}
fmt.Printf("%# v\n", pretty.Formatter(e))
}
gives result
main.Development{
Development: main.Environment{
SkipHeaderValidation: false,
Versions: {
"V1": {
Current: "1.0.0",
MimeTypes: {"application/vnd.company.jk.identity+json;", "application/vnd.company.jk.user+json;", "application/vnd.company.jk.role+json;", "application/vnd.company.jk.scope+json;", "application/vnd.company.jk.test+json;"},
SkipVersionValidation: false,
SkipMimeTypeValidation: true,
},
"V5": {
Current: "5.10.3",
MimeTypes: {"application/vnd.company.jk.identity+json;", "application/vnd.company.jk.user+json;", "application/vnd.company.jk.role+json;", "application/vnd.company.jk.scope+json;", "application/vnd.company.jk.test+json;"},
SkipVersionValidation: false,
SkipMimeTypeValidation: false,
},
},
},
}
Upvotes: 0
Reputation: 3331
First, you are trying to parse the root as an Environment
, but its actual type is map[string]Environment
. Second, you are going to need a custom Unmarshaler
if you want to keep that type structure. Something like this:
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
var data = `
---
development:
skip-header-validation: true
V1:
current: "1.0.0"
mime_types:
- application/vnd.company.jk.identity+json;
- application/vnd.company.jk.user+json;
- application/vnd.company.jk.role+json;
- application/vnd.company.jk.scope+json;
- application/vnd.company.jk.test+json;
skip-mime-type-validation: true
skip-version-validation: true
V2:
current: "2.0.0"
mime_types:
- application/vnd.company.jk.identity+json;
- application/vnd.company.jk.user+json;
- application/vnd.company.jk.role+json;
- application/vnd.company.jk.scope+json;
- application/vnd.company.jk.test+json;
`
type MajorVersion struct {
Current string `yaml:"current"`
MimeTypes []string `yaml:"mime_types"`
SkipVersionValidation bool `yaml:"skip-version-validation"`
SkipMimeTypeValidation bool `yaml:"skip-mime-type-validation"`
}
type Environment struct {
SkipHeaderValidation bool
Versions map[string]MajorVersion
}
func (e *Environment) UnmarshalYAML(unmarshal func(interface{}) error) error {
var params struct {
SkipHeaderValidation bool `yaml:"skip-header-validation"`
}
if err := unmarshal(¶ms); err != nil {
return err
}
var versions map[string]MajorVersion
if err := unmarshal(&versions); err != nil {
// Here we expect an error because a boolean cannot be converted to a
// a MajorVersion
if _, ok := err.(*yaml.TypeError); !ok {
return err
}
}
e.SkipHeaderValidation = params.SkipHeaderValidation
e.Versions = versions
return nil
}
func main() {
var e map[string]Environment
if err := yaml.Unmarshal([]byte(data), &e); err != nil {
fmt.Println(err.Error())
}
fmt.Printf("%#v\n", e)
}
Output (after using makeup):
map[string]main.Environment{
"development": {
SkipHeaderValidation: true,
Versions: {
"V2": {
Current: "2.0.0",
MimeTypes: {"application/vnd.company.jk.identity+json;", "application/vnd.company.jk.user+json;", "application/vnd.company.jk.role+json;", "application/vnd.company.jk.scope+json;", "application/vnd.company.jk.test+json;"},
SkipVersionValidation: false,
SkipMimeTypeValidation: false,
},
"V1": {
Current: "1.0.0",
MimeTypes: {"application/vnd.company.jk.identity+json;", "application/vnd.company.jk.user+json;", "application/vnd.company.jk.role+json;", "application/vnd.company.jk.scope+json;", "application/vnd.company.jk.test+json;"},
SkipVersionValidation: true,
SkipMimeTypeValidation: true,
},
},
},
}
Upvotes: 22