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.
21 HyperDrive DriveType = iota
25 type Passenger struct {
26 Name []string `xml:"name"`
27 Weight float32 `xml:"weight"`
31 XMLName struct{} `xml:"spaceship"`
33 Name string `xml:"name,attr"`
34 Pilot string `xml:"pilot,attr"`
35 Drive DriveType `xml:"drive"`
37 Passenger []*Passenger `xml:"passenger"`
44 XMLName struct{} `xml:"port"`
45 Type string `xml:"type,attr,omitempty"`
46 Comment string `xml:",comment"`
47 Number string `xml:",chardata"`
51 XMLName struct{} `xml:"domain"`
52 Country string `xml:",attr,omitempty"`
53 Name []byte `xml:",chardata"`
54 Comment []byte `xml:",comment"`
58 XMLName struct{} `xml:"book"`
59 Title string `xml:",chardata"`
62 type SecretAgent struct {
63 XMLName struct{} `xml:"agent"`
64 Handle string `xml:"handle,attr"`
66 Obfuscate string `xml:",innerxml"`
69 type NestedItems struct {
70 XMLName struct{} `xml:"result"`
71 Items []string `xml:">item"`
72 Item1 []string `xml:"Items>item1"`
75 type NestedOrder struct {
76 XMLName struct{} `xml:"result"`
77 Field1 string `xml:"parent>c"`
78 Field2 string `xml:"parent>b"`
79 Field3 string `xml:"parent>a"`
82 type MixedNested struct {
83 XMLName struct{} `xml:"result"`
84 A string `xml:"parent1>a"`
86 C string `xml:"parent1>parent2>c"`
87 D string `xml:"parent1>d"`
91 A interface{} `xml:"parent1>parent2>a"`
92 B interface{} `xml:"parent1>b"`
93 C interface{} `xml:"parent1>parent2>c"`
97 XMLName struct{} `xml:"service"`
98 Domain *Domain `xml:"host>domain"`
99 Port *Port `xml:"host>port"`
101 Extra2 interface{} `xml:"host>extra2"`
118 FieldA1 string `xml:"FieldA>A1"`
119 FieldA2 string `xml:"FieldA>A2"`
124 type NameCasing struct {
125 XMLName struct{} `xml:"casing"`
128 XyA string `xml:"Xy,attr"`
129 XYA string `xml:"XY,attr"`
132 type NamePrecedence struct {
133 XMLName Name `xml:"Parent"`
134 FromTag XMLNameWithoutTag `xml:"InTag"`
135 FromNameVal XMLNameWithoutTag
136 FromNameTag XMLNameWithTag
140 type XMLNameWithTag struct {
141 XMLName Name `xml:"InXMLNameTag"`
142 Value string `xml:",chardata"`
145 type XMLNameWithoutTag struct {
147 Value string `xml:",chardata"`
150 type NameInField struct {
151 Foo Name `xml:"ns foo"`
154 type AttrTest struct {
155 Int int `xml:",attr"`
156 Named int `xml:"int,attr"`
157 Float float64 `xml:",attr"`
158 Uint8 uint8 `xml:",attr"`
159 Bool bool `xml:",attr"`
160 Str string `xml:",attr"`
161 Bytes []byte `xml:",attr"`
164 type OmitAttrTest struct {
165 Int int `xml:",attr,omitempty"`
166 Named int `xml:"int,attr,omitempty"`
167 Float float64 `xml:",attr,omitempty"`
168 Uint8 uint8 `xml:",attr,omitempty"`
169 Bool bool `xml:",attr,omitempty"`
170 Str string `xml:",attr,omitempty"`
171 Bytes []byte `xml:",attr,omitempty"`
174 type OmitFieldTest struct {
175 Int int `xml:",omitempty"`
176 Named int `xml:"int,omitempty"`
177 Float float64 `xml:",omitempty"`
178 Uint8 uint8 `xml:",omitempty"`
179 Bool bool `xml:",omitempty"`
180 Str string `xml:",omitempty"`
181 Bytes []byte `xml:",omitempty"`
182 Ptr *PresenceTest `xml:",omitempty"`
185 type AnyTest struct {
186 XMLName struct{} `xml:"a"`
187 Nested string `xml:"nested>value"`
188 AnyField AnyHolder `xml:",any"`
191 type AnyHolder struct {
193 XML string `xml:",innerxml"`
196 type RecurseA struct {
201 type RecurseB struct {
206 type PresenceTest struct {
210 type IgnoreTest struct {
211 PublicSecret string `xml:"-"`
218 Attr []byte `xml:",attr"`
226 // Unless explicitly stated as such (or *Plain), all of the
227 // tests below are two-way tests. When introducing new tests,
228 // please try to make them two-way as well to ensure that
229 // marshalling and unmarshalling are as symmetrical as feasible.
230 var marshalTests = []struct {
236 // Test nil marshals to nothing
237 {Value: nil, ExpectXML: ``, MarshalOnly: true},
238 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
241 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
242 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
243 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
244 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
245 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
246 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
247 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
248 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
249 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
250 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
251 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
252 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
253 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
254 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
255 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
256 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`},
257 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`},
258 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`},
259 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
260 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
261 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
265 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
266 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
269 // A pointer to struct{} may be used to test for an element's presence.
271 Value: &PresenceTest{new(struct{})},
272 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
275 Value: &PresenceTest{},
276 ExpectXML: `<PresenceTest></PresenceTest>`,
279 // A pointer to struct{} may be used to test for an element's presence.
281 Value: &PresenceTest{new(struct{})},
282 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
285 Value: &PresenceTest{},
286 ExpectXML: `<PresenceTest></PresenceTest>`,
289 // A []byte field is only nil if the element was not found.
292 ExpectXML: `<Data></Data>`,
296 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
297 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
301 // Check that []byte works, including named []byte types.
303 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
304 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
311 Identity: "James Bond",
312 Obfuscate: "<redacted/>",
314 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
320 Identity: "James Bond",
321 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
323 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
328 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
329 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
330 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`},
331 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
332 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
333 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`},
334 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
335 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`},
336 {Value: atomValue, ExpectXML: atomXml},
339 Name: "Heart of Gold",
342 Drive: ImprobabilityDrive,
343 Passenger: []*Passenger{
345 Name: []string{"Zaphod", "Beeblebrox"},
349 Name: []string{"Trisha", "McMillen"},
353 Name: []string{"Ford", "Prefect"},
357 Name: []string{"Arthur", "Dent"},
362 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
363 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
366 `<name>Zaphod</name>` +
367 `<name>Beeblebrox</name>` +
368 `<weight>7.25</weight>` +
371 `<name>Trisha</name>` +
372 `<name>McMillen</name>` +
373 `<weight>5.5</weight>` +
376 `<name>Ford</name>` +
377 `<name>Prefect</name>` +
378 `<weight>7</weight>` +
381 `<name>Arthur</name>` +
382 `<name>Dent</name>` +
383 `<weight>6.75</weight>` +
390 Value: &NestedItems{Items: nil, Item1: nil},
391 ExpectXML: `<result>` +
397 Value: &NestedItems{Items: []string{}, Item1: []string{}},
398 ExpectXML: `<result>` +
405 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
406 ExpectXML: `<result>` +
413 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
414 ExpectXML: `<result>` +
422 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
423 ExpectXML: `<result>` +
432 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
433 ExpectXML: `<result>` +
442 Value: &NilTest{A: "A", B: nil, C: "C"},
443 ExpectXML: `<NilTest>` +
445 `<parent2><a>A</a></parent2>` +
446 `<parent2><c>C</c></parent2>` +
449 MarshalOnly: true, // Uses interface{}
452 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
453 ExpectXML: `<result>` +
454 `<parent1><a>A</a></parent1>` +
457 `<parent2><c>C</c></parent2>` +
463 Value: &Service{Port: &Port{Number: "80"}},
464 ExpectXML: `<service><host><port>80</port></host></service>`,
468 ExpectXML: `<service></service>`,
471 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
472 ExpectXML: `<service>` +
473 `<host><port>80</port></host>` +
474 `<Extra1>A</Extra1>` +
475 `<host><extra2>B</extra2></host>` +
480 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
481 ExpectXML: `<service>` +
482 `<host><port>80</port></host>` +
483 `<host><extra2>example</extra2></host>` +
488 // Test struct embedding
492 FieldA1: "", // Shadowed by A.A
493 FieldA2: "", // Shadowed by A.A
502 FieldB: "", // Shadowed by A.B.B
508 ExpectXML: `<EmbedA>` +
509 `<FieldB>A.C.B</FieldB>` +
510 `<FieldC>A.C.C</FieldC>` +
512 `<FieldB>A.B.B</FieldB>` +
514 `<A1>A.B.C.A1</A1>` +
515 `<A2>A.B.C.A2</A2>` +
517 `<FieldC>A.B.C.C</FieldC>` +
519 `<FieldA>A.A</FieldA>` +
523 // Test that name casing matters
525 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
526 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
529 // Test the order in which the XML element name is chosen
531 Value: &NamePrecedence{
532 FromTag: XMLNameWithoutTag{Value: "A"},
533 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
534 FromNameTag: XMLNameWithTag{Value: "C"},
537 ExpectXML: `<Parent>` +
539 `<InXMLName>B</InXMLName>` +
540 `<InXMLNameTag>C</InXMLNameTag>` +
541 `<InFieldName>D</InFieldName>` +
546 Value: &NamePrecedence{
547 XMLName: Name{Local: "Parent"},
548 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
549 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
550 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
553 ExpectXML: `<Parent>` +
555 `<FromNameVal>B</FromNameVal>` +
556 `<InXMLNameTag>C</InXMLNameTag>` +
557 `<InFieldName>D</InFieldName>` +
562 // xml.Name works in a plain field as well.
564 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
565 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
568 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
569 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
573 // Marshaling zero xml.Name uses the tag or field name.
575 Value: &NameInField{},
576 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
589 Bytes: []byte("byt"),
591 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
592 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
595 Value: &AttrTest{Bytes: []byte{}},
596 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
597 ` Bool="false" Str="" Bytes=""></AttrTest>`,
600 Value: &OmitAttrTest{
607 Bytes: []byte("byt"),
609 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
610 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
613 Value: &OmitAttrTest{},
614 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
617 // omitempty on fields
619 Value: &OmitFieldTest{
626 Bytes: []byte("byt"),
627 Ptr: &PresenceTest{},
629 ExpectXML: `<OmitFieldTest>` +
632 `<Float>23.5</Float>` +
633 `<Uint8>255</Uint8>` +
634 `<Bool>true</Bool>` +
636 `<Bytes>byt</Bytes>` +
641 Value: &OmitFieldTest{},
642 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
647 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
651 XMLName: Name{Local: "other"},
652 XML: "<sub>unknown</sub>",
658 Value: &AnyTest{Nested: "known", AnyField: AnyHolder{XML: "<unknown/>"}},
659 ExpectXML: `<a><nested><value>known</value></nested></a>`,
663 // Test recursive types.
668 A: &RecurseA{"a2", nil},
672 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
675 // Test ignoring fields via "-" tag
677 ExpectXML: `<IgnoreTest></IgnoreTest>`,
678 Value: &IgnoreTest{},
681 ExpectXML: `<IgnoreTest></IgnoreTest>`,
682 Value: &IgnoreTest{PublicSecret: "can't tell"},
686 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
687 Value: &IgnoreTest{},
692 func TestMarshal(t *testing.T) {
693 for idx, test := range marshalTests {
694 if test.UnmarshalOnly {
697 data, err := Marshal(test.Value)
699 t.Errorf("#%d: Error: %s", idx, err)
702 if got, want := string(data), test.ExpectXML; got != want {
703 if strings.Contains(want, "\n") {
704 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
706 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
712 var marshalErrorTests = []struct {
718 Value: make(chan bool),
719 Err: "xml: unsupported type: chan bool",
723 Value: map[string]string{
724 "question": "What do you get when you multiply six by nine?",
727 Err: "xml: unsupported type: map[string]string",
731 Value: map[*Ship]bool{nil: false},
732 Err: "xml: unsupported type: map[*xml.Ship]bool",
736 Value: &Domain{Comment: []byte("f--bar")},
737 Err: `xml: comments must not contain "--"`,
741 func TestMarshalErrors(t *testing.T) {
742 for idx, test := range marshalErrorTests {
743 _, err := Marshal(test.Value)
744 if err == nil || err.Error() != test.Err {
745 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
747 if test.Kind != reflect.Invalid {
748 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
749 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
755 // Do invertibility testing on the various structures that we test
756 func TestUnmarshal(t *testing.T) {
757 for i, test := range marshalTests {
758 if test.MarshalOnly {
761 if _, ok := test.Value.(*Plain); ok {
765 vt := reflect.TypeOf(test.Value)
766 dest := reflect.New(vt.Elem()).Interface()
767 err := Unmarshal([]byte(test.ExpectXML), dest)
769 switch fix := dest.(type) {
771 fix.Author.InnerXML = ""
772 for i := range fix.Entry {
773 fix.Entry[i].Author.InnerXML = ""
778 t.Errorf("#%d: unexpected error: %#v", i, err)
779 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
780 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
785 type limitedBytesWriter struct {
787 remain int // until writes fail
790 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
793 return 0, errors.New("write limit hit")
795 if len(p) > lw.remain {
799 return n, errors.New("write limit hit")
801 n, err = lw.w.Write(p)
806 func TestMarshalWriteErrors(t *testing.T) {
808 const writeCap = 1024
809 w := &limitedBytesWriter{&buf, writeCap}
814 for i = 1; i <= n; i++ {
815 err = enc.Encode(&Passenger{
816 Name: []string{"Alice", "Bob"},
824 t.Error("expected an error")
827 t.Errorf("expected to fail before the end")
829 if buf.Len() != writeCap {
830 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
834 func BenchmarkMarshal(b *testing.B) {
835 for i := 0; i < b.N; i++ {
840 func BenchmarkUnmarshal(b *testing.B) {
841 xml := []byte(atomXml)
842 for i := 0; i < b.N; i++ {
843 Unmarshal(xml, &Feed{})