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

huangapple go评论82阅读模式
英文:

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

问题

我尝试从一个具有未知键值对数量的 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",
    ...
}

使用已知值解析 JSON,可以使用 github.com/jmoiron/jsonq,但在这种情况下,localhost 可以是任何值,tag 也可以是任何其他值。

在我的 Go 代码中,我的入口点如下所示:

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

    // 在这里,我需要创建我的字符串映射...

    return configKeyStr
}

希望能对你有所帮助。谢谢!

英文:

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!

答案1

得分: 2

简单易行。只需进行转换。

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

更新

编写了flatten函数(也许能正常工作)

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))
}
英文:

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

答案2

得分: 1

你无法自动完成这个操作,但是你可以遍历“内部”映射,并使用简单的字符串拼接(+运算符)将外部键与内部键组合起来。建议直接将其解组为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]
英文:

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]

答案3

得分: 0

由于问题中提到了“未定义数量的未知键值对”,您可能需要处理具有未知嵌套级别且值不是string的JSON文档。在这种情况下,您需要将JSON解组为map[string]interface{},然后使用递归来创建扁平化的映射。一旦将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)
        }
    }
}

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

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

英文:

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.

huangapple
  • 本文由 发表于 2017年7月1日 18:46:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/44860050.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定