1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
23 HyperDrive DriveType = iota
27 type Passenger struct {
28 Name []string `xml:"name"`
29 Weight float32 `xml:"weight"`
33 XMLName struct{} `xml:"spaceship"`
35 Name string `xml:"name,attr"`
36 Pilot string `xml:"pilot,attr"`
37 Drive DriveType `xml:"drive"`
39 Passenger []*Passenger `xml:"passenger"`
46 XMLName struct{} `xml:"port"`
47 Type string `xml:"type,attr,omitempty"`
48 Comment string `xml:",comment"`
49 Number string `xml:",chardata"`
53 XMLName struct{} `xml:"domain"`
54 Country string `xml:",attr,omitempty"`
55 Name []byte `xml:",chardata"`
56 Comment []byte `xml:",comment"`
60 XMLName struct{} `xml:"book"`
61 Title string `xml:",chardata"`
65 XMLName struct{} `xml:"event"`
66 Year int `xml:",chardata"`
70 XMLName struct{} `xml:"movie"`
71 Length uint `xml:",chardata"`
75 XMLName struct{} `xml:"pi"`
76 Approximation float32 `xml:",chardata"`
79 type Universe struct {
80 XMLName struct{} `xml:"universe"`
81 Visible float64 `xml:",chardata"`
84 type Particle struct {
85 XMLName struct{} `xml:"particle"`
86 HasMass bool `xml:",chardata"`
89 type Departure struct {
90 XMLName struct{} `xml:"departure"`
91 When time.Time `xml:",chardata"`
94 type SecretAgent struct {
95 XMLName struct{} `xml:"agent"`
96 Handle string `xml:"handle,attr"`
98 Obfuscate string `xml:",innerxml"`
101 type NestedItems struct {
102 XMLName struct{} `xml:"result"`
103 Items []string `xml:">item"`
104 Item1 []string `xml:"Items>item1"`
107 type NestedOrder struct {
108 XMLName struct{} `xml:"result"`
109 Field1 string `xml:"parent>c"`
110 Field2 string `xml:"parent>b"`
111 Field3 string `xml:"parent>a"`
114 type MixedNested struct {
115 XMLName struct{} `xml:"result"`
116 A string `xml:"parent1>a"`
118 C string `xml:"parent1>parent2>c"`
119 D string `xml:"parent1>d"`
122 type NilTest struct {
123 A interface{} `xml:"parent1>parent2>a"`
124 B interface{} `xml:"parent1>b"`
125 C interface{} `xml:"parent1>parent2>c"`
128 type Service struct {
129 XMLName struct{} `xml:"service"`
130 Domain *Domain `xml:"host>domain"`
131 Port *Port `xml:"host>port"`
133 Extra2 interface{} `xml:"host>extra2"`
151 FieldA1 string `xml:"FieldA>A1"`
152 FieldA2 string `xml:"FieldA>A2"`
159 FieldE string // Promoted and visible when embedD is embedded.
162 type NameCasing struct {
163 XMLName struct{} `xml:"casing"`
166 XyA string `xml:"Xy,attr"`
167 XYA string `xml:"XY,attr"`
170 type NamePrecedence struct {
171 XMLName Name `xml:"Parent"`
172 FromTag XMLNameWithoutTag `xml:"InTag"`
173 FromNameVal XMLNameWithoutTag
174 FromNameTag XMLNameWithTag
178 type XMLNameWithTag struct {
179 XMLName Name `xml:"InXMLNameTag"`
180 Value string `xml:",chardata"`
183 type XMLNameWithoutTag struct {
185 Value string `xml:",chardata"`
188 type NameInField struct {
189 Foo Name `xml:"ns foo"`
192 type AttrTest struct {
193 Int int `xml:",attr"`
194 Named int `xml:"int,attr"`
195 Float float64 `xml:",attr"`
196 Uint8 uint8 `xml:",attr"`
197 Bool bool `xml:",attr"`
198 Str string `xml:",attr"`
199 Bytes []byte `xml:",attr"`
202 type OmitAttrTest struct {
203 Int int `xml:",attr,omitempty"`
204 Named int `xml:"int,attr,omitempty"`
205 Float float64 `xml:",attr,omitempty"`
206 Uint8 uint8 `xml:",attr,omitempty"`
207 Bool bool `xml:",attr,omitempty"`
208 Str string `xml:",attr,omitempty"`
209 Bytes []byte `xml:",attr,omitempty"`
212 type OmitFieldTest struct {
213 Int int `xml:",omitempty"`
214 Named int `xml:"int,omitempty"`
215 Float float64 `xml:",omitempty"`
216 Uint8 uint8 `xml:",omitempty"`
217 Bool bool `xml:",omitempty"`
218 Str string `xml:",omitempty"`
219 Bytes []byte `xml:",omitempty"`
220 Ptr *PresenceTest `xml:",omitempty"`
223 type AnyTest struct {
224 XMLName struct{} `xml:"a"`
225 Nested string `xml:"nested>value"`
226 AnyField AnyHolder `xml:",any"`
229 type AnyOmitTest struct {
230 XMLName struct{} `xml:"a"`
231 Nested string `xml:"nested>value"`
232 AnyField *AnyHolder `xml:",any,omitempty"`
235 type AnySliceTest struct {
236 XMLName struct{} `xml:"a"`
237 Nested string `xml:"nested>value"`
238 AnyField []AnyHolder `xml:",any"`
241 type AnyHolder struct {
243 XML string `xml:",innerxml"`
246 type RecurseA struct {
251 type RecurseB struct {
256 type PresenceTest struct {
260 type IgnoreTest struct {
261 PublicSecret string `xml:"-"`
268 Attr []byte `xml:",attr"`
278 type EmbedInt struct {
282 type Strings struct {
283 X []string `xml:"A>B,omitempty"`
286 type PointerFieldsTest struct {
287 XMLName Name `xml:"dummy"`
288 Name *string `xml:"name,attr"`
289 Age *uint `xml:"age,attr"`
290 Empty *string `xml:"empty,attr"`
291 Contents *string `xml:",chardata"`
294 type ChardataEmptyTest struct {
295 XMLName Name `xml:"test"`
296 Contents *string `xml:",chardata"`
299 type MyMarshalerTest struct {
302 var _ Marshaler = (*MyMarshalerTest)(nil)
304 func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
306 e.EncodeToken(CharData([]byte("hello world")))
307 e.EncodeToken(EndElement{start.Name})
311 type MyMarshalerAttrTest struct {
314 var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
316 func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
317 return Attr{name, "hello world"}, nil
320 type MarshalerStruct struct {
321 Foo MyMarshalerAttrTest `xml:",attr"`
324 type InnerStruct struct {
325 XMLName Name `xml:"testns outer"`
328 type OuterStruct struct {
330 IntAttr int `xml:"int,attr"`
333 type OuterNamedStruct struct {
335 XMLName Name `xml:"outerns test"`
336 IntAttr int `xml:"int,attr"`
339 type OuterNamedOrderedStruct struct {
340 XMLName Name `xml:"outerns test"`
342 IntAttr int `xml:"int,attr"`
345 type OuterOuterStruct struct {
349 type NestedAndChardata struct {
350 AB []string `xml:"A>B"`
351 Chardata string `xml:",chardata"`
354 type NestedAndComment struct {
355 AB []string `xml:"A>B"`
356 Comment string `xml:",comment"`
359 func ifaceptr(x interface{}) interface{} {
366 contentsAttr = "lorem ipsum"
369 // Unless explicitly stated as such (or *Plain), all of the
370 // tests below are two-way tests. When introducing new tests,
371 // please try to make them two-way as well to ensure that
372 // marshalling and unmarshalling are as symmetrical as feasible.
373 var marshalTests = []struct {
379 // Test nil marshals to nothing
380 {Value: nil, ExpectXML: ``, MarshalOnly: true},
381 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
384 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
385 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
386 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
387 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
388 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
389 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
390 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
391 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
392 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
393 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
394 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
395 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
396 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
397 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
398 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
399 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`},
400 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`},
401 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`},
402 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
403 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
404 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
405 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
409 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
410 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
413 // A pointer to struct{} may be used to test for an element's presence.
415 Value: &PresenceTest{new(struct{})},
416 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
419 Value: &PresenceTest{},
420 ExpectXML: `<PresenceTest></PresenceTest>`,
423 // A pointer to struct{} may be used to test for an element's presence.
425 Value: &PresenceTest{new(struct{})},
426 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
429 Value: &PresenceTest{},
430 ExpectXML: `<PresenceTest></PresenceTest>`,
433 // A []byte field is only nil if the element was not found.
436 ExpectXML: `<Data></Data>`,
440 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
441 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
445 // Check that []byte works, including named []byte types.
447 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
448 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
455 Identity: "James Bond",
456 Obfuscate: "<redacted/>",
458 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
464 Identity: "James Bond",
465 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
467 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
472 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
473 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
474 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`},
475 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
476 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
477 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
478 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
479 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
480 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
481 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
482 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
483 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
484 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
485 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
486 {Value: atomValue, ExpectXML: atomXml},
489 Name: "Heart of Gold",
492 Drive: ImprobabilityDrive,
493 Passenger: []*Passenger{
495 Name: []string{"Zaphod", "Beeblebrox"},
499 Name: []string{"Trisha", "McMillen"},
503 Name: []string{"Ford", "Prefect"},
507 Name: []string{"Arthur", "Dent"},
512 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
513 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
516 `<name>Zaphod</name>` +
517 `<name>Beeblebrox</name>` +
518 `<weight>7.25</weight>` +
521 `<name>Trisha</name>` +
522 `<name>McMillen</name>` +
523 `<weight>5.5</weight>` +
526 `<name>Ford</name>` +
527 `<name>Prefect</name>` +
528 `<weight>7</weight>` +
531 `<name>Arthur</name>` +
532 `<name>Dent</name>` +
533 `<weight>6.75</weight>` +
540 Value: &NestedItems{Items: nil, Item1: nil},
541 ExpectXML: `<result>` +
547 Value: &NestedItems{Items: []string{}, Item1: []string{}},
548 ExpectXML: `<result>` +
555 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
556 ExpectXML: `<result>` +
563 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
564 ExpectXML: `<result>` +
572 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
573 ExpectXML: `<result>` +
582 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
583 ExpectXML: `<result>` +
592 Value: &NilTest{A: "A", B: nil, C: "C"},
593 ExpectXML: `<NilTest>` +
595 `<parent2><a>A</a></parent2>` +
596 `<parent2><c>C</c></parent2>` +
599 MarshalOnly: true, // Uses interface{}
602 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
603 ExpectXML: `<result>` +
604 `<parent1><a>A</a></parent1>` +
607 `<parent2><c>C</c></parent2>` +
613 Value: &Service{Port: &Port{Number: "80"}},
614 ExpectXML: `<service><host><port>80</port></host></service>`,
618 ExpectXML: `<service></service>`,
621 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
622 ExpectXML: `<service>` +
623 `<host><port>80</port></host>` +
624 `<Extra1>A</Extra1>` +
625 `<host><extra2>B</extra2></host>` +
630 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
631 ExpectXML: `<service>` +
632 `<host><port>80</port></host>` +
633 `<host><extra2>example</extra2></host>` +
639 XMLName struct{} `xml:"space top"`
642 C string `xml:"space x>c"`
643 C1 string `xml:"space1 x>c"`
644 D1 string `xml:"space1 x>d"`
652 ExpectXML: `<top xmlns="space">` +
653 `<x><a>a</a><b>b</b><c xmlns="space">c</c>` +
654 `<c xmlns="space1">c1</c>` +
655 `<d xmlns="space1">d1</d>` +
664 C string `xml:"space x>c"`
665 C1 string `xml:"space1 x>c"`
666 D1 string `xml:"space1 x>d"`
678 ExpectXML: `<top xmlns="space0">` +
679 `<x><a>a</a><b>b</b>` +
680 `<c xmlns="space">c</c>` +
681 `<c xmlns="space1">c1</c>` +
682 `<d xmlns="space1">d1</d>` +
688 XMLName struct{} `xml:"top"`
689 B string `xml:"space x>b"`
690 B1 string `xml:"space1 x>b"`
696 `<x><b xmlns="space">b</b>` +
697 `<b xmlns="space1">b1</b></x>` +
701 // Test struct embedding
705 FieldA1: "", // Shadowed by A.A
706 FieldA2: "", // Shadowed by A.A
715 FieldB: "", // Shadowed by A.B.B
724 ExpectXML: `<EmbedA>` +
725 `<FieldB>A.C.B</FieldB>` +
726 `<FieldC>A.C.C</FieldC>` +
728 `<FieldB>A.B.B</FieldB>` +
730 `<A1>A.B.C.A1</A1>` +
731 `<A2>A.B.C.A2</A2>` +
733 `<FieldC>A.B.C.C</FieldC>` +
735 `<FieldA>A.A</FieldA>` +
736 `<FieldE>A.D.E</FieldE>` +
740 // Test that name casing matters
742 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
743 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
746 // Test the order in which the XML element name is chosen
748 Value: &NamePrecedence{
749 FromTag: XMLNameWithoutTag{Value: "A"},
750 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
751 FromNameTag: XMLNameWithTag{Value: "C"},
754 ExpectXML: `<Parent>` +
756 `<InXMLName>B</InXMLName>` +
757 `<InXMLNameTag>C</InXMLNameTag>` +
758 `<InFieldName>D</InFieldName>` +
763 Value: &NamePrecedence{
764 XMLName: Name{Local: "Parent"},
765 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
766 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
767 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
770 ExpectXML: `<Parent>` +
772 `<FromNameVal>B</FromNameVal>` +
773 `<InXMLNameTag>C</InXMLNameTag>` +
774 `<InFieldName>D</InFieldName>` +
779 // xml.Name works in a plain field as well.
781 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
782 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
785 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
786 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
790 // Marshaling zero xml.Name uses the tag or field name.
792 Value: &NameInField{},
793 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
806 Bytes: []byte("byt"),
808 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
809 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
812 Value: &AttrTest{Bytes: []byte{}},
813 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
814 ` Bool="false" Str="" Bytes=""></AttrTest>`,
817 Value: &OmitAttrTest{
824 Bytes: []byte("byt"),
826 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
827 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
830 Value: &OmitAttrTest{},
831 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
836 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
837 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
841 // empty chardata pointer field
843 Value: &ChardataEmptyTest{},
844 ExpectXML: `<test></test>`,
848 // omitempty on fields
850 Value: &OmitFieldTest{
857 Bytes: []byte("byt"),
858 Ptr: &PresenceTest{},
860 ExpectXML: `<OmitFieldTest>` +
863 `<Float>23.5</Float>` +
864 `<Uint8>255</Uint8>` +
865 `<Bool>true</Bool>` +
867 `<Bytes>byt</Bytes>` +
872 Value: &OmitFieldTest{},
873 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
878 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
882 XMLName: Name{Local: "other"},
883 XML: "<sub>unknown</sub>",
888 Value: &AnyTest{Nested: "known",
891 XMLName: Name{Local: "AnyField"},
894 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
897 ExpectXML: `<a><nested><value>b</value></nested></a>`,
903 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
904 Value: &AnySliceTest{
906 AnyField: []AnyHolder{
908 XMLName: Name{Local: "c"},
912 XMLName: Name{Space: "f", Local: "g"},
919 ExpectXML: `<a><nested><value>b</value></nested></a>`,
920 Value: &AnySliceTest{
925 // Test recursive types.
930 A: &RecurseA{"a2", nil},
934 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
937 // Test ignoring fields via "-" tag
939 ExpectXML: `<IgnoreTest></IgnoreTest>`,
940 Value: &IgnoreTest{},
943 ExpectXML: `<IgnoreTest></IgnoreTest>`,
944 Value: &IgnoreTest{PublicSecret: "can't tell"},
948 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
949 Value: &IgnoreTest{},
955 ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`,
957 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
958 AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
962 ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`,
964 Nested: "newline: \n; cr: \r; tab: \t;",
965 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
969 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
971 Nested: "1\n2\n3\n\n4\n5",
976 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
981 // Test omitempty with parent chain; see golang.org/issue/4168.
983 ExpectXML: `<Strings><A></A></Strings>`,
986 // Custom marshalers.
988 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
989 Value: &MyMarshalerTest{},
992 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
993 Value: &MarshalerStruct{},
996 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
997 Value: &OuterStruct{IntAttr: 10},
1000 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
1001 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
1004 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
1005 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
1008 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
1009 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
1012 ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`,
1013 Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"},
1016 ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`,
1017 Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"},
1021 func TestMarshal(t *testing.T) {
1022 for idx, test := range marshalTests {
1023 if test.UnmarshalOnly {
1026 data, err := Marshal(test.Value)
1028 t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
1031 if got, want := string(data), test.ExpectXML; got != want {
1032 if strings.Contains(want, "\n") {
1033 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
1035 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
1041 type AttrParent struct {
1042 X string `xml:"X>Y,attr"`
1045 type BadAttr struct {
1046 Name []string `xml:"name,attr"`
1049 var marshalErrorTests = []struct {
1055 Value: make(chan bool),
1056 Err: "xml: unsupported type: chan bool",
1060 Value: map[string]string{
1061 "question": "What do you get when you multiply six by nine?",
1064 Err: "xml: unsupported type: map[string]string",
1068 Value: map[*Ship]bool{nil: false},
1069 Err: "xml: unsupported type: map[*xml.Ship]bool",
1073 Value: &Domain{Comment: []byte("f--bar")},
1074 Err: `xml: comments must not contain "--"`,
1076 // Reject parent chain with attr, never worked; see golang.org/issue/5033.
1078 Value: &AttrParent{},
1079 Err: `xml: X>Y chain not valid with attr flag`,
1082 Value: BadAttr{[]string{"X", "Y"}},
1083 Err: `xml: unsupported type: []string`,
1087 var marshalIndentTests = []struct {
1094 Value: &SecretAgent{
1096 Identity: "James Bond",
1097 Obfuscate: "<redacted/>",
1101 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
1105 func TestMarshalErrors(t *testing.T) {
1106 for idx, test := range marshalErrorTests {
1107 data, err := Marshal(test.Value)
1109 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
1112 if err.Error() != test.Err {
1113 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
1115 if test.Kind != reflect.Invalid {
1116 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
1117 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
1123 // Do invertibility testing on the various structures that we test
1124 func TestUnmarshal(t *testing.T) {
1125 for i, test := range marshalTests {
1126 if test.MarshalOnly {
1129 if _, ok := test.Value.(*Plain); ok {
1132 if test.ExpectXML == `<top>`+
1133 `<x><b xmlns="space">b</b>`+
1134 `<b xmlns="space1">b1</b></x>`+
1136 // TODO(rogpeppe): re-enable this test in
1137 // https://go-review.googlesource.com/#/c/5910/
1141 vt := reflect.TypeOf(test.Value)
1142 dest := reflect.New(vt.Elem()).Interface()
1143 err := Unmarshal([]byte(test.ExpectXML), dest)
1145 switch fix := dest.(type) {
1147 fix.Author.InnerXML = ""
1148 for i := range fix.Entry {
1149 fix.Entry[i].Author.InnerXML = ""
1154 t.Errorf("#%d: unexpected error: %#v", i, err)
1155 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
1156 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
1161 func TestMarshalIndent(t *testing.T) {
1162 for i, test := range marshalIndentTests {
1163 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
1165 t.Errorf("#%d: Error: %s", i, err)
1168 if got, want := string(data), test.ExpectXML; got != want {
1169 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
1174 type limitedBytesWriter struct {
1176 remain int // until writes fail
1179 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
1182 return 0, errors.New("write limit hit")
1184 if len(p) > lw.remain {
1186 n, _ = lw.w.Write(p)
1188 return n, errors.New("write limit hit")
1190 n, err = lw.w.Write(p)
1195 func TestMarshalWriteErrors(t *testing.T) {
1196 var buf bytes.Buffer
1197 const writeCap = 1024
1198 w := &limitedBytesWriter{&buf, writeCap}
1199 enc := NewEncoder(w)
1203 for i = 1; i <= n; i++ {
1204 err = enc.Encode(&Passenger{
1205 Name: []string{"Alice", "Bob"},
1213 t.Error("expected an error")
1216 t.Errorf("expected to fail before the end")
1218 if buf.Len() != writeCap {
1219 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
1223 func TestMarshalWriteIOErrors(t *testing.T) {
1224 enc := NewEncoder(errWriter{})
1226 expectErr := "unwritable"
1227 err := enc.Encode(&Passenger{})
1228 if err == nil || err.Error() != expectErr {
1229 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
1233 func TestMarshalFlush(t *testing.T) {
1234 var buf bytes.Buffer
1235 enc := NewEncoder(&buf)
1236 if err := enc.EncodeToken(CharData("hello world")); err != nil {
1237 t.Fatalf("enc.EncodeToken: %v", err)
1240 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
1242 if err := enc.Flush(); err != nil {
1243 t.Fatalf("enc.Flush: %v", err)
1245 if buf.String() != "hello world" {
1246 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
1250 func BenchmarkMarshal(b *testing.B) {
1252 for i := 0; i < b.N; i++ {
1257 func BenchmarkUnmarshal(b *testing.B) {
1259 xml := []byte(atomXml)
1260 for i := 0; i < b.N; i++ {
1261 Unmarshal(xml, &Feed{})
1265 // golang.org/issue/6556
1266 func TestStructPointerMarshal(t *testing.T) {
1268 XMLName string `xml:"a"`
1273 Value string `xml:"value"`
1277 a.B = append(a.B, &C{
1278 XMLName: Name{Local: "c"},
1282 b, err := Marshal(a)
1286 if x := string(b); x != "<a><c><value>x</value></c></a>" {
1290 err = Unmarshal(b, &v)
1296 var encodeTokenTests = []struct {
1302 desc: "start element with name space",
1304 StartElement{Name{"space", "local"}, nil},
1306 want: `<local xmlns="space">`,
1308 desc: "start element with no name",
1310 StartElement{Name{"space", ""}, nil},
1312 err: "xml: start tag with no name",
1314 desc: "end element with no name",
1316 EndElement{Name{"space", ""}},
1318 err: "xml: end tag with no name",
1326 desc: "char data with escaped chars",
1338 desc: "comment with invalid content",
1342 err: "xml: EncodeToken of Comment containing --> marker",
1344 desc: "proc instruction",
1346 ProcInst{"Target", []byte("Instruction")},
1348 want: `<?Target Instruction?>`,
1350 desc: "proc instruction with empty target",
1352 ProcInst{"", []byte("Instruction")},
1354 err: "xml: EncodeToken of ProcInst with invalid Target",
1356 desc: "proc instruction with bad content",
1358 ProcInst{"", []byte("Instruction?>")},
1360 err: "xml: EncodeToken of ProcInst with invalid Target",
1368 desc: "more complex directive",
1370 Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"),
1372 want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`,
1374 desc: "directive instruction with bad name",
1378 err: "xml: EncodeToken of Directive containing wrong < or > markers",
1380 desc: "end tag without start tag",
1382 EndElement{Name{"foo", "bar"}},
1384 err: "xml: end tag </bar> without start tag",
1386 desc: "mismatching end tag local name",
1388 StartElement{Name{"", "foo"}, nil},
1389 EndElement{Name{"", "bar"}},
1391 err: "xml: end tag </bar> does not match start tag <foo>",
1394 desc: "mismatching end tag namespace",
1396 StartElement{Name{"space", "foo"}, nil},
1397 EndElement{Name{"another", "foo"}},
1399 err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space",
1400 want: `<foo xmlns="space">`,
1402 desc: "start element with explicit namespace",
1404 StartElement{Name{"space", "local"}, []Attr{
1405 {Name{"xmlns", "x"}, "space"},
1406 {Name{"space", "foo"}, "value"},
1409 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value">`,
1411 desc: "start element with explicit namespace and colliding prefix",
1413 StartElement{Name{"space", "local"}, []Attr{
1414 {Name{"xmlns", "x"}, "space"},
1415 {Name{"space", "foo"}, "value"},
1416 {Name{"x", "bar"}, "other"},
1419 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value" xmlns:x="x" x:bar="other">`,
1421 desc: "start element using previously defined namespace",
1423 StartElement{Name{"", "local"}, []Attr{
1424 {Name{"xmlns", "x"}, "space"},
1426 StartElement{Name{"space", "foo"}, []Attr{
1427 {Name{"space", "x"}, "y"},
1430 want: `<local xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns:space="space" space:x="y">`,
1432 desc: "nested name space with same prefix",
1434 StartElement{Name{"", "foo"}, []Attr{
1435 {Name{"xmlns", "x"}, "space1"},
1437 StartElement{Name{"", "foo"}, []Attr{
1438 {Name{"xmlns", "x"}, "space2"},
1440 StartElement{Name{"", "foo"}, []Attr{
1441 {Name{"space1", "a"}, "space1 value"},
1442 {Name{"space2", "b"}, "space2 value"},
1444 EndElement{Name{"", "foo"}},
1445 EndElement{Name{"", "foo"}},
1446 StartElement{Name{"", "foo"}, []Attr{
1447 {Name{"space1", "a"}, "space1 value"},
1448 {Name{"space2", "b"}, "space2 value"},
1451 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space1"><foo _xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value"></foo></foo><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value">`,
1453 desc: "start element defining several prefixes for the same name space",
1455 StartElement{Name{"space", "foo"}, []Attr{
1456 {Name{"xmlns", "a"}, "space"},
1457 {Name{"xmlns", "b"}, "space"},
1458 {Name{"space", "x"}, "value"},
1461 want: `<foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:a="space" _xmlns:b="space" xmlns:space="space" space:x="value">`,
1463 desc: "nested element redefines name space",
1465 StartElement{Name{"", "foo"}, []Attr{
1466 {Name{"xmlns", "x"}, "space"},
1468 StartElement{Name{"space", "foo"}, []Attr{
1469 {Name{"xmlns", "y"}, "space"},
1470 {Name{"space", "a"}, "value"},
1473 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" _xmlns:y="space" xmlns:space="space" space:a="value">`,
1475 desc: "nested element creates alias for default name space",
1477 StartElement{Name{"space", "foo"}, []Attr{
1478 {Name{"", "xmlns"}, "space"},
1480 StartElement{Name{"space", "foo"}, []Attr{
1481 {Name{"xmlns", "y"}, "space"},
1482 {Name{"space", "a"}, "value"},
1485 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:y="space" xmlns:space="space" space:a="value">`,
1487 desc: "nested element defines default name space with existing prefix",
1489 StartElement{Name{"", "foo"}, []Attr{
1490 {Name{"xmlns", "x"}, "space"},
1492 StartElement{Name{"space", "foo"}, []Attr{
1493 {Name{"", "xmlns"}, "space"},
1494 {Name{"space", "a"}, "value"},
1497 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns="space" xmlns:space="space" space:a="value">`,
1499 desc: "nested element uses empty attribute name space when default ns defined",
1501 StartElement{Name{"space", "foo"}, []Attr{
1502 {Name{"", "xmlns"}, "space"},
1504 StartElement{Name{"space", "foo"}, []Attr{
1505 {Name{"", "attr"}, "value"},
1508 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" attr="value">`,
1510 desc: "redefine xmlns",
1512 StartElement{Name{"", "foo"}, []Attr{
1513 {Name{"foo", "xmlns"}, "space"},
1516 want: `<foo xmlns:foo="foo" foo:xmlns="space">`,
1518 desc: "xmlns with explicit name space #1",
1520 StartElement{Name{"space", "foo"}, []Attr{
1521 {Name{"xml", "xmlns"}, "space"},
1524 want: `<foo xmlns="space" xmlns:_xml="xml" _xml:xmlns="space">`,
1526 desc: "xmlns with explicit name space #2",
1528 StartElement{Name{"space", "foo"}, []Attr{
1529 {Name{xmlURL, "xmlns"}, "space"},
1532 want: `<foo xmlns="space" xml:xmlns="space">`,
1534 desc: "empty name space declaration is ignored",
1536 StartElement{Name{"", "foo"}, []Attr{
1537 {Name{"xmlns", "foo"}, ""},
1540 want: `<foo xmlns:_xmlns="xmlns" _xmlns:foo="">`,
1542 desc: "attribute with no name is ignored",
1544 StartElement{Name{"", "foo"}, []Attr{
1545 {Name{"", ""}, "value"},
1550 desc: "namespace URL with non-valid name",
1552 StartElement{Name{"/34", "foo"}, []Attr{
1553 {Name{"/34", "x"}, "value"},
1556 want: `<foo xmlns="/34" xmlns:_="/34" _:x="value">`,
1558 desc: "nested element resets default namespace to empty",
1560 StartElement{Name{"space", "foo"}, []Attr{
1561 {Name{"", "xmlns"}, "space"},
1563 StartElement{Name{"", "foo"}, []Attr{
1564 {Name{"", "xmlns"}, ""},
1565 {Name{"", "x"}, "value"},
1566 {Name{"space", "x"}, "value"},
1569 want: `<foo xmlns="space" xmlns="space"><foo xmlns="" x="value" xmlns:space="space" space:x="value">`,
1571 desc: "nested element requires empty default name space",
1573 StartElement{Name{"space", "foo"}, []Attr{
1574 {Name{"", "xmlns"}, "space"},
1576 StartElement{Name{"", "foo"}, nil},
1578 want: `<foo xmlns="space" xmlns="space"><foo>`,
1580 desc: "attribute uses name space from xmlns",
1582 StartElement{Name{"some/space", "foo"}, []Attr{
1583 {Name{"", "attr"}, "value"},
1584 {Name{"some/space", "other"}, "other value"},
1587 want: `<foo xmlns="some/space" attr="value" xmlns:space="some/space" space:other="other value">`,
1589 desc: "default name space should not be used by attributes",
1591 StartElement{Name{"space", "foo"}, []Attr{
1592 {Name{"", "xmlns"}, "space"},
1593 {Name{"xmlns", "bar"}, "space"},
1594 {Name{"space", "baz"}, "foo"},
1596 StartElement{Name{"space", "baz"}, nil},
1597 EndElement{Name{"space", "baz"}},
1598 EndElement{Name{"space", "foo"}},
1600 want: `<foo xmlns="space" xmlns="space" xmlns:_xmlns="xmlns" _xmlns:bar="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`,
1602 desc: "default name space not used by attributes, not explicitly defined",
1604 StartElement{Name{"space", "foo"}, []Attr{
1605 {Name{"", "xmlns"}, "space"},
1606 {Name{"space", "baz"}, "foo"},
1608 StartElement{Name{"space", "baz"}, nil},
1609 EndElement{Name{"space", "baz"}},
1610 EndElement{Name{"space", "foo"}},
1612 want: `<foo xmlns="space" xmlns="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`,
1614 desc: "impossible xmlns declaration",
1616 StartElement{Name{"", "foo"}, []Attr{
1617 {Name{"", "xmlns"}, "space"},
1619 StartElement{Name{"space", "bar"}, []Attr{
1620 {Name{"space", "attr"}, "value"},
1623 want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`,
1626 func TestEncodeToken(t *testing.T) {
1628 for i, tt := range encodeTokenTests {
1629 var buf bytes.Buffer
1630 enc := NewEncoder(&buf)
1632 for j, tok := range tt.toks {
1633 err = enc.EncodeToken(tok)
1634 if err != nil && j < len(tt.toks)-1 {
1635 t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err)
1639 errorf := func(f string, a ...interface{}) {
1640 t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...))
1643 case tt.err != "" && err == nil:
1644 errorf(" expected error; got none")
1646 case tt.err == "" && err != nil:
1647 errorf(" got error: %v", err)
1649 case tt.err != "" && err != nil && tt.err != err.Error():
1650 errorf(" error mismatch; got %v, want %v", err, tt.err)
1653 if err := enc.Flush(); err != nil {
1657 if got := buf.String(); got != tt.want {
1658 errorf("\ngot %v\nwant %v", got, tt.want)
1664 func TestProcInstEncodeToken(t *testing.T) {
1665 var buf bytes.Buffer
1666 enc := NewEncoder(&buf)
1668 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
1669 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
1672 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
1673 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
1676 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
1677 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
1681 func TestDecodeEncode(t *testing.T) {
1682 var in, out bytes.Buffer
1683 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
1684 <?Target Instruction?>
1688 dec := NewDecoder(&in)
1689 enc := NewEncoder(&out)
1690 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
1691 err = enc.EncodeToken(tok)
1693 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
1698 // Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
1699 func TestRace9796(t *testing.T) {
1704 var wg sync.WaitGroup
1705 for i := 0; i < 2; i++ {
1708 Marshal(B{[]A{A{}}})
1715 func TestIsValidDirective(t *testing.T) {
1719 "<!DOCTYPE '<' '>' '>' <!--nothing-->>",
1720 "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>",
1721 "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>",
1722 "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >",
1730 "<!dummy <!-- > -->",
1732 "<!DOCTYPE doc '>'",
1733 "<!DOCTYPE doc <!--comment>",
1735 for _, s := range testOK {
1736 if !isValidDirective(Directive(s)) {
1737 t.Errorf("Directive %q is expected to be valid", s)
1740 for _, s := range testKO {
1741 if isValidDirective(Directive(s)) {
1742 t.Errorf("Directive %q is expected to be invalid", s)
1747 // Issue 11719. EncodeToken used to silently eat tokens with an invalid type.
1748 func TestSimpleUseOfEncodeToken(t *testing.T) {
1749 var buf bytes.Buffer
1750 enc := NewEncoder(&buf)
1751 if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil {
1752 t.Errorf("enc.EncodeToken: pointer type should be rejected")
1754 if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil {
1755 t.Errorf("enc.EncodeToken: pointer type should be rejected")
1757 if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil {
1758 t.Errorf("enc.EncodeToken: StartElement %s", err)
1760 if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil {
1761 t.Errorf("enc.EncodeToken: EndElement %s", err)
1763 if err := enc.EncodeToken(Universe{}); err == nil {
1764 t.Errorf("enc.EncodeToken: invalid type not caught")
1766 if err := enc.Flush(); err != nil {
1767 t.Errorf("enc.Flush: %s", err)
1770 t.Errorf("enc.EncodeToken: empty buffer")
1772 want := "<object2></object2>"
1773 if buf.String() != want {
1774 t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String())