Việc sử dụng cho các thẻ trong Go là gì?


392

Trong Đặc tả ngôn ngữ Go , nó đề cập đến một tổng quan ngắn gọn về các thẻ:

Một khai báo trường có thể được theo sau bởi một chuỗi ký tự chuỗi tùy chọn, trở thành một thuộc tính cho tất cả các trường trong khai báo trường tương ứng. Các thẻ được hiển thị thông qua một giao diện phản chiếu nhưng bị bỏ qua.

// A struct corresponding to the TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers.
struct {
  microsec  uint64 "field 1"
  serverIP6 uint64 "field 2"
  process   string "field 3"
}

Đây là một lời giải thích rất ngắn IMO, và tôi đã tự hỏi liệu có ai có thể cung cấp cho tôi những gì sử dụng các thẻ này không?


Tôi có một câu hỏi liên quan để sử dụng các nhận xét 'semantic': stackoverflow.com/questions/53101458/ dọa
Bruce Adams

Sửa liên kết đó phải là stackoverflow.com/q/53487371/1569204
Bruce Adams

Câu trả lời:


641

Thẻ cho một trường cho phép bạn đính kèm thông tin meta vào trường có thể thu được bằng phản xạ. Thông thường, nó được sử dụng để cung cấp thông tin chuyển đổi về cách trường cấu trúc được mã hóa hoặc giải mã từ định dạng khác (hoặc được lưu trữ / truy xuất từ ​​cơ sở dữ liệu), nhưng bạn có thể sử dụng nó để lưu trữ bất kỳ thông tin meta nào bạn muốn, hoặc dành cho mục đích khác gói hoặc để sử dụng của riêng bạn.

Như đã đề cập trong tài liệu về reflect.StructTag, theo quy ước, giá trị của chuỗi thẻ là một danh sách các key:"value"cặp được phân tách bằng dấu cách , ví dụ:

type User struct {
    Name string `json:"name" xml:"name"`
}

Các keythường biểu thị các gói mà sau này "value"là dành cho, ví dụ như jsoncác phím được chế biến / sử dụng bởi các encoding/jsongói.

Nếu nhiều thông tin được truyền vào "value", thông thường nó được chỉ định bằng cách tách nó bằng dấu phẩy ( ','), vd

Name string `json:"name,omitempty" xml:"name"`

Thông thường một giá trị dấu gạch ngang ( '-') cho "value"phương tiện để loại trừ trường khỏi quy trình (ví dụ: trong trường hợp của jsonnó có nghĩa là không soái ca hoặc không thống nhất trường đó).

Ví dụ về việc truy cập các thẻ tùy chỉnh của bạn bằng cách sử dụng sự phản chiếu

Chúng ta có thể sử dụng sự phản chiếu ( reflectgói) để truy cập các giá trị thẻ của các trường cấu trúc. Về cơ bản chúng ta cần có được Typecấu trúc của chúng ta, và sau đó chúng ta có thể truy vấn các trường, ví dụ như với Type.Field(i int)hoặc Type.FieldByName(name string). Các phương thức này trả về một giá trị trong StructFieldđó mô tả / đại diện cho một trường cấu trúc; và StructField.Taglà một giá trị của loại StructTagmô tả / đại diện cho một giá trị thẻ.

Trước đây chúng ta đã nói về "quy ước" . Công ước này có nghĩa rằng nếu bạn làm theo nó, bạn có thể sử dụng các StructTag.Get(key string)phương pháp mà phân tích giá trị của thẻ và trả về cho bạn những "value"của keybạn chỉ định. Quy ước được thực hiện / xây dựng theo Get()phương pháp này . Nếu bạn không tuân theo quy ước, Get()sẽ không thể phân tích key:"value"các cặp và tìm thấy những gì bạn đang tìm kiếm. Đó cũng không phải là vấn đề, nhưng sau đó bạn cần thực hiện logic phân tích cú pháp của riêng mình.

Ngoài ra, còn có StructTag.Lookup()(đã được thêm vào trong Mục 1.7), "giống như Get()nhưng phân biệt thẻ không chứa khóa đã cho với thẻ liên kết một chuỗi trống với khóa đã cho" .

Vì vậy, hãy xem một ví dụ đơn giản:

type User struct {
    Name  string `mytag:"MyName"`
    Email string `mytag:"MyEmail"`
}

u := User{"Bob", "bob@mycompany.com"}
t := reflect.TypeOf(u)

for _, fieldName := range []string{"Name", "Email"} {
    field, found := t.FieldByName(fieldName)
    if !found {
        continue
    }
    fmt.Printf("\nField: User.%s\n", fieldName)
    fmt.Printf("\tWhole tag value : %q\n", field.Tag)
    fmt.Printf("\tValue of 'mytag': %q\n", field.Tag.Get("mytag"))
}

Đầu ra (thử trên Sân chơi Go ):

Field: User.Name
    Whole tag value : "mytag:\"MyName\""
    Value of 'mytag': "MyName"

Field: User.Email
    Whole tag value : "mytag:\"MyEmail\""
    Value of 'mytag': "MyEmail"

GopherCon 2015 đã có một bài thuyết trình về các thẻ struct được gọi là:

Nhiều mặt của thẻ cấu trúc (slide) (và video )

Dưới đây là danh sách các khóa thẻ thường được sử dụng:


28
Câu trả lời tuyệt vời. Cách thông tin hữu ích hơn ở đây hơn là một trong mười lần nghiệp này.
Darth Egregious

2
tóm tắt rất hay!
stevenferrer

