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_atupdated_atUser の直接フィールドであるかのように認識します:

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"`
}

UserProduct の両方が重複なしで IDCreatedAtUpdatedAt を継承します。

フィールドシャドウイング

親が埋め込み構造体と同じJSONキーのフィールドを定義した場合、親のフィールドが優先されます。必要に応じて特定のフィールドをオーバーライドできます。

埋め込み vs 名前付きフィールド

埋め込みはJSONをフラット化します。名前付きフィールドはネストします:

type A struct { Timestamps }       // フラットJSON
type B struct { TS Timestamps }    // "TS"の下にネスト

JSONがフラットで型が共通フィールドを共有する場合は埋め込みを選択します。JSONが明示的にネストされている場合は名前付きフィールドを選択します。

ユースケース

GORMなどのORMはベースモデルフィールド(ID、タイムスタンプ)に構造体の埋め込みを使用します。APIが複数のリソース型間で共通フィールドを持つフラットJSONを返す場合、埋め込みによりコードの重複を回避できます。

試してみる — JSON to Go Struct Converter

フルツールを開く