Реализация интерфейса UnmarshalYAML для пользовательской структуры - PullRequest
2 голосов
/ 22 января 2020

У меня есть следующие структуры, интерфейсы и функции:

type FruitBasket struct {
    Capacity int `yaml:"capacity"`
    Fruits []Fruit
}

type Fruit interface {
    GetFruitName() string
}

type Apple struct {
    Name string `yaml:"name"`
}

func (apple *Apple) GetFruitName() string {
    return apple.Name
}

type tmpFruitBasket []map[string]yaml.Node

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

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

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

    for i := 0; i < len(tmpFruitBasket); i++ {
        for tag, node := range tmpFruitBasket[i] {
            switch tag {
            case "Apple":
                apple := &Apple{}
                if err := node.Decode(apple); err != nil {
                    return err
                }

                fruits = append(fruits, apple)
            default:
                return errors.New("Failed to interpret the fruit of type: \"" + tag + "\"")
            }
        }
    }

    fruitBasket.Fruits = fruits

    return nil
}

С этим кодом я могу проанализировать следующий файл yaml:

FruitBasket:
  - Apple:
      name: "apple1"
  - Apple:
      name: "apple2"

Основная функция выглядит следующим образом:

func main() {
    data := []byte(`
FruitBasket:
  - Apple:
      name: "apple1"
  - Apple:
      name: "apple2"
`)

    fruitBasket := new(FruitBasket)

    err := yaml.Unmarshal(data, &fruitBasket)

    if err != nil {
        log.Fatalf("error: %v", err)
    }

    for i := 0; i < len(fruitBasket.Fruits); i++ {
        switch fruit := fruitBasket.Fruits[i].(type) {
        case *Apple:
            fmt.Println("The name of the apple is: " + fruit.GetFruitName())
        }
    }
}

Однако я НЕ МОГУ анализировать следующую строку yaml:

FruitBasket:
  capacity: 2
  - Apple:
      name: "apple1"
  - Apple:
      name: "apple2"

С этой строкой я получаю следующую ошибку: error: yaml: did not find expected key.

Как сделать Я должен настроить реализацию интерфейса UnmarshalYAML, чтобы также проанализировать последнюю строку?

1 Ответ

1 голос
/ 22 января 2020

Ваш YAML недействителен. Каждое значение YAML является скаляром, последовательностью или отображением.

При capacity: процессор YAML решает, что этот уровень содержит отображение, поскольку он видит первый ключ. Теперь на следующей строке он видит - Apple:, элемент последовательности. Это недопустимо на уровне отображения; вместо этого он ожидает следующий ключ и поэтому выдает ошибку did not find expected key.

Исправление структуры может выглядеть следующим образом:

FruitBasket:
  capacity: 2
  items:
  - Apple:
      name: "apple1"
  - Apple:
      name: "apple2"

Обратите внимание, что элементы последовательности все еще появляются на на том же уровне, однако, поскольку items: не имеет встроенного значения, элементы анализируются как последовательность, которая является значением ключа items. Другой ключ на том же уровне отступа завершит последовательность.

Затем вы можете загрузить его в тип, подобный:

type tmpFruintBasket struct {
  Capacity int
  Items []map[string]yaml.Node
}
...