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

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

このコミットは、Go言語の標準ライブラリ encoding/xml パッケージに関連する変更を含んでいます。具体的には、以下の2つのファイルが変更されています。

  • src/pkg/encoding/xml/example_test.go: encoding/xml パッケージの利用例を記述したテストファイルです。このファイルでは、xml.Unmarshal の新しい例が追加され、既存の xml.MarshalIndent の例が修正されています。
  • src/pkg/encoding/xml/read.go: encoding/xml パッケージの内部実装ファイルで、XMLの読み込み(アンマーシャリング)に関連するロジックが含まれています。このコミットでは、Unmarshal 関数のドキュメントコメントから、以前の Unmarshal の使用例が削除されています。

コミット

  • コミットハッシュ: 133c6bf77fffcbfa38ed58cf06808b38bbc374e0
  • Author: Gustavo Niemeyer gustavo@niemeyer.net
  • Date: Wed Feb 22 23:37:57 2012 -0200

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

https://github.com/golang/go/commit/133c6bf77fffcbfa38ed58cf06808b38bbc374e0

元コミット内容

encoding/xml: move Unmarshal example to function

This also fixes MarshalIndent's example after the
recent formatting convention changes.

Fixes #2831.

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

変更の背景

このコミットは、主に encoding/xml パッケージの Unmarshal 関数の使用例を、ドキュメントコメントから独立したテスト関数 (ExampleUnmarshal) へと移動させることを目的としています。これにより、Goの標準的な例の記述方法に準拠し、go test コマンドで例が実行・検証されるようになります。

また、MarshalIndent 関数の例も修正されています。これは、最近のGoのコードフォーマット規約の変更(おそらくインデントに関するもの)に対応するためです。コミットメッセージにある Fixes #2831 は、この変更が特定の課題(Issue 2831)を解決することを示唆していますが、現在のGitHubリポジトリでは直接的なIssue 2831は見つかりませんでした。しかし、GoのIssueトラッカーは時期によって異なる場合があるため、当時のGoのIssueトラッカーで追跡されていた問題である可能性が高いです。

前提知識の解説

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

encoding/xml パッケージは、GoプログラムとXMLデータの間で構造体とXML要素を相互に変換(マーシャリングとアンマーシャリング)するための機能を提供します。

  • マーシャリング (Marshaling): Goの構造体のデータをXML形式に変換すること。xml.Marshalxml.MarshalIndent 関数がこれにあたります。
    • xml.MarshalIndent(v interface{}, prefix, indent string) ([]byte, error): 構造体 v をXMLバイト列に変換します。prefix は各行の先頭に付加される文字列、indent はインデントに使用される文字列です。これにより、整形された(インデントされた)XMLが出力されます。
  • アンマーシャリング (Unmarshaling): XMLデータをGoの構造体に変換すること。xml.Unmarshal 関数がこれにあたります。
    • xml.Unmarshal(data []byte, v interface{}) error: XMLバイト列 data をGoの構造体 v に変換します。

Go言語の Example 関数

Go言語では、パッケージの使用例を Example 関数として記述する慣習があります。これらの関数は _test.go ファイル内に記述され、go test コマンドを実行する際に自動的にテストされ、ドキュメント生成ツール (go doc) によってドキュメントに組み込まれます。

Example 関数の特徴は以下の通りです。

  • 関数名のプレフィックスが Example であること(例: ExampleUnmarshal)。
  • 関数内に // Output: コメントブロックを含めることで、その例の標準出力が期待される出力と一致するかどうかが go test によって検証されること。これにより、例が常に正しく動作することが保証されます。

XMLの構造とGo構造体へのマッピング

encoding/xml パッケージでは、Goの構造体のフィールドにタグ (xml:"elementName", xml:"attrName,attr") を付与することで、XML要素や属性とのマッピングを制御します。

  • xml:"elementName": フィールドが対応するXML要素の名前を指定します。
  • xml:"attrName,attr": フィールドが対応するXML属性の名前を指定します。
  • xml:"-": フィールドをXMLに含めないことを示します。
  • xml:",innerxml": フィールドが要素の内部XMLコンテンツを保持することを示します。
  • xml:"parent>child": ネストされた要素のパスを指定します。

技術的詳細

このコミットの主要な変更点は、encoding/xml パッケージの Unmarshal 関数の使用例を、src/pkg/encoding/xml/read.go のドキュメントコメントから src/pkg/encoding/xml/example_test.go 内の ExampleUnmarshal 関数へと移動させたことです。

Unmarshal 例の移動

以前は read.go 内の Unmarshal 関数のドキュメントコメントに、XMLデータとそれに対応するGo構造体の定義、そしてアンマーシャリング後の結果が詳細に記述されていました。これはドキュメントとしては有用でしたが、Goの Example 関数の慣習に沿っていませんでした。

