如何在 Go 中为同时包含导出与非导出字段的结构体实现自定义 JSON 编解码

发布时间 - 2026-01-11 00:00:00    点击率:

go 的 json 包默认忽略非导出(小写开头)字段,但可通过实现 marshaljson 和 unmarshaljson 方法,结合中间结构体,安全地序列化/反序列化混合可见性字段,避免递归调用栈溢出。

在 Go 中,JSON 编解码器仅能访问结构体的导出字段(即首字母大写的字段)。像 fieldA string 这样的非导出字段,默认不会参与 json.Marshal 或 json.Unmarshal。若需保留其 JSON 表现力,必须显式实现 json.Marshaler 和 json.Unmarshaler 接口——但关键在于:*绝不能在自定义方法中直接对 `Test调用json.Marshal/json.Unmarshal**,否则会触发无限递归(正如原问题中因嵌入*Test` 导致的栈溢出)。

推荐做法是定义一个纯导出、字段一一对应、仅用于 JSON 传输的中间结构体(如 TestJSON),它不包含任何业务逻辑,仅作为序列化桥梁:

type Test struct {
    fieldA string // 非导出,需手动处理
    FieldB int    // 导出,可直接访问
    FieldC string // 导出
}

// TestJSON:仅用于 JSON 编解码的导出结构体,字段名与 JSON key 对齐
type TestJSON struct {
    FieldA string `json:"fieldA"`
    FieldB int    `json:"fieldB"`
    FieldC string `json:"fieldC"`
}

func (t *Test) MarshalJSON() ([]byte, error) {
    // 将当前 Test 实例的各字段显式赋值给 TestJSON,再序列化
    return json.Marshal(TestJSON{
        FieldA: t.fieldA,
        FieldB: t.FieldB,
        FieldC: t.FieldC,
    })
}

func (t *Test) UnmarshalJSON(b []byte) error {
    var temp TestJSON
    if err := json.Unmarshal(b, &temp); err != nil {
        return err
    }
    // 安全反向赋值:所有字段均为可写导出字段或本结构体内存可访问的非导出字段
    t.fieldA = temp.FieldA
    t.FieldB = temp.FieldB
    t.FieldC = temp.FieldC
    return nil
}

优势说明

  • 无递归风险:TestJSON 是独立类型,与 Test 无嵌入或指针循环依赖;
  • 可维护性强:新增字段时,只需同步更新 TestJSON 定义及两个方法中的字段映射,IDE 可辅助检测遗漏;
  • 语义清晰:分离了数据模型(Test)与序列化契约(TestJSON),符合关注点分离原则。

⚠️ 注意事项

  • 若 Test 含嵌套结构体或切片,TestJSON 中对应字段也需保持相同导出性与 JSON tag;
  • 不要尝试用 unsafe 或反射绕过导出限制——既破坏安全性,又丧失编译期检查;
  • 若非导出字段本质是内部状态(如缓存、锁、连接句柄),则不应参与 JSON 序列化,此时应重新评估设计:是否真需暴露?能否用 json:"-" 显式忽略?或改用导出字段 + json tag 控制别名?

综上,使用专用中间结构体是 Go 中处理混合可见性字段 JSON 编解码的标准、安全且可扩展的方案。


# js  # json  # go  #   # String  # 结构体  # 递归  # 循环  # 指针  # 接口  # 切片  # ide  # 序列化  # 编解码  # 见性  # 句柄  # 只需  # 均为  # 能在  # 自定义  # 不应 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 使用PHP下载CSS文件中的所有图片【几行代码即可实现】  Win11怎么恢复误删照片_Win11数据恢复工具使用【推荐】  敲碗10年!Mac系列传将迎来「触控与联网」双革新  C++时间戳转换成日期时间的步骤和示例代码  Laravel队列任务超时怎么办_Laravel Queue Timeout设置详解  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  Laravel如何实现本地化和多语言支持?(i18n教程)  如何在自有机房高效搭建专业网站?  在线教育网站制作平台,山西立德教育官网?  SQL查询语句优化的实用方法总结  Laravel如何与Vue.js集成_Laravel + Vue前后端分离项目搭建指南  浅析上传头像示例及其注意事项  如何为不同团队 ID 动态生成多个非值班状态按钮  JS弹性运动实现方法分析  Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程  Laravel如何使用Seeder填充数据_Laravel模型工厂Factory批量生成测试数据【方法】  Laravel怎么解决跨域问题_Laravel配置CORS跨域访问  如何快速搭建二级域名独立网站?  Laravel如何实现邮件验证激活账户_Laravel内置MustVerifyEmail接口配置【步骤】  php485函数参数是什么意思_php485各参数详细说明【介绍】  微信小程序 require机制详解及实例代码  北京网站制作的公司有哪些,北京白云观官方网站?  HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】  如何在服务器上三步完成建站并提升流量?  Android自定义listview布局实现上拉加载下拉刷新功能  Claude怎样写约束型提示词_Claude约束提示词写法【教程】  制作电商网页,电商供应链怎么做?  Laravel如何使用Sanctum进行API认证?(SPA实战)  手机网站制作与建设方案,手机网站如何建设?  Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲  Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives  齐河建站公司:营销型网站建设与SEO优化双核驱动策略  如何选择可靠的免备案建站服务器?  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  如何快速搭建个人网站并优化SEO?  HTML透明颜色代码在Angular里怎么设置_Angular透明颜色使用指南【详解】  软银砸40亿美元收购DigitalBridge 强化AI资料中心布局  网站图片在线制作软件,怎么在图片上做链接?  详解免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)  Laravel如何处理文件下载请求?(Response示例)  三星、SK海力士获美批准:可向中国出口芯片制造设备  高性价比服务器租赁——企业级配置与24小时运维服务  Laravel如何使用查询构建器?(Query Builder高级用法)  如何快速搭建高效服务器建站系统?  Laravel如何实现多对多模型关联?(Eloquent教程)  Laravel如何实现API资源集合?(Resource Collection教程)  Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID  如何在万网主机上快速搭建网站?  C++用Dijkstra(迪杰斯特拉)算法求最短路径  Laravel怎么实现模型属性的自动加密