[インデックス 17221] ファイルの概要
このコミットは、Go言語の標準ライブラリencoding/xml
パッケージにおける変更を取り消すものです。具体的には、XMLのマーシャリング(Goのデータ構造からXMLへの変換)をカスタマイズするためのMarshaler
およびMarshalerAttr
インターフェースの導入を試みたコミット(CL 12603044)を元に戻しています。
コミット
commit 56ce83f7ba0b18fd0914439ee75e1164ba82733f
Author: Russ Cox <rsc@golang.org>
Date: Wed Aug 14 00:20:55 2013 -0400
undo CL 12603044 / 2ca230b93195
fat fingers - did not intend to submit.
depends on the Unmarshaler CL anyway.
««« original CL description
encoding/xml: add, support Marshaler interface
See golang.org/s/go12xml for design.
Fixes #2771.
Fixes #4169.
Fixes #5975.
Fixes #6125.
R=golang-dev, iant, dan.kortschak
CC=golang-dev
https://golang.org/cl/12603044
»»»
TBR=golang-dev
CC=golang-dev
https://golang.org/cl/12918043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/56ce83f7ba0b18fd0914439ee75e1164ba82733f
元コミット内容
encoding/xml: add, support Marshaler interface
See golang.org/s/go12xml for design.
Fixes #2771.
Fixes #4169.
Fixes #5975.
Fixes #6125.
変更の背景
このコミットは、encoding/xml
パッケージにMarshaler
インターフェースを追加するコミット(CL 12603044)を「元に戻す (undo)」ものです。コミットメッセージにある「fat fingers - did not intend to submit.」という記述は、コミットの作者であるRuss Cox氏が意図せず誤ってコミットを提出してしまったことを示しています。
また、「depends on the Unmarshaler CL anyway.」という記述から、このMarshaler
インターフェースの導入は、Unmarshaler
インターフェースの導入に依存しており、そのUnmarshaler
インターフェースの変更がまだ準備できていなかったため、このMarshaler
インターフェースの変更も一時的に取り消す必要があったことが伺えます。これは、関連する機能が完全に揃うまで、部分的な変更をメインブランチにマージしないという開発プラクティスに沿ったものです。
結果として、このコミットは、誤ってマージされた変更を迅速にロールバックし、コードベースの一貫性と安定性を維持するための修正コミットとして機能しています。
前提知識の解説
Go言語のencoding/xml
パッケージ
encoding/xml
パッケージは、Goのデータ構造(structなど)とXMLドキュメントの間でデータを変換(マーシャリングとアンマーシャリング)するための機能を提供します。
- マーシャリング (Marshaling): Goのデータ構造をXML形式に変換すること。
xml.Marshal
関数やxml.NewEncoder
を使って行います。 - アンマーシャリング (Unmarshaling): XML形式のデータをGoのデータ構造に変換すること。
xml.Unmarshal
関数やxml.NewDecoder
を使って行います。
デフォルトでは、Goのstructのフィールドタグ(例: xml:"element_name,attr"
)を使用して、GoのフィールドとXML要素/属性のマッピングを定義します。
Marshaler
およびUnmarshaler
インターフェース
Goのencoding/xml
パッケージでは、デフォルトのマーシャリング/アンマーシャリングの動作をカスタマイズするために、Marshaler
およびUnmarshaler
インターフェースが提供されています。
-
xml.Marshaler
インターフェース:type Marshaler interface { MarshalXML(e *Encoder, start StartElement) error }
このインターフェースを実装する型は、
MarshalXML
メソッドを定義することで、自身をXMLにどのようにエンコードするかを完全に制御できます。e *Encoder
はXML出力ストリームへの書き込みに使用され、start StartElement
は通常生成されるXML開始要素の情報を提供します。 -
xml.Unmarshaler
インターフェース:type Unmarshaler interface { UnmarshalXML(d *Decoder, start StartElement) error }
このインターフェースを実装する型は、
UnmarshalXML
メソッドを定義することで、XMLデータから自身をどのようにデコードするかを完全に制御できます。
これらのインターフェースは、デフォルトのタグベースのマーシャリングでは表現できない複雑なXML構造や、特定のデータ型(例: time.Time
)をカスタムフォーマットで処理する場合に非常に有用です。
MarshalerAttr
およびUnmarshalerAttr
インターフェース
同様に、XML属性のマーシャリング/アンマーシャリングをカスタマイズするためのインターフェースも存在します。
-
xml.MarshalerAttr
インターフェース:type MarshalerAttr interface { MarshalXMLAttr(name Name) (Attr, error) }
このインターフェースを実装する型は、
MarshalXMLAttr
メソッドを定義することで、自身をXML属性にどのようにエンコードするかを制御できます。 -
xml.UnmarshalerAttr
インターフェース:type UnmarshalerAttr interface { UnmarshalXMLAttr(attr Attr) error }
このインターフェースを実装する型は、
UnmarshalXMLAttr
メソッドを定義することで、XML属性から自身をどのようにデコードするかを制御できます。
「Fat Fingers」とは
「Fat fingers」は、ソフトウェア開発や金融取引などの分野で使われるスラングで、キーボード入力の際に誤って隣接するキーを押してしまうなど、不注意によるタイプミスや操作ミスを指します。このコミットの文脈では、Russ Cox氏が意図せずgit push
やgo dev submit
のようなコマンドを実行してしまい、未完成または依存関係のある変更を誤ってマージしてしまったことを意味します。
GoのCL (Change List)
Goプロジェクトでは、変更は「Change List (CL)」として管理されます。これは、Gitのコミットに似ていますが、Goの開発ワークフローでは、変更がレビューされ、承認されてから最終的にリポジトリにマージされるまでの一連のプロセスを指します。golang.org/cl/XXXXXXX
のようなURLは、特定のCLへのリンクです。
技術的詳細
このコミットは、CL 12603044によって導入されたMarshaler
およびMarshalerAttr
インターフェース、およびそれらに関連するencoding/xml
パッケージ内のコード変更を完全に元に戻すものです。
具体的には、以下の変更がロールバックされました。
Marshaler
およびMarshalerAttr
インターフェースの定義の削除:src/pkg/encoding/xml/marshal.go
からこれらのインターフェースの型定義が削除されました。Encoder
構造体および関連メソッドの変更の取り消し:Encoder
構造体からprinter
フィールドへの参照が元に戻され、printer
構造体内のencoder
フィールドも削除されました。Encoder.EncodeElement
メソッドやEncoder.EncodeToken
メソッドなど、Marshaler
インターフェースをサポートするために追加されたメソッドが削除されました。Encoder.Indent
メソッド内のenc.p.prefix
やenc.p.indent
へのアクセスがenc.prefix
やenc.indent
に元に戻されました。
printer
構造体内の変更の取り消し:printer
構造体からencoder
フィールドが削除されました。printer.createAttrPrefix
メソッドの戻り値が、isNew
を示すブール値を含まない元の形式に戻されました。printer.markPrefix
、printer.popPrefix
、printer.tags
関連のロジックが削除されました。これらはMarshaler
インターフェースがXML要素の開始/終了タグを適切に管理するために導入されたものでした。
marshalValue
メソッドの変更の取り消し:marshalValue
メソッドのシグネチャが元の形式に戻され、startTemplate *StartElement
引数が削除されました。Marshaler
およびMarshalerAttr
インターフェースの実装をチェックし、それらのMarshalXML
やMarshalXMLAttr
メソッドを呼び出すロジックが削除されました。- XML要素の開始タグの生成ロジックが、
Marshaler
インターフェースのサポートのために変更された部分が元に戻されました。 - 属性のマーシャリングロジックも、
MarshalerAttr
インターフェースのサポートのために変更された部分が元に戻されました。 writeStart
およびwriteEnd
メソッドの呼び出しが、printer
構造体内の直接的なXML書き込みロジックに置き換えられました。
marshalSimple
メソッドの変更の取り消し:marshalSimple
メソッドの戻り値が、string, []byte, error
からerror
に元に戻されました。これは、MarshalerAttr
インターフェースのサポートのために変更されたものでした。EscapeText
やWriteString
の呼び出しが、string
や[]byte
を返す代わりに直接printer
に書き込むように変更されました。
marshalStruct
メソッドの変更の取り消し:parentStack
の初期化がs := parentStack{printer: p}
に元に戻されました。- ポインタやインターフェースのデリファレンスロジックが元に戻されました。
parentStack
構造体および関連メソッドの変更の取り消し:parentStack
構造体内のp *printer
フィールドが*printer
に元に戻されました。trim
およびpush
メソッドのシグネチャが、エラーを返さない元の形式に戻されました。- これらのメソッド内で
writeEnd
やwriteStart
を呼び出す代わりに、直接XMLタグを書き込むロジックが元に戻されました。
- テストコードの削除:
src/pkg/encoding/xml/marshal_test.go
から、MyMarshalerTest
、MyMarshalerAttrTest
、MarshalerStruct
といったMarshaler
インターフェースのテストケースが削除されました。 - APIドキュメントの変更の取り消し:
api/go1.1.txt
およびapi/go1.txt
から、encoding/xml
パッケージに追加されたEncoder
のメソッド(ReadFrom
,Available
,Buffered
,Flush
,Write
,WriteByte
,WriteRune
,WriteString
)の記述が削除されました。これらのメソッドは、Marshaler
インターフェースがEncoder
を介してXMLをより細かく制御できるようにするために追加されたものでしたが、このコミットでロールバックされました。
このコミットは、機能の追加ではなく、誤ってマージされた機能の削除に焦点を当てています。これにより、encoding/xml
パッケージは、Marshaler
インターフェースが導入される前の状態に戻されました。このインターフェースは、後に別のCL(CL 12919043など)で再導入されることになります。
コアとなるコードの変更箇所
このコミットでは、以下の4つのファイルが変更されています。
api/go1.1.txt
: Go 1.1のAPI変更ログ。1行追加が取り消されました。api/go1.txt
: Go 1のAPI変更ログ。7行追加が取り消されました。src/pkg/encoding/xml/marshal.go
:encoding/xml
パッケージのマーシャリングロジックを実装する主要なファイル。459行が変更され、97行が追加され、404行が削除されました。これは、Marshaler
インターフェースとその関連ロジックの大部分が削除されたことを意味します。src/pkg/encoding/xml/marshal_test.go
:encoding/xml
パッケージのマーシャリングテストファイル。34行が削除されました。これは、Marshaler
インターフェースのテストケースが削除されたことを意味します。
合計で、97行が追加され、404行が削除されています。これは、大規模なコードの削除と、それに伴う既存コードの修正が行われたことを示しています。
コアとなるコードの解説
src/pkg/encoding/xml/marshal.go
このファイルは、encoding/xml
パッケージのXMLマーシャリングの中核を担っています。このコミットでは、主に以下の要素が削除されました。
-
Marshaler
インターフェースとMarshalerAttr
インターフェースの定義:--- a/src/pkg/encoding/xml/marshal.go +++ b/src/pkg/encoding/xml/marshal.go @@ -75,41 +75,6 @@ func Marshal(v interface{}) ([]byte, error) { return b.Bytes(), nil } -// Marshaler is the interface implemented by objects that can marshal -// themselves into valid XML elements. -// -// MarshalXML encodes the receiver as zero or more XML elements. -// By convention, arrays or slices are typically encoded as a sequence -// of elements, one per entry. -// Using start as the element tag is not required, but doing so -// will enable Unmarshal to match the XML elements to the correct -// struct field. -// One common implementation strategy is to construct a separate -// value with a layout corresponding to the desired XML and then -// to encode it using e.EncodeElement. -// Another common strategy is to use repeated calls to e.EncodeToken -// to generate the XML output one token at a time. -// The sequence of encoded tokens must make up zero or more valid -// XML elements. -type Marshaler interface { - MarshalXML(e *Encoder, start StartElement) error -} - -// MarshalerAttr is the interface implemented by objects that can marshal -// themselves into valid XML attributes. -// -// MarshalXMLAttr returns an XML attribute with the encoded value of the receiver. -// Using name as the attribute name is not required, but doing so -// will enable Unmarshal to match the attribute to the correct -// struct field. -// If MarshalXMLAttr returns the zero attribute Attr{}, no attribute -// will be generated in the output. -// MarshalXMLAttr is used only for struct fields with the -// "attr" option in the field tag. -type MarshalerAttr interface { - MarshalXMLAttr(name Name) (Attr, error) -} - // MarshalIndent works like Marshal, but each XML 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.
これらのインターフェースは、Goの型がXMLへの独自のマーシャリングロジックを提供できるようにするために導入されましたが、このコミットで一時的に削除されました。
-
Encoder
構造体とprinter
構造体の変更:Encoder
はXMLデータを書き込むための構造体であり、内部的にprinter
構造体を使用して実際の書き込みを行います。元のCLでは、Marshaler
インターフェースがEncoder
のメソッド(EncodeElement
,EncodeToken
など)を呼び出してXMLを生成できるように、Encoder
とprinter
の間に密な結合が導入されていました。このコミットでは、その結合が解除され、Encoder
は再びprinter
を直接埋め込むシンプルな構造に戻されました。--- a/src/pkg/encoding/xml/marshal.go +++ b/src/pkg/encoding/xml/marshal.go @@ -125,22 +90,20 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { // An Encoder writes XML data to an output stream. type Encoder struct { - p printer + printer } // NewEncoder returns a new encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { - e := &Encoder{printer{Writer: bufio.NewWriter(w)}} - e.p.encoder = e - return e + 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.p.prefix = prefix - enc.p.indent = indent + enc.prefix = prefix + enc.indent = indent } // Encode writes the XML encoding of v to the stream. @@ -148,83 +111,15 @@ func (enc *Encoder) Indent(prefix, indent string) { // See the documentation for Marshal for details about the conversion // of Go values to XML. func (enc *Encoder) Encode(v interface{}) error { - err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil) - if err != nil { - return err - } - return enc.p.Flush() -} - -// EncodeElement writes the XML encoding of v to the stream, -// using start as the outermost tag in the encoding. -// -// See the documentation for Marshal for details about the conversion -// of Go values to XML. -func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error { - err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start) + err := enc.marshalValue(reflect.ValueOf(v), nil) if err != nil { return err } - return enc.p.Flush() -} - -var ( - endComment = []byte("-->") - endProcInst = []byte("?>") - endDirective = []byte(">"); -) - -// EncodeToken writes the given XML token to the stream. -// It returns an error if StartElement and EndElement tokens are not properly matched. -func (enc *Encoder) EncodeToken(t Token) error { - p := &enc.p - switch t := t.(type) { - case StartElement: - if err := p.writeStart(&t); err != nil { - return err - } - case EndElement: - if err := p.writeEnd(t.Name); err != nil { - return err - } - case CharData: - EscapeText(p, t) - case Comment: - if bytes.Contains(t, endComment) { - return fmt.Errorf("xml: EncodeToken of Comment containing --> marker") - } - p.WriteString("<!--") - p.Write(t) - p.WriteString("-->") - return p.cachedWriteError() - case ProcInst: - if t.Target == "xml" || !isNameString(t.Target) { - return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target") - } - if bytes.Contains(t.Inst, endProcInst) { - return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker") - } - p.WriteString("<?") - p.WriteString(t.Target) - if len(t.Inst) > 0 { - p.WriteByte(' ') - p.Write(t.Inst) - } - p.WriteString("?>") - case Directive: - if bytes.Contains(t, endDirective) { - return fmt.Errorf("xml: EncodeToken of Directive containing > marker") - } - p.WriteString("<!") - p.Write(t) - p.WriteString(">"); - } - return p.cachedWriteError() + return enc.Flush() } type printer struct { *bufio.Writer - encoder *Encoder seq int indent string prefix string
EncodeElement
やEncodeToken
といったメソッドが削除され、Encoder.Encode
は再びFlush
を呼び出すだけのシンプルな形に戻りました。 -
marshalValue
メソッドの変更:marshalValue
はGoの値をXMLにマーシャリングする中心的なロジックです。元のCLでは、このメソッド内でMarshaler
およびMarshalerAttr
インターフェースの実装を検出し、それらを優先して呼び出すロジックが追加されていました。このコミットでは、そのロジックが削除され、インターフェースによるカスタムマーシャリングのパスがなくなりました。--- a/src/pkg/encoding/xml/marshal.go +++ b/src/pkg/encoding/xml/marshal.go @@ -301,33 +192,9 @@ func (p *printer) deleteAttrPrefix(prefix string) { delete(p.attrNS, prefix) } -func (p *printer) markPrefix() { - p.prefixes = append(p.prefixes, "") -} - -func (p *printer) popPrefix() { - for len(p.prefixes) > 0 { - prefix := p.prefixes[len(p.prefixes)-1] - p.prefixes = p.prefixes[:len(p.prefixes)-1] - if prefix == "" { - break - } - p.deleteAttrPrefix(prefix) - } -} - -var ( - marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() - marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem() -) - // marshalValue writes one or more XML elements representing val. // If val was obtained from a struct field, finfo must have its details. -func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error { - if startTemplate != nil && startTemplate.Name.Local == "" { - return fmt.Errorf("xml: EncodeElement of StartElement with missing name") - } - +func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { if !val.IsValid() { return nil } @@ -343,25 +210,13 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat if val.IsNil() { return nil } - val = val.Elem() - typ = val.Type() - } - - // Check for marshaler. - if typ.Name() != "" && val.CanAddr() { - pv := val.Addr() - if pv.CanInterface() && pv.Type().Implements(marshalerType) { - return p.marshalInterface(pv.Interface().(Marshaler), pv.Type(), finfo, startTemplate) - } - } - if val.CanInterface() && typ.Implements(marshalerType) { - return p.marshalInterface(val.Interface().(Marshaler), typ, finfo, startTemplate) + return p.marshalValue(val.Elem(), finfo) } // Slices and arrays iterate over the elements. They do not have an enclosing tag. if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 { for i, n := 0, val.Len(); i < n; i++ { - if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil { + if err := p.marshalValue(val.Index(i), finfo); err != nil { return err } }
startTemplate
引数の削除、Marshaler
およびMarshalerAttr
インターフェースのチェックと呼び出しロジックの削除が主な変更点です。 -
XMLタグの書き込みロジックの変更: 元のCLでは、
writeStart
やwriteEnd
といったヘルパーメソッドが導入され、Marshaler
インターフェースがXMLタグを生成する際にこれらを使用することが想定されていました。このコミットでは、これらのヘルパーメソッドが削除され、XMLタグの書き込みロジックがmarshalValue
やparentStack
のメソッド内に直接インライン化されました。これにより、コードの複雑さが軽減され、インターフェースによるカスタムマーシャリングのサポートが一時的に解除されました。--- a/src/pkg/encoding/xml/marshal.go +++ b/src/pkg/encoding/xml/marshal.go @@ -373,34 +228,40 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat return err } - // Create start element. // Precedence for the XML element name is: - // 0. startTemplate // 1. XMLName field in underlying struct; // 2. field name/tag in the struct field; and // 3. type name - var start StartElement - - if startTemplate != nil { - start.Name = startTemplate.Name - start.Attr = append(start.Attr, startTemplate.Attr...) - } else if tinfo.xmlname != nil { + var xmlns, name string + if tinfo.xmlname != nil { xmlname := tinfo.xmlname if xmlname.name != "" { - start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name + xmlns, name = xmlname.xmlns, xmlname.name } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" { - start.Name = v + xmlns, name = v.Space, v.Local } } - if start.Name.Local == "" && finfo != nil { - start.Name.Space, start.Name.Local = finfo.xmlns, finfo.name + if name == "" && finfo != nil { + xmlns, name = finfo.xmlns, finfo.name } - if start.Name.Local == "" { - name := typ.Name() + if name == "" { + name = typ.Name() if name == "" { return &UnsupportedTypeError{typ} } - start.Name.Local = name + } + + p.writeIndent(1) + p.WriteByte('<') + p.WriteString(name) + + if xmlns != "" { + p.WriteString(` xmlns="`) + // TODO: EscapeString, to avoid the allocation. + if err := EscapeText(p, []byte(xmlns)); err != nil { + return err + } + p.WriteByte('"') } // Attributes @@ -410,205 +276,70 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplat continue } fv := finfo.value(val) - name := Name{Space: finfo.xmlns, Local: finfo.name} - - if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) { + if (finfo.flags&fOmitEmpty != 0 || fv.Kind() == reflect.Ptr) && isEmptyValue(fv) { continue } - - if fv.CanAddr() { - pv := fv.Addr() - if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) { - attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name) - if err != nil { - return err - } - if attr.Name.Local != "" { - start.Attr = append(start.Attr, attr) - } - continue - } - } - - if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) { - if fv.Kind() == reflect.Interface && fv.IsNil() { - continue - } - attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name) - if err != nil { - return err - } - if attr.Name.Local != "" { - start.Attr = append(start.Attr, attr) + p.WriteByte(' ') + if finfo.xmlns != "" { + prefix, created := p.createAttrPrefix(finfo.xmlns) + if created { + defer p.deleteAttrPrefix(prefix) } - continue + p.WriteString(prefix) + p.WriteByte(':') } - - // Dereference or skip nil pointer, interface values. - switch fv.Kind() { - case reflect.Ptr, reflect.Interface: - if fv.IsNil() { - continue - } + p.WriteString(finfo.name) + p.WriteString(`="`) + // Handle pointer values by following the pointer, + // Pointer is known to be non-nil because we called isEmptyValue above. + if fv.Kind() == reflect.Ptr { fv = fv.Elem() } - - s, b, err := p.marshalSimple(fv.Type(), fv) - if err != nil { + if err := p.marshalSimple(fv.Type(), fv); err != nil { return err } - if b != nil { - s = string(b) - } - start.Attr = append(start.Attr, Attr{name, s}) - } - - if err := p.writeStart(&start); err != nil { - return err + p.WriteByte('"') } + p.WriteByte('>') if val.Kind() == reflect.Struct { err = p.marshalStruct(tinfo, val) } else { - s, b, err1 := p.marshalSimple(typ, val) - if err1 != nil { - err = err1 - } else if b != nil { - EscapeText(p, b) - } else { - p.EscapeString(s) - } - } - if err != nil { - return err - } - - if err := p.writeEnd(start.Name); err != nil { - return err - } - - return p.cachedWriteError() -} - -// marshalInterface marshals a Marshaler interface value. -func (p *printer) marshalInterface(val Marshaler, typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) error { - var start StartElement - - // Precedence for the XML element name is as above, - // except that we do not look inside structs for the first field. - if startTemplate != nil { - start.Name = startTemplate.Name - start.Attr = append(start.Attr, startTemplate.Attr...) - } else if finfo != nil && finfo.name != "" { - start.Name.Local = finfo.name - start.Name.Space = finfo.xmlns - } else if typ.Name() != "" { - start.Name.Local = typ.Name() - } else { - // Must be a pointer to a named type, - // since it has the Marshaler methods. - start.Name.Local = typ.Elem().Name() + err = p.marshalSimple(typ, val) } - - // Push a marker onto the tag stack so that MarshalXML - // cannot close the XML tags that it did not open. - p.tags = append(p.tags, Name{}) - n := len(p.tags) - - err := val.MarshalXML(p.encoder, start) if err != nil { return err } - // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark. - if len(p.tags) > n { - return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local) - } - p.tags = p.tags[:n-1] - return nil -} - -// writeStart writes the given start element. -func (p *printer) writeStart(start *StartElement) error { - if start.Name.Local == "" { - return fmt.Errorf("xml: start tag with no name") - } - - p.tags = append(p.tags, start.Name) - p.markPrefix() - - p.writeIndent(1) - p.WriteByte('<') - p.WriteString(start.Name.Local) - - if start.Name.Space != "" { - p.WriteString(` xmlns="`) - p.EscapeString(start.Name.Space) - p.WriteByte('"') - } - - // Attributes - for _, attr := range start.Attr { - name := attr.Name - if name.Local == "" { - continue - } - p.WriteByte(' ') - if name.Space != "" { - p.WriteString(p.createAttrPrefix(name.Space)) - p.WriteByte(':') - } - p.WriteString(name.Local) - p.WriteString(`="`) - p.EscapeString(attr.Value) - p.WriteByte('"') - } - p.WriteByte('>') - return nil -} - -func (p *printer) writeEnd(name Name) error { - if name.Local == "" { - return fmt.Errorf("xml: end tag with no name") - } - if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" { - return fmt.Errorf("xml: end tag </%s> without start tag", name.Local) - } - if top := p.tags[len(p.tags)-1]; top != name { - if top.Local != name.Local { - return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", name.Local, top.Local) - } - return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space); - } - p.tags = p.tags[:len(p.tags)-1] - p.writeIndent(-1) p.WriteByte('<') p.WriteByte('/') - p.WriteString(name.Local) + p.WriteString(name) p.WriteByte('>') - p.popPrefix() - return nil + return p.cachedWriteError() } var timeType = reflect.TypeOf(time.Time{});
marshalInterface
,writeStart
,writeEnd
メソッドが削除され、XMLタグの生成ロジックがmarshalValue
内に直接記述される形に戻されました。
src/pkg/encoding/xml/marshal_test.go
このファイルは、encoding/xml
パッケージのマーシャリング機能のテストケースを含んでいます。元のCLで追加されたMarshaler
インターフェースのテストケースが削除されました。
--- a/src/pkg/encoding/xml/marshal_test.go
+++ b/src/pkg/encoding/xml/marshal_test.go
@@ -289,31 +289,6 @@ type ChardataEmptyTest struct {
Contents *string `xml:",chardata"`
}
-type MyMarshalerTest struct {
-}
-
-var _ Marshaler = (*MyMarshalerTest)(nil)
-
-func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
- e.EncodeToken(start)
- e.EncodeToken(CharData([]byte("hello world")))
- e.EncodeToken(EndElement{start.Name})
- return nil
-}
-
-type MyMarshalerAttrTest struct {
-}
-
-var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
-
-func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
- return Attr{name, "hello world"}, nil
-}
-
-type MarshalerStruct struct {
- Foo MyMarshalerAttrTest `xml:",attr"`
-}
-
var (
nameAttr = "Sarah"
ageAttr = uint(12)
@@ -869,15 +844,6 @@ var marshalTests = []struct {
ExpectXML: `<Strings><A></A></Strings>`,
Value: &Strings{},
},
- // Custom marshalers.
- {
- ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
- Value: &MyMarshalerTest{},
- },
- {
- ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
- Value: &MarshalerStruct{},
- },
}
func TestMarshal(t *testing.T) {
MyMarshalerTest
やMarshalerStruct
などのテスト構造体と、それらを使用するテストケースが削除されました。
api/go1.1.txt
および api/go1.txt
これらのファイルは、Goの各バージョンで導入されたAPIの変更を記録しています。元のCLでは、encoding/xml
パッケージのEncoder
に新しいメソッドが追加されたため、これらのファイルにその情報が追記されていました。このコミットでは、その追記が削除され、APIの変更がロールバックされたことを示しています。
関連リンク
- 元のCL (取り消された変更): https://golang.org/cl/12603044
- このコミットのCL: https://golang.org/cl/12918043
- デザインドキュメント: golang.org/s/go12xml
- 関連するIssue:
- Fixes #2771: https://github.com/golang/go/issues/2771
- Fixes #4169: https://github.com/golang/go/issues/4169
- Fixes #5975: https://github.com/golang/go/issues/5975
- Fixes #6125: https://github.com/golang/go/issues/6125
参考にした情報源リンク
- Go
encoding/xml
package documentation: https://pkg.go.dev/encoding/xml - Go
xml.Marshaler
interface: https://pkg.go.dev/encoding/xml#Marshaler - Go
xml.Unmarshaler
interface: https://pkg.go.dev/encoding/xml#Unmarshaler - Web search results for "Go encoding/xml Marshaler interface"
- Web search results for "golang.org/cl/12603044"
- Web search results for "golang.org/cl/12918043"
- Web search results for "Go issue 2771 encoding/xml"
- Web search results for "Go issue 4169 encoding/xml"
- Web search results for "Go issue 5975 encoding/xml"
- Web search results for "Go issue 6125 encoding/xml"
- Web search results for "golang.org/s/go12xml"