标题:Go 中嵌入结构体未被 JSON 解码的原因与正确处理方式
技术百科
碧海醫心
发布时间:2026-01-27
浏览: 次 当结构体嵌入了实现 `json.unmarshaler` 接口的类型时,go 的 `json.unmarshal` 会直接调用该嵌入类型的 `unmarshaljson` 方法,跳过对整个外层结构体的字段解析,导致其他嵌入字段(如 `user`)被忽略。
这是 Go 类型系统与 encoding/json 包协同工作的预期行为,而非 bug。其根本原因在于:json.Unmarshal 在解码前会通过反射检查目标值是否实现了 json.Unmarshaler 接口(即 v.Interface().(json.Unmarshaler))。一旦发现 *ServiceAccount 满足该条件(因其嵌入了 *Policy,而 *Policy 实现了 UnmarshalJSON),它便会*直接调用 `(Policy).UnmarshalJSON**,并将原始 JSON 数据全部交由该方法处理——此时json包不再尝试逐字段解码ServiceAccount的其余部分(包括User` 字段),因为“接口已接管”。
为什么 ServiceAccount 被认为实现了 Unmarshaler?
根据 Go 规范,结构体类型若嵌入了实现某接口的字段,则该结构体自动获得该接口的实现(前提是嵌入字段是可导出的、且方法集完整)。由于 Policy 是可导出类型,且 *Policy 实现了 UnmarshalJSON,因此 *ServiceAccount 自动满足 json.Unmarshaler 接口。这正是 json 包选择跳过默认结构体解码逻辑的关键依据。
验证:嵌入多个 Unmarshaler 会发生什么?
有趣的是,若同时为 User 和 Policy 实现 UnmarshalJSON,*ServiceAccount 将

正确解决方案(推荐顺序)
✅ 方案一:避免嵌入 Unmarshaler 类型(最清晰)
改用命名字段,显式控制解码逻辑:
type ServiceAccount struct {
User User `json:"user,omitempty"`
Policy Policy `json:"policy,omitempty"`
}这样 ServiceAccount 不再隐式实现 Unmarshaler,json 包将按字段标签正常解码。
✅ 方案二:在外层类型显式实现 UnmarshalJSON(最可控)
完全掌控解码流程,兼顾嵌入字段:
func (s *ServiceAccount) UnmarshalJSON(data []byte) error {
// 1. 先解码到临时结构体(避免递归调用)
type Alias ServiceAccount // 防止无限递归
aux := &struct {
UserID string `json:"userID"`
Scopes string `json:"scopes,omitempty"`
*Alias
}{
Alias: (*Alias)(s),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
s.User.UserID = aux.UserID
s.Policy.Scopes = aux.Scopes
return nil
}⚠️ 方案三:慎用“多嵌入 Unmarshaler”技巧
虽能迫使 ServiceAccount 失去 Unmarshaler 实现,但属于反模式,易引发维护困惑,不推荐。
总结
- 嵌入 Unmarshaler 类型会“劫持”整个外层结构体的 JSON 解码流程;
- 这是 Go 接口实现规则与 json 包设计逻辑共同作用的结果;
- 最佳实践是:要么避免嵌入 Unmarshaler,要么主动在外层类型中实现完整解码逻辑;
- 始终优先考虑代码可读性与可维护性,而非依赖反射或接口继承的隐式行为。
# 的是
# 这是
# 多个
# 实现了
# 你可以
# 跳过
# 而非
# js
# json
# go
# golang
# 递归
# 接口
# 为什么
# Interface
# bug
# 结构体
# 继承
# 隐式
# 直接调用
# 编译错误
# 代码可读性
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Python对象比较与排序_集合使用说明【指导】
- MySQL 中使用 IF 和 CASE 实现查询字
- Win11怎么关闭用户账户控制UAC_Window
- Win11声音忽大忽小怎么办 Win11音频增强功
- Linux如何使用Curl发送请求_Linux下A
- Win11时间不对怎么同步_Win11自动校准互联
- Win10系统怎么查看显卡温度_Win10任务管理
- Windows10系统怎么查看CPU核心数_Win
- VSC怎样用终端运行PHP_命令行执行脚本的步骤【
- 如何使用Golang进行HTTP服务性能测试_测量
- 如何在 Go 结构体中正确初始化 map 字段
- php怎么下载安装后设置错误日志_phpini l
- 如何在Golang中实现WebSocket广播_使
- Win10怎样安装PPT模板_Win10安装PPT
- Win11怎么检查TPM2.0模块_Windows
- php高频调试功能有哪些_php常用调试函数与工具
- php接口返回数据乱码怎么办_php接口调试编码问
- Windows10如何查看保存的WiFi密码_Wi
- Win10怎么卸载金山毒霸_Win10彻底卸载金山
- Go语言中slice追加操作的底层共享机制解析
- C++中的std::shared_from_thi
- Win11怎么关闭定位服务 Win11禁止应用获取
- Win11怎么开启移动热点_Windows11共享
- Win11怎么激活Windows10_Win11激
- Win11怎么关闭通知中心_Windows11系统
- 如何在Windows中创建新的用户账户?(标准与管
- Win11怎么开启游戏模式_Win11优化游戏帧数
- Win11怎么设置夜间模式_Windows11显示
- PHP主流架构怎么监控运行状态_工具推荐【操作】
- Win10系统映像怎么恢复 Win10使用系统映像
- mac怎么退出id_MAC退出iCloud账号与A
- 如何在包含多值的列中精准搜索指定演员?
- Win11系统占用空间大怎么办 Win11深度瘦身
- 如何在Golang中处理云原生事件_使用Event
- 如何开启Windows的远程服务器管理工具(RSA
- 使用类变量定义字符串常量时的类型安全最佳实践
- php打包exe后无法读取环境变量_变量配置方法【
- Win10怎么限制单程序CPU占用上限_Win10
- php能跑在stm32上吗_php在stm32微控
- Windows Defender扫描失败怎么办_安
- C++如何获取CPU核心数?(std::threa
- Win11怎样安装搜狗输入法_Win11安装搜狗输
- c++如何使用std::bind绑定函数参数_c+
- 零基础学会Python自动化办公_高效处理Exce
- PythonPandas数据分析项目教程_时间序列
- 如何在Golang中实现邮件发送功能_Golang
- Python网页解析流程_html结构说明【指导】
- 如何正确访问 Laravel 模型或对象的属性而非
- c++ reinterpret_cast怎么用 c
- Mac如何与安卓手机传文件_Mac和Android

QQ客服