Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13792] ファイルの概要

このコミットは、Go言語の標準ライブラリである encoding/json パッケージにおける、匿名(埋め込み)フィールドの扱いに関する重要な変更を導入しています。具体的には、JSONエンコーディングおよびデコーディング時に、構造体に埋め込まれた匿名フィールドが適切に処理されるように改善されました。

コミット

commit f97bfb93f45339b1e528bd789495d894a2bfeece
Author: Russ Cox <rsc@golang.org>
Date:   Mon Sep 10 23:31:40 2012 -0400

    encoding/json: handle anonymous fields
    
    Fixes #3069.
    
    R=golang-dev, n13m3y3r
    CC=golang-dev
    https://golang.org/cl/6460044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/f97bfb93f45339b1e528bd789495d894a2bfeece

元コミット内容

encoding/json パッケージが匿名フィールドを処理するように変更されました。これにより、Issue #3069 が修正されます。

変更の背景

Go言語の encoding/json パッケージは、Goの構造体とJSONデータの間で変換を行うための重要なツールです。このコミットが導入される以前(Go 1.1より前)は、encoding/json は構造体内に埋め込まれた匿名フィールドを無視していました。これは、Goの構造体の埋め込み機能が、あたかも埋め込まれた型のフィールドが外側の構造体のフィールドであるかのように振る舞うという言語のセマンティクスと矛盾していました。

具体的には、以下のような問題がありました。

  1. 匿名フィールドの無視: 構造体に匿名で埋め込まれたフィールドは、JSONへのエンコード時、またはJSONからのデコード時に、そのフィールドが持つ値がJSON出力に含まれない、あるいはJSON入力からそのフィールドに値が設定されないという問題がありました。これにより、開発者はGoの埋め込み機能の恩恵をJSONシリアライズ/デシリアライズの文脈で十分に享受できませんでした。
  2. Issue #3069: この問題は、GoのIssueトラッカーで「Issue #3069: encoding/json: handle anonymous fields」として報告されていました。このIssueは、encoding/xml パッケージが埋め込みフィールドを処理する方法と同様に、encoding/json も匿名フィールドをフラット化して扱うべきであるという議論を促しました。

このコミットは、これらの問題を解決し、encoding/json パッケージがGoの構造体の埋め込みセマンティクスにより忠実になるように設計されました。

前提知識の解説

Go言語の構造体と匿名フィールド

Go言語では、構造体の中に他の構造体やインターフェースをフィールド名なしで宣言することができます。これを「匿名フィールド」または「埋め込みフィールド」と呼びます。匿名フィールドは、その型が持つメソッドやフィールドを、外側の構造体が直接持っているかのようにアクセスできるようにする、一種の継承のような機能を提供します。

例:

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person // 匿名フィールド
    ID     string
}

func main() {
    e := Employee{
        Person: Person{Name: "Alice", Age: 30},
        ID:     "E123",
    }
    fmt.Println(e.Name) // Alice (PersonのNameフィールドに直接アクセスできる)
}

encoding/json パッケージの基本的な動作

encoding/json パッケージは、Goの構造体のフィールド名とJSONのキーをマッピングして、Goの値をJSON形式に変換(マーシャリング)したり、JSONデータをGoの値に変換(アンマーシャリング)したりします。フィールド名とJSONキーのマッピングは、構造体タグ(json:"key_name")を使ってカスタマイズできます。

例:

type User struct {
    Name  string `json:"user_name"`
    Email string `json:"email_address"`
}

// User{Name: "Bob", Email: "bob@example.com"} は
// {"user_name": "Bob", "email_address": "bob@example.com"} にマーシャリングされる

変更前の encoding/json の匿名フィールドの扱い

このコミット以前は、encoding/json は匿名フィールドを特別扱いせず、単に無視していました。つまり、上記の Employee 構造体をJSONにマーシャリングしようとすると、Person 構造体のフィールド(Name, Age)はJSON出力に含まれませんでした。これは、Goの言語仕様における匿名フィールドの振る舞いと乖離していました。

技術的詳細

このコミットの主要な変更点は、encoding/json パッケージが構造体のフィールドを走査し、JSONエンコーディング/デコーディングの対象となるフィールドを決定するロジックを根本的に見直したことです。特に、匿名フィールドの処理が大幅に改善されました。

