blob: 8362421db7433f4861a7693133fba384a56baf58 [file] [log] [blame]
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001// 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 (
Jan Ziak32a0cbb2012-06-25 16:00:35 -04008 "bytes"
9 "errors"
Shivakumar GN848d10f2013-02-03 11:21:07 -050010 "fmt"
Jan Ziak32a0cbb2012-06-25 16:00:35 -040011 "io"
Russ Cox965845a2011-11-02 15:54:16 -040012 "reflect"
Kyle Lemonsabd50de2011-06-27 19:07:28 -040013 "strconv"
Russ Cox965845a2011-11-02 15:54:16 -040014 "strings"
Roger Peppeb69ea012015-02-25 13:42:54 +000015 "sync"
Russ Cox965845a2011-11-02 15:54:16 -040016 "testing"
Russ Cox1d8250c2012-02-07 23:37:25 -050017 "time"
Kyle Lemonsabd50de2011-06-27 19:07:28 -040018)
19
20type DriveType int
21
22const (
23 HyperDrive DriveType = iota
24 ImprobabilityDrive
25)
26
27type Passenger struct {
Russ Cox25733a92011-06-29 09:52:34 -040028 Name []string `xml:"name"`
29 Weight float32 `xml:"weight"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040030}
31
32type Ship struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010033 XMLName struct{} `xml:"spaceship"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040034
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010035 Name string `xml:"name,attr"`
36 Pilot string `xml:"pilot,attr"`
Russ Cox25733a92011-06-29 09:52:34 -040037 Drive DriveType `xml:"drive"`
38 Age uint `xml:"age"`
39 Passenger []*Passenger `xml:"passenger"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040040 secret string
41}
42
Kyle Lemonsabd50de2011-06-27 19:07:28 -040043type NamedType string
44
45type Port struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010046 XMLName struct{} `xml:"port"`
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -020047 Type string `xml:"type,attr,omitempty"`
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010048 Comment string `xml:",comment"`
49 Number string `xml:",chardata"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040050}
51
52type Domain struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010053 XMLName struct{} `xml:"domain"`
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -020054 Country string `xml:",attr,omitempty"`
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010055 Name []byte `xml:",chardata"`
56 Comment []byte `xml:",comment"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040057}
58
Kyle Lemonsca6e1db2011-08-17 12:12:08 -040059type Book struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010060 XMLName struct{} `xml:"book"`
61 Title string `xml:",chardata"`
Kyle Lemonsca6e1db2011-08-17 12:12:08 -040062}
63
Vega Garcia Luis Alfonso14bd52d2013-01-22 22:13:40 -050064type Event struct {
65 XMLName struct{} `xml:"event"`
66 Year int `xml:",chardata"`
67}
68
69type Movie struct {
70 XMLName struct{} `xml:"movie"`
71 Length uint `xml:",chardata"`
72}
73
74type Pi struct {
75 XMLName struct{} `xml:"pi"`
76 Approximation float32 `xml:",chardata"`
77}
78
79type Universe struct {
80 XMLName struct{} `xml:"universe"`
81 Visible float64 `xml:",chardata"`
82}
83
84type Particle struct {
85 XMLName struct{} `xml:"particle"`
86 HasMass bool `xml:",chardata"`
87}
88
89type Departure struct {
90 XMLName struct{} `xml:"departure"`
91 When time.Time `xml:",chardata"`
92}
93
Kyle Lemonsabd50de2011-06-27 19:07:28 -040094type SecretAgent struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010095 XMLName struct{} `xml:"agent"`
96 Handle string `xml:"handle,attr"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040097 Identity string
Gustavo Niemeyer1627b462012-01-13 11:05:19 +010098 Obfuscate string `xml:",innerxml"`
Kyle Lemonsabd50de2011-06-27 19:07:28 -040099}
100
Ross Light4541fa92011-08-26 12:29:52 -0300101type NestedItems struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100102 XMLName struct{} `xml:"result"`
Ross Light4541fa92011-08-26 12:29:52 -0300103 Items []string `xml:">item"`
104 Item1 []string `xml:"Items>item1"`
105}
106
107type NestedOrder struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100108 XMLName struct{} `xml:"result"`
109 Field1 string `xml:"parent>c"`
110 Field2 string `xml:"parent>b"`
111 Field3 string `xml:"parent>a"`
Ross Light4541fa92011-08-26 12:29:52 -0300112}
113
114type MixedNested struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100115 XMLName struct{} `xml:"result"`
116 A string `xml:"parent1>a"`
117 B string `xml:"b"`
118 C string `xml:"parent1>parent2>c"`
119 D string `xml:"parent1>d"`
Ross Light4541fa92011-08-26 12:29:52 -0300120}
121
122type NilTest struct {
123 A interface{} `xml:"parent1>parent2>a"`
124 B interface{} `xml:"parent1>b"`
125 C interface{} `xml:"parent1>parent2>c"`
126}
127
128type Service struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100129 XMLName struct{} `xml:"service"`
130 Domain *Domain `xml:"host>domain"`
131 Port *Port `xml:"host>port"`
Ross Light4541fa92011-08-26 12:29:52 -0300132 Extra1 interface{}
133 Extra2 interface{} `xml:"host>extra2"`
134}
135
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400136var nilStruct *Ship
137
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100138type EmbedA struct {
139 EmbedC
140 EmbedB EmbedB
141 FieldA string
142}
143
144type EmbedB struct {
145 FieldB string
Gustavo Niemeyer9242a902012-05-16 23:21:31 -0300146 *EmbedC
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100147}
148
149type EmbedC struct {
150 FieldA1 string `xml:"FieldA>A1"`
151 FieldA2 string `xml:"FieldA>A2"`
152 FieldB string
153 FieldC string
154}
155
156type NameCasing struct {
157 XMLName struct{} `xml:"casing"`
158 Xy string
159 XY string
160 XyA string `xml:"Xy,attr"`
161 XYA string `xml:"XY,attr"`
162}
163
164type NamePrecedence struct {
165 XMLName Name `xml:"Parent"`
166 FromTag XMLNameWithoutTag `xml:"InTag"`
167 FromNameVal XMLNameWithoutTag
168 FromNameTag XMLNameWithTag
169 InFieldName string
170}
171
172type XMLNameWithTag struct {
173 XMLName Name `xml:"InXMLNameTag"`
Gustavo Niemeyerb5d4cff2012-03-01 15:20:13 -0300174 Value string `xml:",chardata"`
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100175}
176
177type XMLNameWithoutTag struct {
178 XMLName Name
Gustavo Niemeyerb5d4cff2012-03-01 15:20:13 -0300179 Value string `xml:",chardata"`
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100180}
181
Gustavo Niemeyerca3e6d12012-01-19 20:15:55 -0200182type NameInField struct {
183 Foo Name `xml:"ns foo"`
184}
185
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100186type AttrTest struct {
187 Int int `xml:",attr"`
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -0200188 Named int `xml:"int,attr"`
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100189 Float float64 `xml:",attr"`
190 Uint8 uint8 `xml:",attr"`
191 Bool bool `xml:",attr"`
192 Str string `xml:",attr"`
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -0200193 Bytes []byte `xml:",attr"`
194}
195
196type OmitAttrTest struct {
197 Int int `xml:",attr,omitempty"`
198 Named int `xml:"int,attr,omitempty"`
199 Float float64 `xml:",attr,omitempty"`
200 Uint8 uint8 `xml:",attr,omitempty"`
201 Bool bool `xml:",attr,omitempty"`
202 Str string `xml:",attr,omitempty"`
203 Bytes []byte `xml:",attr,omitempty"`
204}
205
206type OmitFieldTest struct {
207 Int int `xml:",omitempty"`
208 Named int `xml:"int,omitempty"`
209 Float float64 `xml:",omitempty"`
210 Uint8 uint8 `xml:",omitempty"`
211 Bool bool `xml:",omitempty"`
212 Str string `xml:",omitempty"`
213 Bytes []byte `xml:",omitempty"`
214 Ptr *PresenceTest `xml:",omitempty"`
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100215}
216
217type AnyTest struct {
218 XMLName struct{} `xml:"a"`
219 Nested string `xml:"nested>value"`
220 AnyField AnyHolder `xml:",any"`
221}
222
Chris Jonesa9121a12012-12-22 10:00:36 -0500223type AnyOmitTest struct {
224 XMLName struct{} `xml:"a"`
225 Nested string `xml:"nested>value"`
226 AnyField *AnyHolder `xml:",any,omitempty"`
227}
228
229type AnySliceTest struct {
230 XMLName struct{} `xml:"a"`
231 Nested string `xml:"nested>value"`
232 AnyField []AnyHolder `xml:",any"`
233}
234
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100235type AnyHolder struct {
236 XMLName Name
237 XML string `xml:",innerxml"`
238}
239
240type RecurseA struct {
241 A string
242 B *RecurseB
243}
244
245type RecurseB struct {
246 A *RecurseA
247 B string
248}
249
Gustavo Niemeyer57007fe2012-01-23 00:50:05 -0200250type PresenceTest struct {
251 Exists *struct{}
252}
253
Gustavo Niemeyer5fde5cd2012-01-23 01:34:35 -0200254type IgnoreTest struct {
255 PublicSecret string `xml:"-"`
256}
257
Gustavo Niemeyer57007fe2012-01-23 00:50:05 -0200258type MyBytes []byte
259
260type Data struct {
261 Bytes []byte
262 Attr []byte `xml:",attr"`
263 Custom MyBytes
264}
265
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100266type Plain struct {
267 V interface{}
268}
269
Russ Coxaa81eb52013-03-11 23:58:20 -0400270type MyInt int
271
272type EmbedInt struct {
273 MyInt
274}
275
Russ Coxbfe80e22013-03-12 16:42:25 -0400276type Strings struct {
277 X []string `xml:"A>B,omitempty"`
278}
279
Dmitriy Shelenin547f1a62013-08-08 10:40:51 -0700280type PointerFieldsTest struct {
281 XMLName Name `xml:"dummy"`
282 Name *string `xml:"name,attr"`
283 Age *uint `xml:"age,attr"`
284 Empty *string `xml:"empty,attr"`
285 Contents *string `xml:",chardata"`
286}
287
288type ChardataEmptyTest struct {
289 XMLName Name `xml:"test"`
290 Contents *string `xml:",chardata"`
291}
292
Russ Cox54bdfc02013-08-14 14:58:28 -0400293type MyMarshalerTest struct {
294}
295
296var _ Marshaler = (*MyMarshalerTest)(nil)
297
298func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error {
299 e.EncodeToken(start)
300 e.EncodeToken(CharData([]byte("hello world")))
301 e.EncodeToken(EndElement{start.Name})
302 return nil
303}
304
305type MyMarshalerAttrTest struct {
306}
307
308var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil)
309
310func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) {
311 return Attr{name, "hello world"}, nil
312}
313
314type MarshalerStruct struct {
315 Foo MyMarshalerAttrTest `xml:",attr"`
316}
317
Alexander Zhavnerchik4b42ad22014-04-08 11:12:51 -0400318type InnerStruct struct {
319 XMLName Name `xml:"testns outer"`
320}
321
322type OuterStruct struct {
323 InnerStruct
324 IntAttr int `xml:"int,attr"`
325}
326
327type OuterNamedStruct struct {
328 InnerStruct
329 XMLName Name `xml:"outerns test"`
330 IntAttr int `xml:"int,attr"`
331}
332
333type OuterNamedOrderedStruct struct {
334 XMLName Name `xml:"outerns test"`
335 InnerStruct
336 IntAttr int `xml:"int,attr"`
337}
338
339type OuterOuterStruct struct {
340 OuterStruct
341}
342
Russ Cox4dce7f82013-10-17 12:13:33 -0400343func ifaceptr(x interface{}) interface{} {
344 return &x
345}
346
Dmitriy Shelenin547f1a62013-08-08 10:40:51 -0700347var (
348 nameAttr = "Sarah"
349 ageAttr = uint(12)
350 contentsAttr = "lorem ipsum"
351)
352
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100353// Unless explicitly stated as such (or *Plain), all of the
354// tests below are two-way tests. When introducing new tests,
355// please try to make them two-way as well to ensure that
356// marshalling and unmarshalling are as symmetrical as feasible.
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400357var marshalTests = []struct {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100358 Value interface{}
359 ExpectXML string
360 MarshalOnly bool
361 UnmarshalOnly bool
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400362}{
363 // Test nil marshals to nothing
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100364 {Value: nil, ExpectXML: ``, MarshalOnly: true},
365 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true},
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400366
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100367 // Test value types
368 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`},
369 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`},
370 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
371 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
372 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
373 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
374 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
375 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
376 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
377 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`},
378 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
379 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`},
380 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`},
381 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
382 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`},
383 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
384 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
385 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V>&lt;/&gt;</V></Plain>`},
386 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`},
387 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
388 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`},
Russ Cox4dce7f82013-10-17 12:13:33 -0400389 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`},
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400390
Russ Cox1d8250c2012-02-07 23:37:25 -0500391 // Test time.
392 {
393 Value: &Plain{time.Unix(1e9, 123456789).UTC()},
394 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`,
395 },
396
Gustavo Niemeyer57007fe2012-01-23 00:50:05 -0200397 // A pointer to struct{} may be used to test for an element's presence.
398 {
399 Value: &PresenceTest{new(struct{})},
400 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
401 },
402 {
403 Value: &PresenceTest{},
404 ExpectXML: `<PresenceTest></PresenceTest>`,
405 },
406
407 // A pointer to struct{} may be used to test for an element's presence.
408 {
409 Value: &PresenceTest{new(struct{})},
410 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`,
411 },
412 {
413 Value: &PresenceTest{},
414 ExpectXML: `<PresenceTest></PresenceTest>`,
415 },
416
417 // A []byte field is only nil if the element was not found.
418 {
419 Value: &Data{},
420 ExpectXML: `<Data></Data>`,
421 UnmarshalOnly: true,
422 },
423 {
424 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}},
425 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`,
426 UnmarshalOnly: true,
427 },
428
429 // Check that []byte works, including named []byte types.
430 {
431 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}},
432 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`,
433 },
434
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400435 // Test innerxml
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400436 {
437 Value: &SecretAgent{
438 Handle: "007",
439 Identity: "James Bond",
440 Obfuscate: "<redacted/>",
441 },
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100442 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
443 MarshalOnly: true,
444 },
445 {
446 Value: &SecretAgent{
447 Handle: "007",
448 Identity: "James Bond",
449 Obfuscate: "<Identity>James Bond</Identity><redacted/>",
450 },
451 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`,
452 UnmarshalOnly: true,
453 },
454
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400455 // Test structs
456 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`},
457 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`},
458 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="&lt;unix&gt;"></port>`},
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100459 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`},
460 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true},
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400461 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&amp;friends</domain>`},
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100462 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`},
Kyle Lemonsca6e1db2011-08-17 12:12:08 -0400463 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride &amp; Prejudice</book>`},
Vega Garcia Luis Alfonso14bd52d2013-01-22 22:13:40 -0500464 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`},
465 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`},
466 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`},
467 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`},
468 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`},
469 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`},
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400470 {Value: atomValue, ExpectXML: atomXml},
471 {
472 Value: &Ship{
473 Name: "Heart of Gold",
474 Pilot: "Computer",
475 Age: 1,
476 Drive: ImprobabilityDrive,
477 Passenger: []*Passenger{
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500478 {
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400479 Name: []string{"Zaphod", "Beeblebrox"},
480 Weight: 7.25,
481 },
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500482 {
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400483 Name: []string{"Trisha", "McMillen"},
484 Weight: 5.5,
485 },
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500486 {
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400487 Name: []string{"Ford", "Prefect"},
488 Weight: 7,
489 },
Russ Coxdcf1d7b2011-12-02 14:14:25 -0500490 {
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400491 Name: []string{"Arthur", "Dent"},
492 Weight: 6.75,
493 },
494 },
495 },
496 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` +
497 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` +
498 `<age>1</age>` +
499 `<passenger>` +
500 `<name>Zaphod</name>` +
501 `<name>Beeblebrox</name>` +
502 `<weight>7.25</weight>` +
503 `</passenger>` +
504 `<passenger>` +
505 `<name>Trisha</name>` +
506 `<name>McMillen</name>` +
507 `<weight>5.5</weight>` +
508 `</passenger>` +
509 `<passenger>` +
510 `<name>Ford</name>` +
511 `<name>Prefect</name>` +
512 `<weight>7</weight>` +
513 `</passenger>` +
514 `<passenger>` +
515 `<name>Arthur</name>` +
516 `<name>Dent</name>` +
517 `<weight>6.75</weight>` +
518 `</passenger>` +
519 `</spaceship>`,
520 },
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100521
Ross Light4541fa92011-08-26 12:29:52 -0300522 // Test a>b
523 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100524 Value: &NestedItems{Items: nil, Item1: nil},
Ross Light4541fa92011-08-26 12:29:52 -0300525 ExpectXML: `<result>` +
526 `<Items>` +
527 `</Items>` +
528 `</result>`,
529 },
530 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100531 Value: &NestedItems{Items: []string{}, Item1: []string{}},
532 ExpectXML: `<result>` +
533 `<Items>` +
534 `</Items>` +
535 `</result>`,
536 MarshalOnly: true,
537 },
538 {
539 Value: &NestedItems{Items: nil, Item1: []string{"A"}},
Ross Light4541fa92011-08-26 12:29:52 -0300540 ExpectXML: `<result>` +
541 `<Items>` +
542 `<item1>A</item1>` +
543 `</Items>` +
544 `</result>`,
545 },
546 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100547 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil},
Ross Light4541fa92011-08-26 12:29:52 -0300548 ExpectXML: `<result>` +
549 `<Items>` +
550 `<item>A</item>` +
551 `<item>B</item>` +
552 `</Items>` +
553 `</result>`,
554 },
555 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100556 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}},
Ross Light4541fa92011-08-26 12:29:52 -0300557 ExpectXML: `<result>` +
558 `<Items>` +
559 `<item>A</item>` +
560 `<item>B</item>` +
561 `<item1>C</item1>` +
562 `</Items>` +
563 `</result>`,
564 },
565 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100566 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"},
Ross Light4541fa92011-08-26 12:29:52 -0300567 ExpectXML: `<result>` +
568 `<parent>` +
569 `<c>C</c>` +
570 `<b>B</b>` +
571 `<a>A</a>` +
572 `</parent>` +
573 `</result>`,
574 },
575 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100576 Value: &NilTest{A: "A", B: nil, C: "C"},
577 ExpectXML: `<NilTest>` +
Ross Light4541fa92011-08-26 12:29:52 -0300578 `<parent1>` +
579 `<parent2><a>A</a></parent2>` +
580 `<parent2><c>C</c></parent2>` +
581 `</parent1>` +
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100582 `</NilTest>`,
583 MarshalOnly: true, // Uses interface{}
Ross Light4541fa92011-08-26 12:29:52 -0300584 },
585 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100586 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"},
Ross Light4541fa92011-08-26 12:29:52 -0300587 ExpectXML: `<result>` +
588 `<parent1><a>A</a></parent1>` +
589 `<b>B</b>` +
590 `<parent1>` +
591 `<parent2><c>C</c></parent2>` +
592 `<d>D</d>` +
593 `</parent1>` +
594 `</result>`,
595 },
596 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100597 Value: &Service{Port: &Port{Number: "80"}},
Ross Light4541fa92011-08-26 12:29:52 -0300598 ExpectXML: `<service><host><port>80</port></host></service>`,
599 },
600 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100601 Value: &Service{},
Ross Light4541fa92011-08-26 12:29:52 -0300602 ExpectXML: `<service></service>`,
603 },
604 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100605 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"},
Ross Light4541fa92011-08-26 12:29:52 -0300606 ExpectXML: `<service>` +
607 `<host><port>80</port></host>` +
608 `<Extra1>A</Extra1>` +
609 `<host><extra2>B</extra2></host>` +
610 `</service>`,
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100611 MarshalOnly: true,
Ross Light4541fa92011-08-26 12:29:52 -0300612 },
613 {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100614 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"},
Ross Light4541fa92011-08-26 12:29:52 -0300615 ExpectXML: `<service>` +
616 `<host><port>80</port></host>` +
617 `<host><extra2>example</extra2></host>` +
618 `</service>`,
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100619 MarshalOnly: true,
620 },
Nigel Taob351e1d2015-02-26 10:40:15 +1100621 {
622 Value: &struct {
623 XMLName struct{} `xml:"space top"`
624 A string `xml:"x>a"`
625 B string `xml:"x>b"`
626 C string `xml:"space x>c"`
627 C1 string `xml:"space1 x>c"`
628 D1 string `xml:"space1 x>d"`
Roger Peppeb69ea012015-02-25 13:42:54 +0000629 E1 string `xml:"x>e"`
Nigel Taob351e1d2015-02-26 10:40:15 +1100630 }{
631 A: "a",
632 B: "b",
633 C: "c",
634 C1: "c1",
635 D1: "d1",
Roger Peppeb69ea012015-02-25 13:42:54 +0000636 E1: "e1",
Nigel Taob351e1d2015-02-26 10:40:15 +1100637 },
638 ExpectXML: `<top xmlns="space">` +
Roger Peppeb69ea012015-02-25 13:42:54 +0000639 `<x><a>a</a><b>b</b><c>c</c></x>` +
640 `<x xmlns="space1">` +
641 `<c>c1</c>` +
642 `<d>d1</d>` +
Roger Peppe9f9d66d2015-03-06 11:32:42 +0000643 `</x>` +
644 `<x>` +
Roger Peppeb69ea012015-02-25 13:42:54 +0000645 `<e>e1</e>` +
Nigel Taob351e1d2015-02-26 10:40:15 +1100646 `</x>` +
647 `</top>`,
648 },
649 {
650 Value: &struct {
651 XMLName Name
652 A string `xml:"x>a"`
653 B string `xml:"x>b"`
654 C string `xml:"space x>c"`
655 C1 string `xml:"space1 x>c"`
656 D1 string `xml:"space1 x>d"`
657 }{
658 XMLName: Name{
659 Space: "space0",
660 Local: "top",
661 },
662 A: "a",
663 B: "b",
664 C: "c",
665 C1: "c1",
666 D1: "d1",
667 },
668 ExpectXML: `<top xmlns="space0">` +
Roger Peppeb69ea012015-02-25 13:42:54 +0000669 `<x><a>a</a><b>b</b></x>` +
670 `<x xmlns="space"><c>c</c></x>` +
671 `<x xmlns="space1">` +
672 `<c>c1</c>` +
673 `<d>d1</d>` +
Nigel Taob351e1d2015-02-26 10:40:15 +1100674 `</x>` +
675 `</top>`,
676 },
677 {
678 Value: &struct {
679 XMLName struct{} `xml:"top"`
680 B string `xml:"space x>b"`
681 B1 string `xml:"space1 x>b"`
682 }{
683 B: "b",
684 B1: "b1",
685 },
686 ExpectXML: `<top>` +
Roger Peppeb69ea012015-02-25 13:42:54 +0000687 `<x xmlns="space"><b>b</b></x>` +
688 `<x xmlns="space1"><b>b1</b></x>` +
Nigel Taob351e1d2015-02-26 10:40:15 +1100689 `</top>`,
690 },
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100691
692 // Test struct embedding
693 {
694 Value: &EmbedA{
695 EmbedC: EmbedC{
696 FieldA1: "", // Shadowed by A.A
697 FieldA2: "", // Shadowed by A.A
698 FieldB: "A.C.B",
699 FieldC: "A.C.C",
700 },
701 EmbedB: EmbedB{
702 FieldB: "A.B.B",
Gustavo Niemeyer9242a902012-05-16 23:21:31 -0300703 EmbedC: &EmbedC{
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100704 FieldA1: "A.B.C.A1",
705 FieldA2: "A.B.C.A2",
706 FieldB: "", // Shadowed by A.B.B
707 FieldC: "A.B.C.C",
708 },
709 },
710 FieldA: "A.A",
711 },
712 ExpectXML: `<EmbedA>` +
713 `<FieldB>A.C.B</FieldB>` +
714 `<FieldC>A.C.C</FieldC>` +
715 `<EmbedB>` +
716 `<FieldB>A.B.B</FieldB>` +
717 `<FieldA>` +
718 `<A1>A.B.C.A1</A1>` +
719 `<A2>A.B.C.A2</A2>` +
720 `</FieldA>` +
721 `<FieldC>A.B.C.C</FieldC>` +
722 `</EmbedB>` +
723 `<FieldA>A.A</FieldA>` +
724 `</EmbedA>`,
725 },
726
727 // Test that name casing matters
728 {
729 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"},
730 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`,
731 },
732
733 // Test the order in which the XML element name is chosen
734 {
735 Value: &NamePrecedence{
736 FromTag: XMLNameWithoutTag{Value: "A"},
737 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"},
738 FromNameTag: XMLNameWithTag{Value: "C"},
739 InFieldName: "D",
740 },
741 ExpectXML: `<Parent>` +
Gustavo Niemeyerb5d4cff2012-03-01 15:20:13 -0300742 `<InTag>A</InTag>` +
743 `<InXMLName>B</InXMLName>` +
744 `<InXMLNameTag>C</InXMLNameTag>` +
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100745 `<InFieldName>D</InFieldName>` +
746 `</Parent>`,
747 MarshalOnly: true,
748 },
749 {
750 Value: &NamePrecedence{
751 XMLName: Name{Local: "Parent"},
752 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"},
753 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"},
754 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"},
755 InFieldName: "D",
756 },
757 ExpectXML: `<Parent>` +
Gustavo Niemeyerb5d4cff2012-03-01 15:20:13 -0300758 `<InTag>A</InTag>` +
759 `<FromNameVal>B</FromNameVal>` +
760 `<InXMLNameTag>C</InXMLNameTag>` +
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100761 `<InFieldName>D</InFieldName>` +
762 `</Parent>`,
763 UnmarshalOnly: true,
764 },
765
Gustavo Niemeyerca3e6d12012-01-19 20:15:55 -0200766 // xml.Name works in a plain field as well.
767 {
768 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
769 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
770 },
Gustavo Niemeyer9c497442012-01-30 16:32:48 -0200771 {
772 Value: &NameInField{Name{Space: "ns", Local: "foo"}},
773 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`,
774 UnmarshalOnly: true,
775 },
Gustavo Niemeyerca3e6d12012-01-19 20:15:55 -0200776
777 // Marshaling zero xml.Name uses the tag or field name.
778 {
779 Value: &NameInField{},
780 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`,
781 MarshalOnly: true,
782 },
783
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100784 // Test attributes
785 {
786 Value: &AttrTest{
787 Int: 8,
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -0200788 Named: 9,
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100789 Float: 23.5,
790 Uint8: 255,
791 Bool: true,
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -0200792 Str: "str",
793 Bytes: []byte("byt"),
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100794 },
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -0200795 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
796 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`,
797 },
798 {
799 Value: &AttrTest{Bytes: []byte{}},
800 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` +
801 ` Bool="false" Str="" Bytes=""></AttrTest>`,
802 },
803 {
804 Value: &OmitAttrTest{
805 Int: 8,
806 Named: 9,
807 Float: 23.5,
808 Uint8: 255,
809 Bool: true,
810 Str: "str",
811 Bytes: []byte("byt"),
812 },
813 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` +
814 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`,
815 },
816 {
817 Value: &OmitAttrTest{},
818 ExpectXML: `<OmitAttrTest></OmitAttrTest>`,
819 },
820
Dmitriy Shelenin547f1a62013-08-08 10:40:51 -0700821 // pointer fields
822 {
823 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr},
824 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`,
825 MarshalOnly: true,
826 },
827
828 // empty chardata pointer field
829 {
830 Value: &ChardataEmptyTest{},
831 ExpectXML: `<test></test>`,
832 MarshalOnly: true,
833 },
834
Gustavo Niemeyer0a7ad322012-02-08 01:57:44 -0200835 // omitempty on fields
836 {
837 Value: &OmitFieldTest{
838 Int: 8,
839 Named: 9,
840 Float: 23.5,
841 Uint8: 255,
842 Bool: true,
843 Str: "str",
844 Bytes: []byte("byt"),
845 Ptr: &PresenceTest{},
846 },
847 ExpectXML: `<OmitFieldTest>` +
848 `<Int>8</Int>` +
849 `<int>9</int>` +
850 `<Float>23.5</Float>` +
851 `<Uint8>255</Uint8>` +
852 `<Bool>true</Bool>` +
853 `<Str>str</Str>` +
854 `<Bytes>byt</Bytes>` +
855 `<Ptr></Ptr>` +
856 `</OmitFieldTest>`,
857 },
858 {
859 Value: &OmitFieldTest{},
860 ExpectXML: `<OmitFieldTest></OmitFieldTest>`,
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100861 },
862
863 // Test ",any"
864 {
865 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`,
866 Value: &AnyTest{
867 Nested: "known",
868 AnyField: AnyHolder{
869 XMLName: Name{Local: "other"},
870 XML: "<sub>unknown</sub>",
871 },
872 },
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100873 },
874 {
Chris Jonesa9121a12012-12-22 10:00:36 -0500875 Value: &AnyTest{Nested: "known",
876 AnyField: AnyHolder{
877 XML: "<unknown/>",
878 XMLName: Name{Local: "AnyField"},
879 },
880 },
881 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`,
882 },
883 {
884 ExpectXML: `<a><nested><value>b</value></nested></a>`,
885 Value: &AnyOmitTest{
886 Nested: "b",
887 },
888 },
889 {
890 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`,
891 Value: &AnySliceTest{
892 Nested: "b",
893 AnyField: []AnyHolder{
894 {
895 XMLName: Name{Local: "c"},
896 XML: "<d>e</d>",
897 },
898 {
899 XMLName: Name{Space: "f", Local: "g"},
900 XML: "<h>i</h>",
901 },
902 },
903 },
904 },
905 {
906 ExpectXML: `<a><nested><value>b</value></nested></a>`,
907 Value: &AnySliceTest{
908 Nested: "b",
909 },
Gustavo Niemeyer1627b462012-01-13 11:05:19 +0100910 },
911
912 // Test recursive types.
913 {
914 Value: &RecurseA{
915 A: "a1",
916 B: &RecurseB{
917 A: &RecurseA{"a2", nil},
918 B: "b1",
919 },
920 },
921 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`,
Ross Light4541fa92011-08-26 12:29:52 -0300922 },
Gustavo Niemeyer5fde5cd2012-01-23 01:34:35 -0200923
924 // Test ignoring fields via "-" tag
925 {
926 ExpectXML: `<IgnoreTest></IgnoreTest>`,
927 Value: &IgnoreTest{},
928 },
929 {
930 ExpectXML: `<IgnoreTest></IgnoreTest>`,
931 Value: &IgnoreTest{PublicSecret: "can't tell"},
932 MarshalOnly: true,
933 },
934 {
935 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`,
936 Value: &IgnoreTest{},
937 UnmarshalOnly: true,
938 },
Ian Lance Taylor1e6d9f42012-10-18 13:40:45 -0700939
940 // Test escaping.
941 {
Chris Jonesa9121a12012-12-22 10:00:36 -0500942 ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested><empty></empty></a>`,
Ian Lance Taylor1e6d9f42012-10-18 13:40:45 -0700943 Value: &AnyTest{
Chris Jonesa9121a12012-12-22 10:00:36 -0500944 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
945 AnyField: AnyHolder{XMLName: Name{Local: "empty"}},
Ian Lance Taylor1e6d9f42012-10-18 13:40:45 -0700946 },
947 },
948 {
Chris Jonesa9121a12012-12-22 10:00:36 -0500949 ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</value></nested><AnyField></AnyField></a>`,
Ian Lance Taylor1e6d9f42012-10-18 13:40:45 -0700950 Value: &AnyTest{
Chris Jonesa9121a12012-12-22 10:00:36 -0500951 Nested: "newline: \n; cr: \r; tab: \t;",
952 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}},
Ian Lance Taylor1e6d9f42012-10-18 13:40:45 -0700953 },
954 },
955 {
956 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
957 Value: &AnyTest{
958 Nested: "1\n2\n3\n\n4\n5",
959 },
960 UnmarshalOnly: true,
961 },
Russ Coxaa81eb52013-03-11 23:58:20 -0400962 {
963 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`,
964 Value: &EmbedInt{
965 MyInt: 42,
966 },
967 },
Russ Coxbfe80e22013-03-12 16:42:25 -0400968 // Test omitempty with parent chain; see golang.org/issue/4168.
969 {
970 ExpectXML: `<Strings><A></A></Strings>`,
971 Value: &Strings{},
972 },
Russ Cox54bdfc02013-08-14 14:58:28 -0400973 // Custom marshalers.
974 {
975 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`,
976 Value: &MyMarshalerTest{},
977 },
978 {
979 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
980 Value: &MarshalerStruct{},
981 },
Alexander Zhavnerchik4b42ad22014-04-08 11:12:51 -0400982 {
983 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
984 Value: &OuterStruct{IntAttr: 10},
985 },
986 {
987 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
988 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
989 },
990 {
991 ExpectXML: `<test xmlns="outerns" int="10"></test>`,
992 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
993 },
994 {
995 ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
996 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}},
997 },
Kyle Lemonsabd50de2011-06-27 19:07:28 -0400998}
999
1000func TestMarshal(t *testing.T) {
1001 for idx, test := range marshalTests {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001002 if test.UnmarshalOnly {
1003 continue
1004 }
Gustavo Niemeyer04420872012-01-24 01:10:32 -02001005 data, err := Marshal(test.Value)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001006 if err != nil {
Nigel Taob351e1d2015-02-26 10:40:15 +11001007 t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001008 continue
1009 }
Gustavo Niemeyer04420872012-01-24 01:10:32 -02001010 if got, want := string(data), test.ExpectXML; got != want {
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001011 if strings.Contains(want, "\n") {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001012 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001013 } else {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001014 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001015 }
1016 }
1017 }
1018}
1019
Russ Coxbfe80e22013-03-12 16:42:25 -04001020type AttrParent struct {
1021 X string `xml:"X>Y,attr"`
1022}
1023
Russ Cox10c36fb2013-09-09 16:42:07 -04001024type BadAttr struct {
1025 Name []string `xml:"name,attr"`
1026}
1027
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001028var marshalErrorTests = []struct {
Russ Cox29fb5d32011-10-27 19:40:41 -07001029 Value interface{}
1030 Err string
1031 Kind reflect.Kind
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001032}{
1033 {
Russ Cox29fb5d32011-10-27 19:40:41 -07001034 Value: make(chan bool),
1035 Err: "xml: unsupported type: chan bool",
1036 Kind: reflect.Chan,
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001037 },
1038 {
1039 Value: map[string]string{
1040 "question": "What do you get when you multiply six by nine?",
1041 "answer": "42",
1042 },
Russ Cox434a6c82011-12-02 14:45:07 -05001043 Err: "xml: unsupported type: map[string]string",
Russ Cox29fb5d32011-10-27 19:40:41 -07001044 Kind: reflect.Map,
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001045 },
1046 {
Russ Cox29fb5d32011-10-27 19:40:41 -07001047 Value: map[*Ship]bool{nil: false},
Russ Cox434a6c82011-12-02 14:45:07 -05001048 Err: "xml: unsupported type: map[*xml.Ship]bool",
Russ Cox29fb5d32011-10-27 19:40:41 -07001049 Kind: reflect.Map,
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001050 },
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001051 {
1052 Value: &Domain{Comment: []byte("f--bar")},
1053 Err: `xml: comments must not contain "--"`,
1054 },
Russ Coxbfe80e22013-03-12 16:42:25 -04001055 // Reject parent chain with attr, never worked; see golang.org/issue/5033.
1056 {
1057 Value: &AttrParent{},
1058 Err: `xml: X>Y chain not valid with attr flag`,
1059 },
Russ Cox10c36fb2013-09-09 16:42:07 -04001060 {
1061 Value: BadAttr{[]string{"X", "Y"}},
1062 Err: `xml: unsupported type: []string`,
1063 },
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001064}
1065
Shivakumar GN848d10f2013-02-03 11:21:07 -05001066var marshalIndentTests = []struct {
1067 Value interface{}
1068 Prefix string
1069 Indent string
1070 ExpectXML string
1071}{
1072 {
1073 Value: &SecretAgent{
1074 Handle: "007",
1075 Identity: "James Bond",
1076 Obfuscate: "<redacted/>",
1077 },
1078 Prefix: "",
1079 Indent: "\t",
1080 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"),
1081 },
1082}
1083
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001084func TestMarshalErrors(t *testing.T) {
1085 for idx, test := range marshalErrorTests {
Russ Coxbfe80e22013-03-12 16:42:25 -04001086 data, err := Marshal(test.Value)
1087 if err == nil {
1088 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err)
1089 continue
1090 }
1091 if err.Error() != test.Err {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001092 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001093 }
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001094 if test.Kind != reflect.Invalid {
1095 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind {
1096 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind)
1097 }
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001098 }
1099 }
1100}
1101
1102// Do invertibility testing on the various structures that we test
1103func TestUnmarshal(t *testing.T) {
1104 for i, test := range marshalTests {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001105 if test.MarshalOnly {
1106 continue
1107 }
1108 if _, ok := test.Value.(*Plain); ok {
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001109 continue
1110 }
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001111 vt := reflect.TypeOf(test.Value)
1112 dest := reflect.New(vt.Elem()).Interface()
Gustavo Niemeyer04420872012-01-24 01:10:32 -02001113 err := Unmarshal([]byte(test.ExpectXML), dest)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001114
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001115 switch fix := dest.(type) {
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001116 case *Feed:
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001117 fix.Author.InnerXML = ""
1118 for i := range fix.Entry {
1119 fix.Entry[i].Author.InnerXML = ""
1120 }
1121 }
1122
1123 if err != nil {
1124 t.Errorf("#%d: unexpected error: %#v", i, err)
1125 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001126 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001127 }
1128 }
1129}
1130
Shivakumar GN848d10f2013-02-03 11:21:07 -05001131func TestMarshalIndent(t *testing.T) {
1132 for i, test := range marshalIndentTests {
1133 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent)
1134 if err != nil {
1135 t.Errorf("#%d: Error: %s", i, err)
1136 continue
1137 }
1138 if got, want := string(data), test.ExpectXML; got != want {
1139 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want)
1140 }
1141 }
1142}
1143
Jan Ziak32a0cbb2012-06-25 16:00:35 -04001144type limitedBytesWriter struct {
1145 w io.Writer
1146 remain int // until writes fail
1147}
1148
1149func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) {
1150 if lw.remain <= 0 {
1151 println("error")
1152 return 0, errors.New("write limit hit")
1153 }
1154 if len(p) > lw.remain {
1155 p = p[:lw.remain]
1156 n, _ = lw.w.Write(p)
1157 lw.remain = 0
1158 return n, errors.New("write limit hit")
1159 }
1160 n, err = lw.w.Write(p)
1161 lw.remain -= n
1162 return n, err
1163}
1164
1165func TestMarshalWriteErrors(t *testing.T) {
1166 var buf bytes.Buffer
1167 const writeCap = 1024
1168 w := &limitedBytesWriter{&buf, writeCap}
1169 enc := NewEncoder(w)
1170 var err error
1171 var i int
1172 const n = 4000
1173 for i = 1; i <= n; i++ {
1174 err = enc.Encode(&Passenger{
1175 Name: []string{"Alice", "Bob"},
1176 Weight: 5,
1177 })
1178 if err != nil {
1179 break
1180 }
1181 }
1182 if err == nil {
1183 t.Error("expected an error")
1184 }
1185 if i == n {
1186 t.Errorf("expected to fail before the end")
1187 }
1188 if buf.Len() != writeCap {
1189 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap)
1190 }
1191}
1192
Olivier Saingreafde71c2013-02-20 14:41:23 -08001193func TestMarshalWriteIOErrors(t *testing.T) {
1194 enc := NewEncoder(errWriter{})
1195
1196 expectErr := "unwritable"
1197 err := enc.Encode(&Passenger{})
1198 if err == nil || err.Error() != expectErr {
1199 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr)
1200 }
1201}
1202
Russ Cox3c11dd82013-09-12 16:54:01 -04001203func TestMarshalFlush(t *testing.T) {
1204 var buf bytes.Buffer
1205 enc := NewEncoder(&buf)
1206 if err := enc.EncodeToken(CharData("hello world")); err != nil {
1207 t.Fatalf("enc.EncodeToken: %v", err)
1208 }
1209 if buf.Len() > 0 {
1210 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes())
1211 }
1212 if err := enc.Flush(); err != nil {
1213 t.Fatalf("enc.Flush: %v", err)
1214 }
1215 if buf.String() != "hello world" {
1216 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world")
1217 }
1218}
1219
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001220func BenchmarkMarshal(b *testing.B) {
Dmitry Vyukov437ec6b2015-01-14 21:32:05 +03001221 b.ReportAllocs()
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001222 for i := 0; i < b.N; i++ {
Gustavo Niemeyer04420872012-01-24 01:10:32 -02001223 Marshal(atomValue)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001224 }
1225}
1226
1227func BenchmarkUnmarshal(b *testing.B) {
Dmitry Vyukov437ec6b2015-01-14 21:32:05 +03001228 b.ReportAllocs()
Gustavo Niemeyer1627b462012-01-13 11:05:19 +01001229 xml := []byte(atomXml)
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001230 for i := 0; i < b.N; i++ {
Gustavo Niemeyer04420872012-01-24 01:10:32 -02001231 Unmarshal(xml, &Feed{})
Kyle Lemonsabd50de2011-06-27 19:07:28 -04001232 }
1233}
Russ Cox4dce7f82013-10-17 12:13:33 -04001234
1235// golang.org/issue/6556
1236func TestStructPointerMarshal(t *testing.T) {
1237 type A struct {
1238 XMLName string `xml:"a"`
1239 B []interface{}
1240 }
1241 type C struct {
1242 XMLName Name
1243 Value string `xml:"value"`
1244 }
1245
1246 a := new(A)
1247 a.B = append(a.B, &C{
1248 XMLName: Name{Local: "c"},
1249 Value: "x",
1250 })
1251
1252 b, err := Marshal(a)
1253 if err != nil {
1254 t.Fatal(err)
1255 }
1256 if x := string(b); x != "<a><c><value>x</value></c></a>" {
1257 t.Fatal(x)
1258 }
1259 var v A
1260 err = Unmarshal(b, &v)
1261 if err != nil {
1262 t.Fatal(err)
1263 }
1264}
Shawn Smith58980822014-03-05 14:49:33 -05001265
1266var encodeTokenTests = []struct {
Nigel Taoa9dddb52015-02-09 13:42:45 +11001267 desc string
1268 toks []Token
Shawn Smith58980822014-03-05 14:49:33 -05001269 want string
Nigel Taoa9dddb52015-02-09 13:42:45 +11001270 err string
1271}{{
1272 desc: "start element with name space",
1273 toks: []Token{
1274 StartElement{Name{"space", "local"}, nil},
1275 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001276 want: `<space:local xmlns:space="space">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001277}, {
1278 desc: "start element with no name",
1279 toks: []Token{
1280 StartElement{Name{"space", ""}, nil},
1281 },
1282 err: "xml: start tag with no name",
1283}, {
1284 desc: "end element with no name",
1285 toks: []Token{
1286 EndElement{Name{"space", ""}},
1287 },
1288 err: "xml: end tag with no name",
1289}, {
1290 desc: "char data",
1291 toks: []Token{
1292 CharData("foo"),
1293 },
1294 want: `foo`,
1295}, {
1296 desc: "char data with escaped chars",
1297 toks: []Token{
1298 CharData(" \t\n"),
1299 },
1300 want: ` &#x9;&#xA;`,
1301}, {
1302 desc: "comment",
1303 toks: []Token{
1304 Comment("foo"),
1305 },
1306 want: `<!--foo-->`,
1307}, {
1308 desc: "comment with invalid content",
1309 toks: []Token{
1310 Comment("foo-->"),
1311 },
1312 err: "xml: EncodeToken of Comment containing --> marker",
1313}, {
1314 desc: "proc instruction",
1315 toks: []Token{
1316 ProcInst{"Target", []byte("Instruction")},
1317 },
1318 want: `<?Target Instruction?>`,
1319}, {
1320 desc: "proc instruction with empty target",
1321 toks: []Token{
1322 ProcInst{"", []byte("Instruction")},
1323 },
1324 err: "xml: EncodeToken of ProcInst with invalid Target",
1325}, {
1326 desc: "proc instruction with bad content",
1327 toks: []Token{
1328 ProcInst{"", []byte("Instruction?>")},
1329 },
1330 err: "xml: EncodeToken of ProcInst with invalid Target",
1331}, {
1332 desc: "directive",
1333 toks: []Token{
1334 Directive("foo"),
1335 },
1336 want: `<!foo>`,
1337}, {
1338 desc: "directive instruction with bad name",
1339 toks: []Token{
1340 Directive("foo>"),
1341 },
1342 err: "xml: EncodeToken of Directive containing > marker",
1343}, {
1344 desc: "end tag without start tag",
1345 toks: []Token{
1346 EndElement{Name{"foo", "bar"}},
1347 },
1348 err: "xml: end tag </bar> without start tag",
1349}, {
1350 desc: "mismatching end tag local name",
1351 toks: []Token{
1352 StartElement{Name{"", "foo"}, nil},
1353 EndElement{Name{"", "bar"}},
1354 },
1355 err: "xml: end tag </bar> does not match start tag <foo>",
1356 want: `<foo>`,
1357}, {
1358 desc: "mismatching end tag namespace",
1359 toks: []Token{
1360 StartElement{Name{"space", "foo"}, nil},
1361 EndElement{Name{"another", "foo"}},
1362 },
1363 err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space",
Roger Peppe3be158d2015-01-10 14:00:21 +00001364 want: `<space:foo xmlns:space="space">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001365}, {
1366 desc: "start element with explicit namespace",
1367 toks: []Token{
1368 StartElement{Name{"space", "local"}, []Attr{
1369 {Name{"xmlns", "x"}, "space"},
1370 {Name{"space", "foo"}, "value"},
1371 }},
1372 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001373 want: `<x:local xmlns:x="space" x:foo="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001374}, {
1375 desc: "start element with explicit namespace and colliding prefix",
1376 toks: []Token{
1377 StartElement{Name{"space", "local"}, []Attr{
1378 {Name{"xmlns", "x"}, "space"},
1379 {Name{"space", "foo"}, "value"},
1380 {Name{"x", "bar"}, "other"},
1381 }},
1382 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001383 want: `<x:local xmlns:x_1="x" xmlns:x="space" x:foo="value" x_1:bar="other">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001384}, {
1385 desc: "start element using previously defined namespace",
1386 toks: []Token{
1387 StartElement{Name{"", "local"}, []Attr{
1388 {Name{"xmlns", "x"}, "space"},
1389 }},
1390 StartElement{Name{"space", "foo"}, []Attr{
1391 {Name{"space", "x"}, "y"},
1392 }},
1393 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001394 want: `<local xmlns:x="space"><x:foo x:x="y">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001395}, {
1396 desc: "nested name space with same prefix",
1397 toks: []Token{
1398 StartElement{Name{"", "foo"}, []Attr{
1399 {Name{"xmlns", "x"}, "space1"},
1400 }},
1401 StartElement{Name{"", "foo"}, []Attr{
1402 {Name{"xmlns", "x"}, "space2"},
1403 }},
1404 StartElement{Name{"", "foo"}, []Attr{
1405 {Name{"space1", "a"}, "space1 value"},
1406 {Name{"space2", "b"}, "space2 value"},
1407 }},
1408 EndElement{Name{"", "foo"}},
1409 EndElement{Name{"", "foo"}},
1410 StartElement{Name{"", "foo"}, []Attr{
1411 {Name{"space1", "a"}, "space1 value"},
1412 {Name{"space2", "b"}, "space2 value"},
1413 }},
1414 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001415 want: `<foo xmlns:x="space1"><foo xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" x:b="space2 value"></foo></foo><foo xmlns:space2="space2" x:a="space1 value" space2:b="space2 value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001416}, {
1417 desc: "start element defining several prefixes for the same name space",
1418 toks: []Token{
1419 StartElement{Name{"space", "foo"}, []Attr{
1420 {Name{"xmlns", "a"}, "space"},
1421 {Name{"xmlns", "b"}, "space"},
1422 {Name{"space", "x"}, "value"},
1423 }},
1424 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001425 want: `<a:foo xmlns:a="space" a:x="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001426}, {
1427 desc: "nested element redefines name space",
1428 toks: []Token{
1429 StartElement{Name{"", "foo"}, []Attr{
1430 {Name{"xmlns", "x"}, "space"},
1431 }},
1432 StartElement{Name{"space", "foo"}, []Attr{
1433 {Name{"xmlns", "y"}, "space"},
1434 {Name{"space", "a"}, "value"},
1435 }},
1436 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001437 want: `<foo xmlns:x="space"><x:foo x:a="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001438}, {
1439 desc: "nested element creates alias for default name space",
1440 toks: []Token{
1441 StartElement{Name{"space", "foo"}, []Attr{
1442 {Name{"", "xmlns"}, "space"},
1443 }},
1444 StartElement{Name{"space", "foo"}, []Attr{
1445 {Name{"xmlns", "y"}, "space"},
1446 {Name{"space", "a"}, "value"},
1447 }},
1448 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001449 want: `<foo xmlns="space"><foo xmlns:y="space" y:a="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001450}, {
1451 desc: "nested element defines default name space with existing prefix",
1452 toks: []Token{
1453 StartElement{Name{"", "foo"}, []Attr{
1454 {Name{"xmlns", "x"}, "space"},
1455 }},
1456 StartElement{Name{"space", "foo"}, []Attr{
1457 {Name{"", "xmlns"}, "space"},
1458 {Name{"space", "a"}, "value"},
1459 }},
1460 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001461 want: `<foo xmlns:x="space"><foo xmlns="space" x:a="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001462}, {
1463 desc: "nested element uses empty attribute name space when default ns defined",
1464 toks: []Token{
1465 StartElement{Name{"space", "foo"}, []Attr{
1466 {Name{"", "xmlns"}, "space"},
1467 }},
1468 StartElement{Name{"space", "foo"}, []Attr{
1469 {Name{"", "attr"}, "value"},
1470 }},
1471 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001472 want: `<foo xmlns="space"><foo attr="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001473}, {
1474 desc: "redefine xmlns",
1475 toks: []Token{
1476 StartElement{Name{"", "foo"}, []Attr{
1477 {Name{"foo", "xmlns"}, "space"},
1478 }},
1479 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001480 err: `xml: cannot redefine xmlns attribute prefix`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001481}, {
1482 desc: "xmlns with explicit name space #1",
1483 toks: []Token{
1484 StartElement{Name{"space", "foo"}, []Attr{
1485 {Name{"xml", "xmlns"}, "space"},
1486 }},
1487 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001488 want: `<foo xmlns="space">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001489}, {
1490 desc: "xmlns with explicit name space #2",
1491 toks: []Token{
1492 StartElement{Name{"space", "foo"}, []Attr{
1493 {Name{xmlURL, "xmlns"}, "space"},
1494 }},
1495 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001496 want: `<foo xmlns="space">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001497}, {
1498 desc: "empty name space declaration is ignored",
1499 toks: []Token{
1500 StartElement{Name{"", "foo"}, []Attr{
1501 {Name{"xmlns", "foo"}, ""},
1502 }},
1503 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001504 want: `<foo>`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001505}, {
1506 desc: "attribute with no name is ignored",
1507 toks: []Token{
1508 StartElement{Name{"", "foo"}, []Attr{
1509 {Name{"", ""}, "value"},
1510 }},
1511 },
1512 want: `<foo>`,
1513}, {
1514 desc: "namespace URL with non-valid name",
1515 toks: []Token{
1516 StartElement{Name{"/34", "foo"}, []Attr{
1517 {Name{"/34", "x"}, "value"},
1518 }},
1519 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001520 want: `<_:foo xmlns:_="/34" _:x="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001521}, {
1522 desc: "nested element resets default namespace to empty",
1523 toks: []Token{
1524 StartElement{Name{"space", "foo"}, []Attr{
1525 {Name{"", "xmlns"}, "space"},
1526 }},
1527 StartElement{Name{"", "foo"}, []Attr{
1528 {Name{"", "xmlns"}, ""},
1529 {Name{"", "x"}, "value"},
1530 {Name{"space", "x"}, "value"},
1531 }},
1532 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001533 want: `<foo xmlns="space"><foo xmlns:space="space" xmlns="" x="value" space:x="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001534}, {
1535 desc: "nested element requires empty default name space",
1536 toks: []Token{
1537 StartElement{Name{"space", "foo"}, []Attr{
1538 {Name{"", "xmlns"}, "space"},
1539 }},
1540 StartElement{Name{"", "foo"}, nil},
1541 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001542 want: `<foo xmlns="space"><foo xmlns="">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001543}, {
1544 desc: "attribute uses name space from xmlns",
1545 toks: []Token{
1546 StartElement{Name{"some/space", "foo"}, []Attr{
1547 {Name{"", "attr"}, "value"},
1548 {Name{"some/space", "other"}, "other value"},
1549 }},
1550 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001551 want: `<space:foo xmlns:space="some/space" attr="value" space:other="other value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001552}, {
1553 desc: "default name space should not be used by attributes",
1554 toks: []Token{
1555 StartElement{Name{"space", "foo"}, []Attr{
1556 {Name{"", "xmlns"}, "space"},
1557 {Name{"xmlns", "bar"}, "space"},
1558 {Name{"space", "baz"}, "foo"},
1559 }},
1560 StartElement{Name{"space", "baz"}, nil},
1561 EndElement{Name{"space", "baz"}},
1562 EndElement{Name{"space", "foo"}},
1563 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001564 want: `<foo xmlns:bar="space" xmlns="space" bar:baz="foo"><baz></baz></foo>`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001565}, {
1566 desc: "default name space not used by attributes, not explicitly defined",
1567 toks: []Token{
1568 StartElement{Name{"space", "foo"}, []Attr{
1569 {Name{"", "xmlns"}, "space"},
1570 {Name{"space", "baz"}, "foo"},
1571 }},
1572 StartElement{Name{"space", "baz"}, nil},
1573 EndElement{Name{"space", "baz"}},
1574 EndElement{Name{"space", "foo"}},
1575 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001576 want: `<foo xmlns:space="space" xmlns="space" space:baz="foo"><baz></baz></foo>`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001577}, {
1578 desc: "impossible xmlns declaration",
1579 toks: []Token{
1580 StartElement{Name{"", "foo"}, []Attr{
1581 {Name{"", "xmlns"}, "space"},
1582 }},
1583 StartElement{Name{"space", "bar"}, []Attr{
1584 {Name{"space", "attr"}, "value"},
1585 }},
1586 },
Roger Peppe3be158d2015-01-10 14:00:21 +00001587 want: `<foo><space:bar xmlns:space="space" space:attr="value">`,
Nigel Taoa9dddb52015-02-09 13:42:45 +11001588}}
Shawn Smith58980822014-03-05 14:49:33 -05001589
1590func TestEncodeToken(t *testing.T) {
Nigel Taoa9dddb52015-02-09 13:42:45 +11001591loop:
1592 for i, tt := range encodeTokenTests {
Shawn Smith58980822014-03-05 14:49:33 -05001593 var buf bytes.Buffer
1594 enc := NewEncoder(&buf)
Nigel Taoa9dddb52015-02-09 13:42:45 +11001595 var err error
1596 for j, tok := range tt.toks {
1597 err = enc.EncodeToken(tok)
1598 if err != nil && j < len(tt.toks)-1 {
1599 t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err)
1600 continue loop
1601 }
1602 }
1603 errorf := func(f string, a ...interface{}) {
1604 t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...))
1605 }
Shawn Smith58980822014-03-05 14:49:33 -05001606 switch {
Nigel Taoa9dddb52015-02-09 13:42:45 +11001607 case tt.err != "" && err == nil:
1608 errorf(" expected error; got none")
1609 continue
1610 case tt.err == "" && err != nil:
1611 errorf(" got error: %v", err)
1612 continue
1613 case tt.err != "" && err != nil && tt.err != err.Error():
1614 errorf(" error mismatch; got %v, want %v", err, tt.err)
1615 continue
Shawn Smith58980822014-03-05 14:49:33 -05001616 }
1617 if err := enc.Flush(); err != nil {
Nigel Taoa9dddb52015-02-09 13:42:45 +11001618 errorf(" %v", err)
1619 continue
Shawn Smith58980822014-03-05 14:49:33 -05001620 }
1621 if got := buf.String(); got != tt.want {
Nigel Taoa9dddb52015-02-09 13:42:45 +11001622 errorf("\ngot %v\nwant %v", got, tt.want)
1623 continue
Shawn Smith58980822014-03-05 14:49:33 -05001624 }
1625 }
1626}
Jason Del Ponte92440fb2014-05-12 23:35:56 -04001627
1628func TestProcInstEncodeToken(t *testing.T) {
1629 var buf bytes.Buffer
1630 enc := NewEncoder(&buf)
1631
1632 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
1633 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
1634 }
1635
1636 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
1637 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
1638 }
1639
1640 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
1641 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
1642 }
1643}
1644
1645func TestDecodeEncode(t *testing.T) {
1646 var in, out bytes.Buffer
1647 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
1648<?Target Instruction?>
1649<root>
1650</root>
1651`)
1652 dec := NewDecoder(&in)
1653 enc := NewEncoder(&out)
1654 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
1655 err = enc.EncodeToken(tok)
1656 if err != nil {
Rob Pike86bf6322014-05-16 13:18:28 -07001657 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
Jason Del Ponte92440fb2014-05-12 23:35:56 -04001658 }
1659 }
1660}
Roger Peppeb69ea012015-02-25 13:42:54 +00001661
1662// Issue 9796. Used to fail with GORACE="halt_on_error=1" -race.
1663func TestRace9796(t *testing.T) {
1664 type A struct{}
1665 type B struct {
1666 C []A `xml:"X>Y"`
1667 }
1668 var wg sync.WaitGroup
1669 for i := 0; i < 2; i++ {
1670 wg.Add(1)
1671 go func() {
1672 Marshal(B{[]A{A{}}})
1673 wg.Done()
1674 }()
1675 }
1676 wg.Wait()
1677}