Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

最近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や先ほどの空白に気をつける必要があります。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away