go一个大坑 核心问题:同名同 JSON tag 字段的处理
2026/6/12 12:39:51 网站建设 项目流程

核心问题:同名同 JSON tag 字段的处理

场景描述

外层泛型结构体:

go

type IchubResult[T any] struct { *basedto.IchubResult // 指针嵌入 Data T `json:"data"` // 外层 Data Total int `json:"total"` ... }

内层嵌入结构体basedto.IchubResult

go

type IchubResult struct { Code int `json:"code"` Msg string `json:"msg"` Data any `json:"data"` // 内层也有 Data! ... }

两者都设置了值

go

result := &IchubResult[string]{ IchubResult: &basedto.IchubResult{ Code: 0, Msg: "success", Data: "inner value", // 内层 Data }, Data: "outer value", // 外层 Data Total: 100, }

Go JSON 序列化的实际行为

关键规则:

嵌入类型外层 Data内层 DataJSON 结果
值类型嵌入basedto.IchubResult有值有值外层优先(内层被隐藏)
指针类型嵌入*basedto.IchubResult有值有值内层优先!(内层 Data 被序列化)

为什么内层优先?

这是 Goencoding/json处理指针嵌入的特殊行为:

  1. 指针嵌入的字段提升规则:当嵌入指针非 nil 时,其字段会被提升到外层
  2. 字段冲突处理:外层同名字段会"遮蔽"(shadow)内层字段
  3. 但是!JSON 序列化时,指针嵌入的字段会被单独遍历,可能导致重复 key

实际序列化流程:

go

// 遍历顺序(简化) 1. 遍历外层结构体的直接字段 → 找到 Data (外层) 2. 遍历嵌入指针的字段 → 找到 Data (内层) 3. 两个字段都被序列化,内层覆盖外层(取决于遍历顺序)

解决方案

方案一:使用值类型嵌入

go

type IchubResult[T any] struct { basedto.IchubResult // 值类型嵌入,外层优先 Data T `json:"data"` ... }

方案二:修改内层结构体的 JSON tagbasedto.IchubResultData字段改为其他名称,或移除。

方案三:手动控制序列化实现MarshalJSON方法:

go

func (r *IchubResult[T]) MarshalJSON() ([]byte, error) { type Alias IchubResult[T] return json.Marshal(&struct { *Alias Data T `json:"data"` // 显式指定使用外层 Data }{ Alias: (*Alias)(r), Data: r.Data, }) }

验证测试

go

package main import ( "encoding/json" "fmt" ) type Inner struct { Data any `json:"data"` } type Outer[T any] struct { *Inner Data T `json:"data"` } func main() { outer := &Outer[string]{ Inner: &Inner{Data: "inner"}, Data: "outer", } b, _ := json.Marshal(outer) fmt.Println(string(b)) // 输出: {"data":"inner"} // 内层优先! }

结论

当使用指针嵌入且内外层都有同名同 JSON tag 的字段时,内层字段的值会出现在最终的 JSON 输出中。

这是因为指针嵌入的字段提升机制与 JSON 序列化的遍历顺序共同作用的结果。建议使用值类型嵌入来确保外层字段优先。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询