[インデックス 15110] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/xml
パッケージにおける MarshalIndent
関数の挙動を修正するものです。具体的には、MarshalIndent
が生成するXML出力の先頭に不要な改行が含まれる問題を解決し、より期待される整形済みXML出力を提供します。
コミット
commit 848d10f06cf327212d1ce7041c0eeae5fda317f1
Author: Shivakumar GN <shivakumar.gn@gmail.com>
Date: Sun Feb 3 11:21:07 2013 -0500
xml: omit newline at beginning of MarshalIndent output
(Still valid XML.)
Fixes #3354.
R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/7288047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/848d10f06cf327212d1ce7041c0eeae5fda317f1
元コミット内容
xml: omit newline at beginning of MarshalIndent output
(Still valid XML.)
Fixes #3354.
R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/7288047
変更の背景
この変更は、Go言語の encoding/xml
パッケージの MarshalIndent
関数が、XMLデータを整形して出力する際に、結果のXML文字列の先頭に余分な改行文字(\n
)を挿入してしまうというバグ(Issue 3354)を修正するために行われました。
MarshalIndent
は、XMLデータを人間が読みやすいようにインデントを付けて整形する機能を提供します。しかし、この余分な改行は、特にXML宣言(<?xml version="1.0"?>
)がない場合に、出力の見た目を損ねたり、一部のXMLパーサーやツールで予期せぬ挙動を引き起こす可能性がありました。XMLの仕様上、XML宣言の前に空白文字が存在することは許容されますが、XML宣言がない場合に先頭に改行があるのは、一般的に期待される整形出力ではありませんでした。
この問題は、GoのIssueトラッカーで Issue 3354: encoding/xml: MarshalIndent adds newline at beginning of output として報告されており、このコミットはその報告に対する修正として実装されました。
前提知識の解説
Go言語の encoding/xml
パッケージ
encoding/xml
パッケージは、Go言語でXMLデータをエンコード(Goの構造体からXMLへ変換)およびデコード(XMLからGoの構造体へ変換)するための機能を提供します。このパッケージは、XMLの要素、属性、テキストコンテンツなどをGoのデータ型にマッピングするためのタグ付けメカニズム(構造体フィールドタグ)をサポートしています。
Marshal
と MarshalIndent
関数
-
xml.Marshal(v interface{}) ([]byte, error)
: この関数は、Goの任意のデータ構造v
をXML形式にエンコードし、その結果をバイトスライスとして返します。この関数は、XMLを整形せずに、可能な限りコンパクトな形式で出力します。 -
xml.MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
: この関数もMarshal
と同様にGoのデータ構造をXMLにエンコードしますが、出力されるXMLを人間が読みやすいように整形(インデント)します。prefix
: 各行の先頭に付加される文字列(例: XML宣言の前にスペースを入れる場合など)。indent
: 各インデントレベルで使用される文字列(例:\t
でタブ、
XMLにおける改行と空白文字の扱い
XMLの仕様では、要素間の空白文字(スペース、タブ、改行)は、そのコンテキストによって「意味のある空白」と「意味のない空白」に区別されます。整形されたXMLでは、通常、要素間の改行やインデントは「意味のない空白」と見なされ、XMLパーサーによって無視されるか、正規化されます。
しかし、XMLドキュメントの先頭、特にXML宣言(<?xml ...?>
)の前に空白文字が存在することは、XML 1.0の仕様では許容されています。このコミットで修正された問題は、この「許容される」空白が、MarshalIndent
の意図しない挙動によって「余分な」改行として出力されてしまう点にありました。
技術的詳細
この修正は、encoding/xml/marshal.go
内の printer
構造体と writeIndent
メソッドのロジックを変更することで実現されています。
printer
構造体
printer
構造体は、XMLのエンコード処理中にXML出力を構築するための内部的なヘルパー構造体です。この構造体には、インデントレベル、プレフィックス、現在のインデント状態などを管理するためのフィールドが含まれています。
今回の変更では、printer
構造体に新たに putNewline bool
フィールドが追加されました。
putNewline
: このフラグは、次に改行を出力すべきかどうかを制御します。初期値はfalse
で、最初のインデント処理時には改行を出力しないようにします。一度改行が出力された後はtrue
に設定され、それ以降のインデント処理では通常通り改行が出力されるようになります。
writeIndent
メソッド
writeIndent
メソッドは、XML要素の開始タグや終了タグの前にインデントと改行を書き込む役割を担っています。
修正前は、このメソッドが呼び出されるたびに無条件に改行文字(\n
)を書き込んでいました。これが、MarshalIndent
が生成するXML出力の先頭に余分な改行が含まれる原因でした。
修正後、writeIndent
メソッドは p.putNewline
フラグの状態をチェックするようになりました。
if p.putNewline
:putNewline
がtrue
の場合のみ、改行文字を書き込みます。else
:putNewline
がfalse
の場合(つまり、MarshalIndent
が呼び出されてから最初のインデント処理の場合)、改行は書き込まず、p.putNewline
をtrue
に設定します。これにより、2回目以降のインデント処理では改行が通常通り出力されるようになります。
このロジックにより、MarshalIndent
が生成するXML出力の最初のインデント処理では改行が抑制され、結果としてXML文字列の先頭に余分な改行が含まれなくなります。
コアとなるコードの変更箇所
src/pkg/encoding/xml/marshal.go
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -124,6 +124,7 @@ type printer struct {
prefix string
depth int
indentedIn bool
+ putNewline bool // 新規追加
}
// marshalValue writes one or more XML elements representing val.
@@ -394,7 +395,11 @@ func (p *printer) writeIndent(depthDelta int) {
}
p.indentedIn = false
}
- p.WriteByte('\n') // 変更前: 無条件に改行を書き込む
+ if p.putNewline { // 変更後: putNewlineがtrueの場合のみ改行
+ p.WriteByte('\n')
+ } else { // putNewlineがfalseの場合(初回)
+ p.putNewline = true // 次回以降は改行を許可
+ }
if len(p.prefix) > 0 {
p.WriteString(p.prefix)
}
src/pkg/encoding/xml/marshal_test.go
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -7,6 +7,7 @@ package xml
import (
"bytes"
"errors"
+ "fmt" // fmtパッケージのインポートを追加
"io"
"reflect"
"strconv"
@@ -840,6 +841,24 @@ var marshalErrorTests = []struct {
},\n}\n \n+var marshalIndentTests = []struct { // MarshalIndentの新しいテストケースを追加
+ Value interface{}
+ Prefix string
+ Indent string
+ ExpectXML string
+}{\n+\t{\n+\t\tValue: &SecretAgent{\n+\t\t\tHandle: "007",\n+\t\t\tIdentity: "James Bond",\n+\t\t\tObfuscate: "<redacted/>",\n+\t\t},\n+\t\tPrefix: "",\n+\t\tIndent: "\t",
+ ExpectXML: fmt.Sprintf("<agent handle=\"007\">\\n\\t<Identity>James Bond</Identity><redacted/>\\n</agent>"),
+ },\n}\n+\n func TestMarshalErrors(t *testing.T) {\n \tfor idx, test := range marshalErrorTests {\n \t\t_, err := Marshal(test.Value)\n@@ -884,6 +903,19 @@ func TestUnmarshal(t *testing.T) {\n \t}\n }\n \n+func TestMarshalIndent(t *testing.T) { // MarshalIndentのテスト関数を追加
+\tfor i, test := range marshalIndentTests {\n+\t\tdata, err := MarshalIndent(test.Value, test.Prefix, test.Indent)\n+\t\tif err != nil {\n+\t\t\tt.Errorf("#%d: Error: %s", i, err)\n+\t\t\tcontinue\n+\t\t}\n+\t\tif got, want := string(data), test.ExpectXML; got != want {\n+\t\t\tt.Errorf("#%d: MarshalIndent:\\nGot:%s\\nWant:\\n%s", i, got, want)\n+\t\t}\n+\t}\n+}\n+\n type limitedBytesWriter struct {\n \tw io.Writer\n \tremain int // until writes fail
コアとなるコードの解説
このコミットの核心は、printer
構造体に導入された putNewline
フラグと、それを利用した writeIndent
メソッドの条件付き改行出力ロジックです。
-
putNewline
フラグの導入:printer
構造体にputNewline bool
フィールドが追加されました。このフラグは、MarshalIndent
がXMLのエンコードを開始してから、まだ一度も改行を出力していない状態を追跡するために使用されます。初期値はfalse
です。 -
writeIndent
メソッドの変更:writeIndent
メソッドは、XML要素のインデントと改行を処理する際に呼び出されます。- 初回呼び出し時:
putNewline
がfalse
の場合、これはMarshalIndent
がXML出力の最初のインデント処理を行っていることを意味します。このとき、writeIndent
は改行を出力せず、代わりにputNewline
をtrue
に設定します。これにより、XML出力の先頭に余分な改行が挿入されるのを防ぎます。 - 2回目以降の呼び出し時:
putNewline
がtrue
の場合、これは最初のインデント処理が既に完了していることを意味します。このとき、writeIndent
は通常通り改行文字(\n
)を書き込みます。これにより、XMLの各要素が適切に改行され、整形された出力が維持されます。
- 初回呼び出し時:
このシンプルなフラグと条件分岐の追加により、MarshalIndent
はXMLの構造を損なうことなく、期待される整形済みXML(先頭に余分な改行がない)を生成できるようになりました。
また、この変更を検証するために、marshal_test.go
に marshalIndentTests
という新しいテストケースと TestMarshalIndent
というテスト関数が追加されました。このテストは、MarshalIndent
の出力が、先頭に改行を含まない正しい形式であることを確認します。
関連リンク
- Go CL (Code Review) リンク: https://golang.org/cl/7288047
- Go Issue 3354: encoding/xml: MarshalIndent adds newline at beginning of output