2
Thật là một câu trả lời tuyệt vời
Alberto Megía

1
Câu trả lời chính xác! Cảm ơn bạn!
JumpAlways

1
Câu trả lời tuyệt vời, cảm ơn bạn cho tất cả các thông tin này!
Sam Holmes

157

Dưới đây là một ví dụ thực sự đơn giản về các thẻ được sử dụng cùng với encoding/jsongói để kiểm soát cách các trường được diễn giải trong quá trình mã hóa và giải mã:

Thử trực tiếp: http://play.golang.org/p/BMeR8p1cKf

package main

import (
    "fmt"
    "encoding/json"
)

type Person struct {
    FirstName  string `json:"first_name"`
    LastName   string `json:"last_name"`
    MiddleName string `json:"middle_name,omitempty"`
}

func main() {
    json_string := `
    {
        "first_name": "John",
        "last_name": "Smith"
    }`

    person := new(Person)
    json.Unmarshal([]byte(json_string), person)
    fmt.Println(person)

    new_json, _ := json.Marshal(person)
    fmt.Printf("%s\n", new_json)
}

// *Output*
// &{John Smith }
// {"first_name":"John","last_name":"Smith"}

Gói json có thể xem các thẻ cho trường và được cho biết cách ánh xạ json <=> trường cấu trúc và các tùy chọn bổ sung như liệu nó có nên bỏ qua các trường trống khi nối tiếp trở lại json không.

Về cơ bản, bất kỳ gói nào cũng có thể sử dụng sự phản chiếu trên các trường để xem xét các giá trị thẻ và hành động theo các giá trị đó. Có thêm một chút thông tin về chúng trong gói phản ánh
http://golang.org/pkg/reflect/#SturationTag :

Theo quy ước, các chuỗi thẻ là sự kết hợp của các cặp khóa được phân tách bằng không gian tùy chọn: các cặp "giá trị". Mỗi khóa là một chuỗi không trống bao gồm các ký tự không điều khiển khác với khoảng trắng (U + 0020 ''), trích dẫn (U + 0022 '"') và dấu hai chấm (U + 003A ':'). Mỗi giá trị được trích dẫn sử dụng các ký tự U + 0022 '"và cú pháp chuỗi ký tự.


6
Kiểu như chú thích Java?
Ismail Badawi

7
@vdadawi: Tôi không phải là một người java, nhưng nhìn thoáng qua định nghĩa của các chú thích java, vâng, có vẻ như họ đang đạt được cùng một mục tiêu; đính kèm siêu dữ liệu vào các yếu tố có thể được kiểm tra trong thời gian chạy.
jdi

15
Không thực sự chú thích java. Các chú thích Java là loại an toàn và thời gian biên dịch được kiểm tra - không phải là chuỗi ký tự như đi. Các chú thích Java mạnh hơn và mạnh hơn nhiều so với các điều khoản siêu dữ liệu cơ bản của golang.
ngồi

2
Là một phần của trình điều khiển MongoDB cho Go, mgo, cũng sử dụng các thẻ trong gói bson của nó (cũng có thể được sử dụng bởi chính nó). Nó cho phép bạn kiểm soát chính xác những gì BSON được tạo ra. Xem godoc.org/labix.org/v2/mgo/bson#pkg-files
Eno

1
Có ví dụ nào khác ngoài JSON và BSON không?
Max Heiber

1

Đó là một số loại thông số kỹ thuật chỉ định cách các gói xử lý với một trường được gắn thẻ.

ví dụ:

type User struct {
    FirstName string `json:"first_name"`
    LastName string `json:"last_name"`
}

thẻ json thông báo jsongói mà đầu ra được sắp xếp theo thứ tự của người dùng sau

u := User{
        FirstName: "some first name",
        LastName:  "some last name",
    }

sẽ như thế này:

{"first_name":"some first name","last_name":"some last name"}

ví dụ khác là các gormgói gói khai báo cách di chuyển cơ sở dữ liệu phải được thực hiện:

type User struct {
  gorm.Model
  Name         string
  Age          sql.NullInt64
  Birthday     *time.Time
  Email        string  `gorm:"type:varchar(100);unique_index"`
  Role         string  `gorm:"size:255"` // set field size to 255
  MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
  Num          int     `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
  Address      string  `gorm:"index:addr"` // create index with name `addr` for address
  IgnoreMe     int     `gorm:"-"` // ignore this field
}

Trong ví dụ này cho trường Emailcó thẻ gorm, chúng tôi tuyên bố rằng cột tương ứng trong cơ sở dữ liệu cho email trường phải có kiểu varchar và 100 độ dài tối đa và nó cũng phải có chỉ mục duy nhất.

ví dụ khác là bindingcác thẻ được sử dụng chủ yếu trong gingói.

type Login struct {
    User     string `form:"user" json:"user" xml:"user"  binding:"required"`
    Password string `form:"password" json:"password" xml:"password" binding:"required"`
}


var json Login
if err := c.ShouldBindJSON(&json); err != nil {
     c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
     return
}

thẻ ràng buộc trong ví dụ này đưa ra gợi ý cho gói gin rằng dữ liệu được gửi tới API phải có các trường người dùng và mật khẩu vì các trường này được gắn thẻ theo yêu cầu.

Vì vậy, thẻ tổng quát là dữ liệu mà các gói yêu cầu phải biết cách xử lý với dữ liệu của các loại cấu trúc khác nhau và cách tốt nhất để làm quen với các thẻ mà gói cần là ĐỌC TÀI LIỆU GÓI HOÀN TOÀN.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.