package main
import (
"encoding/json"
"fmt"
"reflect"
"unsafe"
)
type Stu struct {
Name string `json:"name"`
}
func MakeStu() interface{} {
return Stu{
Name: "Test",
}
}
func main() {
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
t2 := MakeStu()
t3 := MakeStu()
json.Unmarshal(jsonData, &t1)
fmt.Println(t1) //Type of t1 becomes map[string]interface{} instead of struct Stu
newPointer := reflect.New(reflect.ValueOf(t2).Type()).Interface()
json.Unmarshal(jsonData, newPointer)
fmt.Println(newPointer) //It works,but it need allocate memory to hold temp new variable
brokenPointer := reflect.NewAt(reflect.ValueOf(t3).Type(), unsafe.Pointer(&t3)).Interface()
json.Unmarshal(jsonData, brokenPointer)
fmt.Println(brokenPointer) // I want to get the pointer of original type based on the existed variable t3,but it crashes.
}
If I don't know the concrete type of the interface{} when coding,I can't use interface.(Type) to cast. so how to use reflect.NewAt on interface{}?
If there is an interface that contains a method who returns interface{} whose concrete type is struct, but I can't determine the type of struct when coding. I need use json.Unmarshal to decode data that encoded by json. I don't want to get map[string]interface{} , so I need make the receiver's type that is the pointer of concrete type of the interface{} . Using reflect.New is easy but costing extra memory, so I am curious about how to use reflect.NewAt with existing interface{}.
I would like to spend a few sentences explaining what the problem is.
The function MakeStu()
, which we have no control over, returns the empty interface - not a concrete type. This will make the concrete type "invisible" to json.Unmarshal()
, and json.Unmarshal()
treats it as the empty interface, not as the concrete type Stu
. Somehow we must convey the concrete type to the unmarshaller.
I would solve this problem with a type switch:
func main() {
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
switch c := t1.(type) {
default:
_ = json.Unmarshal(jsonData, &c)
t1 = c
}
fmt.Println(t1)
}
The type switch turns the empty interface into a concrete type, and json.Unmarshal()
will treat it as such. You will probably still have the extra allocation, but the code is more readable, and you're not depending on reflection.
If I was feeling really adventurous, I would add a helper function accepting a pointer to an empty interface like this:
func unmarshal(data []byte, i *interface{}) (err error) {
switch c := (*i).(type) {
default:
err = json.Unmarshal(data, &c)
*i = c
}
return err
}
That would enable usage like this:
jsonData := []byte(`{"name":"New"}`)
t1 := MakeStu()
unmarshal(jsonData, &t1)
This looks clean to me and doesn't involve reflection, but it doesn't use reflect.NewAt()
as you suggested yourself. I hope you find my suggestions useful none the less.