假设我具有以下结构:

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("---")
    }
}

10-08 04:44