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

[インデックス 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つのインターフェースが追加されたことです。

  1. Marshaler インターフェース:

    type Marshaler interface {
        MarshalXML(e *Encoder, start StartElement) error
    }
    

    このインターフェースを実装する型は、MarshalXML メソッドを提供することで、自身のXML要素としてのマーシャリング方法を完全に制御できます。MarshalXML メソッドは、xml.Encoder のインスタンスと、現在の要素の開始タグ情報 (StartElement) を受け取ります。実装者は、e.EncodeTokene.EncodeElement などのメソッドを使用して、XMLストリームに直接トークンを書き込むことができます。これにより、複雑なネスト構造や、デフォルトのマーシャリングでは表現できないXML形式を生成することが可能になります。

  2. MarshalerAttr インターフェース:

    type MarshalerAttr interface {
        MarshalXMLAttr(name Name) (Attr, error)
    }
    

    このインターフェースを実装する型は、MarshalXMLAttr メソッドを提供することで、自身のXML属性としてのマーシャリング方法を制御できます。このメソッドは、属性名 (Name) を受け取り、対応する xml.Attr 構造体を返します。これは、構造体のフィールドが xml:",attr" タグオプションを持つ場合にのみ使用されます。これにより、Goの特定の型をXML属性の値としてカスタムフォーマットで出力できるようになります。

これらのインターフェースの導入に伴い、xml.Encoder の内部ロジックが大幅に修正されました。

  • Encoder 構造体は、printer フィールドを埋め込み型から p printer に変更し、printerEncoder へのポインタを持つようにしました。これにより、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 メソッドがより堅牢になりました。また、markPrefixpopPrefix が追加され、名前空間プレフィックスのスコープ管理が強化されました。
  • parentStack 構造体と関連メソッド (trim, push) が更新され、writeStart および writeEnd を使用してXML要素の開始/終了を処理するようになりました。これにより、カスタムマーシャリング中に要素の整合性が保たれるようになります。
  • api/go1.1.txt および api/go1.txt から、Encoder の一部の低レベルなI/Oメソッド(ReadFrom, Available, Buffered, Flush, Write, WriteByte, WriteRune, WriteString)が削除されました。これは、Encoderio.Writer を直接ラップするのではなく、printer 構造体を介してバッファリングされた書き込みを行うように内部構造が変更されたためです。ユーザーは EncoderEncodeEncodeElement メソッドを通じて高レベルな操作を行うことが推奨されます。

これらの変更により、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 の更新: MyMarshalerTestMyMarshalerAttrTest という新しいテスト構造体が追加され、MarshalerMarshalerAttr インターフェースの動作を検証するテストケースが追加されました。

コアとなるコードの解説

Marshaler インターフェースと MarshalXML メソッド

type Marshaler interface {
    MarshalXML(e *Encoder, start StartElement) error
}

このインターフェースは、Goの型がXML要素としてどのように自身を表現するかを定義するためのものです。MarshalXML メソッドは、xml.Encoder のインスタンス e と、現在の要素の開始タグ情報 start を引数に取ります。 start 引数は、xml.Encoder がこの型をマーシャリングしようとした際に決定したデフォルトの開始タグ情報(要素名、属性など)を含んでいます。Marshaler の実装者は、この start 情報を使用することも、完全に無視して独自のXML構造を生成することもできます。

典型的な実装パターンは以下の通りです。

  1. e.EncodeToken(start) を呼び出して、デフォルトの開始タグを書き込む。
  2. e.EncodeToken(CharData([]byte("some data")))e.EncodeElement(someStruct, someStartElement) などを使って、要素のコンテンツや子要素を書き込む。
  3. 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の間でより柔軟で表現力豊かなマッピングを実現できるようになりました。

関連リンク

参考にした情報源リンク

  • 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の基本概念に関する一般的な情報源。