Golang json.Unmarshal未将缺失字段的指针显式设为nil的解决方法
问题
首次调用json.Unmarshal解析包含struct_b字段的JSON时,StructB被正确赋值;但第二次解析不含struct_b的JSON时,已经是非nil的StructB不会被设为nil,而新创建的结构体解析同一份JSON时StructB却是nil。怎么修改代码让第二次解析时把已非nil的StructB显式设为nil?
问题代码示例
package main import ( "encoding/json" "fmt" ) type InternalStructFirst struct { Field string `json:"field"` } type ExternalStruct struct { StructA InternalStructFirst `json:"struct_a"` StructB *InternalStructFirst `json:"struct_b"` } func main() { var structVar ExternalStruct string1 := "{\"struct_a\":{\"field\":\"a\"},\"struct_b\":{\"field\":\"b\"}}" json.Unmarshal([]byte(string1), &structVar) fmt.Printf("first: %+v %+v\n", structVar.StructA, structVar.StructB) string2 := "{\"struct_a\":{\"field\":\"c\"}}" json.Unmarshal([]byte(string2), &structVar) fmt.Printf("second: %+v %+v\n", structVar.StructA, structVar.StructB) var structVar2 ExternalStruct json.Unmarshal([]byte(string2), &structVar2) fmt.Printf("third: %+v %+v\n", structVar2.StructA, structVar2) }
输出结果
first: {Field:a} &{Field:b} second: {Field:c} &{Field:b} third: {Field:} <nil>
解决方案
Go标准库的json.Unmarshal默认只会覆盖JSON中存在的字段,不会将未提及的字段重置为零值(比如指针类型的nil)。要实现解析时重置未出现的字段,有两种实用方法:
方法一:解析前手动重置结构体
每次解析新的JSON前,将结构体变量重新赋值为零值实例,这样之前的字段值会被完全清空:
func main() { var structVar ExternalStruct string1 := "{\"struct_a\":{\"field\":\"a\"},\"struct_b\":{\"field\":\"b\"}}" json.Unmarshal([]byte(string1), &structVar) fmt.Printf("first: %+v %+v\n", structVar.StructA, structVar.StructB) // 重置为零值结构体 structVar = ExternalStruct{} string2 := "{\"struct_a\":{\"field\":\"c\"}}" json.Unmarshal([]byte(string2), &structVar) fmt.Printf("second: %+v %+v\n", structVar.StructA, structVar.StructB) var structVar2 ExternalStruct json.Unmarshal([]byte(string2), &structVar2) fmt.Printf("third: %+v %+v\n", structVar2.StructA, structVar2) }
这种方法简单直接,适合结构体结构不复杂的场景。
方法二:实现json.Unmarshaler自定义反序列化
如果不想每次手动重置,可以给ExternalStruct实现自定义的反序列化逻辑,在解析JSON前自动重置结构体:
type ExternalStruct struct { StructA InternalStructFirst `json:"struct_a"` StructB *InternalStructFirst `json:"struct_b"` } func (e *ExternalStruct) UnmarshalJSON(data []byte) error { // 先将结构体重置为零值 *e = ExternalStruct{} // 定义临时类型避免递归调用自定义UnmarshalJSON方法 type temp ExternalStruct return json.Unmarshal(data, (*temp)(e)) }
这样每次调用json.Unmarshal时,都会先把ExternalStruct重置为零值,再解析JSON内容,未在JSON中出现的StructB自然会被设为nil。
修改后的输出
两种方法都会得到如下输出:
first: {Field:a} &{Field:b} second: {Field:c} <nil> third: {Field:} <nil>
内容的提问来源于stack exchange,提问作者serge1peshcoff
相关产品推荐
相关产品推荐

