在Go中将YAML转换为JSON

在Go中将YAML转换为JSON,json,go,yaml,Json,Go,Yaml,我有一个YAML格式的配置文件,我正试图通过http API调用将其输出为JSON。我正在使用gopkg.in/yaml.v2解组。Yaml可以有非字符串键,这意味着Yaml被解组为map[interface{}]interface{},Go的JSON封送器不支持这一点。因此,在解组之前,我将转换为map[string]接口{}。但是我仍然得到:json:unsupported类型:map[interface{}]interface{}。我不明白。变量cfy不是map[interface{}]i

我有一个YAML格式的配置文件,我正试图通过http API调用将其输出为JSON。我正在使用
gopkg.in/yaml.v2
解组。Yaml可以有非字符串键,这意味着Yaml被解组为map[interface{}]interface{},Go的JSON封送器不支持这一点。因此,在解组之前,我将转换为map[string]接口{}。但是我仍然得到:
json:unsupported类型: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)
}

您的解决方案仅转换“顶部”级别的值。如果值也是映射(嵌套映射),则您的解决方案不会转换这些值

此外,您仅使用
字符串
键“复制”值,其余值将从结果映射中删除

下面是一个递归转换嵌套映射的函数:

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))
输出(在上尝试):

需要注意的事项:

  • 为了转换
    接口{}
    键,我使用了
    fmt.Sprint()
    来处理所有类型。对于已经是
    string
    值的键,
    开关可以有一个专用的
    string
    大小写,以避免调用
    fmt.Sprint()
    。这完全是出于性能原因,结果将是相同的

  • 上面的
    convert()
    函数不分片。因此,例如,如果映射包含一个值,该值是一个切片(
    []接口{}
    ),该切片也可能包含映射,则这些值不会被转换。有关完整解决方案,请参阅下面的库

  • 有一个库对此具有优化的内置支持(披露:我是作者)。使用
    dyno
    ,它看起来是这样的:

    var m map[interface{}]interface{} = ...
    
    m2 := dyno.ConvertMapI2MapS(m)
    
    也进入并转换
    []接口{}
    切片中的映射


另请参见可能的重复:

发生错误的原因是您没有递归地进行转换,即
可能仍然是一个映射[interface{}]interface{}。有一个你可能会觉得有用的。完全正确。我刚刚在测试中看到了。你想回答这个问题吗?谢谢。是的,我自己看到了,并开始编写你提供的函数。然后Peter(见注释)将我链接到一个提供yaml到json函数的包,所以我最终使用了它,但将此标记为答案。
var m map[interface{}]interface{} = ...

m2 := dyno.ConvertMapI2MapS(m)