What are the use(s) for struct tags in Go?
go, reflection, struct, tags
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 `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 the `encoding/json` package, detailed at `json.Marshal()`
- `xml ` - used by the `encoding/xml` package, detailed at `xml.Marshal()`
- `bson ` - used by gobson, detailed at `bson.Marshal()`; also by the mongo-go driver, detailed at bson package doc
- `protobuf ` - used by `github.com/golang/protobuf/proto`, detailed in the package doc
- `yaml ` - used by the `gopkg.in/yaml.v2` package, detailed at `yaml.Marshal()`
- `db ` - used by the `github.com/jmoiron/sqlx` package; also used by `github.com/go-gorp/gorp` package
- `orm ` - used by the `github.com/astaxie/beego/orm` package, detailed at Models – Beego ORM
- `gorm ` - used by `gorm.io/gorm`, examples can be found in their docs
- `valid ` - used by the `github.com/asaskevich/govalidator` package, examples can be found in the project page
- `datastore` - used by `appengine/datastore` (Google App Engine platform, Datastore service), detailed at Properties
- `schema ` - used by `github.com/gorilla/schema` to fill a `struct` with HTML form values, detailed in the package doc
- `asn1 ` - used by the `encoding/asn1` package, detailed at `asn1.Marshal()` and `asn1.Unmarshal()`
- `csv ` - used by the `github.com/gocarina/gocsv` package
- `env ` - used by the `github.com/caarlos0/env` package
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. ``` // 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?