Go構造体の埋め込みによるJSON合成
Go構造体の埋め込みが複数の型からのJSONフィールドを単一の構造体にフラット化する方法を学びます。合成パターンとフィールド昇格を解説。
Patterns
詳細な説明
JSON合成のための構造体埋め込み
Go構造体の埋め込み(匿名フィールド)は、フィールド名なしで一つの構造体を別の構造体に含めることで型を合成します。埋め込まれた構造体のフィールドは親レベルに「昇格」されます。
JSONパターン
{
"id": 1,
"name": "Alice",
"created_at": "2024-01-15T10:00:00Z",
"updated_at": "2024-01-20T15:30:00Z"
}
埋め込み構造体
type Timestamps struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Timestamps // 埋め込み — フィールドが昇格される
}
JSONでの動作
GoがJSONを User にアンマーシャリングする際、created_at と updated_at を User の直接フィールドであるかのように認識します:
var user User
json.Unmarshal(data, &user)
fmt.Println(user.CreatedAt) // 動作する — 昇格されたフィールド
マーシャリング時、埋め込みフィールドはフラットにシリアライズされます:
// 出力: {"id":1,"name":"Alice","created_at":"...","updated_at":"..."}
よくある合成パターン
type BaseModel struct {
ID int `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type User struct {
BaseModel
Name string `json:"name"`
Email string `json:"email"`
}
type Product struct {
BaseModel
Title string `json:"title"`
Price float64 `json:"price"`
}
User と Product の両方が重複なしで ID、CreatedAt、UpdatedAt を継承します。
フィールドシャドウイング
親が埋め込み構造体と同じJSONキーのフィールドを定義した場合、親のフィールドが優先されます。必要に応じて特定のフィールドをオーバーライドできます。
埋め込み vs 名前付きフィールド
埋め込みはJSONをフラット化します。名前付きフィールドはネストします:
type A struct { Timestamps } // フラットJSON
type B struct { TS Timestamps } // "TS"の下にネスト
JSONがフラットで型が共通フィールドを共有する場合は埋め込みを選択します。JSONが明示的にネストされている場合は名前付きフィールドを選択します。
ユースケース
GORMなどのORMはベースモデルフィールド(ID、タイムスタンプ)に構造体の埋め込みを使用します。APIが複数のリソース型間で共通フィールドを持つフラットJSONを返す場合、埋め込みによりコードの重複を回避できます。