概要
タイトル通り、GoでJsonをUnmarshalする際にプリミティブ型のデータ欠損を検知する方法を紹介します。
TL;DR
データ欠損を検知したいフィールドをポインタ型で定義しよう。
説明
前提知識として、Goでは変数宣言時にゼロ値で初期化します。(参考記事: ゼロ値を使おう #golang)
==本題==
以下のような構造のJsonを受け取って処理を行うAPIがあるとします。
(具体的に、受け取ったjsonを元にユーザ情報を更新するAPIのようなものを想像してください)
{
"id": 4,
"name": "木板 太郎",
"age": 24,
"profile": "僕はGopherくんがあまり好きではありません"
}
これをパースするGoプログラムは以下です。(httpサーバを実装するのが面倒だったので、そこは割愛します。)
type InputJson struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Profile string `json:"profile"`
}
func main() {
var input InputJson
json.Unmarshal([]byte("{\n \"id\": 4,\n \"name\": \"木板 太郎\",\n \"age\": 24,\n \"profile\": \"僕はGopherくんがあまり好きではありません\" \n}"), &input)
fmt.Printf("%+v\n", input)
}
// Output
// {ID:4 Name:木板 太郎 Age:24 Profile:僕はGopherくんがあまり好きではありません}
うまくパースできていますね。
今度は以下のJsonがリクエストされたと想定します。
{
"id": 5,
"name": "禅 太郎",
"age": 22,
"profile": "最近の趣味はGopher君を愛でることです"
}
しかし、HTTP通信中に何らかの原因で、一部のフィールドが欠損してしまいました。
{
"id": 5,
"name": "禅 太郎",
"age": 22
}
欠損したフィールドの値がnilなってくれれば、Goアプリケーション側でエラーを返すことができます。
しかし、Goでは先述した通り、フィールドにはゼロ値が設定されます。
この場合、欠損したprofileには空の文字列が設定されます。
type InputJson struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Profile string `json:"profile"`
}
func main() {
var input InputJson
json.Unmarshal([]byte("{\n \"id\": 5,\n \"name\": \"禅 太郎\",\n \"age\": 22 \n}"), &input)
fmt.Printf("%+v\n", input)
}
// output
// {ID:5 Name:禅 太郎 Age:22 Profile:} ← nilにならない!!
これでは、意図的に空文字をリクエストしたのか、欠損による空文字なのかを判別できません。
このように、jsonをUnmarshalする際に欠損したデータがあった場合、そのフィールドにはnilを設定したいと思う場面があります。
解決策
以上の問題はフィールドをポインタ型にすることで解決することができます。
以下のように、リクエスト時にゼロ値を許容したくないフィールドをポインタで定義します。
type InputJson struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Profile *string `json:"profile"` // 欠損して欲しくないフィールドをポインタ型にする
}
func main() {
var input InputJson
json.Unmarshal([]byte("{\n \"id\": 5,\n \"name\": \"禅 太郎\",\n \"age\": 22\n \n}"), &input)
fmt.Printf("%+v\n", input)
}
// output
// {ID:5 Name:禅 太郎 Age:22 Profile:<nil>} ← nilになった!
このように、欠損したフィールドがnilに設定されるようになりました。
あとは if input.Profile != nil {return fmt.errors.New("データが欠損してるよ")}
のようにエラーを返すようにすれば、誤ったデータのまま更新が行われずにすみますね。
結び
今回はGoでJsonをUnmarshalする際にプリミティブ型のデータ欠損を検知する方法の紹介でした。
正直、そもそもAPIの設計段階でゼロ値を許容しないようにすればいいじゃないかという気持ちもあります。。。(そもそも空文字や0が設定されない前提の設計にする)
誤字脱字などありましたらコメントいただけると助かります。