package main
import (
"fmt"
"encoding/json"
"reflect"
)
type GeneralConfig map[string]interface{}
var data string = `
{
"key":"value",
"important_key":
{"foo":"bar"}
}`
func main() {
jsonData := &GeneralConfig{}
json.Unmarshal([]byte(data), jsonData)
fmt.Println(reflect.TypeOf(jsonData)) //main.GeneralConfig
jsonTemp := (*jsonData)["important_key"]
fmt.Println(reflect.TypeOf(jsonTemp)) //map[string]interface {}
//newGeneralConfig := GeneralConfig(jsonTemp)
//cannot convert jsonTemp (type interface {}) to type GeneralConfig:
//need type assertion
newGeneralConfig := jsonTemp.(GeneralConfig)
//fmt.Println(reflect.TypeOf(newGeneralConfig))
//panic: interface conversion: interface {} is map[string]interface {},
//not main.GeneralConfig
}
时可用
我知道我可以使用嵌套结构代替GeneralConfig
,但这需要我知道有效负载的确切结构,即它不适用于不同的密钥(我会被锁定为“ important_key“)。
当我不知道“important_key”的值是什么时,是否有golang解决方法?我说golang,因为如果可能的话,可以要求所有“important_keys”都有一个常量的父键,这可以解决这个问题。
总而言之,给定一个任意的json对象,必须有一种方法可以遍历其键,如果值是自定义类型,则将值转换为该类型。现在似乎如果我使用类型转换,它告诉我类型是interface{}
,我需要使用类型断言;但是,如果我使用类型断言,它会告诉我interface{}
是map[string]interface{}
而不是main.GeneralConfig
。
答案 0 :(得分:2)
我同意关于尝试利用传入JSON的预期结构以便编写定义明确的Structs的评论,但无论如何我都会尝试回答这个问题。
从您看到的内容中删除的内容与您看到的错误消息相比,编译器对类型的了解少于运行时,因为运行时可以查看实际值。为了使编译器达到最快速度,我们必须(i)断言(*jsonData)["important_key"]
是map[string]interface{}
- 编译器只知道它是interface{}
- 然后(ii)键入将其转换为GeneralConfig
类型。参见:
package main
import (
"fmt"
"encoding/json"
)
type GeneralConfig map[string]interface{}
func main() {
jsonStruct := new(GeneralConfig)
json.Unmarshal([]byte(`{"parent_key": {"foo": "bar"}}`), jsonStruct)
fmt.Printf("%#v\n", jsonStruct)
// => &main.GeneralConfig{"parent_key":map[string]interface {}{"foo":"bar"}}
nestedStruct := (*jsonStruct)["parent_key"]
fmt.Printf("%#v\n", nestedStruct)
// => map[string]interface {}{"foo":"bar"}
// Whilst this shows the runtime knows its actual type is
// map[string]interface, the compiler only knows it to be an interface{}.
// First we assert for the compiler that it is indeed a
// map[string]interface{} we are working with. You can imagine the issues
// that might arrise if we has passed in `{"parent_key": 123}`.
mapConfig, ok := nestedStruct.(map[string]interface{})
if !ok {
// TODO: Error-handling.
}
// Now that the compiler can be sure mapConfig is a map[string]interface{}
// we can type-cast it to GeneralConfig:
config := GeneralConfig(mapConfig)
fmt.Printf("%#v\n", config)
// => main.GeneralConfig{"foo":"bar"}
}
答案 1 :(得分:0)
您正在寻找json.RawMessage 您可以根据其他值延迟解组,然后强制它解组为特定类型。
这不是一个好主意,但可能更接近您所寻找的目标。
答案 2 :(得分:0)
这是标准的"解决方法"如果得到你以后的东西。处理未知数据时,您可以实现此模式(从您的示例中修改),以递归方式切换类型,以获取未知的json数据体中的具体值。
package main
import (
"encoding/json"
"fmt"
"reflect"
)
var data = `
{
"key":"value",
"important_key":
{"foo":"bar"}
}`
func main() {
var jsonData interface{}
json.Unmarshal([]byte(data), &jsonData)
fmt.Println(reflect.TypeOf(jsonData))
parseArbitraryJSON(jsonData.(map[string]interface{}))
}
func parseArbitraryJSON(data map[string]interface{}) {
for k, v := range data {
switch a := v.(type) {
case string:
fmt.Printf("%v:%v\n", k, a)
case map[string]interface{}:
fmt.Printf("%v:%v\n", k, a)
parseArbitraryJSON(a)
}
}
}
结果输出为:
map[string]interface {}
key:value
important_key:map[foo:bar]
foo:bar
此示例仅说明基本数据是字符串类型,但您可以打开您希望接收的任何类型,并且像您可以对案例进行分组的任何开关一样,因此您可以类似地处理所有数字。