Go语言匿名字段使用与注意事项
1. 定义
Go语言支持一种特殊的字段只需要提供类型而不需要写字段名
的字段,称之为匿名字段
或者嵌套字段
。
所谓匿名字段
实际上是一种结构体嵌套
的方式,所以也可以称作嵌套字段
。
这种方式可以实现组合复用,即通过匿名字段,结构体可以直接访问嵌套结构体的字段和方法,而无需通过字段名或类型进行嵌套。这些方法和属性被称为“提升”的方法和属性。通过类型名称也可以直接访问匿名嵌入字段。
2.代码示例
2.1 简单示例
package mainimport ("fmt"
)type Person struct {Name stringAge intPhone string
}func (p *Person) playBasketball() {fmt.Println("打篮球...")
}type Employee struct {PersonEmployeeId int
}// 测试匿名字段
func TestAnonymous() {emp := Employee{Person: Person{Name: "Liu",Age: 20,Phone: "18899999999",},EmployeeId: 101,}// 可直接使用emp调用嵌套类型的方法emp.playBasketball()fmt.Println("id: ", emp.EmployeeId)// 可直接使用emp打印出嵌套类型的所有字段fmt.Println("name: " + emp.Name)fmt.Println("age: ", emp.Age)fmt.Println("name: " + emp.Phone)// 通过匿名类型名来访问fmt.Println("类型访问的id: ", emp.Person.Name)
}func main() {TestAnonymous()
}
2.2 嵌套类型有重复字段
在上面的例子中,Employee
结构体嵌套了 Person
结构体,通过这种方式,Employee
可以直接访问 Person
的字段和方法,而无需使用类似 emp.Person.Name
这样的方式。
需要注意的是,如果结构体中有多个 匿名字段,并且它们拥有相同的字段名,那么在访问这个同名字段时,需要指定嵌套结构体的类型,以避免歧义。例如:
package mainimport ("fmt"
)type Person struct {Name stringAge intPhone string
}func (p *Person) contact() {fmt.Println("Person联系...")
}// 合同
type Contract struct {EmployeeId intPhone string
}func (p *Contract) contact() {fmt.Println("Contract联系...")
}type Employee struct {PersonEmployeeId intContract
}// 测试匿名字段
func TestAnonymous() {emp := Employee{Person: Person{Name: "Liu",Age: 20,Phone: "18899999999",},EmployeeId: 101,Contract: Contract{EmployeeId: 101,Phone: "16699999999",},}// 多个匿名类型字段的字段名称可以相同,这样在访问时需要通过匿名类型名来访问//emp.contact() //会报错emp.Person.contact()emp.Contract.contact()fmt.Println("id: ", emp.EmployeeId)// 可直接使用emp打印出嵌套类型的所有字段fmt.Println("name: " + emp.Name)fmt.Println("age: ", emp.Age)//fmt.Println("name: " + emp.Phone)//这里会报错,因为Person和Contract中都有Phone字段fmt.Println("person phone: ", emp.Person.Phone)fmt.Println("contract phone: ", emp.Contract.Phone)
}func main() {TestAnonymous()
}
在这个例子中,Person 和 Contract 都有 Phone 字段,因此在访问时需要指定具体的类型以避免歧义。
同样的,Person 和 Contact 都有 contact 方法,因此在访问时也需要指定具体的类型以避免歧义。
如果不指定则会编译报错.
3. 结构体匿名字段的Json序列化、反序列化
结构体序列化规则
@注意:可导出的字段(首字母大写),才能参与Json的序列化
标签 | json的key |
---|---|
有标签,json:"xx" | key=xx |
无标签 | key=结构体原属性字段 |
有标签,json:"-" | 会被忽略,不参与序列化 |
有标签,json:"xxx,omitempty" | 代表该字段为空值时,会被忽略。其中xxx 可以省略,, 不可以省略。如: json:",omitempty" |
有标签,json:"xxx,string" | 代表输出类型会转化为字符串。其中xxx 也可以省略它只适用于字符串、浮点数、整数类型的字段 |
3.1 代码示例
package mainimport ("encoding/json""fmt"
)type Student struct {// 指定json标签时,序列化的key为标签值:nameName string `json:"name"`// 不指定序列化标签时,key为原属性:AgeAge int// 当标签值为`json:"-"`,代表改字段会被忽略Home string `json:"-"`// 标签指定omitempty选项,代表该字段为空值时,会被忽略Phone string `json:"phone,omitempty"`// 标签指定string选项,代表输出类型会转化为字符串// 它只适用于字符串、浮点数、整数类型的字段Score float64 `json:"score,string"`
}func TestMarshal() {// 声明初始化结构体student1 := Student{Name: "Liu",Age: 18,Home: "北京",Score: 90.5,Phone: "",}// 序列化json1, _ := json.Marshal(student1)fmt.Printf("序列化json:%s\n", json1)
}
func main() {TestMarshal()
}
输出结果:
序列化json:{"name":"Liu","Age":18,"score":"90.5"}
3.2 匿名字段序列化
3.2.1 无JSON
标签
a. 字段标签不重复
School.Name和Student.Name,Json标签不一致。
package mainimport ("encoding/json""fmt"
)// 学校
type School struct {Name string `json:"schoolName"`Address string `json:"schoolAddress"`
}// 学生
type Student struct {Name string `json:"name"`// 匿名字段,而且没有json标签School
}// 序列化-匿名字段 (默认字段不冲突)
func TestAnonymousTagDifferent() {var student = Student{Name: "XiaoMing",School: School{Name: "北京大学",Address: "北京海淀区",},}jsonByte, _ := json.Marshal(student)fmt.Printf("json: %s \n", jsonByte)
}
func main() {TestAnonymousTagDifferent()
}
结果:
json: {"name":"XiaoMing","schoolName":"北京大学","schoolAddress":"北京海淀区"}
b. 字段标签重复
School.Name和Student.Name,Json标签一致,都是 json:"name"
。
package mainimport ("encoding/json""fmt"
)// 学校
type School struct {Name string `json:"name"`Address string `json:"schoolAddress"`
}// 学生
type Student struct {Name string `json:"name"`// 匿名字段,而且没有json标签School
}// 序列化-匿名字段 (默认字段冲突)
func TestAnonymousTagDifferent() {var student = Student{Name: "XiaoMing",School: School{Name: "北京大学",Address: "北京海淀区",},}jsonByte, _ := json.Marshal(student)fmt.Printf("json: %s \n", jsonByte)
}
func main() {TestAnonymousTagDifferent()
}
结果:
json: {"name":"XiaoMing","schoolAddress":"北京海淀区"}
根据上面代码,得知如果字段标签冲突,冲突的匿名字段会被忽略。
3.2.2 有JSON
标签
当匿名字段设置json
标签时, 就不会出现冲突的情况,因为序列化后的匿名字段会变成对象。
package mainimport ("encoding/json""fmt"
)// 学校
type School struct {Name string `json:"name"`Address string `json:"schoolAddress"`
}// 学生
type Student struct {Name string `json:"name"`// 匿名字段,而且没有json标签School `json:"school"`
}// 序列化-匿名字段 (默认字段冲突)
func TestAnonymousTagDifferent() {var student = Student{Name: "XiaoMing",School: School{Name: "北京大学",Address: "北京海淀区",},}jsonByte, _ := json.Marshal(student)fmt.Printf("json: %s \n", jsonByte)
}
func main() {TestAnonymousTagDifferent()
}
结果:
json: {"name":"XiaoMing","school":{"name":"北京大学","schoolAddress":"北京海淀区"}}
对比前面两个代码可以发现 当匿名字段设置json
标签时,序列化后的匿名字段会变成对象
3.3 匿名字段反序列化
3.3.1 无JSON
标签
a. 字段标签不重复
package mainimport ("encoding/json""fmt"
)// 学校
type School struct {Name string `json:"schoolName"`Address string `json:"schoolAddress"`
}// 学生
type Student struct {Name string `json:"name"`// 匿名字段,而且没有json标签School
}// 反序列化-匿名字段 (默认字段不冲突)
func TestUnMarshal() {jsonStr := `{"name":"XiaoMing","schoolName":"北京大学","schoolAddress":"北京海淀区"}`stu := Student{}err := json.Unmarshal([]byte(jsonStr), &stu)if err != nil {fmt.Println(err)return}fmt.Printf("反序列化结果:%+v", stu)fmt.Println()
}
func main() {TestUnMarshal()
}
结果:
反序列化结果:{Name:XiaoMing School:{Name:北京大学 Address:北京海淀区}}
b. 字段标签重复
package mainimport ("encoding/json""fmt"
)// 学校
type School struct {Name string `json:"name"`Address string `json:"schoolAddress"`
}// 学生
type Student struct {Name string `json:"name"`// 匿名字段,而且没有json标签School
}// 反序列化-匿名字段 (默认字段冲突)
func TestUnMarshal() {jsonStr := `{"name":"XiaoMing","schoolAddress":"北京海淀区"}`stu := Student{}err := json.Unmarshal([]byte(jsonStr), &stu)if err != nil {fmt.Println(err)return}fmt.Printf("反序列化结果:%+v", stu)fmt.Println()
}
func main() {TestUnMarshal()
}
结果:
反序列化结果:{Name:XiaoMing School:{Name: Address:北京海淀区}}
其中如果上面示例中将jsonStr改为如下
jsonStr := `{"name":"XiaoMing","name":"北京大学","schoolAddress":"北京海淀区"}`
那么结果如下:可以看到Name的值变了,但是School中的依然没有值
反序列化结果:{Name:北京大学 School:{Name: Address:北京海淀区}}
从上面示例中可以看到 当字段标签重复时,反序列化会优先给主属性字段赋值。
3.3.2 有JSON
标签
示例代码:
package mainimport ("encoding/json""fmt"
)// 学校
type School struct {Name string `json:"name"`Address string `json:"schoolAddress"`
}// 学生
type Student struct {Name string `json:"name"`// 匿名字段,而且没有json标签School `json:"school"`
}// 反序列化-匿名字段 (默认字段冲突)
func TestUnMarshal() {jsonStr := `{"name":"XiaoMing","name":"北京大学","schoolAddress":"北京海淀区"}`stu := Student{}err := json.Unmarshal([]byte(jsonStr), &stu)if err != nil {fmt.Println(err)return}fmt.Printf("反序列化结果:%+v", stu)fmt.Println()jsonStr2 := `{"name":"XiaoMing","school":{"name":"北京大学","schoolAddress":"北京海淀区"}} `stu2 := Student{}err = json.Unmarshal([]byte(jsonStr2), &stu2)if err != nil {fmt.Println(err)return}fmt.Printf("2反序列化结果:%+v", stu2)fmt.Println()
}
func main() {TestUnMarshal()
}
结果:
反序列化结果:{Name:北京大学 School:{Name: Address:}}
2反序列化结果:{Name:XiaoMing School:{Name:北京大学 Address:北京海淀区}}
3.4 匿名字段json
总结
3.4.1 序列化
a. 匿名字段无标签
-
当匿名字段没有指定标签时,序列化后的结构为同级,如
{"..":"..",..}
-
当匿名属性和主属性的字段标签一样时,序列化会忽略匿名属性的字段。
-
当匿名属性和主属性的字段标签不一样时,序列化不忽略任何字段。
b. 匿名字段有标签
-
当匿名字段
a
指定标签时,序列化后的结构为上下级,如{"..":"..","a":{"xx":"xx"}}
-
匿名属性和主属性字段,标签无论是否一致,序列化都不会忽略任何字段。
4.2 反序列化
a. 匿名字段无标签
-
当匿名字段没有指定标签时,可解析的
JSON
结构,为:{"..":"..",..}
-
当匿名属性和主属性的字段标签一样时,会优先将值赋给主属性,匿名属性为类型零值。
-
当匿名属性和主属性的字段标签不一样时,会正常解析。
b. 匿名字段有标签
-
当匿名字段指定标签时,可解析的
JSON
结构,为:{"..":"..","a":{"xx":"xx"}}
-
匿名属性和主属性字段,标签无论是否一致,反序列化都能正常赋值。
当结构体中嵌套匿名结构体字段时,在进行序列化和反序列时,推荐为匿名字段加上json标签。
相关文章:

