【问题标题】:Converting YAML to JSON in Go在 Go 中将 YAML 转换为 JSON
【发布时间】:2018-10-28 13:46:05
【问题描述】:

我有一个 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)
}

【问题讨论】:

  • 这个错误是因为你没有进行递归转换,即value可能仍然是一个map[interface{}]interface{}。有一个existing library, github.com/ghodss/yaml 可能对您有用。
  • 完全正确。我刚刚在测试中看到了这一点。你想这样回答吗?

标签: json go yaml


【解决方案1】:

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

您也只能使用 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 case,以避免调用fmt.Sprint()。这仅是出于性能原因,结果将是相同的。

  • 上述convert() 函数不会进入切片。因此,例如,如果地图包含一个切片 ([]interface{}) 的值,该切片也可能包含地图,则不会转换这些值。如需完整的解决方案,请参阅下面的库。

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

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

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

另请参阅可能的重复:Convert yaml to json without struct

【讨论】:

  • 谢谢。是的,我自己看到了它并开始编写您提供的功能。然后彼得(见评论)将我链接到一个提供 yaml 到 json 函数的包,所以我最终使用了它,但将其标记为答案。
  • @icza 很好,谢谢。你能更新函数来处理数组吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-16
  • 1970-01-01
  • 2022-01-02
  • 2014-07-07
  • 2016-12-11
  • 1970-01-01
  • 2015-12-20
相关资源
最近更新 更多