go内嵌结构体json序列化的字段继承覆盖问题
作者:matrix 发布时间:2025-07-31 分类:Golang
TL;DR
内层字段会被提升到外层 在序列化为json时一起显示
json序列化时取值顺序:外层-->内层。即 子结构体继承覆盖内嵌结构体
相同结构体标签json key
package main
import (
"encoding/json"
"fmt"
)
//外层字段优先
type User struct {
Name string `json:"name"`
Age int `json:"age"`
LastIP string `json:"last_ip"`
}
type ListDistanceAscItem struct {
User // 嵌入式结构体
Distance float64 `json:"distance"`
LastIP string `json:"last_ip"`
}
func main() {
subitem := ListDistanceAscItem{
User: User{
Name: "John Doe",
Age: 30,
LastIP: "127.0.0.1",
},
Distance: 10.5,
LastIP: "89.0.142.86",
}
res, _ := json.Marshal(subitem) //{..."last_ip":"89.0.142.86"}
fmt.Println(string(res))
}
不同结构体标签json key
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
LastIP string `json:"last_ip"`
}
type ListDistanceAscItem struct {
User // 嵌入式结构体
Distance float64 `json:"distance"`
LastIP string `json:"override_ip"`
}
func main() {
subitem := ListDistanceAscItem{
User: User{
Name: "John Doe",
Age: 30,
LastIP: "127.0.0.1",
},
Distance: 10.5,
LastIP: "89.0.142.86",
}
res, _ := json.Marshal(subitem)
fmt.Println(string(res)) //{...last_ip":"127.0.0.1","distance":10.5,"override_ip":"89.0.142.86"}
}
- 内外结构体存在相同字段 LastIP,但是结构体标签json key不同
- 这里LastIP的两个结构体标签的json key(last_ip,override_ip)都会显示
- 当json.Marshal序列化操作时嵌入式结构体的字段会被提升到外层结构体
外层结构体标签json:"-"
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
LastIP string `json:"last_ip"`
}
type ListDistanceAscItem struct {
User // 嵌入式结构体
Distance float64 `json:"distance"`
LastIP string `json:"-"`
}
func main() {
subitem := ListDistanceAscItem{
User: User{
Name: "John Doe",
Age: 30,
LastIP: "127.0.0.1",
},
Distance: 10.5,
LastIP: "89.0.142.86",
}
res, _ := json.Marshal(subitem)
fmt.Println(string(res)) //{..."last_ip":"127.0.0.1"...}
}
- 内外结构体存在相同字段 LastIP,结构体标签json key不相同,内层为
last_ip
,外层设置为自动忽略json:"-"
- 当json.Marshal序列化操作时,外层优先,没有数据会查找内层。这里因为外层设置字段忽略 所以查找的内层数据
内层结构体标签json:"-"
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
LastIP string `json:"-"`
}
type ListDistanceAscItem struct {
User // 嵌入式结构体
Distance float64 `json:"distance"`
LastIP string `json:"last_ip"`
}
func main() {
subitem := ListDistanceAscItem{
User: User{
Name: "John Doe",
Age: 30,
LastIP: "127.0.0.1",
},
Distance: 10.5,
LastIP: "89.0.142.86",
}
res, _ := json.Marshal(subitem) //{..."last_ip":"89.0.142.86"...}
fmt.Println(string(res))
}
- 内外结构体存在相同字段 LastIP,结构体标签json key不相同,外层为
last_ip
,内层设置为自动忽略json:"-"
- 当json.Marshal序列化操作时,依然外层优先,覆盖掉内层数据
- 这里经常用于某些敏感字段的结构体输出时使用,外层结构体嵌入基础结构体执行字段覆盖(重写) 也就达到了某些特定情况下输出敏感数据,普通情况下不输出。
LLM:
Go的嵌入式结构体字段提升(Field Promotion)机制:
"字段提升"的意思是:内部结构体的字段会被提升到外部结构体的层级,就像这些字段直接定义在外部结构体中一样。这不是覆盖,而是"继承"的概念。
外层结构体实例化时外层字段要手动赋值
//内层结构体
type TurtleSoupGame struct {
ID int `gorm:"column:id" json:"id"`
TruthPlaceholder string `gorm:"column:truth_placeholder" json:"-"`
}
// 外层
type GameInfoResponse struct {
TurtleSoupGame
TruthPlaceholder string `json:"truth_placeholder"`
}
...
result := GameInfoResponse{
TurtleSoupGame: game, // 赋值数据id,truth_placeholder
TruthPlaceholder: game.TruthPlaceholder, // 必须手动赋值 否则会返回空字符串
}
return result
- 这里依然是外层优先,如果GameInfoResponse结构体字段TruthPlaceholder没有赋值,那外层就没有查找内层又是字段忽略
json:"-"
,导致返回空string
LLM:
在 Go 中,结构体字段名和 JSON tag 无法自动同步值。嵌入结构体的同名字段只是字段遮蔽,不是字段继承。你定义了外层字段后,Go 不会帮你把内层的值传给外层字段,必须手动赋值。
ref:
https://www.sohamkamani.com/Golang/json/