英文:
Unmarshalling JSON into Go interface{}
问题
我有一个包含interface{}
类型字段的结构体。在使用memcached(https://github.com/bradfitz/gomemcache)进行缓存时,该结构体被编组为JSON,然后在从缓存中检索时重新解组回结构体。结果中的interface{}
字段不可避免地指向map[string]interface{}
类型的对象(也就是说,只能将interface{}
字段断言为map[string]interface{}
),编组和解组过程没有保留类型信息。有没有办法在编组过程中保存这些信息,以便可以正确解组?还是我必须使用其他编解码器之类的东西?
type A struct {
value interface{}
}
type B struct {
name string
id string
}
func main() {
a := A{value: B{name: "hi", id: "12345"}}
cache.Set("a", a) // 将'a'编组为JSON并存储在缓存中
result := cache.Get("a") // 从缓存中检索'a'并解组
fmt.Printf("%s", result.value.(B).name) // 生成错误,表示无法将map[string]interface{}断言为'B'结构体
fmt.Printf("%s", result.value.(map[string]interface{})["name"].(string)) // 正确打印"12345"
}
英文:
I have this struct with a field of type interface{}
. In the process of caching it using memcached (https://github.com/bradfitz/gomemcache), the struct is marshalled into a JSON, which is then unmarshalled back into the struct when retrieved from the cache. The resulting interface{}
field inevitably points to an object of type map[string]interface{} (as in, the interface{}
field can only type asserted as map[string]interface{}), the marshalling and unmarshalling process not having preserved the type information. Is there any way to save this information in the marshalling process, in such a way that it can be unmarshalled properly? Or do I have to use some other codec or something?
type A struct {
value interface{}
}
type B struct {
name string
id string
}
func main() {
a := A{value: B{name: "hi", id: "12345"}}
cache.Set("a", a) // Marshals 'a' into JSON and stores in cache
result = cache.Get("a") // Retrieves 'a' from cache and unmarshals
fmt.Printf("%s", result.value.(B).name) // Generates error saying that
// map[string]interface{} cannot be type asserted as a 'B' struct
fmt.Printf("%s", result.value.(map[string]interface{})["name"].(string)) // Correctly prints "12345"
}
答案1
得分: 2
JSON无法编码Go中可以编码的类型信息的深度,因此在解组时,您将始终获得以下基本类型:
布尔值,用于JSON布尔值
float64,用于JSON数字
字符串,用于JSON字符串
[]interface{},用于JSON数组
map[string]interface{},用于JSON对象
JSON null的nil值
来自Go文档:http://golang.org/pkg/encoding/json/#Unmarshal
如果您对所需类型有了解,可以编写一些方法在解组时构造正确的变量。
英文:
JSON isn't able to encode the depth of type information that you can in Go, so you'll always get back the following basic types when unmarshalling:
> bool, for JSON booleans
> float64, for JSON numbers
> string, for JSON strings
> []interface{}, for JSON arrays
> map[string]interface{}, for JSON objects
> nil for JSON null
From go docs: http://golang.org/pkg/encoding/json/#Unmarshal
If you have that knowledge about the types you need, you can write some methods to construct the right variables on unmarshalling perhaps?
答案2
得分: 2
简短版本,不,你不能这样做,但你有几个选项:
- 将
A.Value
的类型从interface{}
改为B
。 - 在
A
中添加一个函数,将A.Value
从map转换为B
(如果它不是B
的话)。 - 使用
encoding/gob
将字节存储在内存缓存中,然后使用类似NewA(b []byte) *A
的函数将其转换回来。
使用gob
时,你需要在编码/解码之前先注册每个结构体,示例代码如下:
func init() {
// 在这里注册你的类型,只需要注册一次
gob.Register(A{})
gob.Register(B{})
}
func main() {
var (
buf bytes.Buffer
enc = gob.NewEncoder(&buf)
dec = gob.NewDecoder(&buf)
val = A{B{"name", "id"}}
r A
)
fmt.Println(enc.Encode(&val))
fmt.Println(dec.Decode(&r))
fmt.Printf("%#v", r)
}
希望对你有所帮助!
英文:
Short version, no you can't do that, you have few options though.
- Change
A.Value
to useB
instead ofinterface{}
. - Add a function to
A
that convertsA.Value
from a map toB
if it isn't alreadyB
. - Use
encoding/gob
and store the bytes in memcache then convert it back with a function likeNewA(b []byte) *A
.
For using gob
, you have to register each struct with it first before encoding / decoding, example:
func init() {
//where you should register your types, just once
gob.Register(A{})
gob.Register(B{})
}
func main() {
var (
buf bytes.Buffer
enc = gob.NewEncoder(&buf)
dec = gob.NewDecoder(&buf)
val = A{B{"name", "id"}}
r A
)
fmt.Println(enc.Encode(&val))
fmt.Println(dec.Decode(&r))
fmt.Printf("%#v", r)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论