Goの構造体にメタ情報を付与するタグの基本

  • 8
    いいね
  • 0
    コメント

最近goを書き始めたので恐れず言語の基本的な内容を書きます。

構造体のタグについてです。
goの構造体にはタグによって実行時に参照可能なメタ情報を付与することができます。

タグの記述と取得

タグは文字列かRAW文字列によって記述し、reflectパッケージを利用して取得します。

package main

import (
    "fmt"
    "reflect"
)

type Organization struct {
    Name     string `label:"名前"`
    Language string `label:"言語"`
}

func main() {
    organization := Organization{
        Name:     "gumi",
        Language: "Elixir",
    }

    fmt.Println(reflect.TypeOf(organization).Field(0).Tag)  // -> label:"名前"
    fmt.Println(reflect.TypeOf(organization).Field(0).Tag.Get("label"))  // -> 名前
}

文字列でもRAW文字列でも記述可能ですが、タグに名前を付ける場合は記述の中にダブルクォーテーションを書くことになるのでRAW文字列を使うほうが多いと思われます

type Organization struct {
    Name     string "名前"
    Language string "言語"
}
type Organization struct {
    Name     string `label:"名前"`
    Language string `label:"言語"`
}

複数のタグ情報の記述

空白によって区切ることで複数の情報を付与することができます

type Organization struct {
    Name     string `label:"名前" json:"name" validate:"required"`
    Language string `label:"言語" json:"language" validate:"required"`
}
tag := reflect.TypeOf(organization).Field(0).Tag
fmt.Println(tag.Get("label"))  // -> 名前
fmt.Println(tag.Get("json"))  // -> name
fmt.Println(tag.Get("validate"))  // -> required

ここで注意したいのは、空白で区切られるため:"の間に空白を空けてはいけないということです。あたり前のことなのですが空白を空けてしまうとタグ名として認識されなくなってしまいます。

構造体を記述するときや他の言語でobject・map・dictなどを記述するときに:のあと空白を空けることに慣れていると誤ってタグの名前と値の間に空白を入れてしまいがちです。

タグの用途

例1) json

jsonパッケージでは構造体をjson形式に変換する際jsonタグを参照してキー名として利用します。

type Organization struct {
    Name     string `json:"organization_name"`
    Language string
}
organization := Organization{
    Name:     "gumi",
    Language: "Elixir",
}

j, _ := json.Marshal(organization)
fmt.Println(string(j))  // {"organization_name":"gumi","Language":"Elixir"}

例2) Google App Engine で Cloud Datastore を使う

Google App Engine で Cloud Datastore を使うときはdatastoreタグでプロパティ名と構造体のそれぞれの情報を紐付けます。

type Organization struct {
    Name     string `datastore:"Name"`
    Language string `datastore:"Lang"`
}

例3) バリデーション

gopkg.in/go-playground/validator.v9
このバリデーションライブラリを使っていますが、こちらのライブラリでは構造体のタグでバリデーションを指定できます。おそらく他のライブラリでも同じような実装になるのではないかと思います。

type Organization struct {
    Name     string `validate:"required"`
    Language string `validate:"required"`
    Email    string `validate:"required,email"`
}
organization := Organization{
    Name:     "gumi",
    Language: "Elixir",
    Email:    "test@example.com",
}

err := validator.New().Struct(&organization)
fmt.Println(err)  // -> <nil>

注意点

不要なところに空白を入れない

先程も書きましたが、不要な箇所に空白を入れると不具合の原因になります。また、バリデーションの例のように,区切りの値を入れる場合も空白を入れることはできません。一応バックスラッシュでエスケープもできるようですが、そうする意味は無いので空白を入れずに書くようにした方がいいと思います。

コンパイル時にエラーを発見しにくい

タグは文字列で指定するので、goの構文的に問題がなければコンパイル時にエラーにならずに実行時に不具合に気づくので、typoや先ほどの空白に気をつける必要があります。