Created
October 20, 2022 16:27
-
-
Save jasonhancock/4d12df9cf4ae4226eefa86712e037d8c to your computer and use it in GitHub Desktop.
Go YAML Unmarshal list of interfaces
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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