首页 > 其他分享 >Go 语言中结构体的 Tag 用法

Go 语言中结构体的 Tag 用法

时间:2023-02-07 17:55:06浏览次数:52  
标签:Name Age tag 用法 json Tag Go string

转自:https://zhuanlan.zhihu.com/p/258978922

抛砖引玉:什么是 Tag?

正常情况下,你定义的结构体是这样子的,每个字段都由名字和字段类型组成

type Person struct {
    Name string 
    Age  int   
    Addr string
}

也有例外,就像下面这样子,字段上还可以额外再加一个属性,用反引号(Esc键下面的那个键)包含的字符串,称之为 Tag,也就是标签。

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Addr string `json:"addr,omitempty"`
}

那么这个标签有什么用呢?

这边先以 encoding/json 库的用法抛砖引玉,看一下它能起到什么样的效果。

package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
    Addr string `json:"addr,omitempty"`
}

func main() {
    p1 := Person{
        Name: "Jack",
        Age:  22,
    }

    data1, err := json.Marshal(p1)
    if err != nil {
        panic(err)
    }

    // p1 没有 Addr,就不会打印了
    fmt.Printf("%s\n", data1)

    // ================

    p2 := Person{
        Name: "Jack",
        Age:  22,
        Addr: "China",
    }

    data2, err := json.Marshal(p2)
    if err != nil {
        panic(err)
    }

    // p2 则会打印所有
    fmt.Printf("%s\n", data2)
}

由于 Person 结构体里的 Addr 字段有 omitempty 属性,因此 encoding/json 在将对象转化 json 字符串时,只要发现对象里的 Addr 为 false, 0, 空指针,空接口,空数组,空切片,空映射,空字符串中的一种,就会被忽略。

因此运行后,输出的结果如下

$ go run demo.go 
{"name":"Jack","age":22}
{"name":"Jack","age":22,"addr":"China"}

不懂就问:如何定义获取 Tag ?

Tag 由反引号包含,由一对或几对的键值对组成,通过空格来分割键值。格式如下

`key01:"value01" key02:"value02" key03:"value03"`

定义完后,如何从结构体中,取出 Tag 呢?

答案就是, "反射"。

  • 获取 Tag 可以分为三个步骤:
  • 获取字段 field
  • 获取标签 tag
  • 获取键值对 key:value

演示如下

// 三种获取 field
field := reflect.TypeOf(obj).FieldByName("Name")
field := reflect.ValueOf(obj).Type().Field(i)  // i 表示第几个字段
field := reflect.ValueOf(&obj).Elem().Type().Field(i)  // i 表示第几个字段

// 获取 Tag
tag := field.Tag 

// 获取键值对
labelValue := tag.Get("label")
labelValue,ok := tag.Lookup("label")

获取键值对,有Get 和 Lookup 两种方法,但其实 Get 只是对 Lookup 函数的简单封装而已,当没有获取到对应 tag 的内容,会返回空字符串。

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

空 Tag 和不设置 Tag 效果是一样的

package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    Name string ``
    Age string
}
func main() {
    p := reflect.TypeOf(Person{})
    name, _ := p.FieldByName("Name")
    fmt.Printf("%q\n", name.Tag) //输出 ""
    age, _ := p.FieldByName("Age")
    fmt.Printf("%q\n", age.Tag) // 输出 ""
}

实战一下:利用 Tag 搞点事情?

学会了如何定义 tag 和 获取 tag,可以试着利用 tag 来做一些事情,来练习一下。

这边我举个例子吧。

如果我想实现一个函数(就叫 Print 吧),在打印 person 对象时,能够美化输出

type Person struct {
    Name        string 
    Age         int    
    Gender      string
}

person := Person{
    Name:        "MING",
    Age:         29,
}

Print(person)

就像下面这样,key 和 value 之间有个 is:,如果没有指定 Gender 的值,那么显示为unknown(未知)。

Name is: MING
Age is: 29
Gender is: unknown

那该怎么做呢?