Go语言匿名字段使用与注意事项
1. 定义 Go语言支持一种特殊的字段只需要提供类型而不需要写字段名的字段,称之为匿名字段或者嵌套字段。 所谓匿名字段实际上是一种结构体嵌套的方式,所以也可以称作嵌套字段。 这种方式可以实现组合复用,即通过匿名字段,结构体…...
2024最新!!Java后端面试题(2)看这一篇就够了
hello uu们 感谢收看!!!!我最近听了一首歌《21》,真的很感慨,马上步入20的我也感觉时间真的飞快...望大家都能过上理想的生活,不负内心的所托...现在口语化更新答案,让大家更加模拟的…...

超好用的10款视频剪辑软件,从入门到精通
视频剪辑软件哪款比较好呢?无论是专业制作团队、自媒体创作者,还是家庭用户,一款好用的视频剪辑软件都能极大地提升创作效率和作品质量。以下是十款备受推崇的视频剪辑软件,分别从适用人群、易用程度和功能特点进行介绍。 1.影忆…...
python股票因子,交易所服务器宕机,量化交易程序怎么应对
炒股自动化:申请官方API接口,散户也可以 python炒股自动化(0),申请券商API接口 python炒股自动化(1),量化交易接口区别 Python炒股自动化(2):获取…...

