将JSON解组为Go接口{}

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

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

简短版本,不,你不能这样做,但你有几个选项:

  1. A.Value的类型从interface{}改为B
  2. A中添加一个函数,将A.Value从map转换为B(如果它不是B的话)。
  3. 使用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.

  1. Change A.Value to use B instead of interface{}.
  2. Add a function to A that converts A.Value from a map to B if it isn't already B.
  3. Use encoding/gob and store the bytes in memcache then convert it back with a function like NewA(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)
}

huangapple
  • 本文由 发表于 2014年7月25日 05:30:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/24944338.html
匿名

发表评论

匿名网友

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

确定