专栏首页子翔的专栏Go语言之旅:Struct Tag的介绍及用法
原创

Go语言之旅:Struct Tag的介绍及用法

前言

在学习之初我们会发现创建一个struct来表示一个model是很正常的事情,特别是在序列化json的时候,在这个学些过程中我们会发现一个内容就是跟在每个struct成员后面的用单引号标示的内容,我们称之为Struct Tag,它其实有很灵活的用途,但它的用途可以归纳为标记和扩展。

正题

为了很好的理解Tag的一些用法,将从最原理的一些部分开始,比如它是怎么定义的,怎么工作的,怎么使用的,到最后我们要如何去用。其中我会拿开源项目gorm 来做事例,因为ORM最重要的部分影射和定义就是靠Tag来完成的

Struct Tag的组成部分

Struct Tag是存在于Struct下面成员的附加属性,它的定义永远都是以key-value的形式出现的,多个定义的情况下以空格分割

Struct Tag的例子

在StructTag的应用中,使用最多的就是json的序列化了,序列化的包会读取tag内容,对json结果进行重命名输出,那么它是怎么读取到的呢?我们需要先了解这个Tag在整个语言段落处于什么层次。

读取并处理Tag的内容

解析方式

如果直接拿到了这串文本我们可以这样做

package main

import (
	"fmt"
	"reflect"
)

func main() {
    // 使用reflect.StructTag解析这段文本的tag内容
	tag := reflect.StructTag(`json:"foo,omitempty" xml:"foo"`)
    // 直接使用Get获取json定义
	value := tag.Get("json")
	fmt.Printf("value: %q\n", value)
}

比如定义了一个车辆的model,给它增加了一些json的tag项

type Vehicle struct {
	ID             int    `json:"id"`
	CityName       string `json:"city_name"`
	Provider       string `json:"provider"`
	PlateNumber    string `json:"plate_number"`
	MaxPeople      int64  `json:"max_people"`
	PowerType      string `json:"power_type"`
}

通过reflect反射机制也可以获取到这个结构体的tag内容,比如以下代码

reflectType := reflect.ValueOf(Vehicle{}).Type()
	fmt.Printf("fields number: %v\n", reflectType.NumField())

	for i := 0; i < reflectType.NumField(); i++ {
		fmt.Printf("%v", reflectType.Field(i).Name)
		fmt.Printf("  tag:%v\n", reflectType.Field(i).Tag.Get("json"))
	}

执行过后的结果,清清楚楚的取得了tag的内容

image.png

实际应用-GORM

实际应用场景中,我们除了经常对json序列化用到以外,还经常在使用开源组件中遇到,比如说GORM,它是一个典型的应用场景,因为它需要对实体中的每一项进行额外定义,以达到映射和功能标记的作用。虽然查看它的官方文档,我们可以很详细的知道用法,我们也可以直接跳到GORM的Github仓库去了解它是如何运作的。

进入model_struct.go文件,在GetModelStruct方法中我们可以看到

    //通过反射获取类型定义
reflectType := reflect.ValueOf(scope.Value).Type()
	for reflectType.Kind() == reflect.Slice || reflectType.Kind() == reflect.Ptr {
		reflectType = reflectType.Elem()
	}
	
	.......

    // 对每一项进行tag的获取和逻辑
    // Get all fields
	for i := 0; i < reflectType.NumField(); i++ {
		if fieldStruct := reflectType.Field(i); ast.IsExported(fieldStruct.Name) {

		......

		field.TagSettingsGet("PRIMARY_KEY")
		......
		if value, ok := field.TagSettingsGet("COLUMN"); ok {
			field.DBName = value
		} else {
			field.DBName = ToColumnName(fieldStruct.Name)
		}
		......

整个GORM的核心就在于根据数据库映射的model,然后再根据定义好的内部结构来拼接sql达到ORM的效果。这里跟dotnet里面的Attribute的作用类似,我们做了一些Attribute也可以通过反射获取信息。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • go 学习笔记之有意思的变量和不安分的常量

    首先希望学习 Go 语言的爱好者至少拥有其他语言的编程经验,如果是完全零基础的小白用户,本教程可能并不适合阅读或尝试阅读看看,系列笔记的目标是站在其他语言的角度...

    雪之梦技术驿站
  • Docker logs 查看实时日志(日志最后的N行、某刻后日志)

    当我们输入 docker logs 的时候会转化为 Docker Client 向 Docker Daemon 发起请求,。

    微风-- 轻许--
  • golang 中获取字符串个数

    在 golang 中不能直接用 len 函数来统计字符串长度,查看了下源码发现字符串是以 UTF-8 为格式存储的,说明 len 函数是取得包含 byte 的个...

    用户1324558
  • 【深度知识】Golang协程调度:协程状态

    在讲解操作系统进程调度的部分时,几乎所有的书籍都会先列出一张进程的状态迁移图,通过状态图,能很清晰的把进程调度的每个环节串联起来,方便理解。

    辉哥
  • Go 语言错误及异常处理篇(二):defer 语句

    Go 语言中的类没有构造函数和析构函数的概念,处理错误和异常时也没有提供 try...catch...finally 之类的语法,那当我们想要在某个资源使用完毕...

    学院君
  • 3 种方法爬一个网页上的所有图片

    然后等谷歌浏览器下载完成,接着你会看得一个 html 文件,当然,还有一个和这个 html 文件命名相同的文件夹。

    伪君子
  • angular-ui-router 多视图views

    UI Router 中有三种方式激活一个路由: (1)$state.go():优先级较高的便利方式 (2)ui-sref:点击包含此指令跳转 (3)...

    奋飛
  • Android集成Dagger2

    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //添加apt命令

    longzeqiu
  • 一个网络请求的冒险之旅

    对于互联网,人们总是高谈阔论,却很少有人愿意去了解电脑、手机、电视这些设备到底是如何被“连接”起来的。

    佛系编程人
  • 【深度知识】Go语言:启动和内存分配初始化

    本文是 Golang 内部机制探索系列博客的后续。这个系列博客的目的是探索 Go 启动过程,这个过程也是理解 Go 运行时(runtime)的关键之处。本文中我...

    辉哥

扫码关注云+社区

领取腾讯云代金券