[インデックス 15027] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/xml
パッケージに、(*Encoder).Indent
メソッドを追加するものです。これにより、XMLエンコーダがストリーム処理中にインデントされたXML出力を生成できるようになります。これまでは、xml.MarshalIndent
関数を使用した場合にのみインデントが可能でしたが、この変更により、xml.NewEncoder
で作成された Encoder
インスタンスに対しても、明示的にインデント設定を適用できるようになりました。
コミット
commit ee908748265debed97592f63a40f41a17e9c9d2a
Author: Russ Cox <rsc@golang.org>
Date: Wed Jan 30 07:57:20 2013 -0800
encoding/xml: add (*Encoder).Indent
Exposing this on the Encoder allows streaming generation of indented XML.
R=golang-dev, rogpeppe
CC=golang-dev
https://golang.org/cl/7221075
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ee908748265debed97592f63a40f41a17e9c9d2a
元コミット内容
encoding/xml
パッケージに (*Encoder).Indent
メソッドを追加します。このメソッドを Encoder
に公開することで、インデントされたXMLをストリーミングで生成することが可能になります。
変更の背景
Go言語の encoding/xml
パッケージには、Goの構造体をXML形式にエンコードするための機能が提供されています。従来、XML出力をインデントして整形するには、xml.MarshalIndent
関数を使用する必要がありました。この関数は、Goのオブジェクトを一度メモリ上でXMLバイト列に変換し、その際にインデントを適用します。
しかし、大規模なデータや、リアルタイムでXMLを生成して出力ストリームに書き込むようなシナリオ(例えば、HTTPレスポンスとしてXMLを返すWebサーバーなど)では、xml.MarshalIndent
のように一度全体をメモリにロードしてから処理する方法は効率的ではありません。このようなストリーミングのユースケースでは、xml.NewEncoder
を使用して io.Writer
に直接書き込む Encoder
インスタンスが利用されます。
このコミット以前は、Encoder
インスタンスには直接インデントを設定する公開されたメソッドがありませんでした。そのため、ストリーミングでインデントされたXMLを生成するには、カスタムのラッパーや複雑なロジックが必要でした。この変更は、Encoder
に Indent
メソッドを追加することで、ストリーミング処理においても簡単に整形されたXMLを出力できるようにし、開発者の利便性と効率性を向上させることを目的としています。
前提知識の解説
Go言語の encoding/xml
パッケージ
encoding/xml
パッケージは、Goの構造体とXMLドキュメントの間でエンコード(マーシャリング)およびデコード(アンマーシャリング)を行うための機能を提供します。
xml.Marshal(v interface{}) ([]byte, error)
: Goの値をXMLバイト列にマーシャリングします。インデントは適用されません。xml.MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
: Goの値をXMLバイト列にマーシャリングし、指定されたprefix
とindent
文字列を使用してインデントを適用します。xml.NewEncoder(w io.Writer) *Encoder
: 指定されたio.Writer
にXMLを書き込む新しいEncoder
を作成します。このEncoder
を使用すると、Encode
メソッドを呼び出すたびに、Goの値をストリームに直接書き込むことができます。これは、メモリ効率が良く、大規模なデータやストリーミング処理に適しています。xml.Encoder
:NewEncoder
によって返される型で、XMLエンコーディングのストリーム処理を管理します。
XMLのインデント
XMLのインデントは、XMLドキュメントの可読性を向上させるために、要素のネストレベルに応じて空白(スペースやタブ)を追加する整形方法です。例えば、以下のようにインデントされます。
<root>
<element1>
<subelement/>
</element1>
<element2/>
</root>
prefix
は各行の先頭に追加される文字列(例: ドキュメント全体の先頭に付く文字列)、indent
はネストレベルごとに繰り返されるインデント文字列(例: スペース2つ、タブ1つ)を指します。
技術的詳細
このコミットの核心は、xml.Encoder
型に Indent
メソッドを追加し、その内部で prefix
と indent
という非公開フィールドを設定できるようにした点です。
encoding/xml
パッケージの内部では、XMLの書き込み処理を行う printer
という構造体が存在し、この printer
が実際にインデントのロジックを管理しています。Encoder
はこの printer
を内包しており、prefix
と indent
フィールドは printer
の設定に直接影響を与えます。
変更前は、xml.MarshalIndent
関数が内部的に NewEncoder
を呼び出した後、直接 enc.prefix = prefix
と enc.indent = indent
のようにフィールドに値を設定していました。これは、MarshalIndent
が一度限りの処理であり、その場でインデント設定を完結させるためでした。
しかし、ストリーミングでXMLを生成する Encoder
の場合、NewEncoder
でインスタンスを作成した後に、任意のタイミングでインデント設定を変更できるメカニズムが必要でした。(*Encoder).Indent
メソッドは、このニーズに応えるために導入されました。
このメソッドが追加されたことで、開発者は以下のように Encoder
を利用できるようになります。
xml.NewEncoder(w io.Writer)
でEncoder
インスタンスを作成する。enc.Indent(prefix, indent)
を呼び出して、インデントの形式を設定する。enc.Encode(v)
を繰り返し呼び出して、Goの値をストリームに書き込む。この際、設定されたインデントが自動的に適用される。
これにより、MarshalIndent
のように一度に全てのXMLをメモリに保持することなく、効率的に整形されたXMLを生成し続けることが可能になります。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下の2つのファイルにあります。
src/pkg/encoding/xml/example_test.go
:ExampleEncoder()
という新しいテスト関数が追加され、(*Encoder).Indent
の使用例が示されています。src/pkg/encoding/xml/marshal.go
:MarshalIndent
関数内で、直接フィールドに値を設定していた部分がenc.Indent(prefix, indent)
の呼び出しに置き換えられました。(*Encoder).Indent
メソッドが新しく追加されました。
src/pkg/encoding/xml/marshal.go
の変更
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -81,8 +81,7 @@ func Marshal(v interface{}) ([]byte, error) {
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
var b bytes.Buffer
enc := NewEncoder(&b)
- enc.prefix = prefix
- enc.indent = indent
+ enc.Indent(prefix, indent)
if err := enc.Encode(v); err != nil {
return nil, err
}
@@ -99,6 +98,14 @@ func NewEncoder(w io.Writer) *Encoder {
return &Encoder{printer{Writer: bufio.NewWriter(w)}}
}
+// Indent sets the encoder to generate XML in which each element
+// begins on a new indented line that starts with prefix and is followed by
+// one or more copies of indent according to the nesting depth.
+func (enc *Encoder) Indent(prefix, indent string) {
+ enc.prefix = prefix
+ enc.indent = indent
+}
+
// Encode writes the XML encoding of v to the stream.
//
// See the documentation for Marshal for details about the conversion
src/pkg/encoding/xml/example_test.go
の変更
--- a/src/pkg/encoding/xml/example_test.go
+++ b/src/pkg/encoding/xml/example_test.go
@@ -50,6 +50,46 @@ func ExampleMarshalIndent() {
// </person>
}
+func ExampleEncoder() {
+ type Address struct {
+ City, State string
+ }
+ type Person struct {
+ XMLName xml.Name `xml:"person"`
+ Id int `xml:"id,attr"`
+ FirstName string `xml:"name>first"`
+ LastName string `xml:"name>last"`
+ Age int `xml:"age"`
+ Height float32 `xml:"height,omitempty"`
+ Married bool
+ Address
+ Comment string `xml:",comment"`
+ }
+
+ v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
+ v.Comment = " Need more details. "
+ v.Address = Address{"Hanga Roa", "Easter Island"}
+
+ enc := xml.NewEncoder(os.Stdout)
+ enc.Indent(" ", " ")
+ if err := enc.Encode(v); err != nil {
+ fmt.Printf("error: %v\n", err)
+ }
+
+ // Output:
+ // <person id="13">
+ // <name>
+ // <first>John</first>
+ // <last>Doe</last>
+ // </name>
+ // <age>42</age>
+ // <Married>false</Married>
+ // <City>Hanga Roa</City>
+ // <State>Easter Island</State>
+ // <!-- Need more details. -->
+ // </person>
+}
+
// This example demonstrates unmarshaling an XML excerpt into a value with
// some preset fields. Note that the Phone field isn\'t modified and that
// the XML <Company> element is ignored. Also, the Groups field is assigned
コアとなるコードの解説
func (enc *Encoder) Indent(prefix, indent string)
このメソッドは Encoder
型のレシーバを持ち、prefix
と indent
という2つの文字列引数を受け取ります。
prefix
: 各行の先頭に付加される文字列です。例えば、XMLドキュメント全体を特定の文字列で囲みたい場合などに使用できます。indent
: 各ネストレベルごとに繰り返されるインデント文字列です。通常はスペース2つ (" "
) やタブ ("\t"
) などが指定されます。
メソッドの内部では、単にレシーバ enc
の非公開フィールドである prefix
と indent
に引数の値を代入しています。
func (enc *Encoder) Indent(prefix, indent string) {
enc.prefix = prefix
enc.indent = indent
}
このシンプルな代入により、Encoder
がXMLを書き出す際に使用するインデント設定が更新されます。Encoder
の内部ロジック(具体的には printer
構造体)は、これらのフィールドの値を参照して、XML要素の開始タグを新しい行に書き込み、適切な数のインデント文字列を付加します。
MarshalIndent
関数の変更
MarshalIndent
関数は、これまで直接 enc.prefix = prefix
と enc.indent = indent
を行っていましたが、この変更により新しく追加された enc.Indent(prefix, indent)
メソッドを呼び出すように修正されました。
- enc.prefix = prefix
- enc.indent = indent
+ enc.Indent(prefix, indent)
これは、コードの重複を避け、Indent
メソッドがインデント設定の唯一の責任を持つようにするためのリファクタリングです。これにより、MarshalIndent
の実装がより簡潔になり、Encoder
のインデント設定ロジックが一元化されました。
ExampleEncoder()
の追加
example_test.go
に追加された ExampleEncoder()
は、(*Encoder).Indent
メソッドの具体的な使用方法を示しています。
enc := xml.NewEncoder(os.Stdout)
enc.Indent(" ", " ") // ここでインデントを設定
if err := enc.Encode(v); err != nil {
fmt.Printf("error: %v\n", err)
}
この例では、os.Stdout
に直接XMLを書き出す Encoder
を作成し、enc.Indent(" ", " ")
を呼び出して、各行の先頭にスペース2つ、ネストレベルごとにスペース4つのインデントを設定しています。その後の enc.Encode(v)
の呼び出しにより、整形されたXMLが出力されることが // Output:
コメントで示されています。
この新しい例は、開発者が Encoder
を使用してストリーミングでインデントされたXMLを生成する方法を理解する上で非常に役立ちます。
関連リンク
- Go CL 7221075: https://golang.org/cl/7221075
参考にした情報源リンク
- Go言語
encoding/xml
パッケージ公式ドキュメント: https://pkg.go.dev/encoding/xml - Go言語のソースコード (GitHub): https://github.com/golang/go
- XML (Extensible Markup Language) の基本概念
- Go言語のストリーム処理と
io.Writer
インターフェースに関する一般的な知識