英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论