GoでJSONのキー存在チェックを行う方法
はじめに
GoでJSONを扱うときに、以下のようなケースがあります。
- キーが存在するかチェックし、しない場合はエラーにしたい
具体的には以下のようなケースです。
views
キーが存在しないJSONの場合、エラーにするviews
キーさえ存在していれば、バリューはnull
でもよい
// ↓ OK { "id": 1 "views": 1, } // ↓ OK { "id": 1 "views": null, } // ↓ エラーにしたい! {"id": null}
こちらのブログを参考にさせていただきました。
How to Check JSON Key not-set or Null in Golang Unmarshalling
どうやるの?
下記のように実装します。
Gopackage main import ( "encoding/json" "fmt" ) // json unmarshal の際に仕様する構造体 type OptionalInt struct { Value int // JSONのバリュー isValid bool // JSONの形式および型が定義通りの場合は true isSet bool // キーが存在する場合は true } // views キーをもつ構造体 type User struct { Views OptionalInt `json:"views"` } func main() { // サンプルJSON body1 := `{"views": 5}` body2 := `{"views": null}` body3 := `{"age": 5}` // views キーが存在しない! // User構造体を生成 var currentUser1 User // JSONをUser構造体に注入 if err := json.Unmarshal([]byte(body1), ¤tUser1); err != nil { fmt.Println(err) } fmt.Printf("isSet: %v, isValid: %v, value: %v\n", currentUser1.Views.isSet, currentUser1.Views.isValid, currentUser1.Views.Value) var currentUser2 User if err := json.Unmarshal([]byte(body2), ¤tUser2); err != nil { fmt.Println(err) } fmt.Printf("isSet: %v, isValid: %v, value: %v\n", currentUser2.Views.isSet, currentUser2.Views.isValid, currentUser2.Views.Value) var currentUser3 User if err := json.Unmarshal([]byte(body3), ¤tUser3); err != nil { fmt.Println(err) } fmt.Printf("isSet: %v, isValid: %v, value: %v\n", currentUser3.Views.isSet, currentUser3.Views.isValid, currentUser3.Views.Value) } // json.Unmarshal をカスタマイズ func (i *OptionalInt) UnmarshalJSON(bytes []byte) error { i.isSet = true if string(bytes) == "null" { i.isValid = false return nil } var value int if err := json.Unmarshal(bytes, &value); err != nil { return err } i.isValid = true i.Value = value return nil }
Go Playground - The Go Programming Language
上記のプログラムを実行すると下記のような結果になります。
isSet: true, isValid: true, value: 5 isSet: true, isValid: false, value: 0 isSet: false, isValid: false, value: 0
isSet
は、キーが存在する場合に true
になります。
isValid
は、JSONの形式および型が定義通りの場合に true
になります。
value
は、JSONのバリューが格納されます。
それぞれの行が、サンプルJSONの1 から 3行目に対応しています。
ここで注目すべきは3行目です。
isSet
に false
が格納されています!
3行目のJSONには views
キーが存在しないので、想定通りの存在チェックができていることがわかります。
あとは、isSet が false
の場合はエラーを返すような処理を記述すれば完成です!
例えば下記のように実装できます。
goif currentUser3.Views.isSet == false { // もし views キーが存在しない場合は 500 エラーにする http.Error(w, "views キーが存在しません", http.StatusInternalServerError) }
おわりに
今回はGoでJSONのキー存在チェックを行う方法をお伝えしました。
最後に、そもそもなぜこんな面倒な実装をする必要があるのかについてお伝えしておきます。
Goは静的型付き言語なので、JSONのキーが存在しない場合は、構造体の初期値でバリューを設定してしまう、という挙動になります。
例えば先の例で使用したJSONをそのまま処理すると、下記のような結果になります。
json{5} {0} {0}
こうなると、例えばLaravelでいうところの「present」のようなチェックができなくなります。
present
フィールドが存在していることをバリデートしますが、存在していれば空を許します。
キーが存在していない場合は、Go側で値をセットしてしまう、ということです。
というわけで、キーの存在チェックをするためには、わざわざ Unmarshal
のカスタマイズをする必要があった、というわけです。
参考になれば幸いです!