[インデックス 12158] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/xml
パッケージにおいて、匿名フィールド(anonymous fields)の扱いに関するドキュメントと例を追加・修正するものです。特に、ポインタではない匿名構造体フィールドがXMLのマーシャリング(Go構造体からXMLへの変換)およびアンマーシャリング(XMLからGo構造体への変換)でどのように扱われるかについて、その挙動を明確化し、具体的な使用例を example_test.go
に追加しています。また、匿名ポインタフィールドがまだサポートされていないこと、およびその問題がIssue 3108で追跡されていることを明記しています。
コミット
commit 6c20f5c0135483da1127d15e724b2bf1608833d7
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date: Thu Feb 23 01:35:50 2012 -0200
encoding/xml: add example and docs for anon fields
Anonymous pointer fields is not yet supported.
The problem is documented in issue 3108.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5694043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6c20f5c0135483da1127d15e724b2bf1608833d7
元コミット内容
encoding/xml: add example and docs for anon fields
Anonymous pointer fields is not yet supported.
The problem is documented in issue 3108.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5694043
変更の背景
Go言語の encoding/xml
パッケージは、Goの構造体とXMLデータ間の変換(マーシャリングとアンマーシャリング)を提供します。Goの構造体には「匿名フィールド(embedded fields)」という特徴があり、これはある構造体の中に別の構造体をフィールド名なしで埋め込むことができる機能です。これにより、埋め込まれた構造体のフィールドが、外側の構造体のフィールドであるかのように直接アクセスできるようになります。
しかし、encoding/xml
パッケージが匿名フィールドをどのように扱うか、特にポインタではない匿名構造体と匿名ポインタ構造体の場合で、その挙動が明確ではありませんでした。このコミットの主な背景は、以下の2点に集約されます。
- 非ポインタ匿名構造体の挙動の明確化:
encoding/xml
は、ポインタではない匿名構造体フィールドを、そのフィールドが外側の構造体の一部であるかのように扱います。つまり、埋め込まれた構造体のフィールドが、XML要素として直接外側の構造体のレベルで現れることを意味します。この挙動は、Goの匿名フィールドのセマンティクスと一致しており、XMLの構造をフラット化するのに役立ちます。このコミットでは、この重要な挙動をドキュメントに明記し、具体的な例を追加することで、開発者がより正確にencoding/xml
を利用できるようにしています。 - 匿名ポインタフィールドの未サポート問題: コミットメッセージにも明記されている通り、当時の
encoding/xml
パッケージは匿名ポインタフィールド(例:*Address
のようにポインタとして埋め込まれた構造体)のマーシャリング/アンマーシャリングをサポートしていませんでした。この問題はGoのIssue 3108として追跡されており、このコミットは、この未サポートの現状をドキュメントに記載することで、開発者が予期せぬ挙動に遭遇するのを防ぐことを目的としています。
これらの変更は、encoding/xml
パッケージの使いやすさと信頼性を向上させ、開発者がXML処理を行う際の混乱を減らすことに貢献しています。
前提知識の解説
Go言語の匿名フィールド(Embedded Fields)
Go言語の構造体は、他の構造体をフィールド名なしで埋め込むことができます。これを匿名フィールド(または埋め込みフィールド)と呼びます。匿名フィールドの主な特徴は以下の通りです。
- フィールドの昇格: 埋め込まれた構造体のフィールドやメソッドは、外側の構造体のフィールドやメソッドであるかのように直接アクセスできます。例えば、
type Person struct { Address }
の場合、Person
のインスタンスp
からp.City
のようにAddress
構造体のCity
フィールドにアクセスできます。 - 継承の代替: Goにはクラス継承の概念はありませんが、匿名フィールドはコードの再利用と「is-a」関係のモデリングにおいて、継承に似た機能を提供します。
- 名前の衝突: 埋め込まれた構造体と外側の構造体でフィールド名が衝突した場合、外側の構造体のフィールドが優先されます。
encoding/xml
パッケージ
encoding/xml
パッケージは、Goの構造体とXMLドキュメントの間でデータを変換するための標準ライブラリです。主な機能は以下の通りです。
- マーシャリング(
xml.Marshal
,xml.MarshalIndent
): Goの構造体のデータをXML形式のバイトスライスに変換します。構造体のフィールドにタグ(xml:"tagname,attr"
など)を付けることで、XML要素名、属性、テキストコンテンツなどを制御できます。 - アンマーシャリング(
xml.Unmarshal
): XML形式のバイトスライスをGoの構造体に変換します。XMLの要素や属性を、対応する構造体フィールドにマッピングします。 - XMLタグ: 構造体フィールドのタグは、XML要素の名前、属性、処理方法(例:
omitempty
で空の場合に省略、,comment
でコメントとして扱う)を定義するために使用されます。
XMLの構造とGo構造体のマッピング
encoding/xml
は、Goの構造体フィールドをXML要素にマッピングする際に、いくつかのルールに従います。
- フィールド名とXML要素名: デフォルトでは、Goの構造体フィールド名がXML要素名として使用されます。タグで明示的に指定することもできます。
- 属性:
xml:"name,attr"
のようにタグに,attr
を追加すると、そのフィールドはXML要素の属性として扱われます。 - テキストコンテンツ: タグなしのフィールドや、特定のタグを持つフィールドがXML要素のテキストコンテンツとして扱われることがあります。
- ネストされた要素: 構造体の中に別の構造体がフィールドとして含まれている場合、通常はネストされたXML要素として表現されます。
このコミットで扱われる「匿名フィールド」は、このネストの挙動に影響を与え、XML構造をフラット化する可能性を秘めています。
Go Issue 3108
GoのIssue 3108は、「encoding/xml
: anonymous pointer fields are not marshaled/unmarshaled」というタイトルで、匿名ポインタフィールドが encoding/xml
パッケージで正しく処理されない問題について議論されています。この問題は、ポインタではない匿名構造体とは異なり、匿名ポインタ構造体(例: *MyStruct
)がXML変換時に無視されるか、予期せぬ挙動を示すことを指摘しています。このコミットが作成された時点では、この問題は未解決であり、コミットはそれをドキュメントに明記することで、ユーザーに注意を促しています。
技術的詳細
このコミットは、encoding/xml
パッケージの挙動、特に匿名構造体フィールドの処理に関するドキュメントとテスト例を改善しています。
-
匿名非ポインタ構造体のサポートの明確化:
src/pkg/encoding/xml/marshal.go
とsrc/pkg/encoding/xml/read.go
のドキュメントコメントに、以下の記述が追加されました。marshal.go
(Marshal
関数のドキュメント内):
これは、ポインタではない匿名構造体フィールドが、そのフィールドの値が外側の構造体の一部であるかのように扱われることを明確にしています。つまり、マーシャリング時に、埋め込まれた構造体のフィールドが、外側の構造体のXML要素の直下にフラットに展開されることを意味します。// - a non-pointer anonymous struct field is handled as if the // fields of its value were part of the outer struct.
read.go
(Unmarshal
関数のドキュメント内):
これは、アンマーシャリング時にも同様に、XML要素が外側の構造体の匿名フィールドの内部に直接マッピングされることを示しています。// * A non-pointer anonymous struct field is handled as if the // fields of its value were part of the outer struct.
- このドキュメントの追加により、開発者は匿名構造体を使用する際に、XMLの構造がどのように変化するかを正確に理解できるようになります。
-
example_test.go
の更新:ExampleMarshalIndent
関数とExampleUnmarshal
関数に、Address
という匿名構造体を追加し、それがPerson
またはResult
構造体に埋め込まれる例が追加されました。- これにより、匿名構造体がXMLにマーシャリングされると、
Address
構造体のCity
とState
フィールドがPerson
またはResult
要素の直下の要素として現れることが視覚的に示されます。 - 同様に、アンマーシャリングの例でも、XML内の
City
とState
要素が、Go構造体の匿名Address
フィールドに正しくマッピングされることが示されています。 - この具体的な例は、ドキュメントの記述を補完し、実際のコードでの挙動を明確に示しています。
-
匿名ポインタフィールドの未サポートの言及:
- コミットメッセージと
example_test.go
のコメントで、匿名ポインタフィールド(例:*Address
)がencoding/xml
パッケージでまだサポートされていないことが明記されています。 - この問題はGoのIssue 3108として追跡されており、このコミットは、この制限をユーザーに知らせることで、誤解やバグの発生を防ぐことを目的としています。
- コミットメッセージと
これらの変更は、encoding/xml
パッケージのドキュメントの正確性を高め、開発者が匿名フィールドをXML処理に利用する際のガイドラインを提供します。特に、非ポインタ匿名構造体がXML構造をフラット化する挙動は、Goの構造体設計とXMLの表現をより密接に連携させる上で重要です。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下の3つのファイルにわたります。
-
src/pkg/encoding/xml/example_test.go
:ExampleMarshalIndent
関数とExampleUnmarshal
関数に、匿名構造体Address
を含む新しい構造体定義と、そのマーシャリング/アンマーシャリングの例が追加されました。Address
構造体はCity
とState
フィールドを持ちます。Person
構造体とResult
構造体にAddress
が匿名フィールドとして追加されています。- 出力されるXMLとGo構造体の値が、匿名フィールドの挙動を反映するように修正されています。
- 匿名ポインタフィールドがまだサポートされていないことに関するコメントが追加されています。
--- a/src/pkg/encoding/xml/example_test.go +++ b/src/pkg/encoding/xml/example_test.go @@ -11,6 +11,9 @@ import ( ) func ExampleMarshalIndent() { + type Address struct { + City, State string + } type Person struct { XMLName xml.Name `xml:"person"` Id int `xml:"id,attr"` @@ -19,11 +22,13 @@ func ExampleMarshalIndent() { Age int `xml:"age"` Height float32 `xml:"height,omitempty"` Married bool - Comment string `xml:",comment"` + Address + Comment string `xml:",comment"` } v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42} - v.Comment = " Need more fields. " + v.Comment = " Need more details. " + v.Address = Address{"Hanga Roa", "Easter Island"} output, err := xml.MarshalIndent(v, " ", " ") if err != nil { @@ -39,7 +44,9 @@ func ExampleMarshalIndent() { // </name> // <age>42</age> // <Married>false</Married> - // <!-- Need more fields. --> + // <City>Hanga Roa</City> + // <State>Easter Island</State> + // <!-- Need more details. --> // </person> } @@ -52,14 +59,19 @@ func ExampleUnmarshal() { Where string `xml:"where,attr"` Addr string } + type Address struct { + City, State string + } type Result struct { XMLName xml.Name `xml:"Person"` Name string `xml:"FullName"` Phone string Email []Email Groups []string `xml:"Group>Value"` + Address } - p := Result{Name: "none", Phone: "none"} + v := Result{Name: "none", Phone: "none"} + v.Address = Address{"Hanga Roa", "Easter Island"} data := ` <Person> @@ -77,20 +89,22 @@ func ExampleUnmarshal() { <Address>123 Main Street</Address> </Person> ` - err := xml.Unmarshal([]byte(data), &p) + err := xml.Unmarshal([]byte(data), &v) if err != nil { fmt.Printf("error: %v", err) return } - fmt.Printf("XMLName: %#v\n", p.XMLName) - fmt.Printf("Name: %q\n", p.Name) - fmt.Printf("Phone: %q\n", p.Phone) - fmt.Printf("Email: %v\n", p.Email) - fmt.Printf("Groups: %v\n", p.Groups) + fmt.Printf("XMLName: %#v\n", v.XMLName) + fmt.Printf("Name: %q\n", v.Name) + fmt.Printf("Phone: %q\n", v.Phone) + fmt.Printf("Email: %v\n", v.Email) + fmt.Printf("Groups: %v\n", v.Groups) + fmt.Printf("Address: %v\n", v.Address) // Output: // XMLName: xml.Name{Space:"", Local:"Person"} // Name: "Grace R. Emlin" // Phone: "none" // Email: [{home gre@example.com} {work gre@work.com}] // Groups: [Friends Squash] + // Address: {Hanga Roa Easter Island} }
-
src/pkg/encoding/xml/marshal.go
:Marshal
関数のドキュメントコメントに、非ポインタ匿名構造体フィールドの挙動に関する説明が追加されました。
--- a/src/pkg/encoding/xml/marshal.go +++ b/src/pkg/encoding/xml/marshal.go @@ -57,6 +57,8 @@ const ( // if the field value is empty. The empty values are false, 0, any // nil pointer or interface value, and any array, slice, map, or // string of length zero. +// - a non-pointer anonymous struct field is handled as if the +// fields of its value were part of the outer struct. // // If a field uses a tag "a>b>c", then the element c will be nested inside // parent elements a and b. Fields that appear next to each other that name
-
src/pkg/encoding/xml/read.go
:Unmarshal
関数のドキュメントコメントに、非ポインタ匿名構造体フィールドの挙動に関する説明が追加されました。
--- a/src/pkg/encoding/xml/read.go +++ b/src/pkg/encoding/xml/read.go @@ -81,6 +81,9 @@ import ( // of the above rules and the struct has a field with tag ",any", // unmarshal maps the sub-element to that struct field. // +// * A non-pointer anonymous struct field is handled as if the +// fields of its value were part of the outer struct. +// // * A struct field with tag "-" is never unmarshalled into. // // Unmarshal maps an XML element to a string or []byte by saving the
コアとなるコードの解説
このコミットのコアとなる変更は、encoding/xml
パッケージが匿名構造体フィールドをどのように扱うかという、そのセマンティクスを明確化し、具体的な例で示すことにあります。
example_test.go
の変更
example_test.go
の変更は、匿名構造体 Address
を Person
および Result
構造体に埋め込むことで、その挙動を実演しています。
-
マーシャリングの例 (
ExampleMarshalIndent
):Person
構造体にAddress
が匿名フィールドとして追加されています。v.Address = Address{"Hanga Roa", "Easter Island"}
でAddress
フィールドに値が設定されます。- 出力されるXMLを見ると、
Address
構造体のフィールドである<City>Hanga Roa</City>
と<State>Easter Island</State>
が、Person
要素の直下に、あたかもPerson
自身のフィールドであるかのようにフラットに展開されています。これは、Goの匿名フィールドの「フィールドの昇格」という概念がXMLマーシャリングにも適用されていることを示しています。
-
アンマーシャリングの例 (
ExampleUnmarshal
):Result
構造体にもAddress
が匿名フィールドとして追加されています。- 入力XMLには
<City>Hanga Roa</City>
と<State>Easter Island</State>
がPerson
要素の直下にあります。 - アンマーシャリング後、
v.Address
にこれらの値が正しくマッピングされていることがfmt.Printf("Address: %v\n", v.Address)
の出力で確認できます。 - この例は、XMLのフラットな構造から、Go構造体の匿名フィールドへ正しくデータを読み込むことができることを示しています。
これらのテスト例は、非ポインタ匿名構造体がXML変換において「外側の構造体の一部であるかのように」扱われるというドキュメントの記述を具体的に裏付けています。
marshal.go
と read.go
のドキュメント変更
marshal.go
と read.go
のドキュメントコメントに追加された記述は、この挙動を公式に定義するものです。
marshal.go
の変更は、xml.Marshal
が非ポインタ匿名構造体のフィールドを外側の構造体のフィールドとして扱うことを明記しています。これは、XML出力がよりフラットになり、埋め込み構造体のフィールドが直接親要素の子要素として現れることを意味します。read.go
の変更は、xml.Unmarshal
が同様にXML要素を非ポインタ匿名構造体のフィールドに直接マッピングすることを示しています。これにより、XMLの構造がGoの匿名フィールドのセマンティクスと一致するように解釈されます。
これらのドキュメントの追加は、encoding/xml
パッケージの挙動に関する曖昧さを解消し、開発者が匿名フィールドを効果的に利用するための明確な指針を提供します。特に、匿名ポインタフィールドがまだサポートされていないという注意書きは、潜在的なバグや混乱を避ける上で非常に重要です。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/6c20f5c0135483da1127d15e724b2bf1608833d7
- Go CL (Code Review): https://golang.org/cl/5694043
- Go Issue 3108:
encoding/xml
: anonymous pointer fields are not marshaled/unmarshaled: https://github.com/golang/go/issues/3108
参考にした情報源リンク
- Go言語の公式ドキュメント:
encoding/xml
パッケージ - Go言語の公式ドキュメント: 構造体(Structs)と匿名フィールド(Embedded Fields)
- Go Issue 3108の議論スレッド