先改造下 Person 结构体,给每个字段加上 tag 标签,三个字段的tag 都有 label 属性,而 Gender 多了一个 default 属性,意在指定默认值。

type Person struct {
    Name        string `label:"Name is: "`
    Age         int    `label:"Age is: "`
    Gender      string `label:"Gender is: " default:"unknown"`
}

然后来写一下这个 Print 函数

func Print(obj interface{}) error {
    // 取 Value
    v := reflect.ValueOf(obj)

    // 解析字段
    for i := 0; i < v.NumField(); i++ {

        // 取tag
        field := v.Type().Field(i)
        tag := field.Tag

        // 解析label 和 default
        label := tag.Get("label")
        defaultValue := tag.Get("default")

        value := fmt.Sprintf("%v", v.Field(i))
        if value == "" {
            // 如果没有指定值,则用默认值替代
            value = defaultValue
        }

        fmt.Println(label + value)
    }

    return nil
}

最后执行一下,看了下输出,符合我们的预期:

$ go run demo.go 
Name is: MING
Age is: 29
Gender is: unknown

到此,我们就把 Tag 的用法介绍完了。

标签:Name,Age,tag,用法,json,Tag,Go,string
From: https://www.cnblogs.com/makalochen/p/17099343.html

相关文章

  • java.lang.NoSuchMethodError: com.google.gson.GsonBuilder.setLenient()Lcom/google
    引入第三方的工具类,重新启动项目就会奇奇怪怪的报错。加载不到gson类。一开始以为是没有加载到,各种引入,清理问题依然存在。直到仔细看报错信息,里面是spring的类报错,才意识......
  • go语言context教程
    预览目录Context原理遵循规则Context包WithCancel例子WithDeadline例子WithTimeout例子WithValue例子参考连接对于golang开发者来说context(上下文)包一......
  • 开心档-软件开发入门之MongoDB 覆盖索引查询
     作者简介:每天分享​​MongoDB教程​的学习经验、和学习笔记。  座右铭:有自制力,做事有始有终;学习能力强,愿意不断地接触学习新知识。个人主页:​​雪奈椰子的主页​​ 前......
  • 开心档-软件开发入门之MongoDB 创建集合
    作者简介:每天分享​​MongoDB教程​​的学习经验、和学习笔记。  座右铭:有自制力,做事有始有终;学习能力强,愿意不断地接触学习新知识。​个人主页:​​雪奈椰子的主页​​​......
  • 学习- vue 中 API $attr 用法
    2.4.0新增定义:包含了父作用域不作为prop被识别(且获取)的attribute绑定(class和style除外)。当一个组件没有声明任何prop时,这里会包含所有父作用域的绑定(除class和......
  • kubectl的多样用法 - 我的小米粥分你一半【转】
    kubectl是K8s官方附带的命令行工具,可以方便的操作K8s集群。这篇文章主要介绍一些kubectl的别样用法,希望读者有一定基础的K8s使用经验。有一篇文章也介绍了一些技......
  • MongoDB--字符串长度计算
    MongoDB--字符串长度计算db.event_coll.aggregate([{$project:{"road":1,"length_Bytes":{$strLenBytes:"$road"},"length_CP":{$strLen......
  • Django+Pytest+Jenkins搭建持续集成自动化测试
    系统环境采用的是Mac系统环境下搭建自动化框架。设计思路持续集成测试:定时某时间自动执行测试脚本,接口测试可用Jmeter+ant+allure+Jenkins进行搭建接口持续集成测试,这个针对......
  • 53.cin、cin.get()、cin.getline()、getline()、gets()等函数的用法
    1.cin用法1:最基本,也是最常用的用法,输入一个数字:#pragmawarning(disable:4996)#define_CRT_SECURE_NO_WARNINGS1#include<iostream>usingnamespacestd;intmain......
  • django后台定制化展示参数
    list_display 可显示的数据库字段 list_display=['id','name']list_filter 右边栏过滤器 list_filter=['name']search_fields 可根据什么搜索 search_fields=['name......