blob: 14f73a75d5fbd8f381461537b45177102d36b724 [file] [log] [blame]
Colin Cross7bb052a2015-02-03 12:59:37 -08001// 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.
4
5package xml
6
7import (
8 "bytes"
9 "errors"
10 "fmt"
11 "io"
12 "reflect"
13 "strconv"
14 "strings"
15 "testing"
16 "time"
17)
18
19type DriveType int
20
21const (
22 HyperDrive DriveType = iota
23 ImprobabilityDrive
24)
25
26type Passenger struct {
27 Name []string `xml:"name"`
28 Weight float32 `xml:"weight"`
29}
30
31type Ship struct {
32 XMLName struct{} `xml:"spaceship"`
33
34 Name string `xml:"name,attr"`
35 Pilot string `xml:"pilot,attr"`
36 Drive DriveType `xml:"drive"`
37 Age uint `xml:"age"`
38 Passenger []*Passenger `xml:"passenger"`
39 secret string
40}
41
42type NamedType string
43
44type Port struct {
45 XMLName struct{} `xml:"port"`
46 Type string `xml:"type,attr,omitempty"`
47 Comment string `xml:",comment"`
48 Number string `xml:",chardata"`
49}
50
51type Domain struct {
52 XMLName struct{} `xml:"domain"`
53 Country string `xml:",attr,omitempty"`
54 Name []byte `xml:",chardata"`
55 Comment []byte `xml:",comment"`
56}
57
58type Book struct {
59 XMLName struct{} `xml:"book"`
60 Title string `xml:",chardata"`
61}
62
63type Event struct {
64 XMLName struct{} `xml:"event"`
65 Year int `xml:",chardata"`
66}
67
68type Movie struct {
69 XMLName struct{} `xml:"movie"`
70 Length uint `xml:",chardata"`
71}
72
73type Pi struct {
74 XMLName struct{} `xml:"pi"`
75 Approximation float32 `xml:",chardata"`
76}
77
78type Universe struct {
79 XMLName struct{} `xml:"universe"`
80 Visible float64 `xml:",chardata"`
81}
82
83type Particle struct {
84 XMLName struct{} `xml:"particle"`
85 HasMass bool `xml:",chardata"`
86}
87
88type Departure struct {
89 XMLName struct{} `xml:"departure"`
90 When time.Time `xml:",chardata"`
91}
92
93type SecretAgent struct {
94 XMLName struct{} `xml:"agent"`
95 Handle string `xml:"handle,attr"`
96 Identity string
97 Obfuscate string `xml:",innerxml"`
98}
99
100type NestedItems struct {
101 XMLName struct{} `xml:"result"`
102 Items []string `xml:">item"`
103 Item1 []string `xml:"Items>item1"`
104}
105
106type NestedOrder struct {
107 XMLName struct{} `xml:"result"`
108 Field1 string `xml:"parent>c"`
109 Field2 string `xml:"parent>b"`
110 Field3 string `xml:"parent>a"`
111}
112
113type MixedNested struct {
114 XMLName struct{} `xml:"result"`
115 A string `xml:"parent1>a"`
116 B string `xml:"b"`
117 C string `xml:"parent1>parent2>c"`
118 D string `xml:"parent1>d"`
119}
120
121type NilTest struct {
122 A interface{} `xml:"parent1>parent2>a"`
123 B interface{} `xml:"parent1>b"`
124 C interface{} `xml:"parent1>parent2>c"`
125}
126
127type Service struct {
128 XMLName struct{} `xml:"service"`
129 Domain *Domain `xml:"host>domain"`
130 Port *Port `xml:"host>port"`
131 Extra1 interface{}
132 Extra2 interface{} `xml:"host>extra2"`
133}
134
135var nilStruct *Ship
136
137type EmbedA struct {
138 EmbedC
139 EmbedB EmbedB
140 FieldA string
141}
142
143type EmbedB struct {
144 FieldB string
145 *EmbedC
146}
147
148type EmbedC struct {
149 FieldA1 string `xml:"FieldA>A1"`
150 FieldA2 string `xml:"FieldA>A2"`
151 FieldB string
152 FieldC string
153}
154
155type NameCasing struct {
156 XMLName struct{} `xml:"casing"`
157 Xy string
158 XY string
159 XyA string `xml:"Xy,attr"`
160 XYA string `xml:"XY,attr"`
161}
162
163type NamePrecedence struct {
164 XMLName Name `xml:"Parent"`
165 FromTag XMLNameWithoutTag `xml:"InTag"`
166 FromNameVal XMLNameWithoutTag
167 FromNameTag XMLNameWithTag
168 InFieldName string
169}
170
171type XMLNameWithTag struct {
172 XMLName Name `xml:"InXMLNameTag"`
173 Value string `xml:",chardata"`
174}
175
176type XMLNameWithoutTag struct {
177 XMLName Name
178 Value string `xml:",chardata"`
179}
180
181type NameInField struct {
182 Foo Name `xml:"ns foo"`
183}
184
185type AttrTest struct {
186 Int int `xml:",attr"`
187 Named int `xml:"int,attr"`
188 Float float64 `xml:",attr"`
189 Uint8 uint8 `xml:",attr"`
190 Bool bool `xml:",attr"`
191 Str string `xml:",attr"`
192 Bytes []byte `xml:",attr"`
193}
194
195type OmitAttrTest struct {
196 Int int `xml:",attr,omitempty"`
197 Named int `xml:"int,attr,omitempty"`
198 Float float64 `xml:",attr,omitempty"`
199 Uint8 uint8 `xml:",attr,omitempty"`
200 Bool bool `xml:",attr,omitempty"`
201 Str string `xml:",attr,omitempty"`
202 Bytes []byte `xml:",attr,omitempty"`
203}
204
205type OmitFieldTest struct {
206 Int int `xml:",omitempty"`
207 Named int `xml:"int,omitempty"`
208 Float float64 `xml:",omitempty"`
209 Uint8 uint8 `xml:",omitempty"`
210 Bool bool `xml:",omitempty"`
211 Str string `xml:",omitempty"`
212 Bytes []byte `xml:",omitempty"`
213 Ptr *PresenceTest `xml:",omitempty"`
214}
215
216type AnyTest struct {
217 XMLName struct{} `xml:"a"`
218 Nested string `xml:"nested>value"`
219 AnyField AnyHolder `xml:",any"`
220}
221
222type AnyOmitTest struct {
223 XMLName struct{} `xml:"a"`
224 Nested string `xml:"nested>value"`
225 AnyField *AnyHolder `xml:",any,omitempty"`
226}
227
228type AnySliceTest struct {
229 XMLName struct{} `xml:"a"`
230 Nested string `xml:"nested>value"`
231 AnyField []AnyHolder `xml:",any"`
232}
233
234type AnyHolder struct {
235 XMLName Name
236 XML string `xml:",innerxml"`
237}
238
239type RecurseA struct {
240 A string
241 B *RecurseB
242}
243
244type RecurseB struct {
245 A *RecurseA
246 B string
247}
248
249type PresenceTest struct {
250 Exists *struct{}
251}
252
253type IgnoreTest struct {
254 PublicSecret string `xml:"-"`
255}
256
257type MyBytes []byte
258
259type Data struct {
260 Bytes []byte
261 Attr []byte `xml:",attr"`
262 Custom MyBytes
263}
264
265type Plain struct {
266 V interface{}
267}
268
269type MyInt int
270
271type EmbedInt struct {
272 MyInt
273}
274
275type Strings struct {
276 X []string `xml:"A>B,omitempty"`
277}
278
279type PointerFieldsTest struct {
280 XMLName Name `xml:"dummy"`
281 Name *string `xml:"name,attr"`
282 Age *uint `xml:"age,attr"`
283 Empty *string `xml:"empty,attr"`
284 Contents *string `xml:",chardata"`
285}
286
287type ChardataEmptyTest struct {
288 XMLName Name `xml:"test"`
289 Contents *string `xml:",chardata"`
290}
291
292type MyMarshalerTest struct {
293}
294
295var _ Marshaler = (*MyMarshalerTest)(nil)
296
297func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
298 e.EncodeToken(start)
299 e.EncodeToken(CharData([]byte("hello world")))
300 e.EncodeToken(EndElement{start.Name})
301 return nil
302}
303
304type MyMarshalerAttrTest struct {
305}
306
307var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
308
309func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
310 return Attr{name, "hello world"}, nil
311}
312
313type MarshalerStruct struct {
314 Foo MyMarshalerAttrTest `xml:",attr"`
315}
316
317type InnerStruct struct {
318 XMLName Name `xml:"testns outer"`
319}
320
321type OuterStruct struct {
322 InnerStruct
323 IntAttr int `xml:"int,attr"`
324}
325
326type OuterNamedStruct struct {
327 InnerStruct
328 XMLName Name `xml:"outerns test"`
329 IntAttr int `xml:"int,attr"`
330}
331
332type OuterNamedOrderedStruct struct {
333 XMLName Name `xml:"outerns test"`
334 InnerStruct
335 IntAttr int `xml:"int,attr"`
336}
337
338type OuterOuterStruct struct {
339 OuterStruct
340}
341
342func ifaceptr(x interface{}) interface{} {
343 return &x
344}
345
346var (
347 nameAttr = "Sarah"
348 ageAttr = uint(12)
349 contentsAttr = "lorem ipsum"
350)
351
352// Unless explicitly stated as such (or *Plain), all of the
353// tests below are two-way tests. When introducing new tests,
354// please try to make them two-way as well to ensure that
355// marshalling and unmarshalling are as symmetrical as feasible.
356var marshalTests = []struct {
357 Value interface{}
358 ExpectXML string
359 MarshalOnly bool
360 UnmarshalOnly bool
361}{
362 // Test nil marshals to nothing
363 {Value: nil, ExpectXML: ``, MarshalOnly: true},
364 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
365
366 // Test value types
367 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
368 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
369 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
370 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
371 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
372 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
373 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
374 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
375 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
376 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
377 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
378 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
379 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
380 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
381 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
382 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
383 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
384 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
385 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
386 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
387 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
388 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
389
390 // Test time.
391 {
392 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
393 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
394 },
395
396 // A pointer to struct{} may be used to test for an element's presence.
397 {
398 Value: &PresenceTest{new(struct{})},
399 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
400 },
401 {
402 Value: &PresenceTest{},
403 ExpectXML: `<PresenceTest></PresenceTest>`,
404 },
405
406 // A pointer to struct{} may be used to test for an element's presence.
407 {
408 Value: &PresenceTest{new(struct{})},
409 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
410 },
411 {
412 Value: &PresenceTest{},
413 ExpectXML: `<PresenceTest></PresenceTest>`,
414 },
415
416 // A []byte field is only nil if the element was not found.
417 {
418 Value: &Data{},
419 ExpectXML: `<Data></Data>`,
420 UnmarshalOnly: true,
421 },
422 {
423 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
424 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
425 UnmarshalOnly: true,
426 },
427
428 // Check that []byte works, including named []byte types.
429 {
430 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
431 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
432 },
433
434 // Test innerxml
435 {
436 Value: &SecretAgent{
437 Handle: "007",
438 Identity: "James Bond",
439 Obfuscate: "<redacted/>",
440 },
441 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
442 MarshalOnly: true,
443 },
444 {
445 Value: &SecretAgent{
446 Handle: "007",
447 Identity: "James Bond",
448 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
449 },
450 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
451 UnmarshalOnly: true,
452 },
453
454 // Test structs
455 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
456 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
457 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
458 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
459 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
460 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
461 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
462 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
463 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
464 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
465 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
466 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
467 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
468 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
469 {Value: atomValue, ExpectXML: atomXml},
470 {
471 Value: &Ship{
472 Name: "Heart of Gold",
473 Pilot: "Computer",
474 Age: 1,
475 Drive: ImprobabilityDrive,
476 Passenger: []*Passenger{
477 {
478 Name: []string{"Zaphod", "Beeblebrox"},
479 Weight: 7.25,
480 },
481 {
482 Name: []string{"Trisha", "McMillen"},
483 Weight: 5.5,
484 },
485 {
486 Name: []string{"Ford", "Prefect"},
487 Weight: 7,
488 },
489 {
490 Name: []string{"Arthur", "Dent"},
491 Weight: 6.75,
492 },
493 },
494 },
495 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
496 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
497 `<age>1</age>` +
498 `<passenger>` +
499 `<name>Zaphod</name>` +
500 `<name>Beeblebrox</name>` +
501 `<weight>7.25</weight>` +
502 `</passenger>` +
503 `<passenger>` +
504 `<name>Trisha</name>` +
505 `<name>McMillen</name>` +
506 `<weight>5.5</weight>` +
507 `</passenger>` +
508 `<passenger>` +
509 `<name>Ford</name>` +
510 `<name>Prefect</name>` +
511 `<weight>7</weight>` +
512 `</passenger>` +
513 `<passenger>` +
514 `<name>Arthur</name>` +
515 `<name>Dent</name>` +
516 `<weight>6.75</weight>` +
517 `</passenger>` +
518 `</spaceship>`,
519 },
520
521 // Test a>b
522 {
523 Value: &NestedItems{Items: nil, Item1: nil},
524 ExpectXML: `<result>` +
525 `<Items>` +
526 `</Items>` +
527 `</result>`,
528 },
529 {
530 Value: &NestedItems{Items: []string{}, Item1: []string{}},
531 ExpectXML: `<result>` +
532 `<Items>` +
533 `</Items>` +
534 `</result>`,
535 MarshalOnly: true,
536 },
537 {
538 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
539 ExpectXML: `<result>` +
540 `<Items>` +
541 `<item1>A</item1>` +
542 `</Items>` +
543 `</result>`,
544 },
545 {
546 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
547 ExpectXML: `<result>` +
548 `<Items>` +
549 `<item>A</item>` +
550 `<item>B</item>` +
551 `</Items>` +
552 `</result>`,
553 },
554 {
555 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
556 ExpectXML: `<result>` +
557 `<Items>` +
558 `<item>A</item>` +
559 `<item>B</item>` +
560 `<item1>C</item1>` +
561 `</Items>` +
562 `</result>`,
563 },
564 {
565 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
566 ExpectXML: `<result>` +
567 `<parent>` +
568 `<c>C</c>` +
569 `<b>B</b>` +
570 `<a>A</a>` +
571 `</parent>` +
572 `</result>`,
573 },
574 {
575 Value: &NilTest{A: "A", B: nil, C: "C"},
576 ExpectXML: `<NilTest>` +
577 `<parent1>` +
578 `<parent2><a>A</a></parent2>` +
579 `<parent2><c>C</c></parent2>` +
580 `</parent1>` +
581 `</NilTest>`,
582 MarshalOnly: true, // Uses interface{}
583 },
584 {
585 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
586 ExpectXML: `<result>` +
587 `<parent1><a>A</a></parent1>` +
588 `<b>B</b>` +
589 `<parent1>` +
590 `<parent2><c>C</c></parent2>` +
591 `<d>D</d>` +
592 `</parent1>` +
593 `</result>`,
594 },
595 {
596 Value: &Service{Port: &Port{Number: "80"}},
597 ExpectXML: `<service><host><port>80</port></host></service>`,
598 },
599 {
600 Value: &Service{},
601 ExpectXML: `<service></service>`,
602 },
603 {
604 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
605 ExpectXML: `<service>` +
606 `<host><port>80</port></host>` +
607 `<Extra1>A</Extra1>` +
608 `<host><extra2>B</extra2></host>` +
609 `</service>`,
610 MarshalOnly: true,
611 },
612 {
613 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
614 ExpectXML: `<service>` +
615 `<host><port>80</port></host>` +
616 `<host><extra2>example</extra2></host>` +
617 `</service>`,
618 MarshalOnly: true,
619 },
620
621 // Test struct embedding
622 {
623 Value: &EmbedA{
624 EmbedC: EmbedC{
625 FieldA1: "", // Shadowed by A.A
626 FieldA2: "", // Shadowed by A.A
627 FieldB: "A.C.B",
628 FieldC: "A.C.C",
629 },
630 EmbedB: EmbedB{
631 FieldB: "A.B.B",
632 EmbedC: &EmbedC{
633 FieldA1: "A.B.C.A1",
634 FieldA2: "A.B.C.A2",
635 FieldB: "", // Shadowed by A.B.B
636 FieldC: "A.B.C.C",
637 },
638 },
639 FieldA: "A.A",
640 },
641 ExpectXML: `<EmbedA>` +
642 `<FieldB>A.C.B</FieldB>` +
643 `<FieldC>A.C.C</FieldC>` +
644 `<EmbedB>` +
645 `<FieldB>A.B.B</FieldB>` +
646 `<FieldA>` +
647 `<A1>A.B.C.A1</A1>` +
648 `<A2>A.B.C.A2</A2>` +
649 `</FieldA>` +
650 `<FieldC>A.B.C.C</FieldC>` +
651 `</EmbedB>` +
652 `<FieldA>A.A</FieldA>` +
653 `</EmbedA>`,
654 },
655
656 // Test that name casing matters
657 {
658 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
659 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
660 },
661
662 // Test the order in which the XML element name is chosen
663 {
664 Value: &NamePrecedence{
665 FromTag: XMLNameWithoutTag{Value: "A"},
666 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
667 FromNameTag: XMLNameWithTag{Value: "C"},
668 InFieldName: "D",
669 },
670 ExpectXML: `<Parent>` +
671 `<InTag>A</InTag>` +
672 `<InXMLName>B</InXMLName>` +
673 `<InXMLNameTag>C</InXMLNameTag>` +
674 `<InFieldName>D</InFieldName>` +
675 `</Parent>`,
676 MarshalOnly: true,
677 },
678 {
679 Value: &NamePrecedence{
680 XMLName: Name{Local: "Parent"},
681 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
682 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
683 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
684 InFieldName: "D",
685 },
686 ExpectXML: `<Parent>` +
687 `<InTag>A</InTag>` +
688 `<FromNameVal>B</FromNameVal>` +
689 `<InXMLNameTag>C</InXMLNameTag>` +
690 `<InFieldName>D</InFieldName>` +
691 `</Parent>`,
692 UnmarshalOnly: true,
693 },
694
695 // xml.Name works in a plain field as well.
696 {
697 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
698 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
699 },
700 {
701 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
702 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
703 UnmarshalOnly: true,
704 },
705
706 // Marshaling zero xml.Name uses the tag or field name.
707 {
708 Value: &NameInField{},
709 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
710 MarshalOnly: true,
711 },
712
713 // Test attributes
714 {
715 Value: &AttrTest{
716 Int: 8,
717 Named: 9,
718 Float: 23.5,
719 Uint8: 255,
720 Bool: true,
721 Str: "str",
722 Bytes: []byte("byt"),
723 },
724 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
725 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
726 },
727 {
728 Value: &AttrTest{Bytes: []byte{}},
729 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
730 ` Bool="false" Str="" Bytes=""></AttrTest>`,
731 },
732 {
733 Value: &OmitAttrTest{
734 Int: 8,
735 Named: 9,
736 Float: 23.5,
737 Uint8: 255,
738 Bool: true,
739 Str: "str",
740 Bytes: []byte("byt"),
741 },
742 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
743 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
744 },
745 {
746 Value: &OmitAttrTest{},
747 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
748 },
749
750 // pointer fields
751 {
752 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
753 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
754 MarshalOnly: true,
755 },
756
757 // empty chardata pointer field
758 {
759 Value: &ChardataEmptyTest{},
760 ExpectXML: `<test></test>`,
761 MarshalOnly: true,
762 },
763
764 // omitempty on fields
765 {
766 Value: &OmitFieldTest{
767 Int: 8,
768 Named: 9,
769 Float: 23.5,
770 Uint8: 255,
771 Bool: true,
772 Str: "str",
773 Bytes: []byte("byt"),
774 Ptr: &PresenceTest{},
775 },
776 ExpectXML: `<OmitFieldTest>` +
777 `<Int>8</Int>` +
778 `<int>9</int>` +
779 `<Float>23.5</Float>` +
780 `<Uint8>255</Uint8>` +
781 `<Bool>true</Bool>` +
782 `<Str>str</Str>` +
783 `<Bytes>byt</Bytes>` +
784 `<Ptr></Ptr>` +
785 `</OmitFieldTest>`,
786 },
787 {
788 Value: &OmitFieldTest{},
789 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
790 },
791
792 // Test ",any"
793 {
794 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
795 Value: &AnyTest{
796 Nested: "known",
797 AnyField: AnyHolder{
798 XMLName: Name{Local: "other"},
799 XML: "<sub>unknown</sub>",
800 },
801 },
802 },
803 {
804 Value: &AnyTest{Nested: "known",
805 AnyField: AnyHolder{
806 XML: "<unknown/>",
807 XMLName: Name{Local: "AnyField"},
808 },
809 },
810 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
811 },
812 {
813 ExpectXML: `<a><nested><value>b</value></nested></a>`,
814 Value: &AnyOmitTest{
815 Nested: "b",
816 },
817 },
818 {
819 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
820 Value: &AnySliceTest{
821 Nested: "b",
822 AnyField: []AnyHolder{
823 {
824 XMLName: Name{Local: "c"},
825 XML: "<d>e</d>",
826 },
827 {
828 XMLName: Name{Space: "f", Local: "g"},
829 XML: "<h>i</h>",
830 },
831 },
832 },
833 },
834 {
835 ExpectXML: `<a><nested><value>b</value></nested></a>`,
836 Value: &AnySliceTest{
837 Nested: "b",
838 },
839 },
840
841 // Test recursive types.
842 {
843 Value: &RecurseA{
844 A: "a1",
845 B: &RecurseB{
846 A: &RecurseA{"a2", nil},
847 B: "b1",
848 },
849 },
850 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
851 },
852
853 // Test ignoring fields via "-" tag
854 {
855 ExpectXML: `<IgnoreTest></IgnoreTest>`,
856 Value: &IgnoreTest{},
857 },
858 {
859 ExpectXML: `<IgnoreTest></IgnoreTest>`,
860 Value: &IgnoreTest{PublicSecret: "can't tell"},
861 MarshalOnly: true,
862 },
863 {
864 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
865 Value: &IgnoreTest{},
866 UnmarshalOnly: true,
867 },
868
869 // Test escaping.
870 {
871 ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested><empty></empty></a>`,
872 Value: &AnyTest{
873 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
874 AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
875 },
876 },
877 {
878 ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</value></nested><AnyField></AnyField></a>`,
879 Value: &AnyTest{
880 Nested: "newline: \n; cr: \r; tab: \t;",
881 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
882 },
883 },
884 {
885 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
886 Value: &AnyTest{
887 Nested: "1\n2\n3\n\n4\n5",
888 },
889 UnmarshalOnly: true,
890 },
891 {
892 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
893 Value: &EmbedInt{
894 MyInt: 42,
895 },
896 },
897 // Test omitempty with parent chain; see golang.org/issue/4168.
898 {
899 ExpectXML: `<Strings><A></A></Strings>`,
900 Value: &Strings{},
901 },
902 // Custom marshalers.
903 {
904 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
905 Value: &MyMarshalerTest{},
906 },
907 {
908 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
909 Value: &MarshalerStruct{},
910 },
911 {
912 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
913 Value: &OuterStruct{IntAttr: 10},
914 },
915 {
916 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
917 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
918 },
919 {
920 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
921 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
922 },
923 {
924 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
925 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
926 },
927}
928
929func TestMarshal(t *testing.T) {
930 for idx, test := range marshalTests {
931 if test.UnmarshalOnly {
932 continue
933 }
934 data, err := Marshal(test.Value)
935 if err != nil {
936 t.Errorf("#%d: Error: %s", idx, err)
937 continue
938 }
939 if got, want := string(data), test.ExpectXML; got != want {
940 if strings.Contains(want, "\n") {
941 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
942 } else {
943 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
944 }
945 }
946 }
947}
948
949type AttrParent struct {
950 X string `xml:"X>Y,attr"`
951}
952
953type BadAttr struct {
954 Name []string `xml:"name,attr"`
955}
956
957var marshalErrorTests = []struct {
958 Value interface{}
959 Err string
960 Kind reflect.Kind
961}{
962 {
963 Value: make(chan bool),
964 Err: "xml: unsupported type: chan bool",
965 Kind: reflect.Chan,
966 },
967 {
968 Value: map[string]string{
969 "question": "What do you get when you multiply six by nine?",
970 "answer": "42",
971 },
972 Err: "xml: unsupported type: map[string]string",
973 Kind: reflect.Map,
974 },
975 {
976 Value: map[*Ship]bool{nil: false},
977 Err: "xml: unsupported type: map[*xml.Ship]bool",
978 Kind: reflect.Map,
979 },
980 {
981 Value: &Domain{Comment: []byte("f--bar")},
982 Err: `xml: comments must not contain "--"`,
983 },
984 // Reject parent chain with attr, never worked; see golang.org/issue/5033.
985 {
986 Value: &AttrParent{},
987 Err: `xml: X>Y chain not valid with attr flag`,
988 },
989 {
990 Value: BadAttr{[]string{"X", "Y"}},
991 Err: `xml: unsupported type: []string`,
992 },
993}
994
995var marshalIndentTests = []struct {
996 Value interface{}
997 Prefix string
998 Indent string
999 ExpectXML string
1000}{
1001 {
1002 Value: &SecretAgent{
1003 Handle: "007",
1004 Identity: "James Bond",
1005 Obfuscate: "<redacted/>",
1006 },
1007 Prefix: "",
1008 Indent: "\t",
1009 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
1010 },
1011}
1012
1013func TestMarshalErrors(t *testing.T) {
1014 for idx, test := range marshalErrorTests {
1015 data, err := Marshal(test.Value)
1016 if err == nil {
1017 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
1018 continue
1019 }
1020 if err.Error() != test.Err {
1021 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
1022 }
1023 if test.Kind != reflect.Invalid {
1024 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
1025 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
1026 }
1027 }
1028 }
1029}
1030
1031// Do invertibility testing on the various structures that we test
1032func TestUnmarshal(t *testing.T) {
1033 for i, test := range marshalTests {
1034 if test.MarshalOnly {
1035 continue
1036 }
1037 if _, ok := test.Value.(*Plain); ok {
1038 continue
1039 }
1040
1041 vt := reflect.TypeOf(test.Value)
1042 dest := reflect.New(vt.Elem()).Interface()
1043 err := Unmarshal([]byte(test.ExpectXML), dest)
1044
1045 switch fix := dest.(type) {
1046 case *Feed:
1047 fix.Author.InnerXML = ""
1048 for i := range fix.Entry {
1049 fix.Entry[i].Author.InnerXML = ""
1050 }
1051 }
1052
1053 if err != nil {
1054 t.Errorf("#%d: unexpected error: %#v", i, err)
1055 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
1056 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
1057 }
1058 }
1059}
1060
1061func TestMarshalIndent(t *testing.T) {
1062 for i, test := range marshalIndentTests {
1063 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
1064 if err != nil {
1065 t.Errorf("#%d: Error: %s", i, err)
1066 continue
1067 }
1068 if got, want := string(data), test.ExpectXML; got != want {
1069 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
1070 }
1071 }
1072}
1073
1074type limitedBytesWriter struct {
1075 w io.Writer
1076 remain int // until writes fail
1077}
1078
1079func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
1080 if lw.remain <= 0 {
1081 println("error")
1082 return 0, errors.New("write limit hit")
1083 }
1084 if len(p) > lw.remain {
1085 p = p[:lw.remain]
1086 n, _ = lw.w.Write(p)
1087 lw.remain = 0
1088 return n, errors.New("write limit hit")
1089 }
1090 n, err = lw.w.Write(p)
1091 lw.remain -= n
1092 return n, err
1093}
1094
1095func TestMarshalWriteErrors(t *testing.T) {
1096 var buf bytes.Buffer
1097 const writeCap = 1024
1098 w := &limitedBytesWriter{&buf, writeCap}
1099 enc := NewEncoder(w)
1100 var err error
1101 var i int
1102 const n = 4000
1103 for i = 1; i <= n; i++ {
1104 err = enc.Encode(&Passenger{
1105 Name: []string{"Alice", "Bob"},
1106 Weight: 5,
1107 })
1108 if err != nil {
1109 break
1110 }
1111 }
1112 if err == nil {
1113 t.Error("expected an error")
1114 }
1115 if i == n {
1116 t.Errorf("expected to fail before the end")
1117 }
1118 if buf.Len() != writeCap {
1119 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
1120 }
1121}
1122
1123func TestMarshalWriteIOErrors(t *testing.T) {
1124 enc := NewEncoder(errWriter{})
1125
1126 expectErr := "unwritable"
1127 err := enc.Encode(&Passenger{})
1128 if err == nil || err.Error() != expectErr {
1129 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
1130 }
1131}
1132
1133func TestMarshalFlush(t *testing.T) {
1134 var buf bytes.Buffer
1135 enc := NewEncoder(&buf)
1136 if err := enc.EncodeToken(CharData("hello world")); err != nil {
1137 t.Fatalf("enc.EncodeToken: %v", err)
1138 }
1139 if buf.Len() > 0 {
1140 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
1141 }
1142 if err := enc.Flush(); err != nil {
1143 t.Fatalf("enc.Flush: %v", err)
1144 }
1145 if buf.String() != "hello world" {
1146 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
1147 }
1148}
1149
1150func BenchmarkMarshal(b *testing.B) {
1151 for i := 0; i < b.N; i++ {
1152 Marshal(atomValue)
1153 }
1154}
1155
1156func BenchmarkUnmarshal(b *testing.B) {
1157 xml := []byte(atomXml)
1158 for i := 0; i < b.N; i++ {
1159 Unmarshal(xml, &Feed{})
1160 }
1161}
1162
1163// golang.org/issue/6556
1164func TestStructPointerMarshal(t *testing.T) {
1165 type A struct {
1166 XMLName string `xml:"a"`
1167 B []interface{}
1168 }
1169 type C struct {
1170 XMLName Name
1171 Value string `xml:"value"`
1172 }
1173
1174 a := new(A)
1175 a.B = append(a.B, &C{
1176 XMLName: Name{Local: "c"},
1177 Value: "x",
1178 })
1179
1180 b, err := Marshal(a)
1181 if err != nil {
1182 t.Fatal(err)
1183 }
1184 if x := string(b); x != "<a><c><value>x</value></c></a>" {
1185 t.Fatal(x)
1186 }
1187 var v A
1188 err = Unmarshal(b, &v)
1189 if err != nil {
1190 t.Fatal(err)
1191 }
1192}
1193
1194var encodeTokenTests = []struct {
1195 tok Token
1196 want string
1197 ok bool
1198}{
1199 {StartElement{Name{"space", "local"}, nil}, "<local xmlns=\"space\">", true},
1200 {StartElement{Name{"space", ""}, nil}, "", false},
1201 {EndElement{Name{"space", ""}}, "", false},
1202 {CharData("foo"), "foo", true},
1203 {Comment("foo"), "<!--foo-->", true},
1204 {Comment("foo-->"), "", false},
1205 {ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true},
1206 {ProcInst{"", []byte("Instruction")}, "", false},
1207 {ProcInst{"Target", []byte("Instruction?>")}, "", false},
1208 {Directive("foo"), "<!foo>", true},
1209 {Directive("foo>"), "", false},
1210}
1211
1212func TestEncodeToken(t *testing.T) {
1213 for _, tt := range encodeTokenTests {
1214 var buf bytes.Buffer
1215 enc := NewEncoder(&buf)
1216 err := enc.EncodeToken(tt.tok)
1217 switch {
1218 case !tt.ok && err == nil:
1219 t.Errorf("enc.EncodeToken(%#v): expected error; got none", tt.tok)
1220 case tt.ok && err != nil:
1221 t.Fatalf("enc.EncodeToken: %v", err)
1222 case !tt.ok && err != nil:
1223 // expected error, got one
1224 }
1225 if err := enc.Flush(); err != nil {
1226 t.Fatalf("enc.EncodeToken: %v", err)
1227 }
1228 if got := buf.String(); got != tt.want {
1229 t.Errorf("enc.EncodeToken = %s; want: %s", got, tt.want)
1230 }
1231 }
1232}
1233
1234func TestProcInstEncodeToken(t *testing.T) {
1235 var buf bytes.Buffer
1236 enc := NewEncoder(&buf)
1237
1238 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
1239 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
1240 }
1241
1242 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
1243 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
1244 }
1245
1246 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
1247 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
1248 }
1249}
1250
1251func TestDecodeEncode(t *testing.T) {
1252 var in, out bytes.Buffer
1253 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
1254<?Target Instruction?>
1255<root>
1256</root>
1257`)
1258 dec := NewDecoder(&in)
1259 enc := NewEncoder(&out)
1260 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
1261 err = enc.EncodeToken(tok)
1262 if err != nil {
1263 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
1264 }
1265 }
1266}