แท็กใน Go มีประโยชน์อะไรบ้าง?


392

ในGo Language Specificationจะกล่าวถึงภาพรวมโดยย่อของแท็ก:

การประกาศฟิลด์อาจตามด้วยแท็กตัวอักษรของสตริงซึ่งเป็นคุณสมบัติสำหรับฟิลด์ทั้งหมดในการประกาศฟิลด์ที่สอดคล้องกัน แท็กนั้นสามารถมองเห็นได้ผ่านอินเทอร์เฟซการสะท้อน แต่ไม่สนใจ

// 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"
}

นี่เป็นคำอธิบายสั้น ๆ ของ IMO และฉันสงสัยว่าใครจะให้แท็กเหล่านี้กับฉันได้บ้าง


ฉันมีคำถามที่เกี่ยวข้องกับการใช้ความคิดเห็น 'semantic': stackoverflow.com/questions/53101458/ …
Bruce Adams

การแก้ไขลิงก์นั้นควรเป็นstackoverflow.com/q/53487371/1569204
Bruce Adams

คำตอบ:


641

แท็กสำหรับเขตข้อมูลช่วยให้คุณสามารถแนบข้อมูลเมตาไปยังเขตข้อมูลที่สามารถรับได้โดยใช้การสะท้อน โดยปกติจะใช้เพื่อให้ข้อมูลการแปลงว่าฟิลด์ struct ถูกเข้ารหัสไปยังหรือถอดรหัสจากรูปแบบอื่น (หรือจัดเก็บ / ดึงจากฐานข้อมูล) แต่คุณสามารถใช้เพื่อเก็บข้อมูลเมตาใด ๆ ที่คุณต้องการ แพคเกจหรือสำหรับการใช้งานของคุณเอง

ตามที่กล่าวไว้ในเอกสารประกอบของreflect.StructTagโดยการประชุมค่าของสตริงแท็กเป็นรายการkey:"value"คู่คั่นด้วยช่องว่างตัวอย่างเช่น:

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

keyมักจะหมายถึงแพคเกจที่ตามมา"value"เป็นเช่นjsonปุ่มมีการประมวลผล / ใช้โดยencoding/jsonแพคเกจ

หากมีการส่งผ่านข้อมูลจำนวนมากใน"value"โดยทั่วไปจะมีการระบุโดยคั่นด้วยเครื่องหมายจุลภาค ( ',') เช่น

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

มักจะเป็นค่าเส้นประ ( '-') สำหรับ"value"วิธีการที่จะแยกสนามออกจากกระบวนการ (เช่นในกรณีที่jsonมันหมายถึงไม่ให้จอมพลหรือ unmarshal ฟิลด์นั้น)

ตัวอย่างการเข้าถึงแท็กที่กำหนดเองโดยใช้การสะท้อน

เราสามารถใช้การสะท้อน ( reflectแพ็คเกจ) เพื่อเข้าถึงค่าแท็กของเขตข้อมูล struct โดยทั่วไปเราต้องการที่จะได้รับTypeของ struct ของเราและจากนั้นเราสามารถสอบถามสาขาเช่นด้วยหรือType.Field(i int) Type.FieldByName(name string)เมธอดเหล่านี้ส่งคืนค่าStructFieldซึ่งอธิบาย / แทนฟิลด์ struct และStructField.Tagเป็นค่าประเภทStructTagที่อธิบาย / แสดงถึงค่าแท็ก

ก่อนหน้านี้เราได้พูดคุยเกี่ยวกับ"การประชุม" อนุสัญญานี้หมายความว่าหากคุณทำตามคุณสามารถใช้StructTag.Get(key string)วิธีการแยกวิเคราะห์ค่าของแท็กและส่งคืนค่า"value"ที่keyคุณระบุ ประชุมจะดำเนินการ / สร้างขึ้นในนี้Get()วิธีการ หากคุณไม่ปฏิบัติตามอนุสัญญาGet()จะไม่สามารถแยกkey:"value"คู่และค้นหาสิ่งที่คุณกำลังมองหา นั่นไม่ใช่ปัญหา แต่คุณต้องใช้ตรรกะการแยกวิเคราะห์ของคุณเอง

นอกจากนี้ยังมีStructTag.Lookup()(ถูกเพิ่มเข้ามาใน Go 1.7) ซึ่งเป็น"เหมือนGet()แต่แตกต่างแท็กไม่ได้มีคีย์ที่กำหนดจากแท็กเชื่อมโยงสตริงที่ว่างเปล่าที่มีคีย์ที่กำหนด"

ดังนั้นเรามาดูตัวอย่างง่ายๆ:

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"))
}

เอาท์พุท (ลองไปที่สนามเด็กเล่นไป ):

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 มีการนำเสนอเกี่ยวกับแท็ก struct ชื่อ:

แท็กโครงสร้างหลายหน้า (ภาพนิ่ง) (และวิดีโอ )

นี่คือรายการของคีย์แท็กที่ใช้กันทั่วไป:


28
คำตอบที่ยอดเยี่ยม ให้ข้อมูลที่เป็นประโยชน์มากขึ้นในที่นี้มากกว่าในที่ที่มีกรรมนี้สิบเท่า
Darth Egregious

2
สรุปดีมาก!
stevenferrer

2
ช่างเป็นคำตอบที่ยอดเยี่ยมมาก
Alberto Megía

