Skip to content

Instantly share code, notes, and snippets.

@jasonhancock
Created October 20, 2022 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonhancock/4d12df9cf4ae4226eefa86712e037d8c to your computer and use it in GitHub Desktop.
Save jasonhancock/4d12df9cf4ae4226eefa86712e037d8c to your computer and use it in GitHub Desktop.
Go YAML Unmarshal list of interfaces
package main
import (
"errors"
"fmt"
"log"
"math"
"gopkg.in/yaml.v3"
)
type Shape interface {
Area() float64
}
type Shapes []Shape
func (s *Shapes) UnmarshalYAML(value *yaml.Node) error {
var list []interface{}
if err := value.Decode(&list); err != nil {
return err
}
for _, v := range list {
vMap, ok := v.(map[string]interface{})
if !ok {
return errors.New("shape wasn't a map")
}
t, err := fromMap[string](vMap, "type")
if err != nil {
return err
}
var shape Shape
switch t {
case "circle":
radius, err := fromMap[float64](vMap, "radius")
if err != nil {
return fmt.Errorf("circle: %w", err)
}
shape = &Circle{radius}
case "square":
length, err := fromMap[float64](vMap, "length")
if err != nil {
return fmt.Errorf("square: %w", err)
}
shape = &Square{length}
default:
return fmt.Errorf("unsupported shape %q", t)
}
*s = append(*s, shape)
}
return nil
}
func fromMap[T any](data map[string]interface{}, key string) (T, error) {
untyped, ok := data[key]
if !ok {
return *new(T), fmt.Errorf("didn't contain key %q", key)
}
str, ok := untyped.(T)
if !ok {
return *new(T), fmt.Errorf("element wasn't correct type, got %T", untyped)
}
return str, nil
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * math.Pow(c.Radius, 2)
}
type Square struct {
Length float64
}
func (s Square) Area() float64 {
return math.Pow(s.Length, 2)
}
func main() {
type doc struct {
Shapes Shapes `yaml:"shapes"`
}
var d doc
if err := yaml.Unmarshal([]byte(config), &d); err != nil {
log.Fatal(err)
}
for _, v := range d.Shapes {
log.Println(v.Area())
}
}
const config = `
shapes:
- type: square
length: !!float 10
- type: circle
radius: !!float 15
`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment