Golang Tag 汇总

2021/05/17 golang tag 汇总 共 4207 字,约 13 分钟

Tag

在 Go 的 struct 定义中,有时需要对某个字段添加额外的元信息,这就需要用到 field tag,即成员标签变量。

Json Tag

示例

type peerInfo struct {
	HTTPPort int    `json:"http_port"`
	TCPPort  int    `json:"tcp_port"`
	versiong string `json:"versiong"`
}

Encode 和 Decode

Encode

要把 golang 的数据结构转换成 JSON 字符串(encode),可以使用 Marshal 函数

func Marshal(v interface{}) ([]byte, error)
Decode

相对应的,要把 JSON 数据转换成 Go 类型的值(Decode), 可以使用 json.Unmarshal。

func Unmarshal(data []byte, v interface{}) error

data 中存放的是 JSON 值,v 会存放解析后的数据,所以必须是指针,可以保证函数中做的修改能保存下来

更多控制

Json Tag 控制字段有三种:

  • -:不要解析这个字段
  • omitempty:当字段为空(默认值)时,不要解析这个字段。比如 false、0、nil、长度为 0 的 array,map,* * slice,string
  • FieldName:当解析 json 的时候,使用这个名字
  • -:忽略字段输出

Xml Tag

导包import "encoding/xml"

解析和读取规则

golang 对 xml 的解析和读取是通过 stuct 和 refect 实现的,对于 struct 中的 tag 以什么方式对应到 xml 的元素上,golang 的文档中做了如下描述:

1、结构体中的 XMLName 字段或者类型为 xml.Name 的字段,会被删除.使用此字段 tag 上定义的属性进行解析
2、结构体 tag 中”-” 在解析过程中会忽略结构体中的这个字段
3、结构体 tag 中”name,attr” 使用 name 作输出为 xml 属性,对应字段值作为属性值
4、结构体 tag 中”,attr” 使用字段名作为 xml 属性,字段值作为 xml 属性值
5、结构体 tag 中”,chardata” 不作为 xml 的节点输出,把该字段对应的值作为字符输出
6、结构体 tag 中 “,innerxml” 如果结构体改字段是基本类型如:string,int 等,和”,chardata”输出无区别,如果是一个结构体,输出值会是一个完整的 xml 结构
7、结构体 tag 中 “,comment” 输出 xml 中的注释
8、结构体 tag 中”omitempty” 该字段是 go 中的空值:false, 0,空指针,空接口,任何长度为 0 的切片,数组,字符串和 map. 都会被忽略
9、结构体中不包含 tag 会以该字段作为 xml 属性名称,值作为 xml 属性值

代码示例

type TNote struct {
    Lang    string `xml:"lang,attr"`
    Content string `xml:",innerxml"`
}

type TFile struct {
    XMLName  struct{} `xml:"file"`
    FileName string   `xml:"name,attr"`
    Size     string   `xml:"size,attr"`
}

type Release struct {
    XMLName   struct{} `xml:"release"`
    Version   string   `xml:"version,attr"`
    TimeStamp string   `xml:",attr"`
    Lang      string   `xml:"-"`
    Skin      string   `xml:",chardata"`
    Site      string   `xml:",omitempty"`
    File      []TFile  `xml:",innerxml"`
    CnNotes   TNote    `xml:"cnnote"`
    EnNotes   TNote    `xml:"ennote"`
    Comment   string   `xml:",comment"`
}

gorm Tag

支持的结构标签 | 标签 | 说明 | | —- | —- | | column | 指定列的名称 | | type | 指定列的类型 | | size | 指定列的大小,默认是 255 | | primary_key | 指定一个列作为主键 | | unique | 指定一个唯一的列 | | default | 指定一个列的默认值 | | precision | 指定列的数据的精度 | | not null | 指定列的数据不为空 | | auto_increment | 指定一个列的数据是否自增 | | index | 创建带或不带名称的索引,同名创建复合索引 | | unique_index | 类似 索引,创建一个唯一的索引 | | embedded | 将 struct 设置为 embedded | | embedded_prefix | 设置嵌入式结构的前缀名称 | | - | 忽略这些字段 |

