TL;DR
- goの構造体につけるタグは、フォーマットが不正だと読み込まれない(当然)
- json.Marshalは、構造体のjsonタグがあればその値をキーとしてJSON文字列を生成する
- json.Unmarshalは、構造体のjsonタグがあればその値を対応するフィールドにマッピングする
- jsonタグがなければ、完全一致もしくはcase-insensitiveなフィールドにマッピングする
では、ひとつづつ確認していきます。
goの構造体につけるタグは、フォーマットが不正だと読み込まれない(当然)
goの構造体にはタグの機能があって、型の後に特定のフォーマットでアノテーションが記述できます
1 2 3 |
|
key:"value"
をスペース区切りで複数かけます。
で、アクセスするにはリフレクションを使います。
1 2 3 4 5 |
|
タグのフォーマットが不正な場合は、値が空になります。コンパイルエラーにはなりません。
1 2 3 4 5 6 7 |
|
このエラーをコンパイルする前に検知するには、go vet
コマンドが使えます。
1 2 |
|
できればgo compile
実行したときに自動的にチェックしてほしいところですが…
で、jsonパッケージのMarshal/Unmarshalは、このタグを使ってアノテーションを記述することができます。
json.Marshalは、構造体のjsonタグがあればその値をキーとしてJSON文字列を生成する
json.Marshalは構造体からJSON文字列への変換する関数です。
1 2 3 4 5 6 7 8 9 |
|
普通にやると、フィールド名がそのままJSONのキーになります。
ここで構造体のタグ機能をつかって、フィールド名のアノテーションを書くことで、任意のフィールド名でJSONが生成できます。
1 2 3 4 5 6 7 8 9 |
|
アノテーション指定した”a”がJSONのキーとして使用されていることが確認できます。
json.Unmarshalは、構造体のjsonタグがあればその値を対応するフィールドにマッピングする
json.UnmarshalはJSON文字列から構造体へ変換する関数です。
まずはタグなしのパターン
1 2 3 4 5 6 7 8 |
|
JSONのフィールド名がsnake_caseのパターン
1 2 3 |
|
ここで「ん?」となるわけです。タグによるアノテーションがないのになぜ小文字のフィールドがちゃんと読み込まれているのか…と。
この仕様、json.Unmarshalのgodocにちゃんと書いてあります。
To unmarshal JSON into a struct, Unmarshal matches incoming object keys to the keys used by Marshal (either the struct field name or its tag), preferring an exact match but also accepting a case-insensitive match.
どうやら、json.Unmarshalは、JSONに含まれるキーと完全一致か、case-insensitiveに一致する構造体のフィールドにマッピングする仕様となっているようです。もちろん、snake_caseなJSONのキーはアノテーションがないとマッピングされず無視されます。(構造体のフィールドとしてsnale_caseが存在しない限りは)
経緯
先日、ヒカルのGO! hikarie.go #2で@yosuke_furukawaさんによるGoでJSON APIを書いてみるというハンズオンを体験してきました。
そこで、アノテーションをつけた構造体を使ってMarshalした時に、書いたコードがこれです。
1 2 3 4 5 6 |
|
小文字で出力してほしいのに、アノテーションが効かない…となったわけです。
他の参加者がjson:"name"
でないといけないことに気づいて、この件は一件落着。
だったのですが、もう少し詳しく調べると上に整理したような仕様が見えてきたというわけです。
1 2 3 4 5 6 |
|
実はこの時書いたjson:name
というアノテーションは不正なフォーマットとして無視されていて、たまたまUser.Nameというフィールドにcase-insensitiveでUnmarshalした時にマッピングができていたにすぎなかったようです。
とまぁそんな感じでJSONの取り扱いについて正しい知識を得られたのですが、一人でやってた時には気づかなかった疑問とか知識として不足している点に気付けるので、ハンズオン形式も良いものですね。