JSONのデフォルト値
JSONを構造体に読み込ませる場合に、常にすべての値を設定することはないと思います。
そういった値の中でももし設定がなかった場合に特定の値を設定したいということが、頻繁ではありませんがあると思います。
その場合は単にUnmarshal対象の構造体にあらかじめ設定しておけば、
JSON内でその値がない限り上書きされずにそのデータが残ります。
package main
import (
"fmt"
"encoding/json"
)
type Gopher struct{
Name string
Price int
}
func main(){
data := []byte(`{"price":1000}`)
one := Gopher{"unnamed"}
json.Unmarshal(data,&one)
fmt.Println(one.Name)//==unnamed
}
複雑なJSON形式
ちょっとしたものあればこれで問題ありませんが
これだけでは構造体のスライスなど、子のデータ構造には対応できません。
構造体のスライスなどに対してもデフォルト値を設定したい場合は
デフォルト値付きでパースする構造体として再定義しましょう。
package main
import (
"encoding/json"
"fmt"
)
type World struct {
Units []GopherWithDefault
}
type Gopher struct {
Name string
Price int
}
type GopherWithDefault struct{ Gopher }//Gopherが持つ関数を必要としないのであれば、埋め込みでなくただの再定義でも問題なし
func (wdef *GopherWithDefault) UnmarshalJSON(b []byte) error {
//もしGopherWithDefaultをポインタ型で持つのであれば、nilチェックをする必要がある
*wdef = GopherWithDefault{Gopher{"unnamed", -1}}
return json.Unmarshal(b, &wdef.Gopher)
}
func main() {
data := []byte(`{"units":[{"price":1000}]}`)
w := World{}
json.Unmarshal(data, &w)
fmt.Println(w.Units[0].Name) //==unnamed
}
埋め込み(もしくは再定義)なので引数に渡す際に元構造体を呼び出すかキャストしないといけないのでやや手間はありますが
どうしてもゼロ値と初期値を混在させたくない場合もありますし、
ゼロ値を初期値として扱わせるために変数の意味を改変するのは好ましくありません。(例えばIsDeadフラグをデフォルトでtrueにしたいからといってIsAliveにするなど。これくらいなら問題ありませんがHasWeaponとかになると名前付けが回りくどくなっていきますし、そういった実装のために仕様をずらすやり方は後半になればなるほど難しくなるのでそういった癖はつけないように気を付けましょう。)
ただ単にゼロ値と初期値を混在させたくないだけであればポインタで持つのもありです。
実際に持たせたい初期値がある場合にどうぞ。
型もデフォルトの状態が存在することを明示できるので可読性も上がると思います。
もちろんJSONに限らずUnmarshalを使用するようなデータ形式すべてに適用可能です。