假设我具有以下结构:
type CarShop struct {
Cars []*Car
}
type Car struct {
ID string `yaml:“id“`
}
type BMW struct {
Car
A string `yaml:“a“`
}
type Mercedes struct {
Car
B string `yaml:“b“
}
我想解析以下字符串:
- BMW:
id: „BMW“
a: „a“
- Mercedes:
id: „Mercedes“
b: „b“
如何动态创建解析此字符串的BMW和Mercedes对象?使用Go和Go-yaml甚至可能吗?
最佳答案
您的类型错误,Go没有继承。您不能将*Mercedes
或*BMW
类型的值存储到[]*Car
类型的数组中;两种类型都只包含一个Car
值作为mixin。要执行您想做的事情,您必须将Car
类型更改为接口。
现在是YAML部分:您可以将YAML结构的一部分存储在yaml.Node
类型的对象中,并在以后反序列化。您可以通过实现UnmarshalYAML
(来自yaml.Unmarshaler
接口)来实现自定义拆组器。因此,我们要做的是为CarShop
实现一个自定义拆组器,将周围的结构反序列化为包含从汽车类型到yaml.Node
(该类型的值)的映射的列表,然后根据给定的汽车类型对每个节点反序列化变成正确的类型。外观如下:
package main
import (
"errors"
"fmt"
"gopkg.in/yaml.v3"
)
type CarShop struct {
Cars []Car
}
type Car interface {
ID() string
}
type BMW struct {
IDVal string `yaml:"id"`
A string `yaml:"a"`
}
func (bmw *BMW) ID() string {
return bmw.IDVal
}
type Mercedes struct {
IDVal string `yaml:"id"`
B string `yaml:"b"`
}
func (merc *Mercedes) ID() string {
return merc.IDVal
}
type tmpCarShop []map[string]yaml.Node
func (cs *CarShop) UnmarshalYAML(value *yaml.Node) error {
var tmp tmpCarShop
if err := value.Decode(&tmp); err != nil {
return err
}
cars := make([]Car, 0, len(tmp))
for i := range tmp {
for kind, raw := range tmp[i] {
switch kind {
case "Mercedes":
m := &Mercedes{}
if err := raw.Decode(m); err != nil {
return err
}
cars = append(cars, m)
case "BMW":
b := &BMW{}
if err := raw.Decode(b); err != nil {
return err
}
cars = append(cars, b)
default:
return errors.New("unknown car type: " + kind)
}
}
}
cs.Cars = cars
return nil
}
func main() {
input := []byte(`
- BMW:
id: "BMW"
a: "a"
- Mercedes:
id: "Mercedes"
b: "b"
`)
var shop CarShop
if err := yaml.Unmarshal(input, &shop); err != nil {
panic(err)
}
for i := range shop.Cars {
fmt.Printf("ID: %s\n", shop.Cars[i].ID())
switch c := shop.Cars[i].(type) {
case *Mercedes:
fmt.Printf("Type: Mercedes\nA: %s\n", c.B)
case *BMW:
fmt.Printf("Type: BMW\nB: %s\n", c.A)
}
fmt.Println("---")
}
}