[インデックス 11353] ファイルの概要
このコミットは、Go言語の標準ライブラリである encoding/xml
パッケージのAPIを、他の encoding
パッケージ(encoding/json
や encoding/gob
など)のAPIとより一貫性のあるものにするための大幅な変更を導入しています。この変更には、gofix
ツールによる自動コード修正のサポートも含まれています。
コミット
commit 0442087f93d49dec95cd327efbc8c760484ac8bb
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date: Tue Jan 24 01:10:32 2012 -0200
encoding/xml: bring API closer to other packages
Includes gofix module. The only case not covered should be
xml.Unmarshal, since it remains with a similar interface, and
would require introspecting the type of its first argument
better.
Fixes #2626.
R=golang-dev, rsc, gustavo
CC=golang-dev
https://golang.org/cl/5574053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0442087f93d49dec95cd327efbc8c760484ac8bb
元コミット内容
encoding/xml
パッケージのAPIを他のパッケージに近づける。
gofix
モジュールを含む。xml.Unmarshal
は同様のインターフェースを維持しており、その最初の引数の型をより適切に内省する必要があるため、カバーされていない唯一のケースである。
Issue #2626 を修正。
変更の背景
Go言語の初期段階において、標準ライブラリ内の様々な encoding
パッケージ(例: json
, gob
, xml
)は、それぞれ異なるAPI設計を持っていました。これは、開発者が異なる形式のデータを扱う際に、学習コストやコードの一貫性の欠如といった問題を引き起こしていました。特に encoding/xml
パッケージは、他のパッケージと比較してストリーム処理の概念が希薄であり、Marshal
や Unmarshal
関数が io.Writer
や io.Reader
を直接引数に取るなど、使い勝手の面で改善の余地がありました。
このコミットは、Go 1のリリースに向けて、標準ライブラリ全体のAPIの一貫性を高めるという大きな目標の一環として行われました。特に、encoding/xml
を encoding/json
や encoding/gob
と同様のパターンに合わせることで、開発者がXMLデータをより直感的に扱えるようにすることを目指しました。
コミットメッセージにある Fixes #2626
については、現在のGitHubリポジトリのIssue #2626は golang/vscode-go
のものであり、このコミットの時期(2012年)とは関連がありません。これは、当時のGoプロジェクトが使用していた別のIssueトラッカー(例: Google CodeのIssueトラッカーなど)の番号を指している可能性が高いです。
前提知識の解説
- Go言語の
encoding
パッケージ: Go言語の標準ライブラリには、JSON、XML、Gobなどの様々なデータ形式をエンコード(Goのデータ構造から外部形式へ変換)およびデコード(外部形式からGoのデータ構造へ変換)するためのパッケージ群が含まれています。これらのパッケージは、通常、Marshal
およびUnmarshal
関数を提供し、それぞれGoの値をバイトスライスに変換したり、バイトスライスをGoの値に変換したりします。 io.Reader
とio.Writer
: Go言語におけるI/O操作の基本的なインターフェースです。io.Reader
はデータの読み込み元を抽象化し、io.Writer
はデータの書き込み先を抽象化します。gofix
ツール:gofix
はGo言語の公式ツールの一つで、GoのAPIや言語仕様の変更に合わせて既存のGoソースコードを自動的に修正するために使用されます。これにより、Go言語の進化に伴う破壊的変更があっても、開発者が手動でコードを修正する手間を大幅に削減できます。gofix
は、AST(抽象構文木)を操作してコードを変換します。- XMLパーシングのストリーム処理: XMLドキュメントを処理する際、ドキュメント全体をメモリに読み込むのではなく、要素を一つずつ読み込んで処理する方式です。これにより、大きなXMLファイルを効率的に処理できます。
encoding/xml
パッケージでは、Decoder
がこのストリーム処理を担います。
技術的詳細
このコミットの主要な変更点は、encoding/xml
パッケージのAPIを、他の encoding
パッケージ(特に encoding/json
)のパターンに合わせることにあります。
-
Parser
からDecoder
への名称変更と機能拡張:- 既存の
xml.Parser
型がxml.Decoder
に名称変更されました。これは、他のencoding
パッケージにおけるjson.Decoder
やgob.Decoder
との整合性を図るためです。 Decoder
型には、新たにDecode
メソッドが追加されました。これにより、NewDecoder(r io.Reader).Decode(v interface{})
という形式で、ストリームから直接Goの構造体にデコードできるようになりました。Unmarshal
メソッドはDecodeElement
に名称変更され、特定の開始要素からデコードする機能を提供します。
- 既存の
-
Marshal
関数の変更:- 従来の
func Marshal(w io.Writer, v interface{}) error
からfunc Marshal(v interface{}) ([]byte, error)
に変更されました。これにより、encoding/json.Marshal
と同様に、Goの値を直接バイトスライスにエンコードするようになりました。 - ストリームへの書き込みが必要な場合は、新たに導入された
Encoder
型を使用するNewEncoder(w io.Writer).Encode(v interface{})
というパターンに移行しました。
- 従来の
-
Unmarshal
関数の変更:- 従来の
func Unmarshal(r io.Reader, val interface{}) error
からfunc Unmarshal(data []byte, v interface{}) error
に変更されました。これにより、encoding/json.Unmarshal
と同様に、バイトスライスから直接Goの構造体にデコードするようになりました。 - ストリームからの読み込みが必要な場合は、
NewDecoder(r io.Reader).Decode(v interface{})
というパターンに移行しました。
- 従来の
-
gofix
ツールの統合:- このAPI変更は破壊的であるため、既存のコードベースを新しいAPIに自動的に移行するための
gofix
モジュール (src/cmd/gofix/xmlapi.go
) が追加されました。 gofix
は、xml.Marshal(a, b)
をxml.NewEncoder(a).Encode(b)
に、xml.NewParser(stream)
をxml.NewDecoder(stream)
に、p.Unmarshal(v, start)
をp.DecodeElement(v, start)
に自動的に書き換えます。- ただし、
xml.Unmarshal
の変更は、その最初の引数の型を内省する必要があるため、gofix
では完全にカバーされていません。これは、Unmarshal
がio.Reader
ではなく[]byte
を受け取るようになったため、io.Reader
を渡している既存のコードを自動的にNewDecoder
を使用するように変換することが難しいことを意味します。
- このAPI変更は破壊的であるため、既存のコードベースを新しいAPIに自動的に移行するための
-
内部実装の変更:
src/pkg/encoding/xml/xml.go
内のParser
構造体とその関連メソッドがDecoder
にリネームされ、d.Strict
,d.AutoClose
,d.Entity
などのフィールドアクセスもp.
からd.
に変更されました。src/pkg/encoding/xml/marshal.go
にEncoder
型とNewEncoder
関数、Encode
メソッドが追加されました。src/pkg/encoding/xml/read.go
にUnmarshal
関数のシグネチャ変更と、Decoder
のDecode
およびDecodeElement
メソッドが追加されました。
これらの変更により、encoding/xml
パッケージは、Go言語の他のエンコーディングパッケージとより統一されたインターフェースを提供するようになりました。
コアとなるコードの変更箇所
このコミットは、主に以下のファイルに影響を与えています。
src/pkg/encoding/xml/marshal.go
:Marshal
関数のシグネチャ変更と、Encoder
型、NewEncoder
関数、Encode
メソッドの追加。src/pkg/encoding/xml/read.go
:Unmarshal
関数のシグネチャ変更と、Decoder
型のDecode
およびDecodeElement
メソッドの追加。src/pkg/encoding/xml/xml.go
:Parser
型をDecoder
にリネームし、関連するメソッドのレシーバ名も変更。src/cmd/gofix/xmlapi.go
:encoding/xml
のAPI変更に対応するためのgofix
モジュールを新規追加。src/cmd/gofix/xmlapi_test.go
:xmlapi.go
のテストコードを新規追加。misc/dashboard/builder/main.go
,src/cmd/godoc/codewalk.go
,src/pkg/encoding/xml/marshal_test.go
,src/pkg/encoding/xml/read_test.go
,src/pkg/encoding/xml/xml_test.go
: 新しいAPIに合わせて既存のコードとテストを修正。
コアとなるコードの解説
src/pkg/encoding/xml/marshal.go
の変更
// 変更前
// func Marshal(w io.Writer, v interface{}) (err error) { ... }
// 変更後
func Marshal(v interface{}) ([]byte, error) {
var b bytes.Buffer
if err := NewEncoder(&b).Encode(v); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// 新規追加
type Encoder struct {
printer
}
// 新規追加
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{printer{bufio.NewWriter(w)}}
}
// 新規追加
func (enc *Encoder) Encode(v interface{}) error {
err := enc.marshalValue(reflect.ValueOf(v), nil)
enc.Flush()
return err
}
Marshal
関数は、io.Writer
を引数に取らず、直接 []byte
を返すように変更されました。内部的には、bytes.Buffer
を使用して NewEncoder
と Encode
メソッドを呼び出すことで、ストリームへの書き込みロジックを再利用しています。Encoder
型が導入され、NewEncoder
で io.Writer
を受け取り、Encode
メソッドでGoの値をXMLとしてストリームに書き込む責任を持つようになりました。
src/pkg/encoding/xml/read.go
の変更
// 変更前
// func Unmarshal(r io.Reader, val interface{}) error { ... }
// 変更後
func Unmarshal(data []byte, v interface{}) error {
return NewDecoder(bytes.NewBuffer(data)).Decode(v)
}
// 新規追加
// Decode works like xml.Unmarshal, except it reads the decoder
// stream to find the start element.
func (d *Decoder) Decode(v interface{}) error {
return d.DecodeElement(v, nil)
}
// 新規追加
// DecodeElement works like xml.Unmarshal except that it takes
// a pointer to the start XML element to decode into v.
// It is useful when a client reads some raw XML tokens itself
// but also wants to defer to Unmarshal for some elements.
func (d *Decoder) DecodeElement(v interface{}, start *StartElement) error {
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr {
return errors.New("non-pointer passed to Unmarshal")
}
return d.unmarshal(val.Elem(), start)
}
// 変更前: func (p *Parser) unmarshal(...)
// 変更後: func (p *Decoder) unmarshal(...)
Unmarshal
関数も io.Reader
ではなく []byte
を引数に取るように変更されました。内部では bytes.NewBuffer
を使って NewDecoder
を呼び出し、Decode
メソッドに処理を委譲しています。Decoder
型には、Decode
と DecodeElement
の2つの新しいメソッドが追加され、それぞれストリーム全体または特定の要素からのデコードを可能にしています。また、Parser
の unmarshal
メソッドのレシーバが Decoder
に変更されています。
src/pkg/encoding/xml/xml.go
の変更
// 変更前: type Parser struct { ... }
// 変更後: type Decoder struct { ... }
// 変更前: func NewParser(r io.Reader) *Parser { ... }
// 変更後: func NewDecoder(r io.Reader) *Decoder { ... }
// 変更前: func (p *Parser) Token() (t Token, err error) { ... }
// 変更後: func (d *Decoder) Token() (t Token, err error) { ... }
// その他、Parserのフィールドやメソッドのレシーバ名がpからdに変更
このファイルでは、Parser
型が Decoder
に完全にリネームされ、それに伴い NewParser
関数も NewDecoder
に変更されました。また、Parser
のメソッドのレシーバ変数名も p
から d
に変更され、コード全体で一貫性が保たれています。
src/cmd/gofix/xmlapi.go
の新規追加
package main
import (
"go/ast"
)
func init() {
register(xmlapiFix)
}
var xmlapiFix = fix{
"xmlapi",
"2012-01-23",
xmlapi,
`
Make encoding/xml's API look more like the rest of the encoding packages.
http://codereview.appspot.com/5574053
`,
}
func xmlapi(f *ast.File) bool {
if !imports(f, "encoding/xml") {
return false
}
typeof, _ := typecheck(xmlapiTypeConfig, f)
fixed := false
walk(f, func(n interface{}) {
// ... (各種ASTノードのパターンマッチと書き換えロジック)
})
return fixed
}
// xmlMarshal, xmlUnmarshal, xmlCallChain などのヘルパー関数も定義
このファイルは、gofix
ツールが encoding/xml
のAPI変更を自動的に適用するためのロジックを含んでいます。xmlapi
関数は、Goのソースファイル(AST)を走査し、古い xml.Marshal
や xml.NewParser
、xml.Parser.Unmarshal
の呼び出しパターンを検出し、新しい xml.NewEncoder(...).Encode(...)
や xml.NewDecoder(...)
、xml.Decoder.DecodeElement(...)
のパターンに書き換えます。
関連リンク
- Go言語の
encoding/xml
パッケージのドキュメント: https://pkg.go.dev/encoding/xml - Go言語の
gofix
ツールに関する情報: https://go.dev/blog/gofix
参考にした情報源リンク
- Web検索結果: "Go encoding/xml API changes 2012 consistency with other packages"
- Web検索結果: "Go gofix tool purpose"
- Web検索結果: "Go issue 2626" (ただし、このコミットが参照しているIssueは現在のGitHubリポジトリのIssueとは異なる可能性が高い)
golang.org/cl/5574053
(このCLの内容は直接取得できませんでした)