struct
alanlarına takılan meta bilgi alanlarına tag
deniyor. Bu bilginin
eklenmesi işine de annotation yani not alma işlemi deniyor. Bu ek bilgiyi
struct’larla çalışırken kullanıyoruz. Struct field’ları ile ilgili ek işlemler
yapacağımız zaman bu tag’leri kullanıyoruz.
Tag; backtick ` karakterleri arasında tanımlanıyor ve
key1:"value1" key2:"value2"
şeklinde N tane tag alabiliyor:
type S struct {
Field fieldtype `key1:"value1" key2:"value2"`
}
Nerelerde işimize yarar ?
- Encoding/Decoding işlemlerinde;
json.Marshal
/json.Unmarshal
gibi... - Field validation (doğrulama) işlemlerinde; alan boş olamaz’ı kontrol ederken...
- Veritabanı işlerinde; database field type definition/validation...
Şimdi kendimize ait bir tag yapalım. Bu tag’e sahip olan string alanları otomatik olarak içindeki yazan değere göre upper/lower case haline dönüşsün.
https://go.dev/play/p/J3QLYdSjXtb
package main
import (
"fmt"
"reflect"
"strings"
)
// User holds user model data.
type User struct {
FirstName string `case:"upper"`
LastName string `case:"lower"`
Age int `case:"lower"` // won't affect
}
// Set sets case tag declarations.
// "case" tag is only operational for strings!
func (u *User) Set() {
v := reflect.Indirect(reflect.ValueOf(u))
t := v.Type()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if !field.CanSet() {
continue
}
k := field.Type().Kind()
if k == reflect.String {
tagCase, ok := t.Field(i).Tag.Lookup("case")
if !ok {
continue
}
switch tagCase {
case "upper":
if field.String() != "" {
field.SetString(strings.ToUpper(field.String()))
}
case "lower":
if field.String() != "" {
field.SetString(strings.ToLower(field.String()))
}
}
}
}
}
func main() {
u1 := User{
FirstName: "Uğur",
LastName: "Özyılmazel",
Age: 51,
}
fmt.Printf("%+v\n", u1) // {FirstName:Uğur LastName:Özyılmazel Age:49}
u1.Set()
fmt.Println(u1.FirstName) // UĞUR
fmt.Println(u1.LastName) // özyılmazel
fmt.Println(u1.Age) // 51
}
struct
’a eklediğimiz Set
method’u ile tanımlanan alanların field’larında
modifikasyon yapıyoruz. Önceki derslerden hatırlayacağınız gibi, method
aslında bir Pointer Receiver : func (u *User) Set()
.
Önce değer analizi yapıyoruz, sonra tipi buluyoruz. Tüm field’ların içinde
dolaşıp tipi string
’e uygun olan alanı bulup tag’ine bakıyoruz. Tag değeri
upper ya da lower ise gerekli işlemi yapıp field’ın value’sunu
değiştiriyoruz.
https://github.com/isacikgoz/defaults
https://go.dev/play/p/-b7GxOVnxjX
package main
import (
"fmt"
"log"
"github.com/isacikgoz/defaults"
)
// User holds basic user information.
type User struct {
Name string `validate:"notempty"`
Email string `validate:"email"`
Homepage string `validate:"url" default:"https://vbyazilim.com"`
}
func main() {
var u User
// set defaults
if err := defaults.Set(&u); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", u) // {Name: Email: Homepage:https://vbyazilim.com}
u.Name = "Uğur Özyılmazel"
u.Email = "[email protected]"
// u.Email = ""
// u.Homepage = ""
// u.Homepage = "foooo"
if err := defaults.Validate(&u); err != nil {
log.Fatal(err)
}
}
Daha kapsamlı doğrulamalar yapmak için genelde;
https://github.com/go-playground/validator
kütüphanesini kullanıyoruz.