[インデックス 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の基本概念に関する一般的な情報源。