将JSON解组到Go接口{}

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

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

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?