在Go中将YAML转换为JSON

时间:2018-05-18 07:20:00

标签: json go yaml

我有一个YAML格式的配置文件,我试图通过http API调用输出为JSON。我正在使用gopkg.in/yaml.v2进行解组。 Yaml可以有非字符串键,这意味着yaml被解组为map [interface {}] interface {},Go的JSON编组器不支持。因此我在解组前转换为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 :(得分:2)

您的解决方案仅转换“顶级”级别的值。如果值也是地图(嵌套地图),则您的解决方案不会转换它们。

此外,您只能使用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{}),它也可能包含地图,那么这些不会被转换。有关完整解决方案,请参阅下面的库。

  • 有一个lib github.com/icza/dyno,它有一个优化的内置支持(披露:我是作者)。使用dyno,它的外观如下:

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

    dyno.ConvertMapI2MapS()也会进入并转换[]interface{}个切片中的地图。

另见可能重复:Convert yaml to json without struct