douhao7677 2018-05-18 07:20
浏览 674
已采纳

在Go中将YAML转换为JSON

I have a config file in YAML format, which I am trying to output as JSON via an http API call. I am unmarshalling using gopkg.in/yaml.v2. Yaml can have non-string keys, which means that the yaml is unmarshalled as map[interface{}]interface{}, which is not supported by Go's JSON marshaller. Therefore I convert to map[string]interface{} before unmarshalling. But I still get: json: unsupported type: map[interface {}]interface" {}. I don't understand. The variable cfy is not 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条回答 默认 最新

  • donglu2523 2018-05-18 07:51
    关注

    Your solution only converts values at the "top" level. If a value is also a map (nested map), your solution does not convert those.

    Also you only "copy" the values with string keys, the rest will be left out of the result map.

    Here's a function that recursively converts nested maps:

    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
    }
    

    Testing it:

    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))
    

    Output (try it on the Go Playground):

    {"1":"one","three":{"3.1":3.1},"two":2}
    

    Some things to note:

    • To covert interface{} keys, I used fmt.Sprint() which will handle all types. The switch could have a dedicated string case for keys that are already string values to avoid calling fmt.Sprint(). This is solely for performance reasons, the result will be the same.

    • The above convert() function does not go into slices. So for example if the map contains a value which is a slice ([]interface{}) which may also contain maps, those will not be converted. For a full solution, see the lib below.

    • There is a lib github.com/icza/dyno which has an optimized, built-in support for this (disclosure: I'm the author). Using dyno, this is how it would look like:

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

      dyno.ConvertMapI2MapS() also goes into and converts maps in []interface{} slices.

    Also see possible duplicate: Convert yaml to json without struct

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 gradio的web端页面格式不对的问题
  • ¥15 求大家看看Nonce如何配置
  • ¥15 Matlab怎么求解含参的二重积分?
  • ¥15 苹果手机突然连不上wifi了?
  • ¥15 cgictest.cgi文件无法访问
  • ¥20 删除和修改功能无法调用
  • ¥15 kafka topic 所有分副本数修改
  • ¥15 小程序中fit格式等运动数据文件怎样实现可视化?(包含心率信息))
  • ¥15 如何利用mmdetection3d中的get_flops.py文件计算fcos3d方法的flops?
  • ¥40 串口调试助手打开串口后,keil5的代码就停止了