我有一个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)
}
答案 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{}
个切片中的地图。