简体   繁体   English

在Go中使用具有未知键值的JSON从JSON创建字符串映射

[英]create a map of string from a JSON with unknow key-values in Go

I try to create a map of strings from a JSON with an undefined number of unknow key-values. 我尝试使用未定义数量的未知键值从JSON创建字符串映射。

Here is my example JSON file: 这是我的示例JSON文件:

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

I want to create a map[string]string with value like this: 我想创建一个map[string]string ,其值如下:

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

To parse a JSON with "github.com/jmoiron/jsonq" with known values, is quite easy, but in this case, localhost can be anything and tag can be any other thing. 用已知值的"github.com/jmoiron/jsonq"解析JSON是很容易的,但是在这种情况下, localhost可以是任何东西,而tag可以是任何其他东西。

My entry point in my Go code is like this: 我在Go代码中的入口点是这样的:

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

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

    return configKeyStr

}

Any help will be really appreciate. 任何帮助将不胜感激。

Thanks! 谢谢!

Easy to do. 容易做。 Simply convert. 简单地转换。

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

UPDATE UPDATE

Wrote flatten (maybe works as charm) 写扁平化(也许可以作为魅力)

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

You can't have this automatically, but you can range over the "internal" maps, and combine the outer keys with the inner keys using simple string concatenation ( + operator). 您不能自动使用此功能,但是可以在“内部”映射上进行调整,并使用简单的字符串连接( +运算符)将外部键与内部键组合在一起。 Also it's recommended to unmarshal directly into a value of map[string]map[string]string so you don't need to use type assertions. 另外,建议直接解组为map[string]map[string]string的值,这样就不必使用类型断言。 Also no need to use any external libraries for this, the standard encoding/json package is perfectly enough for this. 同样,也不需要为此使用任何外部库,为此,标准的encoding/json包就足够了。

Example: 例:

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)

Output is as expected (try it on the Go Playground ): 输出是预期的(在Go Playground上尝试):

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

Since in the question you mentioned undefined number of unknown key-values , you may need to deal with JSON document with unknown number of nesting level and having a value other than string . 由于在问题中您提到了不确定数量的未知键值 ,因此您可能需要处理嵌套数量未知且具有string以外的值的JSON文档。 In this case, you need to Unmarshal json to map[string]interface{} , then use recursion to make flat map. 在这种情况下,您需要Unmarshal json到map[string]interface{} ,然后使用递归制作平面地图。 Once the json document unmrashaled to map[string]interface{} , use the following function: 将json文档完整地map[string]interface{}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)
        }
    }
}

The working solution adapted from mattn answer at https://play.golang.org/p/9SQsbAUFdY 工作解决方案改编自https://play.golang.org/p/9SQsbAUFdY上的 mattn答案

As pointed by mattn, you may have problem when you want to writeback the configuration value. 正如mattn指出的那样,当您想回写配置值时可能会遇到问题。 In that case, use the existing library/framework. 在这种情况下,请使用现有的库/框架。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM