[インデックス 19340] ファイルの概要
このコミットは、Go言語の標準ライブラリ encoding/xml
パッケージ内の marshal.go
および marshal_test.go
ファイルに変更を加えています。具体的には、XMLエンコーダがXML宣言(<?xml ...?>
)を正しく処理できるようにするための修正と、それに対応するテストの追加が行われています。
コミット
commit 92440fb5bd43912e363f32409bba5d6880356d60
Author: Jason Del Ponte <delpontej@gmail.com>
Date: Mon May 12 23:35:56 2014 -0400
encoding/xml: fix to allow xml declaration with EncodeToken
This changes allows the first token encoded to be a xml declaration. A ProcInst with target of xml. Any other ProcInst after that with a target of xml will fail
Fixes #7380.
LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/72410043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/92440fb5bd43912e363f32409bba5d6880356d60
元コミット内容
encoding/xml: fix to allow xml declaration with EncodeToken
この変更は、エンコードされる最初のトークンがXML宣言であることを許可します。ターゲットが xml
の処理命令(ProcInst)がこれに該当します。それ以降にターゲットが xml
の処理命令があった場合、それは失敗します。
Fixes #7380.
変更の背景
このコミットは、Goの encoding/xml
パッケージがXML宣言を正しく処理できないというバグ(Issue #7380)を修正するために行われました。
XML宣言(例: <?xml version="1.0" encoding="UTF-8"?>
)は、XMLドキュメントの冒頭に記述される特別な処理命令(Processing Instruction, ProcInst)です。これはドキュメントのバージョンやエンコーディングなどの情報を提供します。
encoding/xml
パッケージの Encoder.EncodeToken
メソッドは、XMLトークンをエンコードするための低レベルなAPIです。しかし、以前の実装では、ターゲットが xml
である ProcInst
をエンコードしようとすると、それがXML宣言であってもエラーとなっていました。これは、xml
をターゲットとする ProcInst
がXML宣言として特別扱いされるべきであるにもかかわらず、一般的な ProcInst
と同じように扱われていたためです。
この問題により、ユーザーが EncodeToken
を使ってXML宣言を含むXMLドキュメントを生成しようとすると、不必要なエラーが発生し、期待通りの動作が得られませんでした。このコミットは、この制約を緩和し、XML宣言がストリームの最初のトークンとしてのみ許可されるようにすることで、この問題を解決しています。
前提知識の解説
XML宣言 (XML Declaration)
XML宣言は、XMLドキュメントの最初の行に記述されるオプションの構成要素です。これは、XMLパーサに対してドキュメントのバージョン、文字エンコーディング、スタンドアロン宣言(外部DTDを参照するかどうか)などの情報を提供します。
例: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
XML宣言は、厳密にはXMLの処理命令(Processing Instruction)の一種ですが、特別な意味を持ちます。XML仕様では、XML宣言はドキュメントの最初の非コメント、非空白文字である必要があり、一度しか出現してはならないと定められています。
処理命令 (Processing Instruction, ProcInst)
XMLの処理命令は、アプリケーションに特定の情報や指示を伝えるためのメカニズムです。構文は <?target instruction?>
の形式を取ります。target
は命令を受け取るアプリケーションを識別する名前で、instruction
はそのアプリケーションが解釈するデータです。
XML宣言は、ターゲットが xml
である処理命令として扱われますが、その位置と出現回数に厳格な制約があります。
Go言語の encoding/xml
パッケージ
encoding/xml
パッケージは、Go言語でXMLデータをエンコードおよびデコードするための機能を提供します。
xml.Encoder
: XMLデータを書き込むための構造体です。NewEncoder
関数で作成され、Encode
、EncodeElement
、EncodeToken
などのメソッドを通じてXMLトークンを書き出します。xml.Token
: XMLドキュメントの構成要素(要素の開始タグ、終了タグ、文字データ、コメント、処理命令など)を表すインターフェースです。xml.ProcInst
: 処理命令を表すToken
の実装です。Target
(命令のターゲット名)とInst
(命令の内容)のフィールドを持ちます。Encoder.EncodeToken(t Token) error
: 指定されたXMLトークンt
をエンコーダの出力ストリームに書き込みます。このメソッドは低レベルであり、通常はEncode
やEncodeElement
の内部で利用されますが、カスタムのXMLマーシャラなどで直接使用されることもあります。
技術的詳細
このコミットの技術的な核心は、encoding/xml
パッケージが ProcInst
を処理する際のロジックの変更にあります。
以前の EncodeToken
の実装では、ProcInst
の Target
が xml
である場合、無条件にエラーを返すか、または isNameString
関数による一般的なターゲット名の検証に失敗していました。これは、XML宣言が xml
をターゲットとする ProcInst
であるにもかかわらず、その特別な性質が考慮されていなかったためです。
XML仕様では、XML宣言はドキュメントの最初のトークンとしてのみ許可され、それ以降に xml
をターゲットとする ProcInst
が出現することは許されません。このコミットは、この仕様に準拠するように EncodeToken
の動作を調整します。
具体的には、以下のロジックが導入されました。
EncodeToken
が呼び出された際に、エンコーダのバッファが空(つまり、まだ何も書き込まれていない状態)であり、かつエンコードしようとしているトークンがターゲットがxml
のProcInst
である場合、これをXML宣言として許可します。- しかし、エンコーダのバッファに既にデータが存在する(つまり、最初のトークンではない)状態で、ターゲットが
xml
のProcInst
をエンコードしようとすると、エラーを返します。これは、XML宣言が最初のトークンとしてのみ許可されるというXMLの規則を強制するためです。 - その他の
ProcInst
(ターゲットがxml
ではないもの)については、引き続きisNameString
によるターゲット名の検証が行われ、不正なターゲット名であればエラーとなります。
この変更により、ユーザーは EncodeToken
を使ってXML宣言をXMLドキュメントの冒頭に正しく出力できるようになり、同時にXML仕様に違反するような不正なXML宣言の繰り返しを防ぐことができます。
コアとなるコードの変更箇所
変更は主に以下の2つのファイルで行われています。
src/pkg/encoding/xml/marshal.go
:Encoder.EncodeToken
メソッド内のProcInst
の処理ロジックが変更されました。src/pkg/encoding/xml/marshal_test.go
:EncodeToken
の新しい動作を検証するためのテストケースが追加されました。特に、XML宣言のエンコードと、それ以外のxml
ターゲットのProcInst
のエンコードが禁止されることを確認するテストが追加されています。
コアとなるコードの解説
src/pkg/encoding/xml/marshal.go
の変更
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -184,10 +184,12 @@ var (
// EncodeToken does not call Flush, because usually it is part of a larger operation
// such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
// during those), and those will call Flush when finished.
-//
// Callers that create an Encoder and then invoke EncodeToken directly, without
// using Encode or EncodeElement, need to call Flush when finished to ensure
// that the XML is written to the underlying writer.
+//
+// EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token
+// in the stream.
func (enc *Encoder) EncodeToken(t Token) error {
p := &enc.p
switch t := t.(type) {
@@ -210,7 +212,12 @@ func (enc *Encoder) EncodeToken(t Token) error {
\tp.WriteString("-->")
\treturn p.cachedWriteError()
case ProcInst:
-\t\tif t.Target == "xml" || !isNameString(t.Target) {
+\t\t// First token to be encoded which is also a ProcInst with target of xml
+\t\t// is the xml declaration. The only ProcInst where target of xml is allowed.
+\t\tif t.Target == "xml" && p.Buffered() != 0 {
+\t\t\treturn fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
+\t\t}
+\t\tif !isNameString(t.Target) {
\t\t\treturn fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
\t\t}
\t\tif bytes.Contains(t.Inst, endProcInst) {
- コメントの追加:
EncodeToken
のドキュメンテーションに、"xml"
をターゲットとするProcInst
はストリームの最初のトークンとしてのみ許可されるという新しいルールが追加されました。 ProcInst
処理ロジックの変更:case ProcInst:
ブロック内で、新しい条件t.Target == "xml" && p.Buffered() != 0
が追加されました。t.Target == "xml"
: 処理命令のターゲットが"xml"
であることをチェックします。p.Buffered() != 0
: エンコーダの内部バッファに既にデータが書き込まれているかどうかをチェックします。p.Buffered()
はバッファ内のバイト数を返します。これが0でないということは、このProcInst
が最初のトークンではないことを意味します。
- この条件が真の場合(つまり、最初のトークンではないのに
xml
ターゲットのProcInst
をエンコードしようとした場合)、"xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded"
というエラーが返されます。これにより、XML宣言が最初のトークンとしてのみ許可されるというXMLの規則が強制されます。 - 元の
t.Target == "xml" || !isNameString(t.Target)
という条件は!isNameString(t.Target)
に変更されました。これは、xml
ターゲットのProcInst
の特別な処理が上記の新ロジックでカバーされるため、一般的なターゲット名の検証のみを行うように簡略化されたことを意味します。
src/pkg/encoding/xml/marshal_test.go
の変更
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -1203,7 +1203,7 @@ var encodeTokenTests = []struct {
{Comment("foo"), "<!--foo-->", true},
{Comment("foo-->"), "", false},
{ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true},
-\t{ProcInst{"xml", []byte("Instruction")}, "", false},
+\t{ProcInst{"", []byte("Instruction")}, "", false},
{ProcInst{"Target", []byte("Instruction?>")}, "", false},
{Directive("foo"), "<!foo>", true},
{Directive("foo>"), "", false},
@@ -1230,3 +1230,37 @@ func TestEncodeToken(t *testing.T) {
}
}\n}\n+\n+func TestProcInstEncodeToken(t *testing.T) {\n+\tvar buf bytes.Buffer\n+\tenc := NewEncoder(&buf)\n+\n+\tif err := enc.EncodeToken(ProcInst{\"xml\", []byte(\"Instruction\")}); err != nil {\n+\t\tt.Fatalf(\"enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s\", err)\n+\t}\n+\n+\tif err := enc.EncodeToken(ProcInst{\"Target\", []byte(\"Instruction\")}); err != nil {\n+\t\tt.Fatalf(\"enc.EncodeToken: expected to be able to add non-xml target ProcInst\")\n+\t}\n+\n+\tif err := enc.EncodeToken(ProcInst{\"xml\", []byte(\"Instruction\")}); err == nil {\n+\t\tt.Fatalf(\"enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token\")\n+\t}\n+}\n+\n+func TestDecodeEncode(t *testing.T) {\n+\tvar in, out bytes.Buffer\n+\tin.WriteString(`<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n+<?Target Instruction?>\n+<root>\n+</root>\t\n+`)\n+\tdec := NewDecoder(&in)\n+\tenc := NewEncoder(&out)\n+\tfor tok, err := dec.Token(); err == nil; tok, err = dec.Token() {\n+\t\terr = enc.EncodeToken(tok)\n+\t\tif err != nil {\n+\t\t\tt.Fatalf(\"enc.EncodeToken: Unable to encode token (%#v), %d\", tok, err)\n+\t\t}\n+\t}\n+}\n```
* **`encodeTokenTests` の修正**:
* `{ProcInst{"xml", []byte("Instruction")}, "", false},` が `{ProcInst{"", []byte("Instruction")}, "", false},` に変更されました。これは、`xml` ターゲットの `ProcInst` が常にエンコードに失敗するわけではなく、最初のトークンであれば成功するようになったため、テストケースを修正したものです。空のターゲットを持つ `ProcInst` は不正なため、引き続き失敗するテストとして残されています。
* **`TestProcInstEncodeToken` の追加**:
* この新しいテスト関数は、`xml` ターゲットの `ProcInst` の新しい動作を具体的に検証します。
* 最初の `enc.EncodeToken(ProcInst{"xml", []byte("Instruction")})` は、XML宣言として最初のトークンでエンコードが成功することを期待します。
* 次に `enc.EncodeToken(ProcInst{"Target", []byte("Instruction")})` は、通常の `ProcInst` が引き続きエンコードできることを確認します。
* 最後に `enc.EncodeToken(ProcInst{"xml", []byte("Instruction")})` は、最初のトークンではない `xml` ターゲットの `ProcInst` がエンコードに失敗することを期待します。
* **`TestDecodeEncode` の追加**:
* このテストは、XML宣言を含むXMLドキュメントをデコードし、その後エンコードし直すというエンドツーエンドのシナリオを検証します。
* 入力XMLにはXML宣言と通常の処理命令が含まれており、これらが `DecodeToken` で正しく読み取られ、`EncodeToken` でエラーなく書き出されることを確認します。これにより、XML宣言の処理がデコードとエンコードの両方で一貫して機能することが保証されます。
これらの変更により、`encoding/xml` パッケージはXML宣言の処理に関してXML仕様に準拠し、より堅牢で正確なXMLエンコーディング機能を提供するようになりました。
## 関連リンク
* Go Issue #7380: [https://github.com/golang/go/issues/7380](https://github.com/golang/go/issues/7380)
* Go CL 72410043: [https://golang.org/cl/72410043](https://golang.org/cl/72410043)
## 参考にした情報源リンク
* XML 1.0 (Fifth Edition) - Processing Instructions: [https://www.w3.org/TR/xml/#sec-pi](https://www.w3.org/TR/xml/#sec-pi)
* XML 1.0 (Fifth Edition) - XML Declaration: [https://www.w3.org/TR/xml/#sec-prolog-dtd](https://www.w3.org/TR/xml/#sec-prolog-dtd)
* Go `encoding/xml` package documentation: [https://pkg.go.dev/encoding/xml](https://pkg.go.dev/encoding/xml)
* Go `bytes` package documentation: [https://pkg.go.dev/bytes](https://pkg.go.dev/bytes)
* Go `fmt` package documentation: [https://pkg.go.dev/fmt](https://pkg.go.dev/fmt)
* Go `testing` package documentation: [https://pkg.go.dev/testing](https://pkg.go.dev/testing)
* Go `strings` package documentation: [https://pkg.go.dev/strings](https://pkg.go.dev/strings) (indirectly used by `bytes.Contains`)```markdown
# [インデックス 19340] ファイルの概要
このコミットは、Go言語の標準ライブラリ `encoding/xml` パッケージ内の `marshal.go` および `marshal_test.go` ファイルに変更を加えています。具体的には、XMLエンコーダがXML宣言(`<?xml ...?>`)を正しく処理できるようにするための修正と、それに対応するテストの追加が行われています。
## コミット
commit 92440fb5bd43912e363f32409bba5d6880356d60 Author: Jason Del Ponte delpontej@gmail.com Date: Mon May 12 23:35:56 2014 -0400
encoding/xml: fix to allow xml declaration with EncodeToken
This changes allows the first token encoded to be a xml declaration. A ProcInst with target of xml. Any other ProcInst after that with a target of xml will fail
Fixes #7380.
LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/72410043
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/92440fb5bd43912e363f32409bba5d6880356d60](https://github.com/golang/go/commit/92440fb5bd43912e363f32409bba5d6880356d60)
## 元コミット内容
`encoding/xml: fix to allow xml declaration with EncodeToken`
この変更は、エンコードされる最初のトークンがXML宣言であることを許可します。ターゲットが `xml` の処理命令(ProcInst)がこれに該当します。それ以降にターゲットが `xml` の処理命令があった場合、それは失敗します。
Fixes #7380.
## 変更の背景
このコミットは、Goの `encoding/xml` パッケージがXML宣言を正しく処理できないというバグ(Issue #7380)を修正するために行われました。
XML宣言(例: `<?xml version="1.0" encoding="UTF-8"?>`)は、XMLドキュメントの冒頭に記述される特別な処理命令(Processing Instruction, ProcInst)です。これはドキュメントのバージョンやエンコーディングなどの情報を提供します。
`encoding/xml` パッケージの `Encoder.EncodeToken` メソッドは、XMLトークンをエンコードするための低レベルなAPIです。しかし、以前の実装では、ターゲットが `xml` である `ProcInst` をエンコードしようとすると、それがXML宣言であってもエラーとなっていました。これは、`xml` をターゲットとする `ProcInst` がXML宣言として特別扱いされるべきであるにもかかわらず、一般的な `ProcInst` と同じように扱われていたためです。
この問題により、ユーザーが `EncodeToken` を使ってXML宣言を含むXMLドキュメントを生成しようとすると、不必要なエラーが発生し、期待通りの動作が得られませんでした。このコミットは、この制約を緩和し、XML宣言がストリームの最初のトークンとしてのみ許可されるようにすることで、この問題を解決しています。
## 前提知識の解説
### XML宣言 (XML Declaration)
XML宣言は、XMLドキュメントの最初の行に記述されるオプションの構成要素です。これは、XMLパーサに対してドキュメントのバージョン、文字エンコーディング、スタンドアロン宣言(外部DTDを参照するかどうか)などの情報を提供します。
例: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`
XML宣言は、厳密にはXMLの処理命令(Processing Instruction)の一種ですが、特別な意味を持ちます。XML仕様では、XML宣言はドキュメントの最初の非コメント、非空白文字である必要があり、一度しか出現してはならないと定められています。
### 処理命令 (Processing Instruction, ProcInst)
XMLの処理命令は、アプリケーションに特定の情報や指示を伝えるためのメカニズムです。構文は `<?target instruction?>` の形式を取ります。`target` は命令を受け取るアプリケーションを識別する名前で、`instruction` はそのアプリケーションが解釈するデータです。
XML宣言は、ターゲットが `xml` である処理命令として扱われますが、その位置と出現回数に厳格な制約があります。
### Go言語の `encoding/xml` パッケージ
`encoding/xml` パッケージは、Go言語でXMLデータをエンコードおよびデコードするための機能を提供します。
* **`xml.Encoder`**: XMLデータを書き込むための構造体です。`NewEncoder` 関数で作成され、`Encode`、`EncodeElement`、`EncodeToken` などのメソッドを通じてXMLトークンを書き出します。
* **`xml.Token`**: XMLドキュメントの構成要素(要素の開始タグ、終了タグ、文字データ、コメント、処理命令など)を表すインターフェースです。
* **`xml.ProcInst`**: 処理命令を表す `Token` の実装です。`Target`(命令のターゲット名)と `Inst`(命令の内容)のフィールドを持ちます。
* **`Encoder.EncodeToken(t Token) error`**: 指定されたXMLトークン `t` をエンコーダの出力ストリームに書き込みます。このメソッドは低レベルであり、通常は `Encode` や `EncodeElement` の内部で利用されますが、カスタムのXMLマーシャラなどで直接使用されることもあります。
## 技術的詳細
このコミットの技術的な核心は、`encoding/xml` パッケージが `ProcInst` を処理する際のロジックの変更にあります。
以前の `EncodeToken` の実装では、`ProcInst` の `Target` が `xml` である場合、無条件にエラーを返すか、または `isNameString` 関数による一般的なターゲット名の検証に失敗していました。これは、XML宣言が `xml` をターゲットとする `ProcInst` であるにもかかわらず、その特別な性質が考慮されていなかったためです。
XML仕様では、XML宣言はドキュメントの最初のトークンとしてのみ許可され、それ以降に `xml` をターゲットとする `ProcInst` が出現することは許されません。このコミットは、この仕様に準拠するように `EncodeToken` の動作を調整します。
具体的には、以下のロジックが導入されました。
1. `EncodeToken` が呼び出された際に、エンコーダのバッファが空(つまり、まだ何も書き込まれていない状態)であり、かつエンコードしようとしているトークンがターゲットが `xml` の `ProcInst` である場合、これをXML宣言として許可します。
2. しかし、エンコーダのバッファに既にデータが存在する(つまり、最初のトークンではない)状態で、ターゲットが `xml` の `ProcInst` をエンコードしようとすると、エラーを返します。これは、XML宣言が最初のトークンとしてのみ許可されるというXMLの規則を強制するためです。
3. その他の `ProcInst`(ターゲットが `xml` ではないもの)については、引き続き `isNameString` によるターゲット名の検証が行われ、不正なターゲット名であればエラーとなります。
この変更により、ユーザーは `EncodeToken` を使ってXML宣言をXMLドキュメントの冒頭に正しく出力できるようになり、同時にXML仕様に違反するような不正なXML宣言の繰り返しを防ぐことができます。
## コアとなるコードの変更箇所
変更は主に以下の2つのファイルで行われています。
1. `src/pkg/encoding/xml/marshal.go`: `Encoder.EncodeToken` メソッド内の `ProcInst` の処理ロジックが変更されました。
2. `src/pkg/encoding/xml/marshal_test.go`: `EncodeToken` の新しい動作を検証するためのテストケースが追加されました。特に、XML宣言のエンコードと、それ以外の `xml` ターゲットの `ProcInst` のエンコードが禁止されることを確認するテストが追加されています。
## コアとなるコードの解説
### `src/pkg/encoding/xml/marshal.go` の変更
```diff
--- a/src/pkg/encoding/xml/marshal.go
+++ b/src/pkg/encoding/xml/marshal.go
@@ -184,10 +184,12 @@ var (
// EncodeToken does not call Flush, because usually it is part of a larger operation
// such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
// during those), and those will call Flush when finished.
-//
// Callers that create an Encoder and then invoke EncodeToken directly, without
// using Encode or EncodeElement, need to call Flush when finished to ensure
// that the XML is written to the underlying writer.
+//
+// EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token
+// in the stream.
func (enc *Encoder) EncodeToken(t Token) error {
p := &enc.p
switch t := t.(type) {
@@ -210,7 +212,12 @@ func (enc *Encoder) EncodeToken(t Token) error {
\tp.WriteString("-->")
\treturn p.cachedWriteError()
case ProcInst:
-\t\tif t.Target == "xml" || !isNameString(t.Target) {
+\t\t// First token to be encoded which is also a ProcInst with target of xml
+\t\t// is the xml declaration. The only ProcInst where target of xml is allowed.
+\t\tif t.Target == "xml" && p.Buffered() != 0 {
+\t\t\treturn fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
+\t\t}
+\t\tif !isNameString(t.Target) {
\t\t\treturn fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
\t\t}
\t\tif bytes.Contains(t.Inst, endProcInst) {
- コメントの追加:
EncodeToken
のドキュメンテーションに、"xml"
をターゲットとするProcInst
はストリームの最初のトークンとしてのみ許可されるという新しいルールが追加されました。 ProcInst
処理ロジックの変更:case ProcInst:
ブロック内で、新しい条件t.Target == "xml" && p.Buffered() != 0
が追加されました。t.Target == "xml"
: 処理命令のターゲットが"xml"
であることをチェックします。p.Buffered() != 0
: エンコーダの内部バッファに既にデータが書き込まれているかどうかをチェックします。p.Buffered()
はバッファ内のバイト数を返します。これが0でないということは、このProcInst
が最初のトークンではないことを意味します。
- この条件が真の場合(つまり、最初のトークンではないのに
xml
ターゲットのProcInst
をエンコードしようとした場合)、"xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded"
というエラーが返されます。これにより、XML宣言が最初のトークンとしてのみ許可されるというXMLの規則が強制されます。 - 元の
t.Target == "xml" || !isNameString(t.Target)
という条件は!isNameString(t.Target)
に変更されました。これは、xml
ターゲットのProcInst
の特別な処理が上記の新ロジックでカバーされるため、一般的なターゲット名の検証のみを行うように簡略化されたことを意味します。
src/pkg/encoding/xml/marshal_test.go
の変更
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -1203,7 +1203,7 @@ var encodeTokenTests = []struct {
{Comment("foo"), "<!--foo-->", true},
{Comment("foo-->"), "", false},
{ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true},
-\t{ProcInst{"xml", []byte("Instruction")}, "", false},
+\t{ProcInst{"", []byte("Instruction")}, "", false},
{ProcInst{"Target", []byte("Instruction?>")}, "", false},
{Directive("foo"), "<!foo>", true},
{Directive("foo>"), "", false},
@@ -1230,3 +1230,37 @@ func TestEncodeToken(t *testing.T) {
}
}\n}\n+\n+func TestProcInstEncodeToken(t *testing.T) {\n+\tvar buf bytes.Buffer\n+\tenc := NewEncoder(&buf)\n+\n+\tif err := enc.EncodeToken(ProcInst{\"xml\", []byte(\"Instruction\")}); err != nil {\n+\t\tt.Fatalf(\"enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s\", err)\n+\t}\n+\n+\tif err := enc.EncodeToken(ProcInst{\"Target\", []byte(\"Instruction\")}); err != nil {\n+\t\tt.Fatalf(\"enc.EncodeToken: expected to be able to add non-xml target ProcInst\")\n+\t}\n+\n+\tif err := enc.EncodeToken(ProcInst{\"xml\", []byte(\"Instruction\")}); err == nil {\n+\t\tt.Fatalf(\"enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token\")\n+\t}\n+}\n+\n+func TestDecodeEncode(t *testing.T) {\n+\tvar in, out bytes.Buffer\n+\tin.WriteString(`<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n+<?Target Instruction?>\n+<root>\n+</root>\t\n+`)\n+\tdec := NewDecoder(&in)\n+\tenc := NewEncoder(&out)\n+\tfor tok, err := dec.Token(); err == nil; tok, err = dec.Token() {\n+\t\terr = enc.EncodeToken(tok)\n+\t\tif err != nil {\n+\t\t\tt.Fatalf(\"enc.EncodeToken: Unable to encode token (%#v), %d\", tok, err)\n+\t\t}\n+\t}\n+}\n```
* **`encodeTokenTests` の修正**:
* `{ProcInst{"xml", []byte("Instruction")}, "", false},` が `{ProcInst{"", []byte("Instruction")}, "", false},` に変更されました。これは、`xml` ターゲットの `ProcInst` が常にエンコードに失敗するわけではなく、最初のトークンであれば成功するようになったため、テストケースを修正したものです。空のターゲットを持つ `ProcInst` は不正なため、引き続き失敗するテストとして残されています。
* **`TestProcInstEncodeToken` の追加**:
* この新しいテスト関数は、`xml` ターゲットの `ProcInst` の新しい動作を具体的に検証します。
* 最初の `enc.EncodeToken(ProcInst{"xml", []byte("Instruction")})` は、XML宣言として最初のトークンでエンコードが成功することを期待します。
* 次に `enc.EncodeToken(ProcInst{"Target", []byte("Instruction")})` は、通常の `ProcInst` が引き続きエンコードできることを確認します。
* 最後に `enc.EncodeToken(ProcInst{"xml", []byte("Instruction")})` は、最初のトークンではない `xml` ターゲットの `ProcInst` がエンコードに失敗することを期待します。
* **`TestDecodeEncode` の追加**:
* このテストは、XML宣言を含むXMLドキュメントをデコードし、その後エンコードし直すというエンドツーエンドのシナリオを検証します。
* 入力XMLにはXML宣言と通常の処理命令が含まれており、これらが `DecodeToken` で正しく読み取られ、`EncodeToken` でエラーなく書き出されることを確認します。これにより、XML宣言の処理がデコードとエンコードの両方で一貫して機能することが保証されます。
これらの変更により、`encoding/xml` パッケージはXML宣言の処理に関してXML仕様に準拠し、より堅牢で正確なXMLエンコーディング機能を提供するようになりました。
## 関連リンク
* Go Issue #7380: [https://github.com/golang/go/issues/7380](https://github.com/golang/go/issues/7380)
* Go CL 72410043: [https://golang.org/cl/72410043](https://golang.org/cl/72410043)
## 参考にした情報源リンク
* XML 1.0 (Fifth Edition) - Processing Instructions: [https://www.w3.org/TR/xml/#sec-pi](https://www.w3.org/TR/xml/#sec-pi)
* XML 1.0 (Fifth Edition) - XML Declaration: [https://www.w3.org/TR/xml/#sec-prolog-dtd](https://www.w3.org/TR/xml/#sec-prolog-dtd)
* Go `encoding/xml` package documentation: [https://pkg.go.dev/encoding/xml](https://pkg.go.dev/encoding/xml)
* Go `bytes` package documentation: [https://pkg.go.dev/bytes](https://pkg.go.dev/bytes)
* Go `fmt` package documentation: [https://pkg.go.dev/fmt](https://pkg.go.dev/fmt)
* Go `testing` package documentation: [https://pkg.go.dev/testing](https://pkg.go.dev/testing)
* Go `strings` package documentation: [https://pkg.go.dev/strings](https://pkg.go.dev/strings) (indirectly used by `bytes.Contains`)