关联的结构标签 | 标签 | 说明 | | —- | —- | | many2many | 指定连接表名称 | | foreignkey | 指定外键 | | association_foreignkey | 指定关联外键 | | polymorphic | 指定多态类型 | | polymorphic_value | 指定多态的值 | | jointable_foreignkey | 指定连接表的外键 | | association_jointable_foreignkey | 指定连接表的关联外键 | | save_associations | 是否自动保存关联 | | association_autoupdate | 是否自动更新关联 | | association_autocreate | 是否自动创建关联 | | association_save_reference | 是否引用自动保存的关联 | | preload | 是否自动预加载关联 |

Map Tag

mapstructure 大小写不敏感 示例:

type Person struct {
  Name string `mapstructure:"username"`
}

内嵌结构

内嵌结构可设置mapstructure:",squash"将该结构体的字段提到父结构中
另外需要注意一点,如果父结构体中有同名的字段,那么 mapstructure 会将 JSON 中对应的值同时设置到这两个字段中,即这两个字段有相同的值。

示例:

type Friend struct {
  Person `mapstructure:",squash"`
}

type Person struct {
  Name string
}

未映射的值

如果源数据中有未映射的值(即结构体中无对应的字段),mapstructure 默认会忽略它。

我们可以在结构体中定义一个字段,为其设置 mapstructure:”,remain”标签。这样未映射的值就会添加到这个字段中。注意,这个字段的类型只能为map[string]interface{}map[interface{}]interface{}

示例:

type Person struct {
  Name  string
  Age   int
  Job   string
  Other map[string]interface{} `mapstructure:",remain"`
}

逆向转换

前面我们都是将map[string]interface{}解码到 Go 结构体中。mapstructure 当然也可以将 Go 结构体反向解码为map[string]interface{}。在反向解码时,我们可以为某些字段设置mapstructure:",omitempty"。这样当这些字段为默认值时,就不会出现在结构的map[string]interface{}中:

type Person struct {
  Name string
  Age  int
  Job  string `mapstructure:",omitempty"`
}

Metadata

解码时会产生一些有用的信息,mapstructure 可以使用 Metadata 收集这些信息。Metadata 结构如下:

// mapstructure.go
type Metadata struct {
  Keys   []string
  Unused []string
}

Metadata 只有两个导出字段:

  • Keys:解码成功的键名;
  • Unused:在源数据中存在,但是目标结构中不存在的键名。

为了收集这些数据,我们需要使用 DecodeMetadata 来代替 Decode 方法:

type Person struct {
  Name string
  Age  int
}

func main() {
  m := map[string]interface{}{
    "name": "dj",
    "age":  18,
    "job":  "programmer",
  }

  var p Person
  var metadata mapstructure.Metadata
  mapstructure.DecodeMetadata(m, &p, &metadata)

  fmt.Printf("keys:%#v unused:%#v\n", metadata.Keys, metadata.Unused)
}

弱类型输入

有时候,我们并不想对结构体字段类型和map[string]interface{}的对应键值做强类型一致的校验。这时可以使用WeakDecode/WeakDecodeMetadata方法,它们会尝试做类型转换:

type Person struct {
  Name   string
  Age    int
  Emails []string
}

func main() {
  m := map[string]interface{}{
    "name":   123,
    "age":    "18",
    "emails": []int{1, 2, 3},
  }

  var p Person
  err := mapstructure.WeakDecode(m, &p)
  if err == nil {
    fmt.Println("person:", p)
  } else {
    fmt.Println(err.Error())
  }
}

虽然键 name 对应的值 123 是 int 类型,但是在 WeakDecode 中会将其转换为 string 类型以匹配 Person.Name 字段的类型。同样的,age 的值”18”是 string 类型,在 WeakDecode 中会将其转换为 int 类型以匹配 Person.Age 字段的类型。 需要注意一点,如果类型转换失败了,WeakDecode 同样会返回错误。例如将上例中的 age 设置为”bad value”,它就不能转为 int 类型,故而返回错误。

参考博客:
https://zhuanlan.zhihu.com/p/165419292

文档信息

Search

    Table of Contents