Преобразование YAML в JSON в Go - PullRequest
0 голосов
/ 18 мая 2018

У меня есть файл конфигурации в формате YAML, который я пытаюсь вывести как JSON через вызов HTTP API.Я распаковываю, используя gopkg.in/yaml.v2.Yaml может иметь не строковые ключи, что означает, что yaml не распаковывается как map [interface {}] interface {}, что не поддерживается маршаллером JSON Go.Поэтому перед преобразованием я преобразую в map [string] interface {}.Но я все равно получаю: json: unsupported type: map[interface {}]interface" {}.Я не понимаюПеременная cfy не является map[interface{}]interface{}.

import (
    "io/ioutil"
    "net/http"
    "encoding/json"
    "gopkg.in/yaml.v2"
)

func GetConfig(w http.ResponseWriter, r *http.Request) {
    cfy := make(map[interface{}]interface{})
    f, err := ioutil.ReadFile("config/config.yml")
    if err != nil {
        // error handling
    }
    if err := yaml.Unmarshal(f, &cfy); err != nil {
        // error handling
    }
    //convert to a type that json.Marshall can digest
    cfj := make(map[string]interface{})
    for key, value := range cfy {
        switch key := key.(type) {
        case string:
            cfj[key] = value
        }
    }
    j, err := json.Marshal(cfj)
    if err != nil {
        // errr handling. We get: "json: unsupported type: map[interface {}]interface" {}
    }
    w.Header().Set("content-type", "application/json")
    w.Write(j)
}

1 Ответ

0 голосов
/ 18 мая 2018

Ваше решение конвертирует значения только на "верхнем" уровне.Если значение также является картой (вложенная карта), ваше решение не преобразует их.

Кроме того, вы только «копируете» значения с помощью клавиш string, остальное будет исключено из карты результатов.

Вот функция, которая рекурсивно конвертирует вложенные карты:

func convert(m map[interface{}]interface{}) map[string]interface{} {
    res := map[string]interface{}{}
    for k, v := range m {
        switch v2 := v.(type) {
        case map[interface{}]interface{}:
            res[fmt.Sprint(k)] = convert(v2)
        default:
            res[fmt.Sprint(k)] = v
        }
    }
    return res
}

Тестирование:

m := map[interface{}]interface{}{
    1:     "one",
    "two": 2,
    "three": map[interface{}]interface{}{
        "3.1": 3.1,
    },
}
m2 := convert(m)
data, err := json.Marshal(m2)
if err != nil {
    panic(err)
}
fmt.Println(string(data))

Вывод (попробуйте на Go Playground ):

{"1":"one","three":{"3.1":3.1},"two":2}

Несколько замечаний:

  • Чтобы скрыть interface{} ключи, я использовал fmt.Sprint(), который будет обрабатывать всетипы.switch может иметь выделенный регистр string для ключей, которые уже имеют значения string, чтобы избежать вызова fmt.Sprint().Это исключительно из соображений производительности, результат будет таким же.

  • Вышеприведенная функция convert() не разбивает на части.Так, например, если карта содержит значение, представляющее собой срез ([]interface{}), который также может содержать карты, они не будут преобразованы.Для полного решения см. Библиотеку ниже.

  • Существует библиотека github.com/icza/dyno, которая имеет оптимизированную встроенную поддержку для этого (раскрытие: IЯ автор).Использование dyno выглядит следующим образом:

    var m map[interface{}]interface{} = ...
    
    m2 := dyno.ConvertMapI2MapS(m)
    

    dyno.ConvertMapI2MapS() также выполняет преобразование карт в []interface{} срезов.

Также смотрите возможные дубликаты: Конвертируйте yaml в json без структуры

...