https://mp.weixin.qq.com/s/4FmxImNLcU0-up5aVZLMzw

1 介绍

通常情况下,结构体标签被用于提供结构体字段如何被编码为或者解码自另外一种格式的转换信息(或者是以何种形式被保存至/获取自数据库)。不过,你也可以用它存储任何你想要设置的”元信息“,供其他包或者自己使用。

2 使用规范

  • 结构体标签字符串的值是一个由空格分隔的 key:”value” 对列表

    • 键,通常表示后面跟的“值”是被哪个包使用的,例如json这个键会被encoding/json包处理使用。多个键用空格分隔
    • 在“键”对应的“值”中传递多个信息,通常通过用,分隔来指定
      1. type User struct {
      2. Name string `json:"name" xml:"name"`
      3. }
  • 如果一个字段的结构体标签里某个键的“值”被设置成了的破折号 (‘-‘),那么就意味着告诉处理该结构体标签键值的进程排除该字段。就以为进行JSON编码/解码时忽略Name这个字段。

    1. Name string `json:"-"`

    3 通过反射获取 自定义的结构体标签

    ```go // Get方法解析标签的值并返回你指定的键的“值”。 func (tag StructTag) Get(key string) string

// Lookup会通过返回的ok值告知给定key是否存在与标签中。 func (tag StructTag) Lookup(key string) (value string, ok bool)

  1. ```go
  2. package main
  3. import (
  4. "fmt"
  5. "reflect"
  6. )
  7. type User struct {
  8. Name string `mytag:"MyName"`
  9. Email string `mytag:"MyEmail"`
  10. }
  11. func main() {
  12. u := User{"Bob", "bob@mycompany.com"}
  13. t := reflect.TypeOf(u)
  14. for i := 0; i < t.NumField(); i++ {
  15. field := t.Field(i)
  16. fmt.Printf("Field: User.%s\n", field.Name)
  17. fmt.Printf("\tWhole tag value : %s\n", field.Tag)
  18. fmt.Printf("\tValue of 'mytag': %s\n", field.Tag.Get("mytag"))
  19. }
  20. }

上面的程序会输出

  1. Field: User.Name
  2. Whole tag value : mytag:"MyName"
  3. Value of 'mytag': MyName
  4. Field: User.Email
  5. Whole tag value : mytag:"MyEmail"
  6. Value of 'mytag': MyEmail

4 常用的结构体标签key

常用的结构体标签Key,指的是那些被一些常用的开源包声明使用的结构体标签键。在这里总结了一些,都是一些我们平时会用到的包,它们是:

  • json: 由encoding/json 包使用,详见json.Marshal()的使用方法和实现逻辑。
  • xml : 由encoding/xml包使用,详见xml.Marshal()。
  • bson: 由gobson包,和mongo-go包使用。
  • protobuf: 由github.com/golang/protobuf/proto 使用,在包文档中有详细说明。
  • yaml: 由gopkg.in/yaml.v2 包使用,详见yaml.Marshal()。
  • gorm: 由gorm.io/gorm包使用,示例可以在GORM的文档中找到。

    BSON()是一种类json的一种二进制形式的存储格式,简称Binary JSON,它和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如Date和BinData类型。 bson是由10gen开发的一个数据格式,目前主要用于mongoDB中

5 常用的结构体标签val

(1) omitempty

在执行json.Marshal进行序列化时会将payment字段给过滤掉,这就是omitempty的作用会在序列化时过滤掉false、 0、空指针、空接口、空数组、空切片、空映射、空字符串。

  1. type Person struct {
  2. Name string `json:"name"`
  3. Age int `json:"age"`
  4. Payment float64 `json:"payment,omitempty" `
  5. }
  1. type Person struct {
  2. Name string `json:"name"`
  3. Age int `json:"age"`
  4. Payment float64 `json:"payment,omitempty" `
  5. }
  6. func main() {
  7. var chaochao Person
  8. file, err := os.Open("student.json")
  9. if err != nil {
  10. panic(err)
  11. }
  12. defer file.Close()
  13. content, err := ioutil.ReadAll(file)
  14. json.Unmarshal(content, &chaochao)
  15. fmt.Println(chaochao)
  16. data, _ := json.Marshal(&chaochao)
  17. fmt.Println(string(data))
  18. }

输出结果

  1. {chaochao 23 0}
  2. {"name":"chaochao","age":23}

(2) required

默认参数是可选, 不传则为零值, 加上required后参数为必传

  1. type Info struct {
  2. Name string `json:"-"` // 告诉编码器完全跳过该字段。
  3. Sex string `json:"sex,required"`
  4. }