新しい動作は以下の原則に基づいています。

  1. 匿名フィールドのフラット化: 匿名で埋め込まれた構造体のエクスポートされたフィールドは、あたかもそれらが外側の構造体の直接のフィールドであるかのように、JSON出力にフラット化されます。これにより、Goの埋め込みセマンティクスがJSONシリアライズにも適用されるようになります。
  2. 名前の衝突解決: 複数の匿名フィールドや、匿名フィールドと外側の構造体のフィールドで同じJSONキー名(またはGoのフィールド名)が衝突した場合、encoding/json は特定の解決ルールに従います。
    • 優先順位: 一般的に、外側の構造体に近いフィールド、または構造体定義で後に出現するフィールドが優先されます。
    • タグの優先: 明示的な json タグを持つフィールドは、タグを持たないフィールドよりも優先されます。
    • 「アニヒレーション(Annihilation)」: 複数の匿名フィールドが同じ名前のフィールドを持つ場合、それらのフィールドは「アニヒレーション」され、JSONエンコーディング/デコーディングの対象から除外されることがあります。これは、曖昧さを避けるためのメカニズムです。
  3. json:"-" タグのサポート: フィールド(匿名フィールドを含む)に json:"-" タグが指定されている場合、そのフィールドはJSONのエンコーディングおよびデコーディングから完全に無視されます。これは、特定のフィールドを意図的にJSONから除外したい場合に有用です。
  4. omitempty オプションの適用: json:",omitempty" タグは、フィールドがその型のゼロ値(例: 数値の0、空文字列、nilポインタ、空のスライス/マップ)である場合に、JSON出力からそのフィールドを省略するために使用されます。この動作は匿名フィールドのフラット化されたフィールドにも適用されます。

これらの変更は、主に src/pkg/encoding/json/encode.gosrc/pkg/encoding/json/decode.go 内のフィールド探索ロジック、特に typeFields 関数(またはそれに相当するロジック)に影響を与えています。この関数は、構造体の型を受け取り、JSONのエンコーディング/デコーディングに関連するすべてのフィールド(匿名フィールドから昇格されたフィールドを含む)のリストを生成します。このリストは、フィールド名、JSONタグ、インデックスパス(ネストされたフィールドへのアクセスパス)、およびその他のオプション(omitemptystring)を含みます。

コアとなるコードの変更箇所

このコミットでは、主に以下の3つのファイルが変更されています。

  1. src/pkg/encoding/json/decode.go: JSONデコード時の匿名フィールドの処理ロジックが変更されました。特に、構造体のフィールドを検索する部分が、新しい cachedTypeFields 関数を使用するように書き換えられています。これにより、匿名フィールドが適切に解決され、対応するJSONキーがGoの構造体フィールドにマッピングされるようになります。

    • 変更前は、sv.NumField() をループして手動でフィールドを検索し、タグや名前の一致を調べていました。匿名フィールドは continue でスキップされていました。
    • 変更後は、cachedTypeFields(sv.Type()) を呼び出して、事前に計算されたフィールドのリスト (field 構造体のスライス) を取得し、そのリストからキーに一致するフィールドを探すようになりました。これにより、匿名フィールドの解決ロジックが typeFields 関数に集約されました。
  2. src/pkg/encoding/json/decode_test.go: 匿名フィールドのデコードに関する新しいテストケースが多数追加されました。これにより、新しい動作が期待通りであることを検証しています。

    • Point, Top, Embed0, Embed0a, Embed0b, Embed0c, Embed0p, Embed0q, Loop, S5, S6, S7, S8, S9, S10, S11, S12, S13, Ambig といった、匿名フィールドや名前の衝突、タグの優先順位などを検証するための複雑な構造体が定義されています。
    • これらの構造体を用いた unmarshalTest のエントリが追加され、様々なシナリオでのJSONデコードの挙動がテストされています。
    • 既存の TestAnonymous 関数は削除されました。これは、匿名フィールドが無視されることをテストしていたものであり、新しい動作では無視されないため不要になったためです。
  3. src/pkg/encoding/json/encode.go: JSONエンコード時の匿名フィールドの処理ロジックが変更されました。特に、構造体のフィールドを走査してエンコードする部分が、新しい cachedTypeFields 関数を使用するように書き換えられています。

    • 変更前は encodeFields(v.Type()) を使用していましたが、変更後は cachedTypeFields(v.Type()) を使用しています。
    • encodeField 構造体と encodeFields 関数が削除され、代わりに field 構造体と typeFields/cachedTypeFields 関数が導入されました。
    • typeFields 関数は、構造体の型を受け取り、JSONエンコーディングの対象となるすべてのフィールド(匿名フィールドから昇格されたフィールドを含む)のリストを生成します。この関数は、幅優先探索(BFS)アルゴリズムを使用して、埋め込み構造体を再帰的に探索し、フィールドの衝突解決(名前、タグ、深さに基づく)を行います。
    • fieldByIndex ヘルパー関数が追加され、reflect.Value とインデックスパス([]int)からネストされたフィールドの reflect.Value を取得できるようになりました。
    • byNamebyIndex というソートヘルパーが追加され、フィールドのリストを名前やインデックス順にソートするために使用されます。これは、衝突解決と決定論的なフィールド順序を保証するために重要です。
    • fieldCache というグローバルキャッシュが導入され、cachedTypeFields が計算結果をキャッシュすることで、同じ型に対するフィールド探索の繰り返しを避けるように最適化されました。

