
本文旨在解决在使用Go语言的`encoding/json`包进行JSON反序列化时,遇到的“Unmarshal on reflected value”问题。通过示例代码,详细解释了如何正确地使用反射来动态地创建和填充对象,从而实现将JSON数据反序列化到指定类型的对象中。
在使用Go语言的encoding/json包进行JSON反序列化时,有时我们需要动态地创建对象并将其填充为JSON数据。这通常涉及到使用反射,但如果使用不当,可能会遇到“Unmarshal on reflected value”错误。本文将通过一个具体的示例,详细解释如何解决这个问题。
问题分析
问题的核心在于,json.Unmarshal函数需要一个指向可修改值的指针。当我们使用reflect.New创建一个新的反射值时,需要确保传递给Unmarshal的是该值的地址,并且该值是可以被修改的。
解决方案
以下是修改后的Get函数,它解决了上述问题:
func Get(a []byte, b interface{}) {
objType := reflect.TypeOf(b).Elem()
obj := reflect.New(objType).Interface()
MustJSONDecode(a, &obj)
fmt.Printf("obj = %#v\n", obj)
}代码解释:
- objType := reflect.TypeOf(b).Elem(): 获取b的类型,因为b是一个指针,所以使用Elem()来获取指针指向的类型。
- obj := reflect.New(objType).Interface(): 使用reflect.New(objType)创建一个指向objType类型的新值的指针。关键在于调用.Interface()方法,它返回一个包含该指针的interface{}。
- MustJSONDecode(a, &obj): 将JSON数据反序列化到obj指向的值。注意,这里传递的是&obj,即obj的地址,这使得Unmarshal函数可以修改obj指向的值。
- fmt.Printf("obj = %#v\n", obj): 打印反序列化后的对象。%#v格式化动词可以打印出值的完整结构,包括类型信息。
完整示例代码:
package main
import (
"encoding/json"
"fmt"
"reflect"
)
var (
datajson []byte
)
type User struct {
Name string
}
func MustJSONEncode(i interface{}) []byte {
result, err := json.Marshal(i)
if err != nil {
panic(err)
}
return result
}
func MustJSONDecode(b []byte, i interface{}) {
err := json.Unmarshal(b, i)
if err != nil {
panic(err)
}
}
func Store(a interface{}) {
datajson = MustJSONEncode(a)
}
func Get(a []byte, b interface{}) {
objType := reflect.TypeOf(b).Elem()
obj := reflect.New(objType).Interface()
MustJSONDecode(a, &obj)
fmt.Printf("obj = %#v\n", obj)
}
func main() {
dummy := &User{}
david := User{Name: "DavidMahon"}
Store(david)
Get(datajson, dummy)
}运行结果:
obj = &main.User{Name:"DavidMahon"}替代方案
如果你的目标仅仅是将JSON反序列化到已经存在的对象中,更简单的方法是直接将JSON数据反序列化到该对象:
func Get(a []byte, b interface{}) {
MustJSONDecode(a, &b)
fmt.Printf("obj = %#v\n", b)
}这种方法避免了使用反射创建新对象,更加简洁高效。
注意事项
- 类型匹配: 确保JSON数据的结构与目标对象的类型匹配,否则反序列化可能会失败。
- 错误处理: 在实际应用中,应该对json.Unmarshal的返回值进行错误检查,以确保反序列化成功。
- 性能考虑: 反射操作通常比直接操作类型要慢,因此在性能敏感的场景中,应尽量避免过度使用反射。
总结
通过本文,我们学习了如何使用反射来动态地创建对象并将其填充为JSON数据。关键在于理解reflect.New和.Interface()的用法,以及确保传递给json.Unmarshal的是一个指向可修改值的指针。同时,我们也了解了在不需要动态创建对象的情况下,更简洁的替代方案。希望本文能帮助你解决在使用Go语言进行JSON反序列化时遇到的问题。










