Upgrade to Pro — share decks privately, control downloads, hide ads and more …

最近ハマったシリアライズの落とし穴

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →
Avatar for kosuke1012 kosuke1012
February 28, 2026
26

 最近ハマったシリアライズの落とし穴

Avatar for kosuke1012

kosuke1012

February 28, 2026

Transcript

  1. シリアライズ type Event struct { UserID string `json:"user_id"` Amount int

    `json:"amount"` } in := Event{UserID: "u1", Amount: 1200} b, _ := json.Marshal(in) // b = {"user_id":"u1","amount":1200} var out Event _ = json.Unmarshal(b, &out) // out = {UserID:"u1", Amount:1200} 3
  2. struct を変更したくなった // v1 type Event struct { UserID string

    `json:"user_id"` Amount int `json:"amount"` } // v2: フィールド名を変更(互換性なし) type Event struct { UID string `json:"uid"` Amount int `json:"amount"` } 5
  3. 後方互換その1 専用 Unmarshal メソッドをつくる type eventDTO struct { UserID string

    `json:"user_id"` UID string `json:"uid"` Amount int `json:"amount"` } func (e *Event) UnmarshalJSON(b []byte) error { var dto eventDTO if err := json.Unmarshal(b, &dto); err != nil { return err } // uid があれば uid, なければ user_id uid := dto.UID if uid == "" { uid = dto.UserID } *e = Event{UID: uid, Amount: dto.Amount} return nil } 7
  4. 後方互換その2 バージョンごとに DTO と Unmarshal メソッドをつくる type raw struct {

    Version int `json:"version"` Event json.RawMessage `json:"data"` } func (e *Event) UnmarshalJSON(b []byte) error { var r raw _ = json.Unmarshal(b, &r) switch r.Version { case 1: return e.unmarshalJSONV1(r.Event) case 2: return e.unmarshalJSONV2(r.Event) } } 8
  5. 前方互換 v1 も Marshal する type envelope struct { Payloads

    map[string]json.RawMessage `json:"payloads"` } func (e Event) MarshalJSON() ([]byte, error) { v1, _ := json.Marshal(eventV1DTO{ /*...*/ }}) v2, _ := json.Marshal(eventV2DTO{ /*...*/ }}) return json.Marshal(envelope{ Payloads: map[string]json.RawMessage{ "v1": v1, "v2": v2, }, }) } 10