結論:omitemptyは便利だが罠がある

omitemptyタグは空値を省略できて便利だが、false0が保存できなくなる罠がある。

特にboolean型で**「false」と「未設定」を区別したい場合は要注意**だ。

omitemptyとは

空値の場合にフィールドを省略するタグ。JSON/Firestoreへの保存時にデータサイズを削減できる。

type User struct {
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`  // 空なら省略
}

⚠️ 最大の罠:「空」とみなされる値

以下の値は「空」として扱われ、omitempty付きフィールドから消える:

空とみなされる値保存できない値の例
真偽値falsefalseが保存不可
数値00が保存不可
文字列""❌ 空文字列が保存不可
スライス/マップnil または長さ0❌ 空配列が保存不可
ポインタnil-
時刻time.Timeのゼロ値-

実例:boolean型の罠

問題のあるコード

type Config struct {
    EnableFeature bool `firestore:"enableFeature,omitempty"`
}

config := Config{EnableFeature: false}
// ❌ Firestoreに保存されない!
// "enableFeature"フィールド自体が消える

これでは「false」と「未設定」が区別できない。

解決策:ポインタ型を使う

type Config struct {
    EnableFeature *bool `firestore:"enableFeature,omitempty"`
}

// パターン1: 未設定
config := Config{EnableFeature: nil}
// → フィールド省略(未設定として扱える)

// パターン2: 明示的にfalse
falseValue := false
config := Config{EnableFeature: &falseValue}
// → ✅ falseとして保存される

// パターン3: true
trueValue := true
config := Config{EnableFeature: &trueValue}
// → ✅ trueとして保存される

動作比較

omitemptyあり

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age,omitempty"`
    Active bool  `json:"active,omitempty"`
}

user := User{Name: "Alice", Age: 0, Active: false}
// JSON: {"name":"Alice"}
// ❌ Age=0 と Active=false は消える

omitemptyなし

user := User{Name: "Alice", Age: 0, Active: false}
// JSON: {"name":"Alice","age":0,"active":false}
// ✅ 0 と false もちゃんと保存される

Firestore/JSONでの使い分け

データ削減を優先する場合

Email string `json:"email,omitempty"`
// 未入力のメールアドレスは保存しなくていい

値の区別が必要な場合

// boolean型: falseと未設定を区別したい
IsAdmin *bool `firestore:"isAdmin,omitempty"`

// 数値型: 0と未設定を区別したい
Score *int `firestore:"score,omitempty"`

まとめ

  • omitemptyは空値を省略してデータサイズを削減できる
  • false0、空文字列は「空」扱いで消える(最大の罠)
  • 値の区別が必要ならポインタ型を使う
  • JSON/Firestoreどちらでも同じ仕組みで動作

Some illustrations on this site, including the Go Gopher, are by Renée French and licensed under CC BY 4.0