patterngoCritical
What are the use(s) for struct tags in Go?
Viewed 0 times
tagsstructareusetheforwhat
Problem
In the Go Language Specification, it mentions a brief overview of tags:
A field declaration may be followed by an optional string literal tag,
which becomes an attribute for all the fields in the corresponding
field declaration. The tags are made visible through a reflection
interface but are otherwise ignored.
This is a very short explanation IMO, and I was wondering if anyone could provide me with what use these tags would be?
A field declaration may be followed by an optional string literal tag,
which becomes an attribute for all the fields in the corresponding
field declaration. The tags are made visible through a reflection
interface but are otherwise ignored.
// 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"
}This is a very short explanation IMO, and I was wondering if anyone could provide me with what use these tags would be?
Solution
A tag for a field allows you to attach meta-information to the field which can be acquired using reflection. Usually it is used to provide transformation info on how a struct field is encoded to or decoded from another format (or stored/retrieved from a database), but you can use it to store whatever meta-info you want to, either intended for another package or for your own use.
As mentioned in the documentation of
The
If multiple information is to be passed in the
Usually a dash value (
Example of accessing your custom tags using reflection
We can use reflection (
Previously we talked about "convention". This convention means that if you follow it, you may use the
Also there is
So let's see a simple example:
Output (try it on the Go Playground):
GopherCon 2015 had a presentation about struct tags called:
The Many Faces of Struct Tags (slide) (and a video)
Here is a list of commonly used tag keys:
As mentioned in the documentation of
reflect.StructTag, by convention the value of a tag string is a space-separated list of key:"value" pairs, for example:type User struct {
Name string `json:"name" xml:"name"`
}The
key usually denotes the package that the subsequent "value" is for, for example json keys are processed/used by the encoding/json package.If multiple information is to be passed in the
"value", usually it is specified by separating it with a comma (','), e.g.Name string `json:"name,omitempty" xml:"name"`Usually a dash value (
'-') for the "value" means to exclude the field from the process (e.g. in case of json it means not to marshal or unmarshal that field).Example of accessing your custom tags using reflection
We can use reflection (
reflect package) to access the tag values of struct fields. Basically we need to acquire the Type of our struct, and then we can query fields e.g. with Type.Field(i int) or Type.FieldByName(name string). These methods return a value of StructField which describes / represents a struct field; and StructField.Tag is a value of type StructTag which describes / represents a tag value.Previously we talked about "convention". This convention means that if you follow it, you may use the
StructTag.Get(key string) method which parses the value of a tag and returns you the "value" of the key you specify. The convention is implemented / built into this Get() method. If you don't follow the convention, Get() will not be able to parse key:"value" pairs and find what you're looking for. That's also not a problem, but then you need to implement your own parsing logic.Also there is
StructTag.Lookup() (was added in Go 1.7) which is "like Get() but distinguishes the tag not containing the given key from the tag associating an empty string with the given key".So let's see a simple example:
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"))
}Output (try it on the Go Playground):
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 had a presentation about struct tags called:
The Many Faces of Struct Tags (slide) (and a video)
Here is a list of commonly used tag keys:
json- used by theencoding/jsonpackage, detailed atjson.Marshal()
xml- used by theencoding/xmlpackage, detailed atxml.Marshal()
bson- used by gobson, detailed atbson.Marshal(); also by the mongo-go driver, detailed at bson package doc
protobuf- used bygithub.com/golang/protobuf/proto, detailed in the package doc
yaml- used by thegopkg.in/yaml.v2package, detailed atyaml.Marshal()
db- used by thegithub.com/jmoiron/sqlxpackage; also used bygithub.com/go-gorp/gorppackage
orm- used by thegithub.com/astaxie/beego/ormpackage, detailed at Models – Beego ORM
gorm- used bygorm.io/gorm, examples can be found in their docs
valid- used by thegithub.com/asaskevich/govalidatorpackage, examples can be found in the project page
datastore- used byappengine/datastore(Google App Engine platform, Datastore service), detailed at Properties
schema- used bygithub.com/gorilla/schemato fill astructwith HTML form values, detailed in the package doc
asn1- used by theencoding/asn1package, detailed atasn1.Marshal()andasn1.Unmarshal()
csv- used by thegithub.com/gocarina/gocsvpackage
env- used by thegithub.com/caarlos0/envpackage
Code Snippets
type User struct {
Name string `json:"name" xml:"name"`
}Name string `json:"name,omitempty" xml:"name"`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"Context
Stack Overflow Q#10858787, score: 1077
Revisions (0)
No revisions yet.