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

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

このコミットは、Go言語の標準ライブラリ encoding/xml パッケージから、Marshaler インターフェースのサポートを一時的に削除するものです。このインターフェースは、Goの構造体をXML形式に変換(マーシャリング)する際に、カスタムな変換ロジックを提供するために設計されていましたが、属性の扱いやタグ名の決定など、いくつかの未解決の問題を抱えていました。Go 1リリース後に、これらの問題を解決した新しいインターフェースとして再導入される予定でした。

コミット

encoding/xml: remove Marshaler support

Marshaler has a number of open areas that need
further thought (e.g. it doesn't handle attributes,
it's supposed to handle tag names internally but has
no information to do so, etc).

We're removing it now and will bring it back with an
interface that covers these aspects, after Go 1.

Related to issue 2771, but doesn't fix it.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5574057

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

https://github.com/golang/go/commit/66599c40709cb74da2e3ae243e1f42ac9bacf263

元コミット内容

commit 66599c40709cb74da2e3ae243e1f42ac9bacf263
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date:   Tue Jan 24 21:51:15 2012 -0200

    encoding/xml: remove Marshaler support
    
    Marshaler has a number of open areas that need
    further thought (e.g. it doesn't handle attributes,
    it's supposed to handle tag names internally but has
    no information to do so, etc).
    
    We're removing it now and will bring it back with an
    interface that covers these aspects, after Go 1.
    
    Related to issue 2771, but doesn't fix it.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5574057

変更の背景

このコミットの主な背景は、Go言語の encoding/xml パッケージにおける Marshaler インターフェースの設計上の課題と、Go 1のリリースに向けた安定化の必要性です。

Marshaler インターフェースは、ユーザーがGoのデータ構造をXMLにシリアライズ(マーシャリング)する際に、標準のマーシャリングロジックをオーバーライドし、カスタムなXML表現を生成できるようにするために導入されました。しかし、このインターフェースにはいくつかの重要な機能が欠けており、特に以下の点が問題視されていました。

  1. 属性の扱い: Marshaler インターフェースは、XML要素の属性を適切に処理するためのメカニズムを提供していませんでした。これにより、カスタムマーシャリングを行う際に、属性を含む複雑なXML構造を正確に表現することが困難でした。
  2. タグ名の決定: Marshaler は、生成されるXML要素のタグ名を内部的に決定する情報を持っていませんでした。これは、Goの構造体のフィールド名やタグに基づいて自動的にタグ名が決定される通常のマーシャリングとは異なり、一貫性のない動作や予期せぬ結果を招く可能性がありました。
  3. 未解決の設計課題: 上記の点以外にも、Marshaler の設計にはさらなる検討が必要な「オープンな領域」が多数存在していました。

Go 1のリリースが迫る中で、これらの未解決の問題を抱えた Marshaler インターフェースをそのまま残すことは、将来的な互換性や安定性に影響を与える可能性がありました。そのため、一時的に Marshaler のサポートを削除し、Go 1リリース後に、より堅牢で包括的なインターフェースとして再導入するという方針が取られました。

コミットメッセージに「Related to issue 2771, but doesn't fix it.」とあるように、この変更は既存の課題(issue 2771)に部分的に関連していますが、直接的な解決策ではありません。むしろ、将来的なより良い解決策のための準備段階と位置づけられます。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびXMLに関する基本的な知識が必要です。

Go言語の encoding/xml パッケージ

encoding/xml パッケージは、Goのデータ構造とXMLドキュメントの間でエンコード(マーシャリング)およびデコード(アンマーシャリング)を行うための機能を提供します。

  • マーシャリング (Marshalling): Goの構造体やその他のデータ型をXML形式のバイト列に変換するプロセスです。xml.Marshal 関数がこれを行います。
  • アンマーシャリング (Unmarshalling): XML形式のバイト列をGoのデータ構造に変換するプロセスです。xml.Unmarshal 関数がこれを行います。

通常、encoding/xml パッケージは、Goの構造体のフィールド名や、構造体タグ(例: xml:"element_name,attr")を使用して、XML要素名、属性、テキストコンテンツなどを自動的にマッピングします。

Marshaler インターフェース (削除前)

Go言語の encoding/xml パッケージには、json.Marshalerjson.Unmarshaler と同様に、カスタムなマーシャリングロジックを実装するためのインターフェースが提供されていました。このコミットで削除された Marshaler インターフェースは、以下のように定義されていました(コミット前のコードから推測)。

type Marshaler interface {
    MarshalXML() ([]byte, error)
}

このインターフェースを実装する型は、MarshalXML メソッドを定義することで、その型のインスタンスがXMLにマーシャリングされる際に、MarshalXML メソッドが返すバイト列がXML出力として使用されることを意図していました。これにより、開発者は標準のマーシャリングルールでは表現できない複雑なXML構造や、特定のビジネスロジックに基づいたXML出力を生成することが可能になります。

XMLの基本構造

