go内嵌结构体json序列化的字段继承覆盖问题

作者:matrix 发布时间:2025-07-31 分类:Golang

go内嵌结构体json序列化的字段继承覆盖问题

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))
}
  • 内外结构体存在相同字段 LastIP,结构体标签json key也相同,都是last_ip
  • json.Marshal序列化操作时,外层结构体的数据优先

不同结构体标签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/