zoukankan      html  css  js  c++  java
  • Go结构体标签

    结构体标签定义

    通过 reflect.Type 获取结构体成员信息 reflect.StructField 结构中的 Tag 被称为结构体标签(Struct Tag)。结构体标签是对结构体字段的额外信息标签。
    Tag是结构体在编译阶段关联到成员的元信息字符串,在运行的时候通过反射的机制读取出来。
    结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔,具体的格式如下:

    `key1:"value1" key2:"value2" key3:"value3"...`  // 键值对用空格分隔
    

    key会指定反射的解析方式包含 json(JSON标签)、 orm(Beego标签)、gorm(GORM标签)、bson(MongoDB标签)、form(表单标签)、binding(表单验证标签).这些系统使用标签设定字段在处理时应该具备的特殊属性和可能发生的行为。这些信息都是静态的,无须实例化结构体,可以通过反射获取到。

    json标签

    JSON数组可以用于编码Go语言的数组和slice。
    将Go语言中结构体slice转为JSON的过程叫编组(marshaling),编组通过json.Marshal函数完成。
    如果在结构体slice编码成JSON的时候使用自定义的成员名,可以使用结构体成员Tag来实现。
    示例:

    type User1 struct {
    	Name string `json:"username"` // 编码后的字段名为 username
    	Age  int    `json:"userage"`  // 编码后的字段名为 userage
    	Sex  string `json:"usersex"`  // 编码后的字段名为 usersex
    }
    
    type User2 struct {
    	Name string `json:"username"`
    	Age  int    `json:"userage"`
    	Sex  string // 编码后的字段名为 Sex
    }
    
    type User3 struct {
    	Name string `json:"username"`
    	Age  int    `json:"-"` // 字段不进行序列化	Sex  string
    }
    
    func main() {
    	u1 := User1{"ares", 20, "man"}
    	jsondata1, err := json.Marshal(u1)
    	if err != nil {
    		fmt.Println("格式错误")
    	} else {
    		fmt.Printf("User1结构体转json:%s
    ", jsondata1)
    	}
    
    	u2 := User2{"ares", 20, "man"}
    	jsondata2, err := json.Marshal(u2)
    	if err != nil {
    		fmt.Println("格式错误")
    	} else {
    		fmt.Printf("User2结构体转json:%s
    ", jsondata2)
    	}
    
    	u3 := User3{"ares", 20, "man"}
    	jsondata3, err := json.Marshal(u3)
    	if err != nil {
    		fmt.Println("格式错误")
    	} else {
    		fmt.Printf("User3结构体转json:%s
    ", jsondata3)
    	}
    }
    

    输出为:

    User1结构体转json:{"username":"ares","userage":20,"usersex":"man"}
    User2结构体转json:{"username":"ares","userage":20,"Sex":"man"}
    User3结构体转json:{"username":"ares","Sex":"man"}
    

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

    标签选项:

    标签选项 使用说明
    - 字段不进行序列化 例:json:"-"
    omitempy 类型零值或空值,序列化时忽略该字段 例:json:",omitempy" 字段名省略的话用结构体字段名
    Type 重新指定字段类型 例:json:"age,string"

    gorm标签

    模型是标准的 struct,由基本数据类型以及实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成。
    GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。
    GORM 默认定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt,可以嵌套入自建结构体,tag名大小写不敏感,建议使用camelCase风格,多个标签定义用分号(;)分隔:

    // gorm.Model 的定义
    type Model struct {
      ID        uint           `gorm:"primaryKey"`
      CreatedAt time.Time
      UpdatedAt time.Time
      DeletedAt gorm.DeletedAt `gorm:"index"`
    }
    

    建表示例:
    结构体定义如下:

    type AddUserAuth struct {
    	gorm.BaseModel
    	UUID        string `gorm:"column:user_uuid;comment:用户UUID;type:varchar(100);"`        // 用户UUID
    	User        string `gorm:"column:user_name;comment:用户名称;type:varchar(50);"`           // 用户登录名
    	Cluster     string `gorm:"column:cluster_name;comment:集群名称;type:varchar(50);"`     // k8s集群
    	NameSpace   string `gorm:"column:namespace;comment:命名空间;type:varchar(50);"`      // 命名空间
    	ServiceName string ` gorm:"column:service_name;comment:应用名称;type:varchar(50);"` // 应用名称
    	ServiceType string `gorm:"column:service_type;comment:应用类型;type:varchar(50);"` // 应用类型
    }
    

    生成的建表语句如下:

    CREATE TABLE `add_user_auths` (
      `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
      `created_at` datetime DEFAULT NULL,
      `updated_at` datetime DEFAULT NULL,
      `deleted_at` datetime DEFAULT NULL,
      `user_uuid` varchar(100) DEFAULT NULL COMMENT '用户UUID',
      `user_name` varchar(50) DEFAULT NULL COMMENT '用户名称',
      `cluster_name` varchar(50) DEFAULT NULL COMMENT '集群名称',
      `namespace` varchar(50) DEFAULT NULL COMMENT '命名空间',
      `service_name` varchar(50) DEFAULT NULL COMMENT '应用名称',
      `service_type` varchar(50) DEFAULT NULL COMMENT '应用类型',
      PRIMARY KEY (`id`),
      KEY `idx_add_user_auths_deleted_at` (`deleted_at`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4
    

    使用 GORM Migrator 创建表时,不会创建被忽略的字段。
    如果想要保存 UNIX(毫/纳)秒时间戳,而不是 time,只需简单地将 time.Time 修改为 int 即可。

    字段标签

    声明 model 时,tag 是可选的,GORM 支持以下 tag:

    标签名 说明
    column 指定 db 列名
    type 列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INCREMENT
    size 指定列大小,例如:size:256
    primaryKey 指定列为主键
    unique 指定列为唯一
    default 指定列的默认值
    precision 指定列的精度
    scale 指定列大小
    not null 指定列为 NOT NULL
    autoIncrement 指定列为自动增长
    autoIncrementIncrement 自动步长,控制连续记录之间的间隔
    embedded 嵌套字段
    embeddedPrefix 嵌入字段的列名前缀
    autoCreateTime 创建时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano
    autoUpdateTime 创建/更新时追踪当前时间,对于 int 字段,它会追踪秒级时间戳,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli
    index 根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情
    uniqueIndex 与 index 相同,但创建的是唯一索引
    check 创建检查约束,例如 check:age > 13,查看 约束 获取详情
    <- 设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限
    -> 设置字段读的权限,->:false 无读权限
    - 忽略该字段,- 无读写权限
    comment 迁移时为字段添加注释

    关联标签

    GORM 允许通过标签为关联配置外键、约束、many2many 表:

    标签名 说明
    foreignKey 指定当前模型的列作为连接表的外键
    references 指定引用表的列名,其将被映射为连接表外键
    polymorphic 指定多态类型,比如模型名
    polymorphicValue 指定多态值、默认表名
    many2many 指定连接表表名
    joinForeignKey 指定连接表的外键列名,其将被映射到当前表
    joinReferences 指定连接表的外键列名,其将被映射到引用表
    constraint 关系约束,例如:OnUpdate、OnDelete

    form标签

    Gin中提供了模型绑定,将表单数据和模型进行绑定,方便参数校验和使用。
    模型绑定:

    // 表单数据
    type LoginForm struct {
        UserName  string    `form:"username"`    
        Password  string    `form:"password"`
        Email	  string    `form:"email"`  
    }
    // model 或 service 层Model
    type Email struct {
        Email       string
        Password    string
    }
    
    
    func EmailLogin (c *gin.Context) {
        var email LoginForm
        if err := c.ShouldBind(&email); err != nil {
            ...
        }
        // 获取表单数据局
        args := Email {
            Email:     email.Email,
            Password:  email.Password,
        }
        // 对参数进行后续使用
        ...
    }
    

    通过 form:"email" 对表单email数据进行绑定。然后通过Bind()、ShouldBind()等方法获取参数值。

    binding标签

    Gin 主要提供了两组绑定方法 Must bind 与 Should bind 。gin/binding 内置模型绑定实现,将请求数据提取到合适的绑定器。
    Must bind:验证不通过,就会被终止或抛出特定的错误页面
    Should bind:存在绑定错误,这个错误会被返回,需要自行处理相应的请求和错误。
    Gin 框架本身已经实现了多种绑定,通常用来绑定来自请求数据,有不同的结构体实例与之对应。其实现的绑定有 JSON, XML, Form,Query,FormPost,FormMultipart,ProtoBuf,MsgPack,YAML,Uri。
    Gin对于数据的校验使用的是 validator.v10 包,该包提供多种数据校验方法,通过binding:""标签来进行数据校验。
    校验规则见github:validator
    示例:

    type LoginForm struct {
        Email     string    `form:"email" binding:"email"`    
        UserName  string    `form:"username" binding:"username"`    
        Password  string    `form:"password" binging:"required,min=6,max=10"`
    }
    

    特殊符号:

    符号 说明
    , 分隔多个标签选项,逗号之间不能有空格
    - 该字段不做校验
    | 使用多个选项,满足其中一个即可

    必须校验

    标签选项 说明 示例
    required 表示该字段值必输设置,且不能为默认值 binding:required
    omitempty 如果字段未设置,则忽略它 binding:reqomitemptyuired

    字符串校验

    标签选项 使用说明 示例
    contains 参数值包含设置子串 binding:"contains=ares"是否包含ares字符串
    excludes 参数值不包含设置子串 binding:"excludes=ares"是否不包含ares字符串
    startswith 字符串前缀 binding:"startswith=ares"是否以tom开头
    endswith 字符串前缀 binding:"endswith=ares"是否以tom结尾

    范围校验

    范围验证: 切片、数组和map、字符串,验证其长度;数值,验证大小范围。

    标签选项 使用说明 示例
    len 参数值等于给定值 binding:"len=3"等于3
    ne 不等于 binding:"ne=3"不等于3
    max 最大值,小于等于参数值 binding:"max=3"小于等于3
    min 最小值,大于等于参数值 binding:"min=3"大于等于3
    lte 参数值小于等于给定值 binding:"lte=3"小于等于3
    gte 参数值大于等于给定值 binding:"gte=3"大于等于3
    lt 参数值小于给定值 binding:"lt=3"小于3
    gt 参数值大于给定值 binding:"gt=3"大于3
    oneof 参数值只能是枚举值中的一个,值必须是数值或字符串,以空格分隔,如果字符串中有空格,将字符串用单引号包围 binding:"oneof=red green"

    字段校验

    标签选项 使用说明
    eqcsfield 跨不同结构体字段相等,比如struct1 field1 是否等于struct2 field2
    necsfield 跨不同结构体字段不相等
    eqfield 同一结构体字段相等验证,例如:输入两次密码
    nefield 同一结构体字段不相等验证
    gtefield 大于等于同一结构体字段
    ltefield 小于等于同一结构体字段

    示例:

    // 不同结构体校验
    type S1 struct { 
        F1 string `validate:eqcsfield=S2.F2` 
        S2 struct { 
            F2 string 
        } 
    }
    // 同一结构体字段相同校验
    type Email struct { 
        Email  string `validate:"lte=4"` 
        Pwd    string `validate:"min=10"` 
        Pwd2   string `validate:"eqfield=Pwd"`
    }
    // 同一结构体字段不相等
    type User struct { 
        Name     string `validate:"lte=4"` 
        Age      int `validate:"min=20"` 
        Password string `validate:"min=10,nefield=Name"`
    }
    

    其他校验

    标签选项 使用说明 示例
    ip 合法IP地址校验 binding:"ip"
    email 合法邮箱校验 binding:"email"
    url 合法的URL binding:"url"
    uri 合法的URI binding:"uri"
    uuid uuid验证 binding:"uuid"
    datetime 合法时间格式值校验 binding:"datetime=2006-01-02"
    json JSON数据验证 validate:"json"
    numeric 数值验证 正则:^[-+]?[0-9]+(?:\.[0-9]+)?$ validate:"numeric"
    number 整数验证 正则:^[0-9]+$ validate:"number"
    alpha 字母字符串验证 正则:^[a-zA-Z]+$ validate:"alpha"
    alphanum 字母数字字符串验证 正则:^[a-zA-Z0-9]+$ validate:"alphanum"
    ascii Ascii 字符验证 validate:"ascii"

    ini标签

    ini 是 Windows 上常用的配置文件格式, go-ini是 Go 语言中用于操作 ini 文件的第三方库。
    若使用ini格式配置,需要将配置文件字段映射到结构体变量,如果键名与字段名不相同,那么需要在结构标签中指定对应的键名。标准库encoding/json、encoding/xml解析时可以将键名直接对应到字段名,而go-ini库不可以,所以需要在结构体标签指定对应键名。
    示例:

    ## 配置文件 cnf.ini
    user_name  = ares
    age = 20
    
    
    // 配置文件映射 结构体
    type Config struct {
      UserName   string `ini:"user_name"`  // ini标签指定下键名
      Age  string `ini:"age"`
    }
    
    作者:Mr.Ares
    个性签名:许多人的付出都是浅尝辄止!
  • 相关阅读:
    Struts2+Spring3+Mybatis3开发环境搭建
    spring+struts2+mybatis
    【LeetCode】Populating Next Right Pointers in Each Node
    【LeetCode】Remove Duplicates from Sorted Array
    【LeetCode】Remove Duplicates from Sorted Array II
    【LeetCode】Binary Tree Inorder Traversal
    【LeetCode】Merge Two Sorted Lists
    【LeetCode】Reverse Integer
    【LeetCode】Same Tree
    【LeetCode】Maximum Depth of Binary Tree
  • 原文地址:https://www.cnblogs.com/aresxin/p/go-label.html
Copyright © 2011-2022 走看看