XML (Extensible Markup Language) は、データを構造化するためのマークアップ言語です。基本的な構成要素は以下の通りです。

  • 要素 (Element): <tag>content</tag> のように、開始タグと終了タグで囲まれた部分です。
  • 属性 (Attribute): <tag attribute_name="value"> のように、要素の開始タグ内に記述され、要素に関する追加情報を提供します。
  • テキストコンテンツ (Text Content): 要素の開始タグと終了タグの間に含まれるテキストです。
  • XML宣言 (XML Declaration): <?xml version="1.0" encoding="UTF-8"?> のように、XMLドキュメントの先頭に記述され、XMLのバージョンやエンコーディングを指定します。

Marshaler インターフェースが抱えていた問題は、特に「属性の扱い」と「タグ名の決定」というXMLの基本的な構造に関わる部分でした。

技術的詳細

このコミットで削除された Marshaler インターフェースは、Goの encoding/xml パッケージが提供する自動マーシャリングメカニズムをオーバーライドするためのものでした。しかし、その設計にはいくつかの根本的な問題がありました。

  1. 属性のサポートの欠如: Marshaler インターフェースの MarshalXML() ([]byte, error) メソッドは、XMLのバイト列全体を返すことを想定していました。この設計では、Goの構造体のフィールドがXML要素の属性としてマーシャリングされるべきか、それとも子要素としてマーシャリングされるべきか、あるいはカスタムマーシャリングされたXMLバイト列にどのように属性を組み込むか、といった情報が不足していました。 例えば、Goの構造体で xml:"name,attr" のようにタグ付けされたフィールドがあった場合、標準のマーシャリングではそれが属性として扱われます。しかし、Marshaler インターフェースを実装した場合、MarshalXML メソッドが返すバイト列には、その属性が適切に含まれていることを保証する責任が完全に実装側に委ねられていました。これは、属性の動的な生成や条件付きでの追加といった複雑なシナリオにおいて、非常に扱いにくいものでした。

  2. タグ名の決定に関する情報の欠如: encoding/xml パッケージは、Goの構造体をマーシャリングする際に、構造体名やフィールド名、あるいは構造体タグに基づいてXML要素のタグ名を決定します。しかし、Marshaler インターフェースは、自身がマーシャリングされるXML要素の「タグ名」に関する情報を MarshalXML メソッドに提供していませんでした。 これは、例えば type MyType struct { ... }Marshaler を実装している場合、MyType のインスタンスがマーシャリングされる際に、そのXML要素のルートタグが何になるべきか(例: <MyType><customTag> など)という情報が MarshalXML メソッド内では利用できないことを意味します。結果として、MarshalXML の実装は、自身が生成するXMLのルート要素のタグ名をハードコードするか、外部から渡される情報に依存するしかありませんでした。これは、汎用性や再利用性を著しく損なうものでした。

  3. 既存のマーシャリングロジックとの統合の難しさ: encoding/xml パッケージの Marshal 関数は、Goの構造体を再帰的に走査し、フィールドの型やタグに基づいてXMLを構築します。Marshaler インターフェースは、この再帰的なプロセスの中断点として機能することを意図していましたが、上記の問題により、既存の強力なマーシャリングロジック(特に属性やネストされた要素の自動処理)とシームレスに統合することが困難でした。 つまり、Marshaler を使用すると、その型に関するXML生成の責任が完全にユーザーに移譲され、encoding/xml パッケージが提供する便利な機能(例: xml:",chardata"xml:",innerxml" など)を部分的に利用することが難しくなっていました。

これらの問題は、Marshaler インターフェースが「Goのデータ構造をXMLに変換する」という目的を完全に達成するための十分なコンテキストや柔軟性を提供できていなかったことを示しています。そのため、Go 1のリリース前に、これらの設計上の欠陥を抱えたインターフェースを一時的に削除し、将来的にこれらの課題を解決した、より洗練されたインターフェースを導入するという判断が下されました。

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

