Go语言结构体标签与反射(19) | Go主题月

2,434 阅读3分钟

Go语言结构体标签,就是在结构体中的一段字符串,有点像PHP中的注解,有严格的格式要求,通常用于反射包里的方法来访问它,标签用来声明结构体中字段的属性。
我们先举一个例子看结构体标签在json转换中的运用:

package main

import (
	"encoding/json"
	"fmt"
)

type User struct {
	Name   string `json:"username"`
	Age    int    `json:"userage"`
	Salary int    `json:"usersalary"`
}

func main() {
	myself := User{"土味挖掘机", 18, 2000}
	jsondata, err := json.Marshal(myself)
	if err != nil {
		fmt.Println("格式错误")
	} else {
		fmt.Printf("User结构体转json:%s\n", jsondata)
	}
}

输出结果为:

User结构体转json:{"username":"土味挖掘机","userage":18,"usersalary":2000}

可以看出"encoding/json"包的json.Marshal()方法作用就是把结构体转换为json,它读取了User结构体里面的标签,json键值对的键为定义的标签名,结构体的名字起了辅助作用,同时定义了字段数据类型。json.Unmarshal()可以把json字符串转换为结构体,在很多第三方包方法都会读取结构体标签。 注意在标签中的外层符号是键盘Tab键上方的键,不是单引号。

在Gin的文档中可以看到它的身影: image.png 上面的例子中form:"user" 表示是form表单的在参数名为user,binding:"required"表示这个参数是必须的。

可以看出Go语言结构体标签中结构体一个字段可以使用多个标签, 结构体标签需要用反射机制来读取。

Go语言反射

反射通俗来讲就是获取已知变量的类型或者获取已知变量的值,使用realect包的两个方法获取变量的类型和值reflect.ValueOf()和reflect.TypeOf(),我们下面用代码可以展示一下

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Name string `data:"test" test:"haha"`
	Age  string `json:"test"`
}

func main() {
	user := User{"土味", "测试"}
	user_value := reflect.ValueOf(user) //获取变量的值

	user_type := reflect.TypeOf(user) //获取类型
	fmt.Println(user_value, user_type)

	//反射的用法,获取结构体标签的值
	name_data := user_type.Field(0)            //user第一个字段
	name_tag_data := name_data.Tag.Get("data") //获取结构体第一个字段的Tag(标签)里面键为data的值
	fmt.Println("name_data:", name_tag_data)
}

image.png

反射

以上代码实现了通过Go语言的反射获取结构标签的值,通过这个例子是不是想到了前面文章实现的ORM模型方法和数据表结构迁移。我们再简单扩展一下关于反射知识的内容,反射本质是在代码运行的时候尝试获取获取对象的类型信息和内存结构,在Go语言中一般定义变量的时候有一个静态类型,比如,int,string,floast,但是这并不是底层的数据类型,string的底层类型是一个struct,Go语言的数据类型分为静态类型和底层类型,例如:

type MyInt int

var i int
var j MyInt

i和j的底层类型都是int,i的静态类型为int,j的静态类型为MyInt,所以他们不能相等,他们的静态类型不同。
所以一个Go语言变量的有值(value)还有它的静态类型(static type)或者底层类型(concrete type),底层类型也可以叫具体类型,静态类型在编译阶段就已经确定了。反射的具体实现是这个变量在赋值和相应类型的时候会向接口(interface)保存自己的类型信息,type和value用接口(interface)进行转换,上面的代码使用了User的结构体实现了这个转换的接口。