Загрузите файлы YAML со списком элементов различной структуры

#go #yaml

#Вперед #yaml

Вопрос:

У меня есть YAML, отформатированный следующим образом:

 actions:
  - type: one
    onekey: value
  - type: two
    twokey: value
    foo: bar
  

и я хотел бы загрузить его в структуру Go. Я пытался использовать mapstructurs с этим DecodeHook , но я не могу заставить его работать.

Это то, что я пробовал:

 type Actions struct {
    actions []Action
}

type Action interface {
}

type One struct {
    Onekey string
}

type Two struct {
    Twokey string
    Foo    string
}

var actions Actions

func Load() {
...
    config := amp;mapstructure.DecoderConfig{
        DecodeHook: func(sourceType, destType reflect.Type, raw interface{}) (interface{}, error) {
            if fmt.Sprintf("%s", destType) == "Action" {
                var result Action

                switch raw.(map[string]interface{})["type"] {
                case "one":
                    result = One{}
                case "two":
                    result = Two{}
                }
                mapstructure.Decode(raw, amp;result)
                return result, nil
            }
            return raw, nil
        },
        Result: amp;actions,
    }
...
}
  

Это некрасиво и тоже не работает. Я получаю:

 panic: interface conversion: interface {} is map[interface {}]interface {}, not map[string]interface {}
  

Сначала есть:

 if fmt.Sprintf("%s", destType) == "Action"
  

это отвратительно, но единственный способ заставить эту часть работать.

Затем нужно прочитать элемент списка и привести его к нужной структуре с помощью type ключа. Есть ли способ заставить это работать?

Спасибо!

Ответ №1:

В итоге я выбрал этот подход:

     config := amp;mapstructure.DecoderConfig{
        DecodeHook: func(sourceType, destType reflect.Type, raw interface{}) (interface{}, error) {
            var result Action
            if fmt.Sprintf("%s", destType) == "Action" {
                rawMap := raw.(map[interface{}]interface{})
                switch rawMap["type"] {
                case "one":
                    result = One{}
                case "two":
                    result = Two{}
                }

                mapstructure.Decode(raw, amp;result)
                return result, nil
            }
            return raw, nil
        },
        Result: amp;actions,
    }
  

Я только начинаю с Go, так что мог бы быть способ получше, но это как бы решает две проблемы, которые у меня были.

Поправка: мне пришлось вернуться к if fmt.Sprintf("%s", destType) == "Action" , поскольку reflect.TypeOf(result) == nil

Ответ №2:

Взгляните на пакет viper:https://github.com/spf13/viper Это позволяет вам читать из файлов toml, json yaml и т. Д., А Затем вы можете передавать каждое значение конфигурации в свою структуру.

Комментарии:

1. Я не совсем понял, как viper поможет мне со списками, содержащими разные типы…