このコミットでは、主に以下の2つのファイルが変更され、Marshaler インターフェースとその関連コードが削除されました。

  1. src/pkg/encoding/xml/marshal.go:

    • Marshaler インターフェースの定義自体が削除されました。
      --- a/src/pkg/encoding/xml/marshal.go
      +++ b/src/pkg/encoding/xml/marshal.go
      @@ -21,16 +21,8 @@ const (
       	Header = `<?xml version="1.0" encoding="UTF-8"?>` + "\n"
       )
       
      -// A Marshaler can produce well-formatted XML representing its internal state.
      -type Marshaler interface {
      -	MarshalXML() ([]byte, error)
      -}
      -
       // Marshal returns the XML encoding of v.
       //
      -// If v implements Marshaler, then Marshal calls its MarshalXML method.
      -// Otherwise, Marshal uses the following procedure to create the XML.
      -//
       // Marshal handles an array or slice by marshalling each of the elements.
       // Marshal handles a pointer by marshalling the value it points at or, if the
       // pointer is nil, by writing nothing.  Marshal handles an interface value by
      @@ -128,18 +120,6 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
       	kind := val.Kind()
       	typ := val.Type()
       
      -	// Try Marshaler
      -	if typ.NumMethod() > 0 {
      -		if marshaler, ok := val.Interface().(Marshaler); ok {
      -			bytes, err := marshaler.MarshalXML()
      -			if err != nil {
      -				return err
      -			}
      -			p.Write(bytes)
      -			return nil
      -		}
      -	}
      -
       	// Drill into pointers/interfaces
       	if kind == reflect.Ptr || kind == reflect.Interface {
       	\tif val.IsNil() {
      
    • marshalValue 関数内の、Marshaler インターフェースの実装をチェックし、その MarshalXML メソッドを呼び出すロジックが削除されました。このロジックは、Goの型が Marshaler インターフェースを満たしている場合に、カスタムマーシャリングを実行するためのものでした。
  2. src/pkg/encoding/xml/marshal_test.go:

    • RawXML というカスタム型と、その型が Marshaler インターフェースを実装するために定義していた MarshalXML メソッドが削除されました。
      --- a/src/pkg/encoding/xml/marshal_test.go
      +++ b/src/pkg/encoding/xml/marshal_test.go
      @@ -34,12 +34,6 @@ type Ship struct {
       	secret    string
       }\n
      -type RawXML string
      -
      -func (rx RawXML) MarshalXML() ([]byte, error) {
      -	return []byte(rx), nil
      -}
      -
       type NamedType string
       
       type Port struct {
      @@ -298,13 +292,6 @@ var marshalTests = []struct {
       		UnmarshalOnly: true,
       	},
       
      -	// Test marshaller interface
      -	{\n-		Value:       RawXML("</>"),
      -		ExpectXML:   `</>`,
      -		MarshalOnly: true,
      -	},
      -
       	// Test structs
       	{Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
       	{Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
      
    • RawXML 型のマーシャリングをテストするためのテストケースが marshalTests 変数から削除されました。

これらの変更により、encoding/xml パッケージは Marshaler インターフェースのサポートを完全に停止し、カスタムマーシャリングのメカニズムが一時的に利用できなくなりました。

コアとなるコードの解説

削除されたコードは、Goの encoding/xml パッケージが、Goのデータ構造をXMLに変換する際に、カスタムなマーシャリングロジックを適用するための主要なメカニズムでした。

Marshaler インターフェースの削除

type Marshaler interface { MarshalXML() ([]byte, error) } の定義が削除されたことで、Goの型がこのインターフェースを実装しても、encoding/xml パッケージはそれを特別なマーシャリングロジックとして認識しなくなりました。これは、カスタムXML出力の生成を試みる既存のコードがコンパイルエラーになるか、または標準のマーシャリングロジックが適用されるようになることを意味します。

marshalValue 関数内のロジック削除

marshalValue 関数は、encoding/xml パッケージがGoの値をXMLに変換する際の中心的なロジックを担っていました。削除された以下のコードブロックは、この関数が値の型をチェックし、もしそれが Marshaler インターフェースを実装していれば、その MarshalXML メソッドを呼び出してカスタムXMLバイト列を取得し、それを直接出力ストリームに書き込む役割を果たしていました。

// Try Marshaler
if typ.NumMethod() > 0 {
    if marshaler, ok := val.Interface().(Marshaler); ok {
        bytes, err := marshaler.MarshalXML()
        if err != nil {
            return err
        }
        p.Write(bytes)
        return nil
    }
}

このコードが削除されたことで、marshalValue 関数は Marshaler インターフェースの実装を一切考慮しなくなりました。その結果、すべての値は、ポインタのドリルダウン、構造体のフィールド走査、プリミティブ型の変換など、encoding/xml パッケージの標準的なマーシャリングルールに従って処理されることになります。

テストコードの削除

marshal_test.go から RawXML 型とそのテストケースが削除されたのは、Marshaler インターフェースの削除に伴う必然的な変更です。RawXMLMarshaler を実装する具体的な例であり、そのテストケースは Marshaler の機能が正しく動作することを確認するためのものでした。インターフェース自体がなくなったため、これらのテストも不要となりました。

変更の意図

これらの変更の意図は、前述の「変更の背景」と「技術的詳細」で述べたように、Marshaler インターフェースが抱えていた設計上の問題を一時的に棚上げし、Go 1のリリースを安定させることにありました。このインターフェースは、属性の扱い、タグ名の決定、既存のマーシャリングロジックとの統合といった点で不完全であり、これらの問題を解決しないままGo 1に含めることは、将来的な互換性問題や開発者の混乱を招く可能性がありました。

したがって、このコミットは、一時的な後退ではありますが、Goの encoding/xml パッケージの長期的な健全性と、より堅牢で使いやすいカスタムマーシャリングメカニズムの将来的な導入に向けた重要なステップと位置づけられます。実際に、Go 1リリース後には、xml.Marshalerxml.Unmarshaler インターフェースが再導入され、より洗練された形でカスタムマーシャリングがサポートされることになります。

関連リンク

  • Go CL 5574057: このコミットに対応するGoのコードレビューシステム (Gerrit) のチェンジリスト。 https://golang.org/cl/5574057
  • Go Issue 2771: コミットメッセージで言及されている関連するGoのIssue。このIssueは encoding/xmlMarshaler インターフェースに関する議論を含んでいます。 https://github.com/golang/go/issues/2771

参考にした情報源リンク