You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 15:29:55