Golang 中的标签(Tags in Golang)

栏目: Go · 发布时间: 5年前

内容简介:结构体字段的声明可以通过之后放置的文字来标记。标签添加由当前包或外部包使用的元信息。让我们首先回首一下 strcut 声明的样子,然后我们将扔出几个用例,深入研究这个标签。Struct 是一系列字段。每个字段由可选名称和所需类型(T1 域被称为嵌入字段,因为它是用类型声明但没有名称。

结构体字段的声明可以通过之后放置的文字来标记。标签添加由当前包或外部包使用的元信息。让我们首先回首一下 strcut 声明的样子,然后我们将扔出几个用例,深入研究这个标签。

结构体类型(Struct type)

Struct 是一系列字段。每个字段由可选名称和所需类型( 源代码 )组成:

package main
import "fmt"
type T1 struct {
	f1 string
}
type T2 struct {
	T1
	f2     int64
	f3, f4 float64
}
func main() {
	t := T2{T1{"foo"}, 1, 2, 3}
	fmt.Println(t.f1)    // foo
	fmt.Println(t.T1.f1) // foo
	fmt.Println(t.f2)    // 1
}

T1 域被称为嵌入字段,因为它是用类型声明但没有名称。

字段声明可以在 T2 中指定来自第 3 个字段声明的 f3 和 f4 之类的多个标识符。

语言规范声明每个字段声明后面跟着分号,但正如我们上面所见,它可以省略。如果需要将多个字段声明放入同一行(源代码),分号可能很有用( 源代码 ):

package main
import "fmt"
type T struct {
	f1 int64; f2 float64
}
func main() {
	t := T{1, 2}
	fmt.Println(t.f1, t.f2)  // 1 2
}

标签(Tag)

字段声明后面可以跟一个可选的字符串文字(标记),它称为相应字段声明中所有字段的属性(单字段声明可以指定多个标识符)。让我们看看它的实际应用( 源代码 ):

type T struct {
	f1     string "f one"
	f2     string
	f3     string `f three`
	f4, f5 int64  `f four and five`
}

可以使用原始字符串文字或解释的字符串文字,但下面描述的传统格式需要原始字符串文字。 规范 中描述了原始字符串文字和解释字符串文字之间的差异。

如果字段声明包含多个标识符,则标记将附加到字段声明的所有字段(如上面的字段 f4 和 f5)。

反射(Reflection)

标签可通过 reflect 包访问,允许运行时反射( 源代码 ):

package main
import (
	"fmt"
	"reflect"
)
type T struct {
	f1     string "f one"
	f2     string
	f3     string `f three`
	f4, f5 int64  `f four and five`
}
func main() {
	t := reflect.TypeOf(T{})
	f1, _ := t.FieldByName("f1")
	fmt.Println(f1.Tag) // f one
	f4, _ := t.FieldByName("f4")
	fmt.Println(f4.Tag) // f four and five
	f5, _ := t.FieldByName("f5")
	fmt.Println(f5.Tag) // f four and five
}

设置空标记与完全不使用标记的效果相同( 源代码 ):

type T struct {
	f1 string ``
	f2 string
}
func main() {
	t := reflect.TypeOf(T{})
	f1, _ := t.FieldByName("f1")
	fmt.Printf("%q\n", f1.Tag) // ""
	f2, _ := t.FieldByName("f2")
	fmt.Printf("%q\n", f2.Tag) // ""
}

惯用格式(Conventional format)

在提交中引入 “反射:支持多个包使用结构标记” 允许为每个包设置元信息。这提供了简单的命名空间。标签被格式化为键的串联:“值”对。密钥可能是像 JSON 这样的包的名称。对可以选择用空格分隔 - key1: "value1" key2: "value2" key3: "value3" 。 如果使用传统格式,那么我们可以使用 struct tag( StructTag )的两个方法 - Get 或 Lookup。它们允许返回与所需键内部标记相关联的值。

Lookup 函数返回两个值 - 与键关联的值(如果未设置则为空)和 bool,指示是否已找到键( 源代码 ):

type T struct {
	f string `one:"1" two:"2"blank:""`
}
func main() {
	t := reflect.TypeOf(T{})
	f, _ := t.FieldByName("f")
	fmt.Println(f.Tag) // one:"1" two:"2"blank:""
	v, ok := f.Tag.Lookup("one")
	fmt.Printf("%s, %t\n", v, ok) // 1, true
	v, ok = f.Tag.Lookup("blank")
	fmt.Printf("%s, %t\n", v, ok) // , true
	v, ok = f.Tag.Lookup("five")
	fmt.Printf("%s, %t\n", v, ok) // , false
}

Get 方法只是 Lookup 简单的包封装器,它丢弃了 bool 值( 源代码 ):

func (tag StructTag) Get(key string) string {
	v, _ := tag.Lookup(key)
	return v
}

如果标签不是常规模式,则不指定 Get 或 Lookup 的返回值。

即使 tag 是任何字符串值(不管是释义或原始值),只有在双引号( 源代码 )之间包含值时,Lookup 和 Get 方法才会找到 key 的值:

type T struct {
	f string "one:`1`"
}
func main() {
	t := reflect.TypeOf(T{})
	f, _ := t.FieldByName("f")
	fmt.Println(f.Tag) // one:`1`
	v, ok := f.Tag.Lookup("one")
	fmt.Printf("%s, %t\n", v, ok) // , false
}

可以在解释的字符串值中对双引号进行转义( 源代码 ):

type T struct {
	f string "one:\"1\""
}
func main() {
	t := reflect.TypeOf(T{})
	f, _ := t.FieldByName("f")
	fmt.Println(f.Tag) // one:"1"
	v, ok := f.Tag.Lookup("one")
	fmt.Printf("%s, %t\n", v, ok) // 1, true
}

但可读性就要低很多。

结论(Conversion)

将结构体类型转换为其他类型要求底层类型相同,但忽略掉 tag( 源代码 ):

type T1 struct {
	 f int `json:"foo"`
 }
 type T2 struct {
	 f int `json:"bar"`
 }
 t1 := T1{10}
 var t2 T2
 t2 = T2(t1)
 fmt.Println(t2) // {10}

Go 1.8 (提案)中引入了此行为。在 Go 1.7 及更早版本的代码中,可能会抛出编译时错误。

用例(Use cases)

(Un)marshaling

Go 中标签最常见的用途可能是 marshalling 。让我们看一下来自 JSON 包的函数 Marshal 如何使用它( 源代码 ):

import (
	"encoding/json"
	"fmt"
)
func main() {
	type T struct {
	   F1 int `json:"f_1"`
	   F2 int `json:"f_2,omitempty"`
	   F3 int `json:"f_3,omitempty"`
	   F4 int `json:"-"`
	}
	t := T{1, 0, 2, 3}
	b, err := JSON.Marshal(t)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", b) // {"f_1":1,"f_3":2}
}

xml 包也利用了标签 - https://golang.org/pkg/encoding/xml/#MarshalIndent .

ORM

像 GORM 这样的对象关系映射工具,也广泛使用标签 - 例子 .

摘要数据(Digesting forms data)

https://godoc.org/github.com/gorilla/schema

其他(Other)

标签的更多潜在用例,如配置管理,结构的默认值,验证,命令行参数描述等( 众所周知的结构标记列表 )。

Go vet

Go 编译器没有强制执行传统的 struct 标签格式,但是 vet 就是这样做的,所以值得使用它,例如作为 CI 管道的一部分。

package main
type T struct {
	f string "one two three"
}
func main() {}
> Go vet tags.go
tags.go:4: struct field tag `one two three` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair

...

由于 struct 标签,程序员可以从单一来源中受益。Go 是一门实用性语言,所以即使可以使用专用数据结构等其他方式来控制整个过程来解决 JSON/XML 编码,Golang 也能让软件工程师的生活变得更轻松。值得一提的是,标签的长度不受规格的限制。


以上所述就是小编给大家介绍的《Golang 中的标签(Tags in Golang)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

MySQL权威指南

MySQL权威指南

Randy Jay Yarger / 林琪、朱涛江 / 中国电力出版社 / 2003-11-1 / 49.00元

为一种开源数据库,MySQL已经成为最流行的服务器软件包之一。开发人员在其数据库引擎中提供了丰富的特性(只需很少的内存和CPU支持)。 因此,众多Linux和Unix服务器(以及一些Windows服务器)都采用MySQL作为其数据库引擎。由于MySQL作为Web站点后端时速度特别快而且相当方便,所有在目前流行的一个词LAMP(表示Linux、Apache、MySQL和Perl、Python或......一起来看看 《MySQL权威指南》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

SHA 加密
SHA 加密

SHA 加密工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试