[インデックス 17218] ファイルの概要
このコミットは、Go言語の encoding/xml
パッケージに Marshaler
インターフェースと MarshalerAttr
インターフェースを追加し、XMLマーシャリングのカスタマイズを可能にするものです。これにより、ユーザー定義型が自身のXML表現を制御できるようになります。
コミット
commit 85f3acd788484d9b34aa48c113d4c8f0c4b4ea2f
Author: Russ Cox <rsc@golang.org>
Date: Wed Aug 14 00:17:42 2013 -0400
encoding/xml: add, support Marshaler interface
See golang.org/s/go12xml for design.
Fixes #2771.
Fixes #4169.
Fixes #5975.
Fixes #6125.
R=golang-dev, iant, dan.kortschak
CC=golang-dev
https://golang.org/cl/12603044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/85f3acd788484d9b34aa48c113d4c8f0c4b4ea2f
元コミット内容
encoding/xml: add, support Marshaler interface
このコミットは、encoding/xml
パッケージに Marshaler
インターフェースを追加し、そのサポートを実装します。これにより、Goの型がXMLへの自身のマーシャリング方法を定義できるようになります。設計については golang.org/s/go12xml
を参照してください。
以下のIssueを修正します:
- #2771
- #4169
- #5975
- #6125
変更の背景
Goの encoding/xml
パッケージは、Goの構造体をXMLに、またはXMLをGoの構造体に変換するための機能を提供します。しかし、Go 1.1以前のバージョンでは、ユーザーが自身の型をXMLにマーシャリングする際の挙動を細かく制御する手段が限られていました。特に、特定のデータ構造をXMLの特定の形式に正確にマッピングしたい場合や、デフォルトのマーシャリングルールでは対応できない複雑なXML構造を扱いたい場合に、柔軟性が不足していました。
このコミットは、golang.org/s/go12xml
で提案された設計に基づいています。この設計は、encoding/json
パッケージの json.Marshaler
インターフェースと同様に、ユーザーがXMLマーシャリングのロジックをカスタム実装できるようにすることを目的としています。これにより、以下のような問題が解決されます。
- Issue #2771: XML出力のカスタマイズに関する一般的な要望。
- Issue #4169: XML属性のカスタムマーシャリングに関する要望。
- Issue #5975: XML要素のカスタムマーシャリングに関する要望。
- Issue #6125:
xml.Encoder
の低レベルな操作(トークン単位での書き込み)の必要性。
これらの問題は、Goの encoding/xml
パッケージが、より複雑なXML構造や特定のXMLスキーマに準拠した出力を生成する際に直面していた制約を示しています。Marshaler
および MarshalerAttr
インターフェースの導入は、これらの制約を緩和し、開発者により大きな制御と柔軟性を提供することを目的としています。
前提知識の解説
このコミットの理解には、以下の概念が役立ちます。
- XML (Extensible Markup Language): 構造化されたデータを表現するためのマークアップ言語。タグ、属性、要素、テキストデータなどで構成されます。
- マーシャリング (Marshaling): プログラム内のデータ構造(Goの構造体など)を、外部形式(この場合はXML)に変換するプロセス。
- アンマーシャリング (Unmarshaling): 外部形式(XML)をプログラム内のデータ構造に変換するプロセス。
- Goの
encoding/xml
パッケージ: Go標準ライブラリの一部で、Goの構造体とXMLの間でデータを変換するための機能を提供します。 - Goのインターフェース: Goにおけるポリモーフィズムを実現するための重要な機能。特定のメソッドセットを持つ型がそのインターフェースを実装していると見なされます。
reflect
パッケージ: Goのランタイムリフレクション機能を提供し、プログラムの実行中に型情報や値の操作を可能にします。encoding/xml
パッケージは、Goの構造体をXMLにマーシャリングする際に、このリフレクションを広範に利用します。io.Writer
: データを書き込むための基本的なインターフェース。xml.Encoder
はこのインターフェースを介してXMLデータを書き出します。- XML名前空間 (XML Namespace): XML文書内で要素名や属性名の衝突を避けるためのメカニズム。URIで識別されます。
- XMLタグ (XML Tag): XML要素の開始と終了を示すマークアップ。例:
<element>
と</element>
。 - XML属性 (XML Attribute): XML要素に追加情報を与えるためのキーと値のペア。例:
<element key="value">
。
技術的詳細
このコミットの主要な変更点は、encoding/xml
パッケージに以下の2つのインターフェースが追加されたことです。
-
Marshaler
インターフェース:type Marshaler interface { MarshalXML(e *Encoder, start StartElement) error }
このインターフェースを実装する型は、
MarshalXML
メソッドを提供することで、自身のXML要素としてのマーシャリング方法を完全に制御できます。MarshalXML
メソッドは、xml.Encoder
のインスタンスと、現在の要素の開始タグ情報 (StartElement
) を受け取ります。実装者は、e.EncodeToken
やe.EncodeElement
などのメソッドを使用して、XMLストリームに直接トークンを書き込むことができます。これにより、複雑なネスト構造や、デフォルトのマーシャリングでは表現できないXML形式を生成することが可能になります。 -
MarshalerAttr
インターフェース:type MarshalerAttr interface { MarshalXMLAttr(name Name) (Attr, error) }
このインターフェースを実装する型は、
MarshalXMLAttr
メソッドを提供することで、自身のXML属性としてのマーシャリング方法を制御できます。このメソッドは、属性名 (Name
) を受け取り、対応するxml.Attr
構造体を返します。これは、構造体のフィールドがxml:",attr"
タグオプションを持つ場合にのみ使用されます。これにより、Goの特定の型をXML属性の値としてカスタムフォーマットで出力できるようになります。
これらのインターフェースの導入に伴い、xml.Encoder
の内部ロジックが大幅に修正されました。
Encoder
構造体は、printer
フィールドを埋め込み型からp printer
に変更し、printer
がEncoder
へのポインタを持つようにしました。これにより、printer
からEncoder
のメソッド(特にEncodeToken
)を呼び出すことが可能になります。EncodeElement
メソッドがEncoder
に追加されました。これは、特定の開始タグ (StartElement
) を指定して値をXMLエンコードするための新しい公開APIです。EncodeToken
メソッドがEncoder
に追加されました。これは、XMLトークン(StartElement
,EndElement
,CharData
,Comment
,ProcInst
,Directive
)を直接XMLストリームに書き込むための低レベルAPIです。これにより、Marshaler
の実装がXML出力をより細かく制御できるようになります。marshalValue
関数(内部関数)が更新され、値がMarshaler
インターフェースを実装しているかどうかをチェックするようになりました。実装している場合、marshalInterface
関数が呼び出され、カスタムマーシャリングロジックが実行されます。marshalSimple
関数(内部関数)の戻り値が変更され、文字列、バイトスライス、エラーを返すようになりました。これにより、属性値や文字データとして単純な型をマーシャリングする際の柔軟性が向上しました。- XML名前空間の処理ロジックが
printer
構造体内で改善され、createAttrPrefix
およびdeleteAttrPrefix
メソッドがより堅牢になりました。また、markPrefix
とpopPrefix
が追加され、名前空間プレフィックスのスコープ管理が強化されました。 parentStack
構造体と関連メソッド (trim
,push
) が更新され、writeStart
およびwriteEnd
を使用してXML要素の開始/終了を処理するようになりました。これにより、カスタムマーシャリング中に要素の整合性が保たれるようになります。api/go1.1.txt
およびapi/go1.txt
から、Encoder
の一部の低レベルなI/Oメソッド(ReadFrom
,Available
,Buffered
,Flush
,Write
,WriteByte
,WriteRune
,WriteString
)が削除されました。これは、Encoder
がio.Writer
を直接ラップするのではなく、printer
構造体を介してバッファリングされた書き込みを行うように内部構造が変更されたためです。ユーザーはEncoder
のEncode
やEncodeElement
メソッドを通じて高レベルな操作を行うことが推奨されます。
これらの変更により、encoding/xml
パッケージは、より複雑なXML構造の生成と、ユーザー定義型によるXMLマーシャリングの挙動のカスタマイズを強力にサポートするようになりました。
コアとなるコードの変更箇所
主な変更は src/pkg/encoding/xml/marshal.go
に集中しています。
Marshaler
インターフェースの定義: 約75行目に追加。MarshalerAttr
インターフェースの定義: 約89行目に追加。Encoder
構造体の変更:printer
フィールドがp printer
に変更され、encoder *Encoder
フィールドがprinter
構造体に追加。NewEncoder
関数の変更:Encoder
の初期化時にe.p.encoder = e
が追加され、printer
が自身のEncoder
を参照できるように。EncodeElement
メソッドの追加:Encoder
に新しい公開メソッドとして追加。EncodeToken
メソッドの追加:Encoder
に新しい公開メソッドとして追加。marshalValue
関数の変更:Marshaler
インターフェースの実装チェックと、marshalInterface
の呼び出しロジックが追加。marshalInterface
関数の追加:Marshaler
インターフェースを実装する値のマーシャリングを処理する新しい関数。writeStart
関数の追加:StartElement
をXMLストリームに書き込むための新しい関数。writeEnd
関数の変更:EndElement
をXMLストリームに書き込むための関数が、名前空間のスタック管理を含むように変更。marshalSimple
関数の変更: 戻り値の型が変更され、文字列とバイトスライスを返すように。marshal_test.go
の更新:MyMarshalerTest
とMyMarshalerAttrTest
という新しいテスト構造体が追加され、Marshaler
とMarshalerAttr
インターフェースの動作を検証するテストケースが追加されました。
コアとなるコードの解説
Marshaler
インターフェースと MarshalXML
メソッド
type Marshaler interface {
MarshalXML(e *Encoder, start StartElement) error
}
このインターフェースは、Goの型がXML要素としてどのように自身を表現するかを定義するためのものです。MarshalXML
メソッドは、xml.Encoder
のインスタンス e
と、現在の要素の開始タグ情報 start
を引数に取ります。
start
引数は、xml.Encoder
がこの型をマーシャリングしようとした際に決定したデフォルトの開始タグ情報(要素名、属性など)を含んでいます。Marshaler
の実装者は、この start
情報を使用することも、完全に無視して独自のXML構造を生成することもできます。
典型的な実装パターンは以下の通りです。
e.EncodeToken(start)
を呼び出して、デフォルトの開始タグを書き込む。e.EncodeToken(CharData([]byte("some data")))
やe.EncodeElement(someStruct, someStartElement)
などを使って、要素のコンテンツや子要素を書き込む。e.EncodeToken(EndElement{start.Name})
を呼び出して、対応する終了タグを書き込む。
このメカニズムにより、例えば、Goの time.Time
型を特定のXML日付フォーマットで出力したり、複雑なGoのマップをXMLのキー-値ペアのリストとして表現したりといった、高度なカスタマイズが可能になります。
MarshalerAttr
インターフェースと MarshalXMLAttr
メソッド
type MarshalerAttr interface {
MarshalXMLAttr(name Name) (Attr, error)
}
このインターフェースは、Goの型がXML属性としてどのように自身を表現するかを定義するためのものです。MarshalXMLAttr
メソッドは、属性名 name
を引数に取ります。実装者は、この name
を使用して属性を構築することも、異なる名前の属性を返すこともできます。
このインターフェースは、構造体のフィールドに xml:",attr"
タグが指定されている場合にのみ、encoding/xml
パッケージによって呼び出されます。例えば、type MyCustomString string
という型があり、これをXML属性として特定のエンコーディングで出力したい場合に利用できます。
xml.Encoder
の変更点
-
EncodeElement(v interface{}, start StartElement) error
: この新しい公開メソッドは、指定されたv
のXMLエンコーディングをストリームに書き込みます。start
引数で、最外側のタグを指定できます。これは、Marshaler
インターフェースの実装内で、特定の子要素を特定のタグ名でエンコードしたい場合などに特に有用です。 -
EncodeToken(t Token) error
: この低レベルな公開メソッドは、任意のXMLトークン(StartElement
,EndElement
,CharData
,Comment
,ProcInst
,Directive
)を直接XMLストリームに書き込みます。Marshaler
インターフェースの実装において、XML出力をトークンレベルで細かく制御するために不可欠な機能です。例えば、特定の条件に基づいてコメントや処理命令を挿入するといったことが可能になります。
これらの変更により、encoding/xml
パッケージは、Goの型とXMLの間でより柔軟で表現力豊かなマッピングを実現できるようになりました。
関連リンク
- Go CL (Change List) 12603044: https://golang.org/cl/12603044
- Go Issue #2771: https://github.com/golang/go/issues/2771
- Go Issue #4169: https://github.com/golang/go/issues/4169
- Go Issue #5975: https://github.com/golang/go/issues/5975
- Go Issue #6125: https://github.com/golang/go/issues/6125
参考にした情報源リンク
golang.org/s/go12xml
(Go 1.2 XML設計ドキュメント): このコミットの設計思想の基盤となったドキュメント。Web検索で内容を確認しました。- Go言語の
encoding/xml
パッケージの公式ドキュメント:https://pkg.go.dev/encoding/xml
- Go言語の
encoding/json
パッケージの公式ドキュメント:https://pkg.go.dev/encoding/json
(json.Marshaler
との比較のため) - XMLの基本概念に関する一般的な情報源。