为何Go中json.Unmarshal将静态类型any的结构体解码为map而非填充结构体?
为什么传入静态类型为any的结构体变量给json.Unmarshal会被解析为map[string]any?
问题核心
当把静态类型为any、实际存储Foo结构体实例的变量指针传给json.Unmarshal时,Unmarshal不会复用原本的结构体实例,反而会将其替换为map[string]any类型对象,这是Go标准库json包的解析规则导致的。
原因解析
Go的any本质就是interface{},当你传入&dest2(类型为*any)时,Unmarshal会根据指针指向的静态类型(即any)来处理:
- 对于指向
interface{}的指针,json包默认会把JSON对象解析为map[string]any,并将这个map赋值给该interface{}变量,直接覆盖原本存储的结构体实例。 - 而第一个案例中
&dest的类型是*Foo,Unmarshal明确知道要解析到Foo结构体,因此会匹配JSON字段到结构体对应字段完成填充。
解决方案
方案1:类型断言后传入指针
如果提前知道any变量存储的具体类型,可以先做类型断言,再将断言后的实例指针传入:
dest2 := getFooAny() // 断言为Foo类型,创建可修改的实例指针 foo, ok := dest2.(Foo) if !ok { // 处理类型不匹配的异常情况 } err = decodeAny(&foo) dest2 = foo // 将解析后的结果赋值回dest2
方案2:使用reflect动态创建实例
如果无法提前确定类型,可以利用reflect包获取原实例的类型,创建新的实例指针传入解析,再将结果赋值回原变量:
dest2 := getFooAny() // 获取原实例的类型 t := reflect.TypeOf(dest2) // 创建该类型的指针实例 newDest := reflect.New(t).Interface() // 解析JSON到新实例 err = decodeAny(newDest) if err != nil { // 处理解析错误 } // 将新实例的值提取出来赋值给dest2 dest2 = reflect.ValueOf(newDest).Elem().Interface()
原示例代码
package main import ( "encoding/json" "fmt" "reflect" ) type Foo struct { Name string `json:"name"` Number int `json:"number"` } const jsonStr = `{"name": "testing", "number": 22}` func decodeAny(dest any) error { return json.Unmarshal([]byte(jsonStr), dest) } func getFooAny() any { return Foo{} } func main() { dest := Foo{} err := decodeAny(&dest) fmt.Printf("err: %v\n", err) // err is nil fmt.Printf("dest %v of type %T\n", dest, dest) // dest is a Foo dest2 := getFooAny() fmt.Printf("Initial type of dest2: %T\n", dest2) // dest2 is a Foo fmt.Printf("Checking with reflect: %v\n", reflect.TypeOf(dest2)) // TypeOf shows dest2 is Foo err = decodeAny(&dest2) fmt.Printf("err: %v\n", err) // Err is nil fmt.Printf("dest2 %v of type %T\n", dest2, dest2) // dest2 is now a map[string]any }
内容的提问来源于stack exchange,提问作者Oliver Dain
相关产品推荐
相关产品推荐