コアとなるコードの解説

このコミットの核心は、src/pkg/encoding/json/encode.go に新しく導入された typeFields 関数とその関連ヘルパーです。この関数は、Goの構造体からJSONエンコーディング/デコーディングの対象となるフィールドのリストを構築する複雑なロジックをカプセル化しています。

typeFields 関数の役割

typeFields(t reflect.Type) []field 関数は、与えられた reflect.Type (通常は構造体の型)に対して、JSONのマーシャリングおよびアンマーシャリングで考慮すべきすべてのフィールドを field 構造体のスライスとして返します。この関数は、以下の重要な処理を行います。

  1. 幅優先探索 (BFS): 構造体とその中に埋め込まれた匿名フィールドを幅優先で探索します。これにより、外側の構造体に近いフィールドが優先されるというGoの埋め込みセマンティクスが反映されます。
  2. フィールドの収集: 各フィールドについて、その名前、JSONタグの有無、インデックスパス([]int で表現される、reflect.Value.FieldByIndex で使用されるパス)、元の reflect.Typeomitempty オプション、string オプションなどの情報を field 構造体に格納します。
  3. 匿名フィールドの昇格: 匿名フィールドの場合、そのエクスポートされたフィールドが外側の構造体のフィールドとして「昇格」されます。ただし、json:"-" タグが指定されている場合は無視されます。
  4. 名前の衝突解決: 複数のフィールドが同じJSONキー名に解決される場合(特に匿名フィールドが原因で発生しやすい)、typeFields は以下のルールで衝突を解決します。
    • byName ソート: まず、フィールドを名前でソートし、次に深さ(インデックスパスの長さ)、そしてJSONタグの有無でソートします。これにより、同じ名前のフィールドが隣接するように配置されます。
    • アニヒレーション: ソート後、同じ名前を持つフィールドが複数存在し、かつそれらのフィールドが明示的なJSONタグを持たない場合(またはタグの優先順位が低い場合)、それらのフィールドは「アニヒレーション」され、結果のリストから削除されます。これは、曖昧なフィールドをJSON処理から除外することで、予期せぬ動作を防ぐためのものです。
  5. byIndex ソート: 最終的に、衝突解決されたフィールドのリストは、元の構造体での宣言順(インデックスパス順)にソートされます。これにより、JSON出力のフィールド順序が決定論的になります。

field 構造体

新しく導入された field 構造体は、JSONエンコーディング/デコーディングに関連する各フィールドのメタデータを保持します。

type field struct {
    name      string    // JSONキー名
    tag       bool      // JSONタグが明示的に指定されたか
    index     []int     // reflect.Value.FieldByIndexで使用するインデックスパス
    typ       reflect.Type // フィールドの型
    omitEmpty bool      // omitemptyオプションが指定されたか
    quoted    bool      // ,stringオプションが指定されたか
}

cachedTypeFields 関数

cachedTypeFields(t reflect.Type) []field は、typeFields のラッパー関数であり、計算されたフィールドのリストをグローバルキャッシュ (fieldCache) に保存し、再利用することでパフォーマンスを向上させます。これにより、同じ構造体型に対して何度もフィールド探索を行う必要がなくなります。

これらの変更により、encoding/json パッケージはGoの匿名フィールドをより自然に、かつ予測可能な方法で処理できるようになり、Goの構造体とJSON間のマッピングがより強力で柔軟になりました。

関連リンク

参考にした情報源リンク

# [インデックス 13792] ファイルの概要

