2016-11-22 3 views
3
Services: 
- Orders: 
    - ID: $save ID1 
     SupplierOrderCode: $SupplierOrderCode 
    - ID: $save ID2 
     SupplierOrderCode: 111111 

Я хочу, чтобы преобразовать эту YAML строку JSON, привести данные источника динамична, поэтому я не могу отобразить его на структуру:Преобразовать YAML в формате JSON без структуры (Golang)

var body interface{} 
err := yaml.Unmarshal([]byte(s), &body) 

Тогда я хочу, чтобы преобразовать этот интерфейс JSON строку снова:

b, _ := json.Marshal(body) 

Но ошибка происходит:

panic: json: unsupported type: map[interface {}]interface {} 
+0

создать тело var следующим образом: 'body: = make (map [string] interface {})' –

ответ

11

Предисловие: Я оптимизировал и улучшил решение ниже и выпустил его в виде библиотеки здесь: github.com/icza/dyno. Нижеследующая функция convert() доступна как dyno.ConvertMapI2MapS().


Проблема заключается в том, что если вы используете самый общий тип interface{} для распаковать в тип по умолчанию, используемый github.com/go-yaml/yaml пакета распаковать пар ключ-значение будет map[interface{}]interface{}.

Первая идея будет использовать map[string]interface{}:

var body map[string]interface{} 

Но эта попытка терпит неудачу, если глубина YAML конфигурации больше, чем один, так как это body карта будет содержать дополнительные карты, тип которых будет снова map[interface{}]interface{} ,

Проблема в том, что глубина неизвестна, и могут быть другие значения, чем карты, поэтому использование map[string]map[string]interface{} не очень хорошо.

Жизнеспособной подход должен позволить yaml распаковать в значение типа interface{}, и пройти через результат рекурсивно и преобразовать каждый сталкивался map[interface{}]interface{} к значению map[string]interface{}. Обе карты и срезы должны обрабатываться.

Вот пример этого конвертера функции:

func convert(i interface{}) interface{} { 
    switch x := i.(type) { 
    case map[interface{}]interface{}: 
     m2 := map[string]interface{}{} 
     for k, v := range x { 
      m2[k.(string)] = convert(v) 
     } 
     return m2 
    case []interface{}: 
     for i, v := range x { 
      x[i] = convert(v) 
     } 
    } 
    return i 
} 

И использовать его:

func main() { 
    fmt.Printf("Input: %s\n", s) 
    var body interface{} 
    if err := yaml.Unmarshal([]byte(s), &body); err != nil { 
     panic(err) 
    } 

    body = convert(body) 

    if b, err := json.Marshal(body); err != nil { 
     panic(err) 
    } else { 
     fmt.Printf("Output: %s\n", b) 
    } 
} 

const s = `Services: 
- Orders: 
    - ID: $save ID1 
     SupplierOrderCode: $SupplierOrderCode 
    - ID: $save ID2 
     SupplierOrderCode: 111111 
` 

Выход:

Input: Services: 
- Orders: 
    - ID: $save ID1 
     SupplierOrderCode: $SupplierOrderCode 
    - ID: $save ID2 
     SupplierOrderCode: 111111 

Output: {"Services":[{"Orders":[ 
    {"ID":"$save ID1","SupplierOrderCode":"$SupplierOrderCode"}, 
    {"ID":"$save ID2","SupplierOrderCode":111111}]}]} 

Одно замечание: при переходе от YAML в формате JSON через карты Go вы потеряете порядок элементов, поскольку элементы (пары ключ-значение) на карте Go не упорядочены. Это может быть или не быть проблемой.

+0

Большое спасибо Icza, вы так хороши: D – huynhminhtuan

0

https://github.com/ghodss/yaml является «оберткой вокруг go-yaml, разработанной, чтобы обеспечить лучший способ обработки YAML при маршалинге и из структур». Помимо всего прочего, он предоставляет метод yaml.YAMLToJSON, который должен делать то, что вы хотите.