(3) min max

  1. type Info struct {
  2. Name string `json:"-"`
  3. Age int `json:"age,min=17,max=60"`
  4. }

(4) 类型转化

  1. type Info struct {
  2. Name string
  3. Age int `json:"age,string"`
  4. //这样生成的json对象中,age就为字符串
  5. Sex string
  6. }

6 自定义结构体标签

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. "regexp"
  6. "strings"
  7. )
  8. //Name of the struct tag used in example.
  9. const tagName = "validate"
  10. //Regular expression to validate email address.
  11. var mailRe = regexp.MustCompile(`\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z`)
  12. //Generic data validator
  13. type Validator interface {
  14. //Validate method performs validation and returns results and optional error.
  15. Validate(interface{}) (bool, error)
  16. }
  17. //DefaultValidator does not perform any validations
  18. type DefaultValidator struct {
  19. }
  20. func (v DefaultValidator) Validate(val interface{}) (bool, error) {
  21. return true, nil
  22. }
  23. type NumberValidator struct {
  24. Min int
  25. Max int
  26. }
  27. func (v NumberValidator) Validate(val interface{}) (bool, error) {
  28. num := val.(int)
  29. if num < v.Min {
  30. return false, fmt.Errorf("should be greater than %v", v.Min)
  31. }
  32. if v.Max >= v.Min && num > v.Max {
  33. return false, fmt.Errorf("should be less than %v", v.Max)
  34. }
  35. return true, nil
  36. }
  37. //StringValidator validates string presence and/or its length
  38. type StringValidator struct {
  39. Min int
  40. Max int
  41. }
  42. func (v StringValidator) Validate(val interface{}) (bool, error) {
  43. l := len(val.(string))
  44. if l == 0 {
  45. return false, fmt.Errorf("cannot be blank")
  46. }
  47. if l < v.Min {
  48. return false, fmt.Errorf("should be at least %v chars long", v.Min)
  49. }
  50. if v.Max >= v.Min && l > v.Max {
  51. return false, fmt.Errorf("should be less than %v chars long", v.Max)
  52. }
  53. return true, nil
  54. }
  55. type EmailValidator struct {
  56. }
  57. func (v EmailValidator) Validate(val interface{}) (bool, error) {
  58. if !mailRe.MatchString(val.(string)) {
  59. return false, fmt.Errorf("is not a valid email address")
  60. }
  61. return true, nil
  62. }
  63. //Returns validator struct corresponding to validation type
  64. func getValidatorFromTag(tag string) Validator {
  65. args := strings.Split(tag, ",")
  66. switch args[0] {
  67. case "number":
  68. validator := NumberValidator{}
  69. fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &validator.Min, &validator.Max)
  70. return validator
  71. case "string":
  72. validator := StringValidator{}
  73. fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &validator.Min, &validator.Max)
  74. return validator
  75. case "email":
  76. return EmailValidator{}
  77. }
  78. return DefaultValidator{}
  79. }
  80. //Performs actual data validation using validator definitions on the struct
  81. func validateStruct(s interface{}) []error {
  82. errs := []error{}
  83. //ValueOf returns a Value representing the run-time data
  84. v := reflect.ValueOf(s)
  85. for i := 0; i < v.NumField(); i++ {
  86. //Get the field tag value
  87. tag := v.Type().Field(i).Tag.Get(tagName)
  88. //Skip if tag is not defined or ignored
  89. if tag == "" || tag == "-" {
  90. continue
  91. }
  92. //Get a validator that corresponds to a tag
  93. validator := getValidatorFromTag(tag)
  94. //Perform validation
  95. valid, err := validator.Validate(v.Field(i).Interface())
  96. //Append error to results
  97. if !valid && err != nil {
  98. errs = append(errs, fmt.Errorf("%s %s", v.Type().Field(i).Name, err.Error()))
  99. }
  100. }
  101. return errs
  102. }
  103. type User struct {
  104. Id int `validate:"number,min=1,max=1000"`
  105. Name string `validate:"string,min=2,max=10"`
  106. Bio string `validate:"string"`
  107. Email string `validate:"string"`
  108. }
  109. func main() {
  110. user := User{
  111. Id: 0,
  112. Name: "superlongstring",
  113. Bio: "",
  114. Email: "foobar",
  115. }
  116. fmt.Println("Errors: ")
  117. for i, err := range validateStruct(user) {
  118. fmt.Printf("\t%d. %s\n", i+1, err.Error())
  119. }
  120. }