登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go问答

Go json.UnmarshalJSON 自定义方法为什么会递归

来源:17golang原创

时间:2026-09-11 16:47:11 464浏览 收藏

先看清递归是怎样发生的

UnmarshalJSONjson.Unmarshaler 接口的方法。标准库在解码一个值时,会优先检查它是否实现这个接口。因此下面的写法不是“调用默认解析”,而是把同一个 *Order 再次交给接口分派。

type Order struct {
	ID     int    `json:"id"`
	Status string `json:"status"`
}

func (o *Order) UnmarshalJSON(data []byte) error {
	// 这里仍然把 *Order 交给 json,接口分派会再次进入本方法。
	return json.Unmarshal(data, o)
}

调用链可以简化为:json.Unmarshal → (*Order).UnmarshalJSON → json.Unmarshal → (*Order).UnmarshalJSON。只要目标类型没有改变,递归就不会自然停止。

JSON 解码进入自定义方法后重复回到同一方法的技术流程图
自定义解码器再次接收原类型指针时,会形成重复分派回路。

用定义类型切断方法分派

修复时不要使用类型别名,而要声明一个新的定义类型。两者外形相似,但语义不同:type plainOrder = Order 只是换名字,方法集仍然存在;type plainOrder Order 才会产生新的类型,原来的 UnmarshalJSON 不在它的方法集里。

func (o *Order) UnmarshalJSON(data []byte) error {
	// 定义类型保留字段结构,但不会再次满足 Order 的 Unmarshaler 方法集。
	type plainOrder Order
	var decoded plainOrder
	if err := json.Unmarshal(data, &decoded); err != nil {
		// 底层 JSON 语法或字段类型错误要原样返回给调用方。
		return err
	}
	*o = Order(decoded)
	return nil
}
定义类型让 JSON 解码绕过自定义方法并保留字段结构的技术流程图
定义类型保留字段布局,同时把默认字段解码与自定义方法分开。

把自定义校验放回正确位置

自定义方法的价值通常是“默认解析后再校验”,例如状态只能是 pendingpaidcancelled。先让标准库负责 JSON 到字段的转换,再做业务判断,边界会更清楚。

func (o *Order) UnmarshalJSON(data []byte) error {
	type plainOrder Order
	var decoded plainOrder
	if err := json.Unmarshal(data, &decoded); err != nil {
		// 输入不是合法 JSON,或字段类型不匹配时停止后续校验。
		return err
	}
	if decoded.ID 

如果业务需要区分“字段缺失”和“字段为零值”,可先用 map[string]json.RawMessage 检查字段是否存在。

排查别名写法和嵌套字段陷阱

遇到递归时先按下面的清单检查:

检查点正确判断
类型声明type plainOrder Order 是定义类型;等号写法不会切断方法集。
接收者指针接收者意味着 *Order 参与接口分派,转换目标必须避开原方法。
错误处理先返回 JSON 解析错误,再做业务校验,最后才覆盖原对象。
嵌套字段嵌套类型若自己实现了 UnmarshalJSON,它仍会按自己的规则被调用。

标准库会在输入为 null 时调用实现了 Unmarshaler 的方法,是否接受 null 要写清约定。预填充对象若想每次得到干净结果,应使用局部 decoded

相关问题:

  • 为什么 type alias = Order 仍然递归? 因为目标仍是原来的 Order,方法集没有变化。
  • 什么时候应该改用辅助结构体? 当输入字段与领域对象差异很大,或兼容逻辑超过一两个校验条件时更易维护。
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>