接口及其实现的自定义 UnmarshalYAML 接口

我实现了一个接口Fruit及其两个实现:Apple和Banana。


我想从 yaml 文件加载数据到两个实现的对象中:


capacity: 4

Apple:

- name: "apple1"

  number: 1

- name: "apple2"

  number: 1

Banana:

- name: "banana1"

  number: 2

我实现了UnmarshalYaml将数据加载到对象中的接口:


package main


import (

    "errors"

    "gopkg.in/yaml.v3"

    "log"

    "fmt"

)


type FruitBasket struct {

    Capacity int `yaml:"capacity"`

    Fruits []Fruit

}


func NewFruitBasket() *FruitBasket {

    fb := new(FruitBasket)


    return fb

}


type Fruit interface {

    GetFruitName() string

    GetNumber() int

}


type Apple struct {

    Name string `yaml:"name"`

    Number int `yaml:"number"`

}


type Banana struct {

    Name string `yaml:"name"`

    Number int `yaml:"number"`

}


func (apple *Apple) GetFruitName() string {

    return apple.Name

}


func (apple *Apple) GetNumber() int {

    return apple.Number

}


func (banana *Banana) GetFruitName() string {

    return banana.Name

}


func (banana *Banana) GetNumber() int {

    return banana.Number

}


type tmpFruitBasket struct {

    Capacity int `yaml:"capacity"`

    Fruits []map[string]yaml.Node

}


func (fruitBasket *FruitBasket) UnmarshalYAML(value *yaml.Node) error {

    var tmpFruitBasket tmpFruitBasket


    if err := value.Decode(&tmpFruitBasket); err != nil {

        return err

    }


    fruitBasket.Capacity = tmpFruitBasket.Capacity


    fruits := make([]Fruit, 0, len(tmpFruitBasket.Fruits))


    for i := 0; i < len(tmpFruitBasket.Fruits); i++ {

        for tag, node := range tmpFruitBasket.Fruits[i] {

            switch tag {

            case "Apple":

                apple := &Apple{}

                if err := node.Decode(apple); err != nil {

                    return err

                }


                fruits = append(fruits, apple)

            case "Banana":

                banana := &Banana{}

                if err := node.Decode(banana); err != nil {

                    return err


然而,这是行不通的。似乎Apple和Banana标签的数据未加载。Fruits可能是因为我的结构中缺少切片的 yaml 标志tmpFruitBasket。但是,作为Fruit一个接口,我无法定义 yaml 标志。将来,我想实现代表具体水果(例如草莓)的其他结构,实现该接口Fruit。


关于如何解决这个问题有什么想法吗?


ibeautiful
浏览 109回答 1
1回答

Smart猫小萌

这是您需要的中间类型:type tmpFruitBasket struct {&nbsp; Capacity int&nbsp; Apple&nbsp; &nbsp; []yaml.Node `yaml:"Apple"`&nbsp; Banana&nbsp; &nbsp;[]yaml.Node `yaml:"Banana"`}然后,加载函数将如下所示:// helper to load a list of nodes as a concrete typefunc appendFruits(fruits []Fruit, kind reflect.Type, input []yaml.Node) ([]Fruit, error) {&nbsp; for i := range input {&nbsp; &nbsp; val := reflect.New(kind).Interface()&nbsp; &nbsp; if err := input[i].Decode(val); err != nil {&nbsp; &nbsp; &nbsp; return nil, err&nbsp; &nbsp; }&nbsp; &nbsp; fruits = append(fruits, val.(Fruit))&nbsp; }&nbsp; return fruits, nil}func (fruitBasket *FruitBasket) UnmarshalYAML(value *yaml.Node) error {&nbsp; &nbsp; var tmp tmpFruitBasket&nbsp; &nbsp; if err := value.Decode(&tmp); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; fruitBasket.Capacity = tmp.Capacity&nbsp; &nbsp; var fruits []Fruit&nbsp; &nbsp; var err error&nbsp; &nbsp; // sadly, there is no nicer way to get the reflect.Type of Apple / Banana&nbsp; &nbsp; fruits, err = appendFruits(&nbsp; &nbsp; &nbsp; fruits, reflect.TypeOf((*Apple)(nil)).Elem(), tmp.Apple)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; fruits, err = appendFruits(&nbsp; &nbsp; &nbsp; fruits, reflect.TypeOf((*Banana)(nil)).Elem(), tmp.Banana)&nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; }&nbsp; &nbsp; fruitBasket.Fruits = fruits&nbsp; &nbsp; return nil}编辑:如果您坚持将每种类型排序到专用切片中,您当然可以直接将它们键入为[]Appleand[]Banana并合并它们。这个答案是从您之前的问题开始深入探讨动态加载不同类型的输入问题的延续。仅当您在某些时候不再知道静态类型时,这样做才有意义(例如,如果您提供 API 在运行时添加其他水果类型)。
打开App,查看更多内容
随时随地看视频慕课网APP