dongzhuo1958
2017-07-01 10:46
浏览 77

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

I try to create a map of strings from a JSON with an undefined number of unknow key-values.

Here is my example JSON file:

{
         "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:

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.

My entry point in my Go code is like this:

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!

图片转代码服务由CSDN问答提供 功能建议

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

这是我的示例JSON文件:

  {
“ localhost”:
 {
“ tag”:“ dev_latest”,\  n“ vhost”:“ localhost.com” 
},
“开发”:
 {
“ 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()\  nb,err:= ioutil.ReadFile(configPath)
 
 //在这里,我需要创建字符串映射。.
 
 return configKeyStr 
 
} 
    
 
 

任何帮助将不胜感激。

谢谢!

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

3条回答 默认 最新

  • dongshushen4392 2017-07-01 11:00
    已采纳

    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

    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))
    }
    
    打赏 评论
  • dongpa6867 2017-07-01 12:14

    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. Also no need to use any external libraries for this, the standard encoding/json package is perfectly enough for this.

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

    map[localhost-tag:dev_latest localhost-vhost:localhost.com
        development-tag:dev_latest development-vhost:dev.com]
    
    打赏 评论
  • donglu8344812 2017-07-01 12:51

    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. In this case, you need to Unmarshal json to map[string]interface{}, then use recursion to make flat map. Once the json document unmrashaled to map[string]interface{}, use the following function:

    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

    As pointed by mattn, you may have problem when you want to writeback the configuration value. In that case, use the existing library/framework.

    打赏 评论

相关推荐 更多相似问题