emahiro/b.log

Drastically Repeat Yourself !!!!

structのomitemptyの挙動と使い所の検討

golangのstructでjsonのencodingのためのpropertyに omitempty をつけた時の挙動とその使いどこを検討します。

omitemptyタグとは

https://golang.org/pkg/encoding/json/#Marshal には以下の用に記載されている。

The "omitempty" option specifies that the field should be omitted from the encoding if the field has an emptyvalue, 
defined as false, 0, a nil pointer, a nil interface value, and any empty array, slice, map, or string. 

omitempty属性をつけたstructのjsonのpropertyははその値がfalse, 0, nilのpointer,長さが0の文字列と配列、sliceの時はjsonにencodeされるときに、propertyごと省略される

type Person struct {
  Name string `json:"name"`
  Age  int64 `json:"age,omitempty"` //省略可能
}

上記hのようなstructを考える。

jon := Person {
  Name: "Jon",
  Age: 20
}

// jsonにencodeしたときのoutput
/*
{
  "name": "jon",
  "age" : 20
}
*/

taro := Person {
  Name: "taro",
}

// jsonにencodeしたときのoutput
/*
{
  "name": "taro"
}
*/

jsonにencodeしたときに omitempty 属性をつけておくとstructのinstance化のときに指定したなかったkeyについてはencodeしたjson文字列のpropertyとして含めない(省略される)。
ここまでは一般的なgoのencoding/jsonに関する知識。

ではこのomitemptyを実際のプロジェクトに使うときの注意点について検討する

omitemptyの使い所の検討

使い所はどこなのか。

omitempty 属性を使ういいところは必要ないstructのkeyをjsonにencodeしたときに自動で省略してくれるところであるので、例えば特定のリクエストのクエリパラメーターの値からjsonを生成して、別のAPIにPOSTするような動作を考えたときに、パラメータの有無によって生成するstructを返るなどの手間がいらなくなると思います。

具体的には以下のようなtokenと送信元をクエリパラメーターにタグとしてくっつけて、POSTした先でtokenと送信元をロギングするような挙動があったとします。

curl -i https:sample.com/user/1?token=hogehoge&from=yahoo.co.jp

こんなURLを叩くと以下のようなhandlerにリクエストが入ってくるとします。

package handler

type Logging struct {
  Token string `json: "token"`
  From  string `json: "from, omitempty"`
}

func SenderLogging(w http.ResponseWriter, r *http.Request) {
  values := r.URL.Query()
  token := values.Get("token")
  from := values.Get("from")
  
  // tokenもfromもクエリパラメーターがなければ空文字が入る。
  logging := Logging{
    Token: token,
    From:  from,
  }
  
  b, _ := json.Marshal(&logging)
  
  // encodeされたjsonのbyte配列になる。
}

このとき from が指定されたリクエストの時は from に値が入ったjsonが生成され、from が指定されない(直叩きされた)場合は from のpropertyが省略され、ログにもfrom要素は記録されません。 from の有無を見て、条件分岐等はする必要がありません。

omitemptyをうまく使うと、そこで差分を吸収してくれます。

懸念点

omitemptyの懸念点は上記にも記載しましたが false or "" or 0 or nil pointer などそれ自体が意味を持っていそうな値の場合も omiempty 属性が付いていることでjson化したときにkeyは省略されてしまいます。
つまり、ともすると omitempty 属性をつけていることで意図するjsonが生成されないことが想定されます。

もし、認証のプロセスで必須のパラメータに omitempty をつけていたら、何かの問題で空になってしまい、想定している正しいjsonが作られず、認証が通らない、ということが起きるかもしれません。

特に false or 0 のようなそれ自体が意味を持ちそうな値の時ですら省略されてしまうのは気をつけないと、生成されるつもりだったということが起きかねないので注意が必要です。

まとめ

omitemptyは便利な属性だけど、使い方を見誤ると想定したjsonを生成しないということになるので注意が必要です。
ただし、使い勝手のいい機能なので、ちゃんと使っていきたい。