はじめに
この記事は MYJLab Advent Calendar 2023 の 12/24 の記事です。
こんにちは! 3年の @ea_gitro です!
昨日はいーじー先輩の「初心者さん向けのUnity」講座でした!
僕は昔Unityをやろうと思って、そのあまりの重さにパソコンくんがダウンしてしまったので断念したことがあります。先輩の記事を機にまたはじめてみようかな!
さて、はやいものでもう 12/24 です...
今年ももう少しだというのにやり残したことが山ほどあります...
残された時間は大切に使っていきたいですね!
この記事について
皆さんが「~をやってみた!」「~入門!」という前向きな記事を出す中、今日僕が出す記事は「Go言語の Struct Tag と JSON エンコーディング」というとてもニッチなものになってしまいました...
自分が書き溜めていたものを出そうと思っていたので、問題解決的な記事になってしまって申し訳ないです。
この記事が誰かの役に立つことを願ってます!
Struct Tag とは?
struct tag
とは各フィールドの末尾につけるメタ情報のこと。tag string
ともいう。
`key:"value"`
という RAW 文字列で表されることがおおい。
(この形でなくてもいいが、多くのパッケージではkey
, value
の組で使われる。)
また key
と value
の間には空白を開けてはならない。
逆に空白を開けることで、複数の struct tag を設定することができる。
type 構造体型名 struct{
フィールド名 型 `key1:"value1"`
フィールド名 型 `key2:"value2" key3:"value3"`
}
reflect
パッケージを使えば、 tag string
を取得できる。
(コードは https://pkg.go.dev/reflect#example-StructTag より。少し改変)
package main
import (
"fmt"
"reflect"
)
func main() {
type S struct {
F string `species:"gopher" color:"blue"`
}
s := S{}
st := reflect.TypeOf(s) // `main.S`
field := st.Field(0) // F のフィールドを取得(`Field()`はインデクスで取得。)
// field, _ := st.FieldByName("F") // `FieldByName()` はフィールド名で取得。存在するか否かも返す
fmt.Println( // `フィールド変数.Tag.Get("キー名")` でそのキーの値を返す
field.Tag.Get("species"), // gopher
field.Tag.Get("color"), // blue
)
}
Strcut Tag の活用(JSONエンコーディング)
この struct tag
が実際に使われるのが json
パッケージ ("encoding/json"
)などである(パッケージ名に注意。 encoding
の中の json
である)
json
パッケージでは構造体を JSON に変換することができるが、その際に struct tag
を用いることがある。
-
フィールド定義の後ろで
`json:"JSONのキー名"`
とすることで、構造体のフィールド名ではなく 設定したキー名を JSON のキー名にする ことができる- 何も設定しなければ構造体のフィールド名がそのまま JSON のキー名になる
-
ただし 小文字で始まるフィールド名は encode/decode されない ので、フィールドを 大文字にする必要 がある
- これは変数と同様に、構造体のフィールド名も大文字でないとパッケージの外から参照できないためである
-
The Go Spec - Exported identifiers
Exported identifiers
An identifier may be exported to permit access to it from another package. An identifier is exported if both:
- the first character of the identifier's name is a Unicode uppercase letter (Unicode character category Lu); and
- the identifier is declared in the package block or it is a field name or method name.
All other identifiers are not exported.
[DeepL翻訳]
識別子は、他のパッケージからのアクセスを許可するためにエクスポートする ことができる。識別子がエクスポートされるのは
識別子名の最初の文字がUnicodeの大文字(Unicode文字カテゴリLu)である。
識別子がパッケージブロックで宣言されているか、フィールド名またはメソッド名である。
それ以外の識別子はエクスポートされません。 -
The Go Blog
The json package only accesses the exported fields of struct types (those that begin with an uppercase letter). Therefore only the exported fields of a struct will be present in the JSON output.
[DeepL翻訳]
jsonパッケージは、構造体タイプのエクスポートされたフィールド(大文字で始まるフィールド)のみにアクセスします。したがって、JSON出力には、構造体のエクスポートされたフィールドのみが存在します。
-
The Go Spec - Exported identifiers
- これは変数と同様に、構造体のフィールド名も大文字でないとパッケージの外から参照できないためである
-
encode
パッケージは バイトコード <=> テキスト の変換を行う -
json.Marshal(構造体)
とすることで構造体を JSON に変換できるが、返値がtype []byte
なのでここではstring()
を使って表示している- 逆に JSON => 構造体 とするには
json.Unmarshal(JSONバイト配列, 構造体ポインタ)
とする- この場合もキー名が小文字の場合フィールドの内容が消えるので注意
- 逆に JSON => 構造体 とするには
package main
import (
"encoding/json"
"fmt"
)
type Human struct {
Name string `json:"name"` // "name"
Age int // 何も設定していないので "Age" になる
Hobbies []string `json:"hobbies"` // "hobbies"
tf bool `json: "tf"` // そもそもフィールド名が小文字なので encode されない
}
func main() {
taro := Human{
Name: "Taro",
Age: 6,
Hobbies: []string{"baseball", "game", "books"},
tf: true,
}
j, err := json.Marshal(taro) // 構造体 => JSON
if err == nil {
fmt.Println(
string(j), // {"name":"Taro","Age":6,"hobbies":["baseball","game","books"]}
)
} else {
fmt.Println(err)
}
var taro2 Human
err = json.Unmarshal(j, &taro2) // JSON => 構造体
fmt.Printf("%#v\n", taro2) // main.Human{Name:"Taro", Age:6, Hobbies:[]string{"baseball", "game", "books"}, tf:false}
// 構造体に値が代入されている。この場合大文字小文字は無視される("age" => "Age") 値が入ったように見えるのはゼロ値。(false)
fmt.Println(taro2.tf) // false <= boolean のゼロ値(実際には値は入ってない。元からの値)
fmt.Println(taro2.Age) // 6 <= "age"(JSON) の値が "Age"(構造体) に入った
}
参考文献
-
The Go Spec.
https://go.dev/ref/spec, (accessed: 2023-11-24) -
@itkr. "Goの構造体にメタ情報を付与するタグの基本". Qiita.
https://qiita.com/itkr/items/9b4e8d8c6d574137443c, (accessed: 2023-11-24)
-
Go Packages. "reflect - StructTag".
https://pkg.go.dev/reflect#StructTag, (accessed: 2023-11-24) -
Go Packages. "json - Marshal".
https://pkg.go.dev/encoding/json#Marshal, (accessed: 2023-11-24) -
まくまく. "Golang の構造体にタグ情報を追加する (struct tags)". まくまく Golang ノート.
https://maku77.github.io/p/hxhzfbs/, (accessed: 2023-11-24) -
@Yarimizu14. "【Golang】structのField名で気をつけるところ". Qiita.
https://qiita.com/Yarimizu14/items/e93097c4f4cfd5468259, (accessed: 2023-11-24)
- Andrew Gerrand. "JSON and Go". The Go Blog
https://go.dev/blog/json, (accessed: 2023-11-24)