Google Cloud Go SDK の ForceSendFields について
要約
Google Cloud の Go SDK では、構造体の ForceSendFields
フィールドにフィールド名を指定することで、 omitempty
を回避してリクエストにゼロ値を強制的に含めることができる。
本題
Google Cloud の Go SDK(google.golang.org/api) では、定義された構造体のフィールドに一部の例外を除き json:"omitempty"
タグがついています。これによって、あるフィールドがゼロ値であるとき、そのフィールドは省略されます。しかし、これは次のような問題を引き起こすことがあります。
例
Cloud SQL のインスタンスを作成する v1.instances.insert
メソッドを例にとります(ドキュメント)。 このメソッドでは、パブリック IP アドレスの有効/無効はリクエストの DatabaseInstance.Settings.ipConfiguration.ipv4Enabled
フィールドの値によって決まります。
Go SDK を使用する場合、上述の omitempty
タグにより、このフィールドを false
にした場合、フィールドは省略されて送信されます。しかし、Google Cloud の API は DatabaseInstance.Settings.ipConfiguration.ipv4Enabled
フィールドが省略された場合デフォルト値である true
を使用してしまいます(これに関して明言している公式ドキュメントは見つけられませんでした)。これによって、 DatabaseInstance.Settings.ipConfiguration.ipv4Enabled
を true
にしても false
にしても Cloud SQL インスタンスはパブリック IP アドレスが有効化された状態で作成されてしまいます。
対処
これを防ぐためには、フィールドがゼロ値であっても omitempty
せずにそのままリクエストに含めて送信するように設定する必要があります。このために使うのが Go SDK の各構造体に定義されている ForceSendFields
です。 ForceSendFields
は []string
型のフィールドで、常にリクエストに明示するフィールドの名前を配列で指定します。このように指定したフィールドは省略されることなくリクエストに含めることができます。上記の Cloud SQL の例では、 IpConfiguration
構造体の ForceSendFields
に Ipv4Enabled
を設定すればいいです。
実装の説明
ForceSendFields
がどうやって実装されているのか個人的に興味を持ったので調べてみました。Google Cloud Go SDK で使用されている MarsharlJSON
は以下のように実装されています。
MarshalJSON
では、 ForceSendFields
フィールドをもとに schemaToMap
関数に mustInclude
という map を渡して、 schemaToMap
の返り値をそのまま JSON にエンコードしています。 schemaToMap
関数はリフレクションを使って各フィールドが ForceSendFields
に含まれているかを判定し、ゼロ値のフィールドであって含まれていない場合は返却する map にフィールドを含めないようにしています。
Discussion