Go语言struct要使用 tags的原因解析

目录
  • struct tags 的使用
  • 使用反引号
  • 避免使用空格
  • 避免重复
  • 使用标准化的 tag 名称
  • 多个 tag 值
  • struct tags 的原理
  • struct tags 的优势
  • 常用的 struct tags

在 Go 语言中,struct 是一种常见的数据类型,它可以用来表示复杂的数据结构。在 struct 中,我们可以定义多个字段,每个字段可以有不同的类型和名称。

除了这些基本信息之外,Go 还提供了 struct tags,它可以用来指定 struct 中每个字段的元信息。

在本文中,我们将探讨为什么 Go 语言中需要使用 struct tags,以及 struct tags 的使用场景和优势。

struct tags 的使用

struct tags 使用还是很广泛的,特别是在 json 序列化,或者是数据库 ORM 映射方面。

在定义上,它以 key:value 的形式出现,跟在 struct 字段后面,除此之外,还有以下几点需要注意:

使用反引号

在声明 struct tag 时,使用反引号 ` 包围 tag 的值,可以防止转义字符的影响,使 tag 更容易读取和理解。例如:

type User struct {
    ID    int    `json:"id" db:"id"`
    Name  string `json:"name" db:"name"`
    Email string `json:"email" db:"email"`
}

避免使用空格

在 struct tag 中,应该避免使用空格,特别是在 tag 名称和 tag 值之间。使用空格可能会导致编码或解码错误,并使代码更难以维护。例如:

// 不规范的写法
type User struct {
    ID    int    `json: "id" db: "id"`
    Name  string `json: "name" db: "name"`
    Email string `json: "email" db: "email"`
}

// 规范的写法
type User struct {
    ID    int    `json:"id" db:"id"`
    Name  string `json:"name" db:"name"`
    Email string `json:"email" db:"email"`
}

避免重复

在 struct 中,应该避免重复使用同一个 tag 名称。如果重复使用同一个 tag 名称,编译器可能会无法识别 tag,从而导致编码或解码错误。例如:

// 不规范的写法
type User struct {
    ID    int    `json:"id" db:"id"`
    Name  string `json:"name" db:"name"`
    Email string `json:"email" db:"name"`
}

// 规范的写法
type User struct {
    ID    int    `json:"id" db:"id"`
    Name  string `json:"name" db:"name"`
    Email string `json:"email" db:"email"`
}

使用标准化的 tag 名称

为了使 struct tag 更加标准化和易于维护,应该使用一些标准化的 tag 名称。

例如,对于序列化和反序列化,可以使用 jsonxmlyaml 等;对于数据库操作,可以使用 db

type User struct {
    ID       int    `json:"id" db:"id"`
    Name     string `json:"name" db:"name"`
    Password string `json:"-" db:"password"` // 忽略该字段
    Email    string `json:"email" db:"email"`
}

其中,Password 字段后面的 - 表示忽略该字段,也就是说该字段不会被序列化或反序列化。

多个 tag 值

如果一个字段需要指定多个 tag 值,可以使用 , 将多个 tag 值分隔开。例如:

type User struct {
    ID        int    `json:"id" db:"id"`
    Name      string `json:"name" db:"name"`
    Email     string `json:"email,omitempty" db:"email,omitempty"`
}

其中 omitempty 表示如果该字段值为空,则不序列化该字段。

struct tags 的原理

Go 的反射库提供了一些方法,可以让我们在程序运行时获取和解析结构体标签。

介绍这些方法之前,先来看看 reflect.StructField ,它是描述结构体字段的数据类型。定义如下:

type StructField struct {
    Name      string      // 字段名
    Type      Type        // 字段类型
    Tag       StructTag   // 字段标签
}

结构体中还有一些其他字段,被我省略了,只保留了和本文相关的。

在结构体的反射中,我们经常使用 reflect.TypeOf 获取类型信息,然后使用 Type.FieldType.FieldByName() 获取结构体字段的 reflect.StructField,然后根据 StructField 中的信息做进一步处理。

例如,可以通过 StructField.Tag.Get 方法获取结构体字段的标签值。

下面看一段代码:

package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

type Manager struct {
    Title string `json:"title"`
    User
}

func main() {
    m := Manager{Title: "Manager", User: User{Name: "Alice", Age: 25}}

    mt := reflect.TypeOf(m)

    // 获取 User 字段的 reflect.StructField
    userField, _ := mt.FieldByName("User")
    fmt.Println("Field 'User' exists:", userField.Name, userField.Type)

    // 获取 User.Name 字段的 reflect.StructField
    nameField, _ := userField.Type.FieldByName("Name")
    tag := nameField.Tag.Get("json")
    fmt.Println("User.Name tag:", tag)
}

运行以上代码,输出结果如下:

Field 'User' exists: User {string int}
User.Name tag: "name"

struct tags 的优势

使用 struct tag 的主要优势之一是可以在运行时通过反射来访问和操作 struct 中的字段。

比如在 Go Web 开发中,常常需要将 HTTP 请求中的参数绑定到一个 struct 中。这时,我们可以使用 struct tag 指定每个字段对应的参数名称、验证规则等信息。在接收到 HTTP 请求时,就可以使用反射机制读取这些信息,并根据信息来验证参数是否合法。

另外,在将 struct 序列化为 JSON 或者其他格式时,我们也可以使用 struct tag 来指定每个字段在序列化时的名称和规则。

此外,使用 struct tag 还可以提高代码的可读性和可维护性。在一个大型的项目中,struct 中的字段通常会包含很多不同的元信息,比如数据库中的表名、字段名、索引、验证规则等等。

如果没有 struct tag,我们可能需要将这些元信息放在注释中或者在代码中进行硬编码。这样会让代码变得难以维护和修改。而使用 struct tag 可以将这些元信息与 struct 字段紧密关联起来,使代码更加清晰和易于维护。

常用的 struct tags

在 Go 的官方 wiki 中,有一个常用的 struct tags 的库的列表,我复制在下面了,感兴趣的同学可以看看源码,再继续深入学习。

Tag Documentation
xml https://pkg.go.dev/encoding/xml
json https://pkg.go.dev/encoding/json
asn1 https://pkg.go.dev/encoding/asn1
reform https://pkg.go.dev/gopkg.in/reform.v1
dynamodb https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/dynamodbattribute/#Marshal
bigquery https://pkg.go.dev/cloud.google.com/go/bigquery
datastore https://pkg.go.dev/cloud.google.com/go/datastore
spanner https://pkg.go.dev/cloud.google.com/go/spanner
bson https://pkg.go.dev/labix.org/v2/mgo/bsonhttps://pkg.go.dev/go.mongodb.org/mongo-driver/bson/bsoncodec
gorm https://pkg.go.dev/github.com/jinzhu/gorm
yaml https://pkg.go.dev/gopkg.in/yaml.v2
toml https://pkg.go.dev/github.com/pelletier/go-toml
validate https://github.com/go-playground/validator
mapstructure https://pkg.go.dev/github.com/mitchellh/mapstructure
parser https://pkg.go.dev/github.com/alecthomas/participle
protobuf https://github.com/golang/protobuf
db https://github.com/jmoiron/sqlx
url https://github.com/google/go-querystring
feature https://github.com/nikolaydubina/go-featureprocessing

以上就是本文的全部内容,如果觉得还不错的话欢迎点赞,转发和关注,感谢支持。

参考文章:

https://github.com/golang/go/wiki/Well-known-struct-tags

到此这篇关于为什么 Go 语言 struct 要使用 tags的文章就介绍到这了,更多相关Go 语言 struct 要使用 tags内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • golang如何使用struct的tag属性的详细介绍

    从一个例子说起 我们经常会碰到下面格式的struct定义: type Person struct { Name string `json:"name"` Age int `json:"age"` } 这个struct定义一个叫做Person的类型,包含两个域Name和Age:但是在域的后面有神奇的 json:"name" ,这个用来干什么用?这篇文章试图来解释这个问题. 当golang的对象需要和json做转换的时候,我们就经常用到这个特性. 有

  • 详解Angular 中 ngOnInit 和 constructor 使用场景

    1. constructor constructor应该是ES6中明确使用constructor来表示构造函数的,构造函数使用在class中,用来做初始化操作.当包含constructor的类被实例化时,构造函数将被调用. 来看例子: class AppComponent { public name: string; constructor(name) { console.log('Constructor initialization'); this.name = name; } } let a

  • golang struct json tag的使用以及深入讲解

    目录 一.sturct json tag的使用 1.tag格式说明 2.具体使用格式说明 二.源码角度的设计处理过程 1.typeFields 2.encode 三.总结 一.sturct json tag的使用 1.tag格式说明 struct json tag主要在struct与json数据转换的过程(Marshal/Unmarshal)中使用. json的tag格式如下: Key type  `json:"name,opt1,opt2,opts..."` 说明: 变量必须是可导出

  • golang中json和struct的使用说明

    1.返回json响应结果 在struct的字段后面加入json:"key"可以进行json格式输出,其中key为json的键名 type SuccessResponse struct { Code int `json:"code"` Msg string `json:"msg"` Data interface{} `json:"data"` } func SuccessRsp(ctx *gin.Context, data in

  • golang中struct和interface的基础使用教程

    前言 本文主要给大家介绍了关于golang中struct和interface的相关内容,是属于golang的基本知识,下面话不多说了,来一起看看详细的介绍吧. struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套:go中的struct类型理解为类,可以定义方法,和函数定义有些许区别:struct类型是值类型. struct定义 type User struct { Name string Age int32 mess string } var user User

  • Angular2中constructor和ngOninit的使用讲解

    目录 constructor和ngOninit的使用 区别 适用场景 ngOnInit函数学习小记 例子 constructor和ngOninit的使用 Angular中根据适用场景定义了很多生命周期函数,其本质上是事件的响应函数,其中最常用的就是ngOnInit.在TypeScript或ES6中还存在着名为constructor的构造函数,开发过程中经常会混淆二者,两者在含义上有部分重复,下面主要解析一下它们的区别和各自的使用场景. 区别 constructor:Es6引入类的概念后出来的东西

  • Go 使用Unmarshal将json赋给struct出错的原因及解决

    例如: 将json: { "name": "Laura" "age": "18" } 赋给struct: type PersonalInfo struct { Name string `json:"name"` Age string `json:"age"` } 用语句: person := PersonalInfo{} err := json.Unmarshal(json, &

  • Go语言struct要使用 tags的原因解析

    目录 struct tags 的使用 使用反引号 避免使用空格 避免重复 使用标准化的 tag 名称 多个 tag 值 struct tags 的原理 struct tags 的优势 常用的 struct tags 在 Go 语言中,struct 是一种常见的数据类型,它可以用来表示复杂的数据结构.在 struct 中,我们可以定义多个字段,每个字段可以有不同的类型和名称. 除了这些基本信息之外,Go 还提供了 struct tags,它可以用来指定 struct 中每个字段的元信息. 在本文中

  • C语言 struct结构体超详细讲解

    目录 一.本章重点 二.创建结构体 三.typedef与结构体的渊源 四.匿名结构体 五.结构体大小 六.结构体指针 七.其他 一.本章重点 创建结构体 typedef与结构体的渊源 匿名结构体 结构体大小 结构体指针 其他 二.创建结构体 先来个简单的结构体创建 这就是一个比较标准的结构体 struct people { int age; int id; char address[10]; char sex[5]; };//不要少了分号. 需要注意的是不要少了分号. 那么这样创建结构体呢? s

  • C语言struct结构体介绍

    目录 struct struct的嵌套 实验 struct C 语言没有其他语言的对象(object)和类(class)的概念,struct 结构很大程度上提供了对象和类的功能. 下面是struct自定义数据类型的一个例子. struct tag { member-list member-list member-list ... } variable-list; 声明了数据类型car和该类型的变量car. struct car { char *name; float price; int spe

  • Python Ruby 等语言弃用自增运算符原因剖析

    目录 正文 为什么会存在自增自减运算符? 起源 提高程序运行效率?原子性? 简洁性 为什么一些现代编程语言取消了自增自减运算符? 副作用 迭代器替代了大多数自增自减运算符的使用场景 赋值语句返回值的消失 想要获取下标怎么办? 运算符重载带来歧义 一些其他的讨论 总结 正文 许多人也许会注意到一个现象,那就是在一些现代编程语言(当然,并不是指“最近出现”的编程语言)中,自增和自减运算符被取消了.也就是说,在这些语言中不存在i++或j--这样的表达,而是只存在i += 1或j -= 1这样的表达方式

  • Python不支持 i ++ 语法的原因解析

    简要讨论为什么它不提供++作为运算符 正常情况下,当有人问起++原因而不是Python中的运算符时,这一行引起了我的注意. 如果您想知道最初的原因,则必须翻阅旧的Python邮件列表,或询问那里的某个人(例如Guido)〜通过stackoverflow 这迫使我像上图一样思考. 真的我必须问Guido原因吗? 好的,也许但是在那之前,我应该尝试一下,这促使我写这篇文章 在C / C ++ / Java之类的语言中,对整数变量进行自增或自减运算是标准的,可以分为前缀运算(++ i和–i)和后缀运算

  • Python 的 f-string 可以连接字符串与数字的原因解析

    本文出自"Python为什么"系列,归档在 Github 上:https://github.com/chinesehuazhou/python-whydo 毫无疑问,Python 是一门强类型语言.强类型语言.强类型语言!(关于强弱类型话题,推荐阅读这篇 技术科普文) 这就意味着,不同类型的对象通常需要先做显式地类型转化, 然后才能进行某些操作. 下面以字符串和数字为例,看看强行操作会产生什么结果: >>> "Python猫" + 666 Trac

  • C语言结构体计算内存占用问题解析

        c语言中结构体使用是非常广泛的,但是结构体有一个问题,就是如果开头的字段属性是字符类型(char),紧跟着的是其他类型,比如整型.长整型.双精度.浮点型,这时候结构体的大小会发生改变,下面给出一个示例: #include <stdio.h> struct person{ char sex; int age; char name[8]; }; int main() { printf("sizeof(person) = %d\n",sizeof(struct perso

  • C语言数据结构之vector底层实现机制解析

    目录 一.vector底层实现机制刨析 二.vector的核心框架接口的模拟实现 1.vector的迭代器实现 2.reserve()扩容 3.尾插尾删(push_back(),pop_back()) 4.对insert()插入时迭代器失效刨析 5.对erase()数据删除时迭代器失效刨析 一.vector底层实现机制刨析 通过分析 vector 容器的源代码不难发现,它就是使用 3 个迭代器(可以理解成指针)来表示的: 其中statrt指向vector 容器对象的起始字节位置: finish指

  • Redis 的查询很快的原因解析及Redis 如何保证查询的高效

    目录 Redis如何保证高效的查询效率 为什么Redis比较快 Redis中的数据结构 1.简单动态字符串 2.链表 3.字典 4.跳表 5.整数数组 6.压缩列表 为什么单线程还能很快 基于多路复用的高性能I/O模型 单线程处理IO请求性能瓶颈 总结 参考 Redis 如何保证高效的查询效率 为什么 Redis 比较快 Redis 中的查询速度为什么那么快呢? 1.因为它是内存数据库: 2.归功于它的数据结构: 3.Redis 中是单线程: 4.Redis 中使用了多路复用. Redis 中的

  • go语言通过结构体生成json示例解析

    目录 通过结构体生成json 通过map生成json json解析到结构体 json解析到map 通过结构体生成json buf, err := json.MarshalIndent(s, "", " ") //格式化编码 package main import ( "encoding/json" "fmt" ) //成员变量名首字母必须大写 type IT struct { Company string `json:&quo

  • C语言驱动开发内核枚举IoTimer定时器解析

    目录 正文 枚举Io定时器过程 GetIoInitializeTimerAddress()函数 nt!IoInitializeTimer+0x5d 输出位置 特征搜索部分 IO_TIMER结构体定义 正文 今天继续分享内核枚举系列知识,这次我们来学习如何通过代码的方式枚举内核IoTimer定时器,内核定时器其实就是在内核中实现的时钟,该定时器的枚举非常简单,因为在IoInitializeTimer初始化部分就可以找到IopTimerQueueHead地址,该变量内存储的就是定时器的链表头部.枚举

随机推荐

其他