【问题标题】:create a map of string from a JSON with unknow key-values in Go在 Go 中从具有未知键值的 JSON 创建字符串映射
【发布时间】:2017-12-05 05:11:03
【问题描述】:

我尝试从具有未定义数量的未知键值的 JSON 创建字符串映射。

这是我的 JSON 文件示例:

{
         "localhost":
        {
                "tag": "dev_latest",
                "vhost": "localhost.com"
        },
        "development":
        {
                "tag": "dev_latest",
                "vhost": "dev.com"
        }
}

我想创建一个map[string]string,其值如下:

config := map[string]string{
    "localhost-tag":      "dev_latest",
    "localhost-vhost": "localhost.com,
    "development-tag":   "dev_latest,
    ...
}

用已知值解析带有"github.com/jmoiron/jsonq" 的JSON 非常简单,但在这种情况下,localhost 可以是任何东西,tag 可以是任何其他东西。

我的 Go 代码中的入口点是这样的:

func ParseJson(){
    configPath := GetConfigPath()
    b, err := ioutil.ReadFile(configPath) 

     //  Here, I need to create my map of strings..

    return configKeyStr

}

任何帮助将不胜感激。

谢谢!

【问题讨论】:

    标签: json string dictionary go unmarshalling


    【解决方案1】:

    很容易做到。只需转换。

    package main
    
    import (
        "encoding/json"
        "fmt"
        "log"
    )
    
    const s = `
    {
             "localhost":
            {
                    "tag": "dev_latest",
                    "vhost": "localhost.com"
            },
            "development":
            {
                    "tag": "dev_latest",
                    "vhost": "dev.com"
            }
    }
    `
    
    func main() {
        var m map[string]interface{}
        err := json.Unmarshal([]byte(s), &m)
        if err != nil {
            log.Fatal(err)
        }
        mm := make(map[string]string)
        for k, v := range m {
            mm[k] = fmt.Sprint(v)
        }
        fmt.Println(mm)
    }
    

    更新

    写扁平化(也许可以作为魅力)

    package main
    
    import (
        "encoding/json"
        "fmt"
        "log"
        "reflect"
    )
    
    const s = `
    {
             "localhost":
            {
                    "tag": "dev_latest",
                    "vhost": "localhost.com"
            },
            "development":
            {
                    "tag": "dev_latest",
                    "vhost": "dev.com"
            }
    }
    `
    
    func flatten(m map[string]interface{}) map[string]string {
        mm := make(map[string]string)
        for k, v := range m {
            switch reflect.TypeOf(v).Kind() {
            case reflect.Map:
                mv := flatten(v.(map[string]interface{}))
                for kk, vv := range mv {
                    mm[k+"-"+kk] = vv
                }
            case reflect.Array, reflect.Slice:
                for kk, vv := range m {
                    if reflect.TypeOf(vv).Kind() == reflect.Map {
                        mv := flatten(vv.(map[string]interface{}))
                        for kkk, vvv := range mv {
                            mm[k+"-"+kkk] = vvv
                        }
                    } else {
                        mm[k+"-"+kk] = fmt.Sprint(vv)
                    }
                }
            default:
                mm[k] = fmt.Sprint(v)
            }
        }
        return mm
    }
    
    func main() {
        var m map[string]interface{}
        err := json.Unmarshal([]byte(s), &m)
        if err != nil {
            log.Fatal(err)
        }
        b, _ := json.MarshalIndent(flatten(m), "", "  ")
        println(string(b))
    }
    

    【讨论】:

    • 非常感谢,它可以工作,但这不是我想要的,我怎样才能有这样的地图:pastebin.com/6EJX01D6
    • mm 是您的预期吗?
    • 我认为他想要flatten map,即JSON键递归以“-”作为分隔符。
    • 是的,这正是我需要的,但我无法让它工作:p
    • 啊,我明白你现在想要的。
    【解决方案2】:

    您不能自动拥有它,但您可以在“内部”映射上进行范围,并使用简单的字符串连接(+ 运算符)将外部键与内部键组合。此外,建议直接解组为 map[string]map[string]string 的值,这样您就不需要使用类型断言。也不需要为此使用任何外部库,标准的encoding/json 包就足够了。

    例子:

    var mm map[string]map[string]string
    if err := json.Unmarshal([]byte(src), &mm); err != nil {
        panic(err)
    }
    config := map[string]string{}
    for mk, m := range mm {
        for k, v := range m {
            config[mk+"-"+k] = v
        }
    }
    fmt.Println(config)
    

    输出符合预期(在Go Playground 上尝试):

    map[localhost-tag:dev_latest localhost-vhost:localhost.com
        development-tag:dev_latest development-vhost:dev.com]
    

    【讨论】:

    • 非常感谢,非常完美!
    【解决方案3】:

    由于您在问题中提到未定义的未知键值数,您可能需要处理嵌套级别数未知且值不是string 的 JSON 文档。在这种情况下,您需要将Unmarshal json 转为map[string]interface{},然后使用递归来制作 flat 映射。将 json 文档解压缩到 map[string]interface{} 后,使用以下函数:

    func flatMap(src map[string]interface{}, baseKey, sep string, dest map[string]string) {
        for key, val := range src {
            if len(baseKey) != 0 {
                key = baseKey + sep + key
            }
            switch val := val.(type) {
            case map[string]interface{}:
                flatMap(val, key, sep, dest)
            case string:
                dest[key] = val
            case fmt.Stringer:
                dest[key] = val.String()
            default:
                //TODO: You may need to handle ARRAY/SLICE
    
                //simply convert to string using `Sprintf`
                //NOTE: modify as needed.
                dest[key] = fmt.Sprintf("%v", val)
            }
        }
    }
    

    工作解决方案改编自 https://play.golang.org/p/9SQsbAUFdY 的 mattn 回答

    正如 mattn 所指出的,当您想要写回配置值时可能会遇到问题。在这种情况下,请使用现有的库/框架。

    【讨论】:

      猜你喜欢
      • 2015-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多