Go语言匿名字段使用与注意事项

1. 定义

Go语言支持一种特殊的字段只需要提供类型而不需要写字段名的字段称之为匿名字段或者嵌套字段

所谓匿名字段实际上是一种结构体嵌套的方式,所以也可以称作嵌套字段

这种方式可以实现组合复用,即通过匿名字段,结构体可以直接访问嵌套结构体的字段和方法,而无需通过字段名或类型进行嵌套。这些方法和属性被称为“提升”的方法和属性。通过类型名称也可以直接访问匿名嵌入字段。

2.代码示例

2.1 简单示例

package main

import (
	"fmt"
)

type Person struct {
	Name  string
	Age   int
	Phone string
}

func (p *Person) playBasketball() {
	fmt.Println("打篮球...")
}

type Employee struct {
	Person
	EmployeeId 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 main

import (
	"fmt"
)

type Person struct {
	Name  string
	Age   int
	Phone string
}

func (p *Person) contact() {
	fmt.Println("Person联系...")
}

// 合同
type Contract struct {
	EmployeeId int
	Phone      string
}

func (p *Contract) contact() {
	fmt.Println("Contract联系...")
}

type Employee struct {
	Person
	EmployeeId int
	Contract
}

// 测试匿名字段
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 main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	// 指定json标签时,序列化的key为标签值:name
	Name string `json:"name"`
	// 不指定序列化标签时,key为原属性:Age
	Age 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 main

import (
	"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 main

import (
	"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 main

import (
	"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 main

import (
	"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 main

import (
	"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 main

import (
	"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标签。

图片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883708.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

说说海外云手机的自动化功能

在全球社交媒体营销中,通过自动化功能,企业不再需要耗费大量时间和精力手动监控和操作每台设备。这意味着,企业可以显著提升效率、节省成本,同时减少对人力资源的依赖。那么,海外云手机的自动化功能具体能带来哪些优势…

使用ucharts写的小程序页面柱状图上方没有数字

使用uCharts官网 - 秋云uCharts跨平台图表库写的柱状图如何让柱子上放没有数据 更改前 更改后 使用uCharts官网 - 秋云uCharts跨平台图表库 写的小程序图表,无论是柱状图还是折线图添加一个 dataLabel: false, // 不显示数据 九可以实现不显示数据 const opts …

IDEA Dependency Analyzer 分析 maven 项目包的依赖

一、场景分析 javax.validation 是我们 SpringMVC 常用的数据校验框架。但是 javax.validation 是一个规范(Java Bean Validation,简称 JSR 380),它并没有具体的实现,它的常用实现,是hibernate-validator。…

数据结构 - 数组

今天我们将开始第一个数据类型-数组的学习。 经常会看到这样的问题,怎么学习数据结构,我的答案是搞清楚具体数据结构对应的抽象数据类型ADT,抛开语言层面自带的数据类型,然后自己从头 实现一遍。 其实数据结构没多复杂&#xff…

基于SSM+小程序的医院核酸检测服务管理系统(医院2)(源码+sql脚本+视频导入教程+文档)

👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM小程序的医院核酸检测服务管理系统实现了管理员、用户管理、普通管理员、医护人员。 1、管理员实现了首页、用户管理、医护人员管理、普通管理员、通知公告管理、疫苗接种管理、核…

华为GaussDB数据库(单机版)在ARM环境下的安装指南

一、软件版本 机器配置:8核16G,CPU: Huawei Kunpeng 920 2.9GHz操作系统:EulerOS 2.8 64bit with ARM数据库版本:GaussDB Kernel 505.1.0 build 44f4fa53 二、部署流程 2.1 新建用户 ① 以omm用户为例,添加一个omm用…

11. Map和Set

一、二叉搜索树 1. 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值若它的右子树不为空,则右子树上所有节点的值都大于根…

IvorySQL 3.4 来了

9 月 26 日,IvorySQL 3.4 发版。本文将带大家快速了解新版本特性。 IvorySQL 3.4 发版说明 IvorySQL 3.4 基于 PostgreSQL 16.4,修复了多个问题,并增强多项功能。 PostgreSQL 16.4 的变更 在未经授权时防止 pg_dump 执行,并引入一…

Qt-QTableWidget多元素控件(37)

目录 描述 QTableWidget 方法 QTableWidgetItem 信号 QTableWidgetItem 方法 使用 图形化界面操作 代码操作 描述 这是一个表格控件,表格中的每一个单元格,都是一个 QTableWidgetItem 对象 QTableWidget 方法 item(int row,int column)根据⾏数…

Snap AR眼镜Spectacles的技术揭秘:通往真正AR体验的道路

Snap公司自2010年成立以来,一直致力于探索增强现实(AR)技术的边界。经过多年的研发与迭代,Snap终于在最新一代Spectacles中实现了重大突破,为用户带来了前所未有的沉浸式AR体验。本文将深入探讨Spectacles的发展历程、…

【docker】debian中配置docker(2024年9月)

首先Follow了一下菜鸟教程,然后遇到了curl的问题。 curl存在的问题 参见这篇文章。其中用到了vim进行编辑,笔者的环境是windows10putty,vim的粘贴操作参考这篇文章。 修改之后的curl没有问题了,成功把脚本下载下来了。 但是在…

即插即用篇 | DenseNet卷土重来! YOLOv8 引入全新密集连接卷积网络 | ECCV 2024

本改进已同步到YOLO-Magic框架! 本文重新审视了密集连接卷积网络(DenseNets),并揭示了其在主流的ResNet风格架构中被低估的有效性。我们认为,由于未触及的训练方法和传统设计元素没有完全展现其能力,DenseNets的潜力被忽视了。我们的初步研究表明,通过连接实现的密集连接…

工作安排 - 华为OD统一考试(E卷)

2024华为OD机试(C卷+D卷)最新题库【超值优惠】Java/Python/C++合集 题目描述 小明每周上班都会拿到自己的工作清单,工作清单内包含n项工作,每项工作都有对应的耗时时长(单位h)和报酬,工作的总报酬为所有已完成工作的报酬之和。那么请你帮小明安排一下工作,保证小明在指定…

基于微信小程序的智能汽车充电站系设计与实现(源码+定制+文档)

博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…

Java线程池和原子性

文章目录 前言1 线程池1.1 线程池概述1.1.1 线程池存在的意义1.1.2 Executors默认线程池 1.2 线程状态介绍1.2.1 线程状态源码1.2.2 线程状态含义1.2.3 线程状态转换图 2 原子性2.1 volatile关键字2.2 synchronized解决2.3 原子性2.4 AtomicInteger类2.5 悲观锁和乐观锁 前言 …

代码随想录算法训练营第56天 | 1、冗余连接,2、冗余连接II

目录 1、冗余连接 2、冗余连接II 1、冗余连接 题目描述 有一个图,它是一棵树,他是拥有 n 个节点(节点编号1到n)和 n - 1 条边的连通无环无向图(其实就是一个线形图),如图: 现在在…

JavaScript 学习

一、输出 为方便调试可以输出内容&#xff0c;但是用户是看不到的。要在开发者模式中看。 console . log ( "Hello" )&#xff1b; 二、外部文件引用 可以直接在html中写JS <head> <meta charset"utf-8"> <script> console.log("he…

RFID手持机——物联网时代的核心工具

一、行业背景 在当今物联网技术高速发展的时代&#xff0c;RFID技术作为核心的数据采集与识别手段&#xff0c;在物流、仓储、资产管理等众多领域发挥着至关重要的作用。以物流行业为例&#xff0c;利用RFID技术能够对货物进行全程精准跟踪&#xff0c;从入库、存储、搬运到出…

每日OJ题_牛客_NC40链表相加(二)_链表+高精度加法_C++_Java

目录 牛客_NC40链表相加&#xff08;二&#xff09;_链表高精度加法 题目解析 C代码 Java代码 牛客_NC40链表相加&#xff08;二&#xff09;_链表高精度加法 链表相加(二)_牛客题霸_牛客网 题目解析 模拟⾼精度加法的过程&#xff0c;只不过是在链表中模拟。 C代码 /*…

buuctf [ACTF2020 新生赛]Include

学习笔记。 开启靶机。 进入靶场&#xff1a; 我们跟进 tips瞅瞅&#xff1a; 额&#xff0c;纯小白&#xff0c;能想到的就是先F12看看&#xff0c;在CTRLu、以及抓包。 得&#xff0c;不会了&#xff0c;看wp呗&#xff0c;不会死磕没脑子0,0&#xff1f; 参考&#xff1a;…