このコミットは、Go言語の標準ライブラリである `encoding/json` パッケージにおける、匿名(埋め込み)フィールドの扱いに関する重要な変更を導入しています。具体的には、JSONエンコーディングおよびデコーディング時に、構造体に埋め込まれた匿名フィールドが適切に処理されるように改善されました。

## コミット

commit f97bfb93f45339b1e528bd789495d894a2bfeece Author: Russ Cox rsc@golang.org Date: Mon Sep 10 23:31:40 2012 -0400

encoding/json: handle anonymous fields

Fixes #3069.

R=golang-dev, n13m3y3r
CC=golang-dev
https://golang.org/cl/6460044

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/f97bfb93f45339b1e528bd789495d894a2bfeece](https://github.com/golang/go/commit/f97bfb93f45339b1e528bd789495d894a2bfeece)

## 元コミット内容

`encoding/json` パッケージが匿名フィールドを処理するように変更されました。これにより、Issue #3069 が修正されます。

## 変更の背景

Go言語の `encoding/json` パッケージは、Goの構造体とJSONデータの間で変換を行うための重要なツールです。このコミットが導入される以前(Go 1.1より前)は、`encoding/json` は構造体内に埋め込まれた匿名フィールドを無視していました。これは、Goの構造体の埋め込み機能が、あたかも埋め込まれた型のフィールドが外側の構造体のフィールドであるかのように振る舞うという言語のセマンティクスと矛盾していました。

具体的には、以下のような問題がありました。

1.  **匿名フィールドの無視**: 構造体に匿名で埋め込まれたフィールドは、JSONへのエンコード時、またはJSONからのデコード時に、そのフィールドが持つ値がJSON出力に含まれない、あるいはJSON入力からそのフィールドに値が設定されないという問題がありました。これにより、開発者はGoの埋め込み機能の恩恵をJSONシリアライズ/デシリアライズの文脈で十分に享受できませんでした。
2.  **Issue #3069**: この問題は、GoのIssueトラッカーで「Issue #3069: encoding/json: handle anonymous fields」として報告されていました。このIssueは、`encoding/xml` パッケージが埋め込みフィールドを処理する方法と同様に、`encoding/json` も匿名フィールドをフラット化して扱うべきであるという議論を促しました。

このコミットは、これらの問題を解決し、`encoding/json` パッケージがGoの構造体の埋め込みセマンティクスにより忠実になるように設計されました。

## 前提知識の解説

### Go言語の構造体と匿名フィールド

Go言語では、構造体の中に他の構造体やインターフェースをフィールド名なしで宣言することができます。これを「匿名フィールド」または「埋め込みフィールド」と呼びます。匿名フィールドは、その型が持つメソッドやフィールドを、外側の構造体が直接持っているかのようにアクセスできるようにする、一種の継承のような機能を提供します。

例:
```go
type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person // 匿名フィールド
    ID     string
}

func main() {
    e := Employee{
        Person: Person{Name: "Alice", Age: 30},
        ID:     "E123",
    }
    fmt.Println(e.Name) // Alice (PersonのNameフィールドに直接アクセスできる)
}

encoding/json パッケージの基本的な動作

encoding/json パッケージは、Goの構造体のフィールド名とJSONのキーをマッピングして、Goの値をJSON形式に変換(マーシャリング)したり、JSONデータをGoの値に変換(アンマーシャリング)したりします。フィールド名とJSONキーのマッピングは、構造体タグ(json:"key_name")を使ってカスタマイズできます。

例:

type User struct {
    Name  string `json:"user_name"`
    Email string `json:"email_address"`
}

// User{Name: "Bob", Email: "bob@example.com"} は
// {"user_name": "Bob", "email_address": "bob@example.com"} にマーシャリングされる

変更前の encoding/json の匿名フィールドの扱い

このコミット以前は、encoding/json は匿名フィールドを特別扱いせず、単に無視していました。つまり、上記の Employee 構造体をJSONにマーシャリングしようとすると、Person 構造体のフィールド(Name, Age)はJSON出力に含まれませんでした。これは、Goの言語仕様における匿名フィールドの振る舞いと乖離していました。

技術的詳細

このコミットの主要な変更点は、encoding/json パッケージが構造体のフィールドを走査し、JSONエンコーディング/デコーディングの対象となるフィールドを決定するロジックを根本的に見直したことです。特に、匿名フィールドの処理が大幅に改善されました。

新しい動作は以下の原則に基づいています。

  1. 匿名フィールドのフラット化: 匿名で埋め込まれた構造体のエクスポートされたフィールドは、あたかもそれらが外側の構造体の直接のフィールドであるかのように、JSON出力にフラット化されます。これにより、Goの埋め込みセマンティクスがJSONシリアライズにも適用されるようになります。
  2. 名前の衝突解決: 複数の匿名フィールドや、匿名フィールドと外側の構造体のフィールドで同じJSONキー名(またはGoのフィールド名)が衝突した場合、encoding/json は特定の解決ルールに従います。
    • 優先順位: 一般的に、外側の構造体に近いフィールド、または構造体定義で後に出現するフィールドが優先されます。
    • タグの優先: 明示的な json タグを持つフィールドは、タグを持たないフィールドよりも優先されます。
    • 「アニヒレーション(Annihilation)」: 複数の匿名フィールドが同じ名前のフィールドを持つ場合、それらのフィールドは「アニヒレーション」され、JSONエンコーディング/デコーディングの対象から除外されることがあります。これは、曖昧さを避けるためのメカニズムです。
  3. json:"-" タグのサポート: フィールド(匿名フィールドを含む)に json:"-" タグが指定されている場合、そのフィールドはJSONのエンコーディングおよびデコーディングから完全に無視されます。これは、特定のフィールドを意図的にJSONから除外したい場合に有用です。
  4. omitempty オプションの適用: json:",omitempty" タグは、フィールドがその型のゼロ値(例: 数値の0、空文字列、nilポインタ、空のスライス/マップ)である場合に、JSON出力からそのフィールドを省略するために使用されます。この動作は匿名フィールドのフラット化されたフィールドにも適用されます。

これらの変更は、主に src/pkg/encoding/json/encode.gosrc/pkg/encoding/json/decode.go 内のフィールド探索ロジック、特に typeFields 関数(またはそれに相当するロジック)に影響を与えています。この関数は、構造体の型を受け取り、JSONのエンコーディング/デコーディングに関連するすべてのフィールド(匿名フィールドから昇格されたフィールドを含む)のリストを生成します。このリストは、フィールド名、JSONタグ、インデックスパス(ネストされたフィールドへのアクセスパス)、およびその他のオプション(omitemptystring)を含みます。

コアとなるコードの変更箇所

このコミットでは、主に以下の3つのファイルが変更されています。

  1. src/pkg/encoding/json/decode.go: JSONデコード時の匿名フィールドの処理ロジックが変更されました。特に、構造体のフィールドを検索する部分が、新しい cachedTypeFields 関数を使用するように書き換えられています。これにより、匿名フィールドが適切に解決され、対応するJSONキーがGoの構造体フィールドにマッピングされるようになります。

    • 変更前は、sv.NumField() をループして手動でフィールドを検索し、タグや名前の一致を調べていました。匿名フィールドは continue でスキップされていました。
    • 変更後は、cachedTypeFields(sv.Type()) を呼び出して、事前に計算されたフィールドのリスト (field 構造体のスライス) を取得し、そのリストからキーに一致するフィールドを探すようになりました。これにより、匿名フィールドの解決ロジックが typeFields 関数に集約されました。
  2. src/pkg/encoding/json/decode_test.go: 匿名フィールドのデコードに関する新しいテストケースが多数追加されました。これにより、新しい動作が期待通りであることを検証しています。

    • Point, Top, Embed0, Embed0a, Embed0b, Embed0c, Embed0p, Embed0q, Loop, S5, S6, S7, S8, S9, S10, S11, S12, S13, Ambig といった、匿名フィールドや名前の衝突、タグの優先順位などを検証するための複雑な構造体が定義されています。
    • これらの構造体を用いた unmarshalTest のエントリが追加され、様々なシナリオでのJSONデコードの挙動がテストされています。
    • 既存の TestAnonymous 関数は削除されました。これは、匿名フィールドが無視されることをテストしていたものであり、新しい動作では無視されないため不要になったためです。
  3. src/pkg/encoding/json/encode.go: JSONエンコード時の匿名フィールドの処理ロジックが変更されました。特に、構造体のフィールドを走査してエンコードする部分が、新しい cachedTypeFields 関数を使用するように書き換えられています。

    • 変更前は encodeFields(v.Type()) を使用していましたが、変更後は cachedTypeFields(v.Type()) を使用しています。
    • encodeField 構造体と encodeFields 関数が削除され、代わりに field 構造体と typeFields/cachedTypeFields 関数が導入されました。
    • typeFields 関数は、構造体の型を受け取り、JSONエンコーディングの対象となるすべてのフィールド(匿名フィールドから昇格されたフィールドを含む)のリストを生成します。この関数は、幅優先探索(BFS)アルゴリズムを使用して、埋め込み構造体を再帰的に探索し、フィールドの衝突解決(名前、タグ、深さに基づく)を行います。
    • fieldByIndex ヘルパー関数が追加され、reflect.Value とインデックスパス([]int)からネストされたフィールドの reflect.Value を取得できるようになりました。
    • byNamebyIndex というソートヘルパーが追加され、フィールドのリストを名前やインデックス順にソートするために使用されます。これは、衝突解決と決定論的なフィールド順序を保証するために重要です。
    • fieldCache というグローバルキャッシュが導入され、cachedTypeFields が計算結果をキャッシュすることで、同じ型に対するフィールド探索の繰り返しを避けるように最適化されました。

コアとなるコードの解説

このコミットの核心は、src/pkg/encoding/json/encode.go に新しく導入された typeFields 関数とその関連ヘルパーです。この関数は、Goの構造体からJSONエンコーディング/デコーディングの対象となるフィールドのリストを構築する複雑なロジックをカプセル化しています。

typeFields 関数の役割

typeFields(t reflect.Type) []field 関数は、与えられた reflect.Type (通常は構造体の型)に対して、JSONのマーシャリングおよびアンマーシャリングで考慮すべきすべてのフィールドを field 構造体のスライスとして返します。この関数は、以下の重要な処理を行います。

  1. 幅優先探索 (BFS): 構造体とその中に埋め込まれた匿名フィールドを幅優先で探索します。これにより、外側の構造体に近いフィールドが優先されるというGoの埋め込みセマンティクスが反映されます。
  2. フィールドの収集: 各フィールドについて、その名前、JSONタグの有無、インデックスパス([]int で表現される、reflect.Value.FieldByIndex で使用されるパス)、元の reflect.Typeomitempty オプション、string オプションなどの情報を field 構造体に格納します。
  3. 匿名フィールドの昇格: 匿名フィールドの場合、そのエクスポートされたフィールドが外側の構造体のフィールドとして「昇格」されます。ただし、json:"-" タグが指定されている場合は無視されます。
  4. 名前の衝突解決: 複数のフィールドが同じJSONキー名に解決される場合(特に匿名フィールドが原因で発生しやすい)、typeFields は以下のルールで衝突を解決します。
    • byName ソート: まず、フィールドを名前でソートし、次に深さ(インデックスパスの長さ)、そしてJSONタグの有無でソートします。これにより、同じ名前のフィールドが隣接するように配置されます。
    • アニヒレーション: ソート後、同じ名前を持つフィールドが複数存在し、かつそれらのフィールドが明示的なJSONタグを持たない場合(またはタグの優先順位が低い場合)、それらのフィールドは「アニヒレーション」され、結果のリストから削除されます。これは、曖昧なフィールドをJSON処理から除外することで、予期せぬ動作を防ぐためのものです。
  5. byIndex ソート: 最終的に、衝突解決されたフィールドのリストは、元の構造体での宣言順(インデックスパス順)にソートされます。これにより、JSON出力のフィールド順序が決定論的になります。

field 構造体

新しく導入された field 構造体は、JSONエンコーディング/デコーディングに関連する各フィールドのメタデータを保持します。

type field struct {
    name      string    // JSONキー名
    tag       bool      // JSONタグが明示的に指定されたか
    index     []int     // reflect.Value.FieldByIndexで使用するインデックスパス
    typ       reflect.Type // フィールドの型
    omitEmpty bool      // omitemptyオプションが指定されたか
    quoted    bool      // ,stringオプションが指定されたか
}

cachedTypeFields 関数

cachedTypeFields(t reflect.Type) []field は、typeFields のラッパー関数であり、計算されたフィールドのリストをグローバルキャッシュ (fieldCache) に保存し、再利用することでパフォーマンスを向上させます。これにより、同じ構造体型に対して何度もフィールド探索を行う必要がなくなります。

これらの変更により、encoding/json パッケージはGoの匿名フィールドをより自然に、かつ予測可能な方法で処理できるようになり、Goの構造体とJSON間のマッピングがより強力で柔軟になりました。

関連リンク

参考にした情報源リンク