このコミットにより、この例は example_test.go に完全に移植され、ExampleUnmarshal という独立した関数になりました。これにより、以下の利点が得られます。

  1. 自動テストと検証: go test コマンドを実行するたびに、この例が実際に動作し、期待される出力 (// Output: コメントで指定されたもの) と一致することが検証されます。これにより、例が古くなったり、バグを含んだりするリスクが低減します。
  2. ドキュメントの改善: go doc コマンドで encoding/xml パッケージのドキュメントを生成する際に、この例が自動的に組み込まれ、よりインタラクティブで信頼性の高いドキュメントが提供されます。
  3. コードとドキュメントの一貫性: コードベース内の例の記述方法が統一され、Goの標準的なプラクティスに準拠します。

MarshalIndent 例の修正

example_test.go 内の既存の ExampleMarshalIndent 関数も修正されています。以前はインデントにタブ (\t) を使用していましたが、このコミットではスペース ( ) に変更されています。これは、コミットメッセージにある「最近のフォーマット規約の変更」に対応するためです。Goコミュニティでは、コードの可読性と一貫性を保つために、インデントにタブではなくスペースを使用する慣習が広まっていました(特に gofmt ツールによって強制される)。この変更は、その規約に合わせたものです。

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

src/pkg/encoding/xml/example_test.go

  • 追加: ExampleUnmarshal 関数が新規に追加されました。この関数は、EmailResult という2つの構造体を定義し、XML文字列を Result 構造体にアンマーシャリングするプロセスを示しています。特に、Phone フィールドが変更されないこと、<address> 要素が無視されること、Groups フィールドが Group>Value というパスでマッピングされることが示されています。
  • 変更: ExampleMarshalIndent 関数内の xml.MarshalIndent の呼び出しで、インデントの引数が "\t", "\t" から " ", " " に変更されました。また、// Output: コメントブロックも新しいインデントに合わせて更新されています。
  • 削除: ExampleMarshalIndent 関数の上部にあった、XML出力のコメントブロックが削除されました。これは、// Output: コメントブロックがその役割を果たすためです。

src/pkg/encoding/xml/read.go

  • 削除: Unmarshal 関数のドキュメントコメントから、XMLの例(EmailResult 構造体の定義、XML入力、アンマーシャリング結果のコメント)が完全に削除されました。これは、これらの例が example_test.go に移動されたためです。

コアとなるコードの解説

ExampleUnmarshal の追加

func ExampleUnmarshal() {
	type Email struct {
		Where string `xml:"where,attr"`
		Addr  string
	}
	type Result struct {
		XMLName xml.Name `xml:"Person"`
		Name    string   `xml:"FullName"`
		Phone   string
		Email   []Email
		Groups  []string `xml:"Group>Value"`
	}
	p := Result{Name: "none", Phone: "none"}

	data := `
		<Person>
			<FullName>Grace R. Emlin</FullName>
			<Email where="home">
				<Addr>gre@example.com</Addr>
			</Email>
			<Email where='work'>
				<Addr>gre@work.com</Addr>
			</Email>
			<Group>
				<Value>Friends</Value>
				<Value>Squash</Value>
			</Group>
			<Address>123 Main Street</Address>
		</Person>
	`
	err := xml.Unmarshal([]byte(data), &p)
	if err != nil {
		fmt.Printf("error: %v", err)
		return
	}
	fmt.Printf("XMLName: %#v\\n", p.XMLName)
	fmt.Printf("Name: %q\\n", p.Name)
	fmt.Printf("Phone: %q\\n", p.Phone)
	fmt.Printf("Email: %v\\n", p.Email)
	fmt.Printf("Groups: %v\\n", p.Groups)
	// Output:
	// XMLName: xml.Name{Space:"", Local:"Person"}
	// Name: "Grace R. Emlin"
	// Phone: "none"
	// Email: [{home gre@example.com} {work gre@work.com}]
	// Groups: [Friends Squash]
}

この新しい ExampleUnmarshal 関数は、xml.Unmarshal の動作を具体的に示しています。

  • Email 構造体は、XML属性 (where,attr) と要素 (Addr) の両方をどのようにマッピングするかを示します。
  • Result 構造体は、XMLName を使ってルート要素名を指定する方法、FullName のようにXML要素名と異なるフィールド名にマッピングする方法、Phone のようにXMLに存在しないフィールドが変更されないこと、Email のように複数の要素をスライスにマッピングする方法、そして Group>Value のようにネストされた要素パスを Groups スライスにマッピングする方法を示しています。
  • Address 要素は Result 構造体にマッピングするフィールドがないため、アンマーシャリング時に無視されることが示されています。
  • // Output: コメントブロックは、この例を実行した際の期待される標準出力を正確に定義しており、go test による自動検証を可能にしています。

MarshalIndent のインデント修正

func ExampleMarshalIndent() {
	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
		Married   bool
		Comment   string `xml:",comment"`
	}

	v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
	v.Comment = " Need more fields. "

	output, err := xml.MarshalIndent(v, "  ", "    ") // 変更点
	if err != nil {
		fmt.Printf("error: %v\\n", err)
	}

	os.Stdout.Write(output)
	// Output: // 変更点
	//   <person id="13">
	//       <name>
	//           <first>John</first>
	//           <last>Doe</last>
	//       </name>
	//       <age>42</age>
	//       <Married>false</Married>
	//       <!-- Need more fields. -->
	//   </person>
}

xml.MarshalIndent(v, " ", " ") の呼び出しは、生成されるXMLのインデントを制御します。

  • 最初の引数 " " は、各要素の開始タグの前に付加されるプレフィックスです。ここでは2つのスペースが指定されています。
  • 2番目の引数 " " は、ネストされた要素のインデントに使用される文字列です。ここでは4つのスペースが指定されています。

この変更により、生成されるXMLのインデントがタブからスペースベースに変わり、Goの一般的なコーディングスタイルガイドラインに準拠するようになりました。// Output: コメントも、この新しいインデントに合わせて更新されています。

関連リンク

参考にした情報源リンク