瑞芯微RK3566鸿蒙开发板Android11修改第三方输入法为默认输入法
本文适用于触觉智能所有支持Android11系统的开发板修改第三方输入法为默认输入法。本次使用的是触觉智能的Purple Pi OH鸿蒙开源主板,搭载了瑞芯微RK3566芯片,类树莓派设计,是Laval官方社区主荐的一款鸿蒙开发主板。 一、安装输入法并查看输入…...
使用nest+typeorm框架写数据库导致mysql的binlog暴增记录
这 两天用nesttypeorm写了一个商城,上线后mysql日志binlog两天就达到了10几个G,排查结果如下: 有个功能是定时遍历所有未签收的订单,看看是否到了自动签收时间,如果到了,就把订单状态设置成已签收。 代码…...

组合逻辑元件与时序逻辑元件
组合逻辑元件和时序逻辑元件都是数字电路中的基本构建块,但它们在功能和结构上存在显著差异。 1. 组合逻辑元件: 内容: 组合逻辑元件的输出仅取决于当前的输入,而与之前的输入无关。 它们没有记忆功能。 常见的组合逻辑元件包括: 与门 (AND…...

天龙八部怀旧单机微改人面桃花+安装教程+GM工具+虚拟机一键端
今天给大家带来一款单机游戏的架设:天龙八部怀旧单机微改人面桃花。 另外:本人承接各种游戏架设(单机联网) 本人为了学习和研究软件内含的设计思想和原理,带了架设教程仅供娱乐。 教程是本人亲自搭建成功的…...
docker管理
拉取容器镜像 docker pull 镜像名:镜像版本查看镜像 docker images查看容器列表 # 查看正在运行的容器 docker ps # 查看全部的容器(包括停止的容器) docker ps -a进入容器 docker exec -it 容器id /bin/bash停止容器 docker stop 容器id运行容器 docker start 容器id删除…...

electron教程(三)窗口设置
在main.js文件中,创建窗口时会设置窗口的大小,其实还有很多其他属性,可以根据实际需求选择设置,但部分属性存在局限性,官网也有明确告知:自定义窗口 | Electron (electronjs.org) 项目文件目录如下&#x…...

图像增强论文精读笔记-Deep Retinex Decomposition for Low-Light Enhancement(Retinex-Net)
1. 论文基本信息 论文标题:Deep Retinex Decomposition for Low-Light Enhancement 作者:Chen Wei等 发表时间和期刊:2018;BMVC 论文链接:https://arxiv.org/abs/1808.04560 2. 研究背景和动机 低光照条件下拍摄的…...

2024年配置YOLOX运行环境+windows+pycharm24.0.1+GPU
1.配置时间2024/9/25 2.Anaconda-python版本3.7,yolox版本0.2.0 YOLOX网址: https://github.com/Megvii-BaseDetection/YOLOX 本人下载的这个版本 1.创建虚拟环境 conda create -n yolox37 python37 激活 conda activate yolox37 2.安装Pytorch cuda等&…...

vue-i18n在使用$t时提示类型错误
1. 问题描述 Vue3项目中,使用vue-i18n,在模版中使用$t时,页面可以正常渲染,但是类型报错。 相关依赖版本如下: "dependencies": {"vue": "^3.4.29","vue-i18n": "^9.1…...
大厂面试真题-什么是CAS单点登录?什么原理
CAS(Central Authentication Service,中央认证服务)单点登录(SSO,Single Sign-On)的原理主要基于统一的认证机制和票据验证过程,使得用户只需在多个相互信任的应用系统中登录一次,即…...

用Java提取PDF表格到文本、CSV、Excel工作表
如何精准地提取PDF格式中嵌入的表格数据,并将其无缝转换为更加易于分析和操作的形式,如纯文本、CSV文件或Excel工作表,是一项重要的文档处理技巧。使用Java,我们可以简单地实现这一过程。本文将介绍如何利用Java从PDF文档提取表格…...
OpenCV视频I/O(10)视频采集类VideoCapture之从视频流中检索一帧图像函数 retrieve()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 解码并返回已抓取的视频帧。 cv::VideoCapture::retrieve() 是 VideoCapture 类的一个成员函数,用于从视频流中检索一帧图像。 retr…...

【RocketMQ】SpringBoot整合RocketMQ
🎯 导读:本文档详细介绍了如何在Spring Boot应用中集成Apache RocketMQ,并实现消息生产和消费功能。首先通过创建消息生产者项目,配置POM文件引入RocketMQ依赖,实现同步消息发送,并展示了如何发送普通字符串…...
mysql replace无法替换空格?如何解决
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互…...

Redis篇(环境搭建)
目录 一、安装包 1. Windows版下载地址 2. Linux版下载地址 二、安装Redis 1. 在Linux中安装Redis 2. 在Windows中安装Redis 3. 细节问题 三、Redis服务启动 1. 默认启动 2. 指定配置启动 3. 开机自启 四、Redis服务停止 1. Linux系统中启动和停止Redis 2. Window…...

【C++题目】7.双指针_和为 s 的两个数字
文章目录 题目链接:题目描述:解法C 算法代码:图解 题目链接: LCR 179.查找总价格为目标值的两个商品 题目描述: 解法 解法一(暴力解法,会超时) 两层 for 循环列出所有两个数字的组合…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...