1
คำตอบที่ดี! ขอบคุณ!
JumpAlways

1
คำตอบที่น่าอัศจรรย์ขอบคุณสำหรับข้อมูลทั้งหมดนี้!
Sam Holmes

157

นี่คือตัวอย่างง่ายๆของแท็กที่ใช้กับencoding/jsonแพคเกจเพื่อควบคุมวิธีการตีความฟิลด์ในระหว่างการเข้ารหัสและถอดรหัส:

ลองใช้สด: 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"}

แพคเกจ json สามารถดูแท็กสำหรับฟิลด์และบอกวิธีการแมป json <=> โครงสร้าง struct และตัวเลือกพิเศษเช่นว่าควรละเว้นฟิลด์ว่างเมื่อทำการซีเรียลกลับเป็น json

โดยทั่วไปแพ็คเกจใด ๆ สามารถใช้การสะท้อนบนฟิลด์เพื่อดูค่าแท็กและดำเนินการกับค่าเหล่านั้น มีข้อมูลเพิ่มเติมเล็กน้อยเกี่ยวกับพวกเขาในแพ็คเกจการสะท้อน
http://golang.org/pkg/reflect/#StructTag :

โดยการประชุมสตริงแท็กคือการต่อเชื่อมของคีย์คั่นด้วยช่องว่างทางเลือก: "ค่า" คู่ แต่ละคีย์คือสตริงที่ไม่ว่างประกอบด้วยอักขระที่ไม่สามารถควบคุมได้นอกเหนือจากช่องว่าง (U + 0020 ''), เครื่องหมายคำพูด (U + 0022 '' '), และเครื่องหมายโคลอน (U + 003A': ') แต่ละค่าจะถูกยกมา ใช้อักขระ U + 0022 '"' และใช้ตัวอักษรไวยากรณ์ตัวอักษรของสตริง


6
คำอธิบายประกอบแบบ Java ชอบอะไร
Ismail Badawi

7
@isbadawi: ฉันไม่ใช่คนที่แต่งตัวประหลาดจาวา แต่อย่างรวดเร็วความหมายของคำอธิบายประกอบจาวาใช่ดูเหมือนว่าพวกเขาจะบรรลุเป้าหมายเดียวกัน; แนบข้อมูลเมตาไปยังองค์ประกอบที่สามารถตรวจสอบได้ที่รันไทม์
jdi

15
ไม่ใช่คำอธิบายประกอบ java จริงๆ คำอธิบายประกอบ Java เป็นชนิดที่ปลอดภัยและตรวจสอบเวลาการรวบรวม - ไม่ใช่ตัวอักษรสตริงเช่น go การเพิ่มความคิดเห็น Java มีประสิทธิภาพและความแข็งแกร่งมากกว่าข้อกำหนดเมตาดาต้าพื้นฐานของ golang
นั่ง

2
ในฐานะที่เป็นส่วนหนึ่งของไดรเวอร์ MongoDB สำหรับ Go, mgo ยังใช้แท็กในแพ็คเกจ bson (ซึ่งสามารถใช้งานได้ด้วยตัวเอง) มันช่วยให้คุณสามารถควบคุมสิ่งที่ BSON สร้างขึ้นได้อย่างแม่นยำ ดูgodoc.org/labix.org/v2/mgo/bson#pkg-files
Eno

1
มีตัวอย่างอื่นนอกเหนือจาก JSON และ BSON หรือไม่
Max Heiber

1

เป็นข้อมูลจำเพาะบางประเภทที่ระบุว่าแพ็คเกจปฏิบัติกับฟิลด์ที่ถูกแท็กอย่างไร

ตัวอย่างเช่น:

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

แท็ก json แจ้งjsonแพ็กเกจที่ marshalled เอาต์พุตของผู้ใช้ต่อไปนี้

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

จะเป็นเช่นนี้:

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

ตัวอย่างอื่น ๆ คือgormแท็กแพ็กเกจประกาศว่าจะต้องทำการโยกย้ายฐานข้อมูลอย่างไร:

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
}

ในตัวอย่างนี้สำหรับฟิลด์ที่Emailมีแท็ก gorm เราประกาศว่าคอลัมน์ที่สอดคล้องกันในฐานข้อมูลสำหรับอีเมลภาคสนามจะต้องเป็นประเภท varchar และความยาวสูงสุด 100 และมันจะต้องมีดัชนีที่ไม่ซ้ำกัน

ตัวอย่างอื่น ๆ คือbindingแท็กที่ใช้ส่วนใหญ่ในginแพ็คเกจ

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
}

แท็กการโยงในตัวอย่างนี้ให้คำแนะนำกับแพคเกจจินที่ข้อมูลที่ส่งไปยัง API ต้องมีฟิลด์ผู้ใช้และรหัสผ่านทำให้เกิดการติดแท็กฟิลด์เหล่านี้ตามที่ต้องการ

ดังนั้นแท็กทั่วไปคือข้อมูลที่แพ็กเกจต้องการทราบว่าควรจัดการกับข้อมูลประเภท struct ที่แตกต่างกันอย่างไรและวิธีที่ดีที่สุดในการทำความคุ้นเคยกับแท็กความต้องการของแพ็กเกจคือการอ่านเอกสารเสร็จสมบูรณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.