]>
Commit | Line | Data |
---|---|---|
adb0401d ILT |
1 | // Copyright 2011 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
5 | package xml | |
6 | ||
7 | import ( | |
4ccad563 ILT |
8 | "bytes" |
9 | "errors" | |
be47d6ec | 10 | "fmt" |
4ccad563 | 11 | "io" |
9c63abc9 | 12 | "reflect" |
2fd401c8 | 13 | "strconv" |
9c63abc9 | 14 | "strings" |
af146490 | 15 | "sync" |
9c63abc9 | 16 | "testing" |
cbb6491d | 17 | "time" |
adb0401d ILT |
18 | ) |
19 | ||
20 | type DriveType int | |
21 | ||
22 | const ( | |
23 | HyperDrive DriveType = iota | |
24 | ImprobabilityDrive | |
25 | ) | |
26 | ||
27 | type Passenger struct { | |
28 | Name []string `xml:"name"` | |
29 | Weight float32 `xml:"weight"` | |
30 | } | |
31 | ||
32 | type Ship struct { | |
df1304ee | 33 | XMLName struct{} `xml:"spaceship"` |
adb0401d | 34 | |
df1304ee ILT |
35 | Name string `xml:"name,attr"` |
36 | Pilot string `xml:"pilot,attr"` | |
adb0401d ILT |
37 | Drive DriveType `xml:"drive"` |
38 | Age uint `xml:"age"` | |
39 | Passenger []*Passenger `xml:"passenger"` | |
40 | secret string | |
41 | } | |
42 | ||
adb0401d ILT |
43 | type NamedType string |
44 | ||
45 | type Port struct { | |
df1304ee | 46 | XMLName struct{} `xml:"port"` |
cbb6491d | 47 | Type string `xml:"type,attr,omitempty"` |
df1304ee ILT |
48 | Comment string `xml:",comment"` |
49 | Number string `xml:",chardata"` | |
adb0401d ILT |
50 | } |
51 | ||
52 | type Domain struct { | |
df1304ee | 53 | XMLName struct{} `xml:"domain"` |
cbb6491d | 54 | Country string `xml:",attr,omitempty"` |
df1304ee ILT |
55 | Name []byte `xml:",chardata"` |
56 | Comment []byte `xml:",comment"` | |
adb0401d ILT |
57 | } |
58 | ||
59 | type Book struct { | |
df1304ee ILT |
60 | XMLName struct{} `xml:"book"` |
61 | Title string `xml:",chardata"` | |
adb0401d ILT |
62 | } |
63 | ||
d6f2922e ILT |
64 | type Event struct { |
65 | XMLName struct{} `xml:"event"` | |
66 | Year int `xml:",chardata"` | |
67 | } | |
68 | ||
69 | type Movie struct { | |
70 | XMLName struct{} `xml:"movie"` | |
71 | Length uint `xml:",chardata"` | |
72 | } | |
73 | ||
74 | type Pi struct { | |
75 | XMLName struct{} `xml:"pi"` | |
76 | Approximation float32 `xml:",chardata"` | |
77 | } | |
78 | ||
79 | type Universe struct { | |
80 | XMLName struct{} `xml:"universe"` | |
81 | Visible float64 `xml:",chardata"` | |
82 | } | |
83 | ||
84 | type Particle struct { | |
85 | XMLName struct{} `xml:"particle"` | |
86 | HasMass bool `xml:",chardata"` | |
87 | } | |
88 | ||
89 | type Departure struct { | |
90 | XMLName struct{} `xml:"departure"` | |
91 | When time.Time `xml:",chardata"` | |
92 | } | |
93 | ||
adb0401d | 94 | type SecretAgent struct { |
df1304ee ILT |
95 | XMLName struct{} `xml:"agent"` |
96 | Handle string `xml:"handle,attr"` | |
adb0401d | 97 | Identity string |
df1304ee | 98 | Obfuscate string `xml:",innerxml"` |
adb0401d ILT |
99 | } |
100 | ||
d8f41257 | 101 | type NestedItems struct { |
df1304ee | 102 | XMLName struct{} `xml:"result"` |
d8f41257 ILT |
103 | Items []string `xml:">item"` |
104 | Item1 []string `xml:"Items>item1"` | |
105 | } | |
106 | ||
107 | type NestedOrder struct { | |
df1304ee ILT |
108 | XMLName struct{} `xml:"result"` |
109 | Field1 string `xml:"parent>c"` | |
110 | Field2 string `xml:"parent>b"` | |
111 | Field3 string `xml:"parent>a"` | |
d8f41257 ILT |
112 | } |
113 | ||
114 | type MixedNested struct { | |
df1304ee ILT |
115 | 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"` | |
d8f41257 ILT |
120 | } |
121 | ||
122 | type NilTest struct { | |
123 | A interface{} `xml:"parent1>parent2>a"` | |
124 | B interface{} `xml:"parent1>b"` | |
125 | C interface{} `xml:"parent1>parent2>c"` | |
126 | } | |
127 | ||
128 | type Service struct { | |
df1304ee ILT |
129 | XMLName struct{} `xml:"service"` |
130 | Domain *Domain `xml:"host>domain"` | |
131 | Port *Port `xml:"host>port"` | |
d8f41257 ILT |
132 | Extra1 interface{} |
133 | Extra2 interface{} `xml:"host>extra2"` | |
134 | } | |
135 | ||
adb0401d ILT |
136 | var nilStruct *Ship |
137 | ||
df1304ee ILT |
138 | type EmbedA struct { |
139 | EmbedC | |
140 | EmbedB EmbedB | |
141 | FieldA string | |
f5eb9a8e | 142 | embedD |
df1304ee ILT |
143 | } |
144 | ||
145 | type EmbedB struct { | |
146 | FieldB string | |
4ccad563 | 147 | *EmbedC |
df1304ee ILT |
148 | } |
149 | ||
150 | type EmbedC struct { | |
151 | FieldA1 string `xml:"FieldA>A1"` | |
152 | FieldA2 string `xml:"FieldA>A2"` | |
153 | FieldB string | |
154 | FieldC string | |
155 | } | |
156 | ||
f5eb9a8e ILT |
157 | type embedD struct { |
158 | fieldD string | |
159 | FieldE string // Promoted and visible when embedD is embedded. | |
160 | } | |
161 | ||
df1304ee ILT |
162 | type NameCasing struct { |
163 | XMLName struct{} `xml:"casing"` | |
164 | Xy string | |
165 | XY string | |
166 | XyA string `xml:"Xy,attr"` | |
167 | XYA string `xml:"XY,attr"` | |
168 | } | |
169 | ||
170 | type NamePrecedence struct { | |
171 | XMLName Name `xml:"Parent"` | |
172 | FromTag XMLNameWithoutTag `xml:"InTag"` | |
173 | FromNameVal XMLNameWithoutTag | |
174 | FromNameTag XMLNameWithTag | |
175 | InFieldName string | |
176 | } | |
177 | ||
178 | type XMLNameWithTag struct { | |
179 | XMLName Name `xml:"InXMLNameTag"` | |
593f74bb | 180 | Value string `xml:",chardata"` |
df1304ee ILT |
181 | } |
182 | ||
183 | type XMLNameWithoutTag struct { | |
184 | XMLName Name | |
593f74bb | 185 | Value string `xml:",chardata"` |
df1304ee ILT |
186 | } |
187 | ||
af92e385 ILT |
188 | type NameInField struct { |
189 | Foo Name `xml:"ns foo"` | |
190 | } | |
191 | ||
df1304ee ILT |
192 | type AttrTest struct { |
193 | Int int `xml:",attr"` | |
cbb6491d | 194 | Named int `xml:"int,attr"` |
df1304ee ILT |
195 | Float float64 `xml:",attr"` |
196 | Uint8 uint8 `xml:",attr"` | |
197 | Bool bool `xml:",attr"` | |
198 | Str string `xml:",attr"` | |
cbb6491d ILT |
199 | Bytes []byte `xml:",attr"` |
200 | } | |
201 | ||
c2047754 ILT |
202 | type AttrsTest struct { |
203 | Attrs []Attr `xml:",any,attr"` | |
204 | Int int `xml:",attr"` | |
205 | Named int `xml:"int,attr"` | |
206 | Float float64 `xml:",attr"` | |
207 | Uint8 uint8 `xml:",attr"` | |
208 | Bool bool `xml:",attr"` | |
209 | Str string `xml:",attr"` | |
210 | Bytes []byte `xml:",attr"` | |
211 | } | |
212 | ||
cbb6491d ILT |
213 | type OmitAttrTest struct { |
214 | Int int `xml:",attr,omitempty"` | |
215 | Named int `xml:"int,attr,omitempty"` | |
216 | Float float64 `xml:",attr,omitempty"` | |
217 | Uint8 uint8 `xml:",attr,omitempty"` | |
218 | Bool bool `xml:",attr,omitempty"` | |
219 | Str string `xml:",attr,omitempty"` | |
220 | Bytes []byte `xml:",attr,omitempty"` | |
c2047754 | 221 | PStr *string `xml:",attr,omitempty"` |
cbb6491d ILT |
222 | } |
223 | ||
224 | type OmitFieldTest struct { | |
225 | Int int `xml:",omitempty"` | |
226 | Named int `xml:"int,omitempty"` | |
227 | Float float64 `xml:",omitempty"` | |
228 | Uint8 uint8 `xml:",omitempty"` | |
229 | Bool bool `xml:",omitempty"` | |
230 | Str string `xml:",omitempty"` | |
231 | Bytes []byte `xml:",omitempty"` | |
c2047754 | 232 | PStr *string `xml:",omitempty"` |
cbb6491d | 233 | Ptr *PresenceTest `xml:",omitempty"` |
df1304ee ILT |
234 | } |
235 | ||
236 | type AnyTest struct { | |
237 | XMLName struct{} `xml:"a"` | |
238 | Nested string `xml:"nested>value"` | |
239 | AnyField AnyHolder `xml:",any"` | |
240 | } | |
241 | ||
d6f2922e ILT |
242 | type AnyOmitTest struct { |
243 | XMLName struct{} `xml:"a"` | |
244 | Nested string `xml:"nested>value"` | |
245 | AnyField *AnyHolder `xml:",any,omitempty"` | |
246 | } | |
247 | ||
248 | type AnySliceTest struct { | |
249 | XMLName struct{} `xml:"a"` | |
250 | Nested string `xml:"nested>value"` | |
251 | AnyField []AnyHolder `xml:",any"` | |
252 | } | |
253 | ||
df1304ee ILT |
254 | type AnyHolder struct { |
255 | XMLName Name | |
256 | XML string `xml:",innerxml"` | |
257 | } | |
258 | ||
259 | type RecurseA struct { | |
260 | A string | |
261 | B *RecurseB | |
262 | } | |
263 | ||
264 | type RecurseB struct { | |
265 | A *RecurseA | |
266 | B string | |
267 | } | |
268 | ||
9af4cb95 ILT |
269 | type PresenceTest struct { |
270 | Exists *struct{} | |
271 | } | |
272 | ||
273 | type IgnoreTest struct { | |
274 | PublicSecret string `xml:"-"` | |
275 | } | |
276 | ||
277 | type MyBytes []byte | |
278 | ||
279 | type Data struct { | |
280 | Bytes []byte | |
281 | Attr []byte `xml:",attr"` | |
282 | Custom MyBytes | |
283 | } | |
284 | ||
df1304ee ILT |
285 | type Plain struct { |
286 | V interface{} | |
287 | } | |
288 | ||
be47d6ec ILT |
289 | type MyInt int |
290 | ||
291 | type EmbedInt struct { | |
292 | MyInt | |
293 | } | |
294 | ||
295 | type Strings struct { | |
296 | X []string `xml:"A>B,omitempty"` | |
297 | } | |
298 | ||
f038dae6 ILT |
299 | type PointerFieldsTest struct { |
300 | XMLName Name `xml:"dummy"` | |
301 | Name *string `xml:"name,attr"` | |
302 | Age *uint `xml:"age,attr"` | |
303 | Empty *string `xml:"empty,attr"` | |
304 | Contents *string `xml:",chardata"` | |
305 | } | |
306 | ||
307 | type ChardataEmptyTest struct { | |
308 | XMLName Name `xml:"test"` | |
309 | Contents *string `xml:",chardata"` | |
310 | } | |
311 | ||
312 | type MyMarshalerTest struct { | |
313 | } | |
314 | ||
315 | var _ Marshaler = (*MyMarshalerTest)(nil) | |
316 | ||
317 | func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error { | |
318 | e.EncodeToken(start) | |
319 | e.EncodeToken(CharData([]byte("hello world"))) | |
320 | e.EncodeToken(EndElement{start.Name}) | |
321 | return nil | |
322 | } | |
323 | ||
324 | type MyMarshalerAttrTest struct { | |
325 | } | |
326 | ||
327 | var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil) | |
328 | ||
329 | func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) { | |
330 | return Attr{name, "hello world"}, nil | |
331 | } | |
332 | ||
c2047754 ILT |
333 | func (m *MyMarshalerAttrTest) UnmarshalXMLAttr(attr Attr) error { |
334 | return nil | |
335 | } | |
336 | ||
f038dae6 ILT |
337 | type MarshalerStruct struct { |
338 | Foo MyMarshalerAttrTest `xml:",attr"` | |
339 | } | |
340 | ||
00d86ac9 ILT |
341 | type InnerStruct struct { |
342 | XMLName Name `xml:"testns outer"` | |
343 | } | |
344 | ||
345 | type OuterStruct struct { | |
346 | InnerStruct | |
347 | IntAttr int `xml:"int,attr"` | |
348 | } | |
349 | ||
350 | type OuterNamedStruct struct { | |
351 | InnerStruct | |
352 | XMLName Name `xml:"outerns test"` | |
353 | IntAttr int `xml:"int,attr"` | |
354 | } | |
355 | ||
356 | type OuterNamedOrderedStruct struct { | |
357 | XMLName Name `xml:"outerns test"` | |
358 | InnerStruct | |
359 | IntAttr int `xml:"int,attr"` | |
360 | } | |
361 | ||
362 | type OuterOuterStruct struct { | |
363 | OuterStruct | |
364 | } | |
365 | ||
af146490 ILT |
366 | type NestedAndChardata struct { |
367 | AB []string `xml:"A>B"` | |
368 | Chardata string `xml:",chardata"` | |
369 | } | |
370 | ||
371 | type NestedAndComment struct { | |
372 | AB []string `xml:"A>B"` | |
373 | Comment string `xml:",comment"` | |
374 | } | |
375 | ||
f98dd1a3 ILT |
376 | type CDataTest struct { |
377 | Chardata string `xml:",cdata"` | |
378 | } | |
379 | ||
380 | type NestedAndCData struct { | |
381 | AB []string `xml:"A>B"` | |
382 | CDATA string `xml:",cdata"` | |
383 | } | |
384 | ||
f038dae6 ILT |
385 | func ifaceptr(x interface{}) interface{} { |
386 | return &x | |
387 | } | |
388 | ||
00b2a30f ILT |
389 | func stringptr(x string) *string { |
390 | return &x | |
391 | } | |
392 | ||
393 | type T1 struct{} | |
394 | type T2 struct{} | |
00b2a30f ILT |
395 | |
396 | type IndirComment struct { | |
397 | T1 T1 | |
398 | Comment *string `xml:",comment"` | |
399 | T2 T2 | |
400 | } | |
401 | ||
402 | type DirectComment struct { | |
403 | T1 T1 | |
404 | Comment string `xml:",comment"` | |
405 | T2 T2 | |
406 | } | |
407 | ||
408 | type IfaceComment struct { | |
409 | T1 T1 | |
410 | Comment interface{} `xml:",comment"` | |
411 | T2 T2 | |
412 | } | |
413 | ||
414 | type IndirChardata struct { | |
415 | T1 T1 | |
416 | Chardata *string `xml:",chardata"` | |
417 | T2 T2 | |
418 | } | |
419 | ||
420 | type DirectChardata struct { | |
421 | T1 T1 | |
422 | Chardata string `xml:",chardata"` | |
423 | T2 T2 | |
424 | } | |
425 | ||
426 | type IfaceChardata struct { | |
427 | T1 T1 | |
428 | Chardata interface{} `xml:",chardata"` | |
429 | T2 T2 | |
430 | } | |
431 | ||
432 | type IndirCDATA struct { | |
433 | T1 T1 | |
434 | CDATA *string `xml:",cdata"` | |
435 | T2 T2 | |
436 | } | |
437 | ||
438 | type DirectCDATA struct { | |
439 | T1 T1 | |
440 | CDATA string `xml:",cdata"` | |
441 | T2 T2 | |
442 | } | |
443 | ||
444 | type IfaceCDATA struct { | |
445 | T1 T1 | |
446 | CDATA interface{} `xml:",cdata"` | |
447 | T2 T2 | |
448 | } | |
449 | ||
450 | type IndirInnerXML struct { | |
451 | T1 T1 | |
452 | InnerXML *string `xml:",innerxml"` | |
453 | T2 T2 | |
454 | } | |
455 | ||
456 | type DirectInnerXML struct { | |
457 | T1 T1 | |
458 | InnerXML string `xml:",innerxml"` | |
459 | T2 T2 | |
460 | } | |
461 | ||
462 | type IfaceInnerXML struct { | |
463 | T1 T1 | |
464 | InnerXML interface{} `xml:",innerxml"` | |
465 | T2 T2 | |
466 | } | |
467 | ||
468 | type IndirElement struct { | |
469 | T1 T1 | |
470 | Element *string | |
471 | T2 T2 | |
472 | } | |
473 | ||
474 | type DirectElement struct { | |
475 | T1 T1 | |
476 | Element string | |
477 | T2 T2 | |
478 | } | |
479 | ||
480 | type IfaceElement struct { | |
481 | T1 T1 | |
482 | Element interface{} | |
483 | T2 T2 | |
484 | } | |
485 | ||
486 | type IndirOmitEmpty struct { | |
487 | T1 T1 | |
488 | OmitEmpty *string `xml:",omitempty"` | |
489 | T2 T2 | |
490 | } | |
491 | ||
492 | type DirectOmitEmpty struct { | |
493 | T1 T1 | |
494 | OmitEmpty string `xml:",omitempty"` | |
495 | T2 T2 | |
496 | } | |
497 | ||
498 | type IfaceOmitEmpty struct { | |
499 | T1 T1 | |
500 | OmitEmpty interface{} `xml:",omitempty"` | |
501 | T2 T2 | |
502 | } | |
503 | ||
504 | type IndirAny struct { | |
505 | T1 T1 | |
506 | Any *string `xml:",any"` | |
507 | T2 T2 | |
508 | } | |
509 | ||
510 | type DirectAny struct { | |
511 | T1 T1 | |
512 | Any string `xml:",any"` | |
513 | T2 T2 | |
514 | } | |
515 | ||
516 | type IfaceAny struct { | |
517 | T1 T1 | |
518 | Any interface{} `xml:",any"` | |
519 | T2 T2 | |
520 | } | |
521 | ||
f038dae6 ILT |
522 | var ( |
523 | nameAttr = "Sarah" | |
524 | ageAttr = uint(12) | |
525 | contentsAttr = "lorem ipsum" | |
c2047754 | 526 | empty = "" |
f038dae6 ILT |
527 | ) |
528 | ||
df1304ee ILT |
529 | // Unless explicitly stated as such (or *Plain), all of the |
530 | // tests below are two-way tests. When introducing new tests, | |
531 | // please try to make them two-way as well to ensure that | |
c2047754 | 532 | // marshaling and unmarshaling are as symmetrical as feasible. |
adb0401d | 533 | var marshalTests = []struct { |
00b2a30f ILT |
534 | Value interface{} |
535 | ExpectXML string | |
536 | MarshalOnly bool | |
537 | MarshalError string | |
538 | UnmarshalOnly bool | |
539 | UnmarshalError string | |
adb0401d ILT |
540 | }{ |
541 | // Test nil marshals to nothing | |
df1304ee ILT |
542 | {Value: nil, ExpectXML: ``, MarshalOnly: true}, |
543 | {Value: nilStruct, ExpectXML: ``, MarshalOnly: true}, | |
544 | ||
545 | // Test value types | |
546 | {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`}, | |
547 | {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`}, | |
548 | {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
549 | {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
550 | {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
551 | {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
552 | {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
553 | {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
554 | {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
555 | {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, | |
556 | {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, | |
557 | {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, | |
558 | {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`}, | |
559 | {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, | |
560 | {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, | |
561 | {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`}, | |
562 | {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`}, | |
563 | {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`}, | |
564 | {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`}, | |
565 | {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, | |
566 | {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, | |
f038dae6 | 567 | {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`}, |
adb0401d | 568 | |
cbb6491d ILT |
569 | // Test time. |
570 | { | |
571 | Value: &Plain{time.Unix(1e9, 123456789).UTC()}, | |
572 | ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`, | |
573 | }, | |
574 | ||
9af4cb95 ILT |
575 | // A pointer to struct{} may be used to test for an element's presence. |
576 | { | |
577 | Value: &PresenceTest{new(struct{})}, | |
578 | ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`, | |
579 | }, | |
580 | { | |
581 | Value: &PresenceTest{}, | |
582 | ExpectXML: `<PresenceTest></PresenceTest>`, | |
583 | }, | |
584 | ||
9af4cb95 ILT |
585 | // A []byte field is only nil if the element was not found. |
586 | { | |
587 | Value: &Data{}, | |
588 | ExpectXML: `<Data></Data>`, | |
589 | UnmarshalOnly: true, | |
590 | }, | |
591 | { | |
592 | Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}}, | |
593 | ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`, | |
594 | UnmarshalOnly: true, | |
595 | }, | |
596 | ||
597 | // Check that []byte works, including named []byte types. | |
598 | { | |
599 | Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}}, | |
600 | ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`, | |
601 | }, | |
602 | ||
adb0401d | 603 | // Test innerxml |
adb0401d ILT |
604 | { |
605 | Value: &SecretAgent{ | |
606 | Handle: "007", | |
607 | Identity: "James Bond", | |
608 | Obfuscate: "<redacted/>", | |
609 | }, | |
df1304ee ILT |
610 | ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, |
611 | MarshalOnly: true, | |
612 | }, | |
613 | { | |
614 | Value: &SecretAgent{ | |
615 | Handle: "007", | |
616 | Identity: "James Bond", | |
617 | Obfuscate: "<Identity>James Bond</Identity><redacted/>", | |
618 | }, | |
619 | ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, | |
620 | UnmarshalOnly: true, | |
621 | }, | |
622 | ||
adb0401d ILT |
623 | // Test structs |
624 | {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`}, | |
625 | {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`}, | |
626 | {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`}, | |
df1304ee ILT |
627 | {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`}, |
628 | {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true}, | |
adb0401d | 629 | {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`}, |
df1304ee | 630 | {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`}, |
adb0401d | 631 | {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`}, |
d6f2922e ILT |
632 | {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`}, |
633 | {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`}, | |
634 | {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`}, | |
635 | {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`}, | |
636 | {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`}, | |
637 | {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`}, | |
1a2f01ef | 638 | {Value: atomValue, ExpectXML: atomXML}, |
adb0401d ILT |
639 | { |
640 | Value: &Ship{ | |
641 | Name: "Heart of Gold", | |
642 | Pilot: "Computer", | |
643 | Age: 1, | |
644 | Drive: ImprobabilityDrive, | |
645 | Passenger: []*Passenger{ | |
d5363590 | 646 | { |
adb0401d ILT |
647 | Name: []string{"Zaphod", "Beeblebrox"}, |
648 | Weight: 7.25, | |
649 | }, | |
d5363590 | 650 | { |
adb0401d ILT |
651 | Name: []string{"Trisha", "McMillen"}, |
652 | Weight: 5.5, | |
653 | }, | |
d5363590 | 654 | { |
adb0401d ILT |
655 | Name: []string{"Ford", "Prefect"}, |
656 | Weight: 7, | |
657 | }, | |
d5363590 | 658 | { |
adb0401d ILT |
659 | Name: []string{"Arthur", "Dent"}, |
660 | Weight: 6.75, | |
661 | }, | |
662 | }, | |
663 | }, | |
664 | ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` + | |
665 | `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` + | |
666 | `<age>1</age>` + | |
667 | `<passenger>` + | |
668 | `<name>Zaphod</name>` + | |
669 | `<name>Beeblebrox</name>` + | |
670 | `<weight>7.25</weight>` + | |
671 | `</passenger>` + | |
672 | `<passenger>` + | |
673 | `<name>Trisha</name>` + | |
674 | `<name>McMillen</name>` + | |
675 | `<weight>5.5</weight>` + | |
676 | `</passenger>` + | |
677 | `<passenger>` + | |
678 | `<name>Ford</name>` + | |
679 | `<name>Prefect</name>` + | |
680 | `<weight>7</weight>` + | |
681 | `</passenger>` + | |
682 | `<passenger>` + | |
683 | `<name>Arthur</name>` + | |
684 | `<name>Dent</name>` + | |
685 | `<weight>6.75</weight>` + | |
686 | `</passenger>` + | |
687 | `</spaceship>`, | |
688 | }, | |
df1304ee | 689 | |
d8f41257 ILT |
690 | // Test a>b |
691 | { | |
df1304ee ILT |
692 | Value: &NestedItems{Items: nil, Item1: nil}, |
693 | ExpectXML: `<result>` + | |
694 | `<Items>` + | |
695 | `</Items>` + | |
696 | `</result>`, | |
697 | }, | |
698 | { | |
699 | Value: &NestedItems{Items: []string{}, Item1: []string{}}, | |
d8f41257 ILT |
700 | ExpectXML: `<result>` + |
701 | `<Items>` + | |
702 | `</Items>` + | |
703 | `</result>`, | |
df1304ee | 704 | MarshalOnly: true, |
d8f41257 ILT |
705 | }, |
706 | { | |
df1304ee | 707 | Value: &NestedItems{Items: nil, Item1: []string{"A"}}, |
d8f41257 ILT |
708 | ExpectXML: `<result>` + |
709 | `<Items>` + | |
710 | `<item1>A</item1>` + | |
711 | `</Items>` + | |
712 | `</result>`, | |
713 | }, | |
714 | { | |
df1304ee | 715 | Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil}, |
d8f41257 ILT |
716 | ExpectXML: `<result>` + |
717 | `<Items>` + | |
718 | `<item>A</item>` + | |
719 | `<item>B</item>` + | |
720 | `</Items>` + | |
721 | `</result>`, | |
722 | }, | |
723 | { | |
df1304ee | 724 | Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}}, |
d8f41257 ILT |
725 | ExpectXML: `<result>` + |
726 | `<Items>` + | |
727 | `<item>A</item>` + | |
728 | `<item>B</item>` + | |
729 | `<item1>C</item1>` + | |
730 | `</Items>` + | |
731 | `</result>`, | |
732 | }, | |
733 | { | |
df1304ee | 734 | Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"}, |
d8f41257 ILT |
735 | ExpectXML: `<result>` + |
736 | `<parent>` + | |
737 | `<c>C</c>` + | |
738 | `<b>B</b>` + | |
739 | `<a>A</a>` + | |
740 | `</parent>` + | |
741 | `</result>`, | |
742 | }, | |
743 | { | |
df1304ee ILT |
744 | Value: &NilTest{A: "A", B: nil, C: "C"}, |
745 | ExpectXML: `<NilTest>` + | |
d8f41257 ILT |
746 | `<parent1>` + |
747 | `<parent2><a>A</a></parent2>` + | |
748 | `<parent2><c>C</c></parent2>` + | |
749 | `</parent1>` + | |
df1304ee ILT |
750 | `</NilTest>`, |
751 | MarshalOnly: true, // Uses interface{} | |
d8f41257 ILT |
752 | }, |
753 | { | |
df1304ee | 754 | Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"}, |
d8f41257 ILT |
755 | ExpectXML: `<result>` + |
756 | `<parent1><a>A</a></parent1>` + | |
757 | `<b>B</b>` + | |
758 | `<parent1>` + | |
759 | `<parent2><c>C</c></parent2>` + | |
760 | `<d>D</d>` + | |
761 | `</parent1>` + | |
762 | `</result>`, | |
763 | }, | |
764 | { | |
df1304ee | 765 | Value: &Service{Port: &Port{Number: "80"}}, |
d8f41257 ILT |
766 | ExpectXML: `<service><host><port>80</port></host></service>`, |
767 | }, | |
768 | { | |
df1304ee | 769 | Value: &Service{}, |
d8f41257 ILT |
770 | ExpectXML: `<service></service>`, |
771 | }, | |
772 | { | |
df1304ee | 773 | Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"}, |
d8f41257 ILT |
774 | ExpectXML: `<service>` + |
775 | `<host><port>80</port></host>` + | |
776 | `<Extra1>A</Extra1>` + | |
777 | `<host><extra2>B</extra2></host>` + | |
778 | `</service>`, | |
df1304ee | 779 | MarshalOnly: true, |
d8f41257 ILT |
780 | }, |
781 | { | |
df1304ee | 782 | Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"}, |
d8f41257 ILT |
783 | ExpectXML: `<service>` + |
784 | `<host><port>80</port></host>` + | |
785 | `<host><extra2>example</extra2></host>` + | |
786 | `</service>`, | |
df1304ee ILT |
787 | MarshalOnly: true, |
788 | }, | |
af146490 ILT |
789 | { |
790 | Value: &struct { | |
791 | XMLName struct{} `xml:"space top"` | |
792 | A string `xml:"x>a"` | |
793 | B string `xml:"x>b"` | |
794 | C string `xml:"space x>c"` | |
795 | C1 string `xml:"space1 x>c"` | |
796 | D1 string `xml:"space1 x>d"` | |
797 | }{ | |
798 | A: "a", | |
799 | B: "b", | |
800 | C: "c", | |
801 | C1: "c1", | |
802 | D1: "d1", | |
803 | }, | |
804 | ExpectXML: `<top xmlns="space">` + | |
805 | `<x><a>a</a><b>b</b><c xmlns="space">c</c>` + | |
806 | `<c xmlns="space1">c1</c>` + | |
807 | `<d xmlns="space1">d1</d>` + | |
808 | `</x>` + | |
809 | `</top>`, | |
810 | }, | |
811 | { | |
812 | Value: &struct { | |
813 | XMLName Name | |
814 | A string `xml:"x>a"` | |
815 | B string `xml:"x>b"` | |
816 | C string `xml:"space x>c"` | |
817 | C1 string `xml:"space1 x>c"` | |
818 | D1 string `xml:"space1 x>d"` | |
819 | }{ | |
820 | XMLName: Name{ | |
821 | Space: "space0", | |
822 | Local: "top", | |
823 | }, | |
824 | A: "a", | |
825 | B: "b", | |
826 | C: "c", | |
827 | C1: "c1", | |
828 | D1: "d1", | |
829 | }, | |
830 | ExpectXML: `<top xmlns="space0">` + | |
831 | `<x><a>a</a><b>b</b>` + | |
832 | `<c xmlns="space">c</c>` + | |
833 | `<c xmlns="space1">c1</c>` + | |
834 | `<d xmlns="space1">d1</d>` + | |
835 | `</x>` + | |
836 | `</top>`, | |
837 | }, | |
838 | { | |
839 | Value: &struct { | |
840 | XMLName struct{} `xml:"top"` | |
841 | B string `xml:"space x>b"` | |
842 | B1 string `xml:"space1 x>b"` | |
843 | }{ | |
844 | B: "b", | |
845 | B1: "b1", | |
846 | }, | |
847 | ExpectXML: `<top>` + | |
848 | `<x><b xmlns="space">b</b>` + | |
849 | `<b xmlns="space1">b1</b></x>` + | |
850 | `</top>`, | |
851 | }, | |
df1304ee ILT |
852 | |
853 | // Test struct embedding | |
854 | { | |
855 | Value: &EmbedA{ | |
856 | EmbedC: EmbedC{ | |
857 | FieldA1: "", // Shadowed by A.A | |
858 | FieldA2: "", // Shadowed by A.A | |
859 | FieldB: "A.C.B", | |
860 | FieldC: "A.C.C", | |
861 | }, | |
862 | EmbedB: EmbedB{ | |
863 | FieldB: "A.B.B", | |
4ccad563 | 864 | EmbedC: &EmbedC{ |
df1304ee ILT |
865 | FieldA1: "A.B.C.A1", |
866 | FieldA2: "A.B.C.A2", | |
867 | FieldB: "", // Shadowed by A.B.B | |
868 | FieldC: "A.B.C.C", | |
869 | }, | |
870 | }, | |
871 | FieldA: "A.A", | |
f5eb9a8e ILT |
872 | embedD: embedD{ |
873 | FieldE: "A.D.E", | |
874 | }, | |
df1304ee ILT |
875 | }, |
876 | ExpectXML: `<EmbedA>` + | |
877 | `<FieldB>A.C.B</FieldB>` + | |
878 | `<FieldC>A.C.C</FieldC>` + | |
879 | `<EmbedB>` + | |
880 | `<FieldB>A.B.B</FieldB>` + | |
881 | `<FieldA>` + | |
882 | `<A1>A.B.C.A1</A1>` + | |
883 | `<A2>A.B.C.A2</A2>` + | |
884 | `</FieldA>` + | |
885 | `<FieldC>A.B.C.C</FieldC>` + | |
886 | `</EmbedB>` + | |
887 | `<FieldA>A.A</FieldA>` + | |
f5eb9a8e | 888 | `<FieldE>A.D.E</FieldE>` + |
df1304ee ILT |
889 | `</EmbedA>`, |
890 | }, | |
891 | ||
892 | // Test that name casing matters | |
893 | { | |
894 | Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"}, | |
895 | ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`, | |
896 | }, | |
897 | ||
898 | // Test the order in which the XML element name is chosen | |
899 | { | |
900 | Value: &NamePrecedence{ | |
901 | FromTag: XMLNameWithoutTag{Value: "A"}, | |
902 | FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"}, | |
903 | FromNameTag: XMLNameWithTag{Value: "C"}, | |
904 | InFieldName: "D", | |
905 | }, | |
906 | ExpectXML: `<Parent>` + | |
593f74bb ILT |
907 | `<InTag>A</InTag>` + |
908 | `<InXMLName>B</InXMLName>` + | |
909 | `<InXMLNameTag>C</InXMLNameTag>` + | |
df1304ee ILT |
910 | `<InFieldName>D</InFieldName>` + |
911 | `</Parent>`, | |
912 | MarshalOnly: true, | |
913 | }, | |
914 | { | |
915 | Value: &NamePrecedence{ | |
916 | XMLName: Name{Local: "Parent"}, | |
917 | FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"}, | |
918 | FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"}, | |
919 | FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"}, | |
920 | InFieldName: "D", | |
921 | }, | |
922 | ExpectXML: `<Parent>` + | |
593f74bb ILT |
923 | `<InTag>A</InTag>` + |
924 | `<FromNameVal>B</FromNameVal>` + | |
925 | `<InXMLNameTag>C</InXMLNameTag>` + | |
df1304ee ILT |
926 | `<InFieldName>D</InFieldName>` + |
927 | `</Parent>`, | |
928 | UnmarshalOnly: true, | |
929 | }, | |
930 | ||
af92e385 ILT |
931 | // xml.Name works in a plain field as well. |
932 | { | |
933 | Value: &NameInField{Name{Space: "ns", Local: "foo"}}, | |
934 | ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, | |
935 | }, | |
94252f4b ILT |
936 | { |
937 | Value: &NameInField{Name{Space: "ns", Local: "foo"}}, | |
938 | ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`, | |
939 | UnmarshalOnly: true, | |
940 | }, | |
af92e385 ILT |
941 | |
942 | // Marshaling zero xml.Name uses the tag or field name. | |
943 | { | |
944 | Value: &NameInField{}, | |
945 | ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, | |
946 | MarshalOnly: true, | |
947 | }, | |
948 | ||
df1304ee ILT |
949 | // Test attributes |
950 | { | |
951 | Value: &AttrTest{ | |
952 | Int: 8, | |
cbb6491d ILT |
953 | Named: 9, |
954 | Float: 23.5, | |
955 | Uint8: 255, | |
956 | Bool: true, | |
957 | Str: "str", | |
958 | Bytes: []byte("byt"), | |
959 | }, | |
960 | ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` + | |
961 | ` Bool="true" Str="str" Bytes="byt"></AttrTest>`, | |
962 | }, | |
963 | { | |
964 | Value: &AttrTest{Bytes: []byte{}}, | |
965 | ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` + | |
966 | ` Bool="false" Str="" Bytes=""></AttrTest>`, | |
967 | }, | |
c2047754 ILT |
968 | { |
969 | Value: &AttrsTest{ | |
970 | Attrs: []Attr{ | |
971 | {Name: Name{Local: "Answer"}, Value: "42"}, | |
972 | {Name: Name{Local: "Int"}, Value: "8"}, | |
973 | {Name: Name{Local: "int"}, Value: "9"}, | |
974 | {Name: Name{Local: "Float"}, Value: "23.5"}, | |
975 | {Name: Name{Local: "Uint8"}, Value: "255"}, | |
976 | {Name: Name{Local: "Bool"}, Value: "true"}, | |
977 | {Name: Name{Local: "Str"}, Value: "str"}, | |
978 | {Name: Name{Local: "Bytes"}, Value: "byt"}, | |
979 | }, | |
980 | }, | |
981 | ExpectXML: `<AttrsTest Answer="42" Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="str" Bytes="byt" Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes=""></AttrsTest>`, | |
982 | MarshalOnly: true, | |
983 | }, | |
984 | { | |
985 | Value: &AttrsTest{ | |
986 | Attrs: []Attr{ | |
987 | {Name: Name{Local: "Answer"}, Value: "42"}, | |
988 | }, | |
989 | Int: 8, | |
990 | Named: 9, | |
991 | Float: 23.5, | |
992 | Uint8: 255, | |
993 | Bool: true, | |
994 | Str: "str", | |
995 | Bytes: []byte("byt"), | |
996 | }, | |
997 | ExpectXML: `<AttrsTest Answer="42" Int="8" int="9" Float="23.5" Uint8="255" Bool="true" Str="str" Bytes="byt"></AttrsTest>`, | |
998 | }, | |
999 | { | |
1000 | Value: &AttrsTest{ | |
1001 | Attrs: []Attr{ | |
1002 | {Name: Name{Local: "Int"}, Value: "0"}, | |
1003 | {Name: Name{Local: "int"}, Value: "0"}, | |
1004 | {Name: Name{Local: "Float"}, Value: "0"}, | |
1005 | {Name: Name{Local: "Uint8"}, Value: "0"}, | |
1006 | {Name: Name{Local: "Bool"}, Value: "false"}, | |
1007 | {Name: Name{Local: "Str"}}, | |
1008 | {Name: Name{Local: "Bytes"}}, | |
1009 | }, | |
1010 | Bytes: []byte{}, | |
1011 | }, | |
1012 | ExpectXML: `<AttrsTest Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes="" Int="0" int="0" Float="0" Uint8="0" Bool="false" Str="" Bytes=""></AttrsTest>`, | |
1013 | MarshalOnly: true, | |
1014 | }, | |
cbb6491d ILT |
1015 | { |
1016 | Value: &OmitAttrTest{ | |
1017 | Int: 8, | |
1018 | Named: 9, | |
1019 | Float: 23.5, | |
1020 | Uint8: 255, | |
1021 | Bool: true, | |
1022 | Str: "str", | |
1023 | Bytes: []byte("byt"), | |
c2047754 | 1024 | PStr: &empty, |
cbb6491d ILT |
1025 | }, |
1026 | ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` + | |
c2047754 | 1027 | ` Bool="true" Str="str" Bytes="byt" PStr=""></OmitAttrTest>`, |
cbb6491d ILT |
1028 | }, |
1029 | { | |
1030 | Value: &OmitAttrTest{}, | |
1031 | ExpectXML: `<OmitAttrTest></OmitAttrTest>`, | |
1032 | }, | |
1033 | ||
f038dae6 ILT |
1034 | // pointer fields |
1035 | { | |
1036 | Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr}, | |
1037 | ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`, | |
1038 | MarshalOnly: true, | |
1039 | }, | |
1040 | ||
1041 | // empty chardata pointer field | |
1042 | { | |
1043 | Value: &ChardataEmptyTest{}, | |
1044 | ExpectXML: `<test></test>`, | |
1045 | MarshalOnly: true, | |
1046 | }, | |
1047 | ||
cbb6491d ILT |
1048 | // omitempty on fields |
1049 | { | |
1050 | Value: &OmitFieldTest{ | |
1051 | Int: 8, | |
1052 | Named: 9, | |
df1304ee ILT |
1053 | Float: 23.5, |
1054 | Uint8: 255, | |
1055 | Bool: true, | |
cbb6491d ILT |
1056 | Str: "str", |
1057 | Bytes: []byte("byt"), | |
c2047754 | 1058 | PStr: &empty, |
cbb6491d | 1059 | Ptr: &PresenceTest{}, |
df1304ee | 1060 | }, |
cbb6491d ILT |
1061 | ExpectXML: `<OmitFieldTest>` + |
1062 | `<Int>8</Int>` + | |
1063 | `<int>9</int>` + | |
1064 | `<Float>23.5</Float>` + | |
1065 | `<Uint8>255</Uint8>` + | |
1066 | `<Bool>true</Bool>` + | |
1067 | `<Str>str</Str>` + | |
1068 | `<Bytes>byt</Bytes>` + | |
c2047754 | 1069 | `<PStr></PStr>` + |
cbb6491d ILT |
1070 | `<Ptr></Ptr>` + |
1071 | `</OmitFieldTest>`, | |
1072 | }, | |
1073 | { | |
1074 | Value: &OmitFieldTest{}, | |
1075 | ExpectXML: `<OmitFieldTest></OmitFieldTest>`, | |
df1304ee ILT |
1076 | }, |
1077 | ||
1078 | // Test ",any" | |
1079 | { | |
1080 | ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`, | |
1081 | Value: &AnyTest{ | |
1082 | Nested: "known", | |
1083 | AnyField: AnyHolder{ | |
1084 | XMLName: Name{Local: "other"}, | |
1085 | XML: "<sub>unknown</sub>", | |
1086 | }, | |
1087 | }, | |
df1304ee ILT |
1088 | }, |
1089 | { | |
d6f2922e ILT |
1090 | Value: &AnyTest{Nested: "known", |
1091 | AnyField: AnyHolder{ | |
1092 | XML: "<unknown/>", | |
1093 | XMLName: Name{Local: "AnyField"}, | |
1094 | }, | |
1095 | }, | |
1096 | ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`, | |
1097 | }, | |
1098 | { | |
1099 | ExpectXML: `<a><nested><value>b</value></nested></a>`, | |
1100 | Value: &AnyOmitTest{ | |
1101 | Nested: "b", | |
1102 | }, | |
1103 | }, | |
1104 | { | |
1105 | ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`, | |
1106 | Value: &AnySliceTest{ | |
1107 | Nested: "b", | |
1108 | AnyField: []AnyHolder{ | |
1109 | { | |
1110 | XMLName: Name{Local: "c"}, | |
1111 | XML: "<d>e</d>", | |
1112 | }, | |
1113 | { | |
1114 | XMLName: Name{Space: "f", Local: "g"}, | |
1115 | XML: "<h>i</h>", | |
1116 | }, | |
1117 | }, | |
1118 | }, | |
1119 | }, | |
1120 | { | |
1121 | ExpectXML: `<a><nested><value>b</value></nested></a>`, | |
1122 | Value: &AnySliceTest{ | |
1123 | Nested: "b", | |
1124 | }, | |
df1304ee ILT |
1125 | }, |
1126 | ||
1127 | // Test recursive types. | |
1128 | { | |
1129 | Value: &RecurseA{ | |
1130 | A: "a1", | |
1131 | B: &RecurseB{ | |
1132 | A: &RecurseA{"a2", nil}, | |
1133 | B: "b1", | |
1134 | }, | |
1135 | }, | |
1136 | ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`, | |
d8f41257 | 1137 | }, |
9af4cb95 ILT |
1138 | |
1139 | // Test ignoring fields via "-" tag | |
1140 | { | |
1141 | ExpectXML: `<IgnoreTest></IgnoreTest>`, | |
1142 | Value: &IgnoreTest{}, | |
1143 | }, | |
1144 | { | |
1145 | ExpectXML: `<IgnoreTest></IgnoreTest>`, | |
1146 | Value: &IgnoreTest{PublicSecret: "can't tell"}, | |
1147 | MarshalOnly: true, | |
1148 | }, | |
1149 | { | |
1150 | ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`, | |
1151 | Value: &IgnoreTest{}, | |
1152 | UnmarshalOnly: true, | |
1153 | }, | |
fabcaa8d ILT |
1154 | |
1155 | // Test escaping. | |
1156 | { | |
d6f2922e | 1157 | ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`, |
fabcaa8d | 1158 | Value: &AnyTest{ |
d6f2922e ILT |
1159 | Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`, |
1160 | AnyField: AnyHolder{XMLName: Name{Local: "empty"}}, | |
fabcaa8d ILT |
1161 | }, |
1162 | }, | |
1163 | { | |
d6f2922e | 1164 | ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`, |
fabcaa8d | 1165 | Value: &AnyTest{ |
d6f2922e ILT |
1166 | Nested: "newline: \n; cr: \r; tab: \t;", |
1167 | AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}}, | |
fabcaa8d ILT |
1168 | }, |
1169 | }, | |
1170 | { | |
1171 | ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>", | |
1172 | Value: &AnyTest{ | |
1173 | Nested: "1\n2\n3\n\n4\n5", | |
1174 | }, | |
1175 | UnmarshalOnly: true, | |
1176 | }, | |
be47d6ec ILT |
1177 | { |
1178 | ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`, | |
1179 | Value: &EmbedInt{ | |
1180 | MyInt: 42, | |
1181 | }, | |
1182 | }, | |
f98dd1a3 ILT |
1183 | // Test outputting CDATA-wrapped text. |
1184 | { | |
1185 | ExpectXML: `<CDataTest></CDataTest>`, | |
1186 | Value: &CDataTest{}, | |
1187 | }, | |
1188 | { | |
1189 | ExpectXML: `<CDataTest><![CDATA[http://example.com/tests/1?foo=1&bar=baz]]></CDataTest>`, | |
1190 | Value: &CDataTest{ | |
1191 | Chardata: "http://example.com/tests/1?foo=1&bar=baz", | |
1192 | }, | |
1193 | }, | |
1194 | { | |
1195 | ExpectXML: `<CDataTest><![CDATA[Literal <![CDATA[Nested]]]]><![CDATA[>!]]></CDataTest>`, | |
1196 | Value: &CDataTest{ | |
1197 | Chardata: "Literal <![CDATA[Nested]]>!", | |
1198 | }, | |
1199 | }, | |
1200 | { | |
1201 | ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`, | |
1202 | Value: &CDataTest{ | |
1203 | Chardata: "<![CDATA[Nested]]> Literal!", | |
1204 | }, | |
1205 | }, | |
1206 | { | |
1207 | ExpectXML: `<CDataTest><![CDATA[<![CDATA[Nested]]]]><![CDATA[> Literal! <![CDATA[Nested]]]]><![CDATA[> Literal!]]></CDataTest>`, | |
1208 | Value: &CDataTest{ | |
1209 | Chardata: "<![CDATA[Nested]]> Literal! <![CDATA[Nested]]> Literal!", | |
1210 | }, | |
1211 | }, | |
1212 | { | |
1213 | ExpectXML: `<CDataTest><![CDATA[<![CDATA[<![CDATA[Nested]]]]><![CDATA[>]]]]><![CDATA[>]]></CDataTest>`, | |
1214 | Value: &CDataTest{ | |
1215 | Chardata: "<![CDATA[<![CDATA[Nested]]>]]>", | |
1216 | }, | |
1217 | }, | |
1218 | ||
be47d6ec ILT |
1219 | // Test omitempty with parent chain; see golang.org/issue/4168. |
1220 | { | |
1221 | ExpectXML: `<Strings><A></A></Strings>`, | |
1222 | Value: &Strings{}, | |
1223 | }, | |
f038dae6 ILT |
1224 | // Custom marshalers. |
1225 | { | |
1226 | ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`, | |
1227 | Value: &MyMarshalerTest{}, | |
1228 | }, | |
1229 | { | |
1230 | ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`, | |
1231 | Value: &MarshalerStruct{}, | |
1232 | }, | |
00d86ac9 ILT |
1233 | { |
1234 | ExpectXML: `<outer xmlns="testns" int="10"></outer>`, | |
1235 | Value: &OuterStruct{IntAttr: 10}, | |
1236 | }, | |
1237 | { | |
1238 | ExpectXML: `<test xmlns="outerns" int="10"></test>`, | |
1239 | Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, | |
1240 | }, | |
1241 | { | |
1242 | ExpectXML: `<test xmlns="outerns" int="10"></test>`, | |
1243 | Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, | |
1244 | }, | |
1245 | { | |
1246 | ExpectXML: `<outer xmlns="testns" int="10"></outer>`, | |
1247 | Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}}, | |
1248 | }, | |
af146490 ILT |
1249 | { |
1250 | ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`, | |
1251 | Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"}, | |
1252 | }, | |
1253 | { | |
1254 | ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`, | |
1255 | Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"}, | |
1256 | }, | |
f98dd1a3 ILT |
1257 | { |
1258 | ExpectXML: `<NestedAndCData><A><B></B><B></B></A><![CDATA[test]]></NestedAndCData>`, | |
1259 | Value: &NestedAndCData{AB: make([]string, 2), CDATA: "test"}, | |
1260 | }, | |
00b2a30f ILT |
1261 | // Test pointer indirection in various kinds of fields. |
1262 | // https://golang.org/issue/19063 | |
1263 | { | |
1264 | ExpectXML: `<IndirComment><T1></T1><!--hi--><T2></T2></IndirComment>`, | |
1265 | Value: &IndirComment{Comment: stringptr("hi")}, | |
1266 | MarshalOnly: true, | |
1267 | }, | |
1268 | { | |
1269 | ExpectXML: `<IndirComment><T1></T1><T2></T2></IndirComment>`, | |
1270 | Value: &IndirComment{Comment: stringptr("")}, | |
1271 | MarshalOnly: true, | |
1272 | }, | |
1273 | { | |
1274 | ExpectXML: `<IndirComment><T1></T1><T2></T2></IndirComment>`, | |
1275 | Value: &IndirComment{Comment: nil}, | |
1276 | MarshalError: "xml: bad type for comment field of xml.IndirComment", | |
1277 | }, | |
1278 | { | |
1279 | ExpectXML: `<IndirComment><T1></T1><!--hi--><T2></T2></IndirComment>`, | |
1280 | Value: &IndirComment{Comment: nil}, | |
1281 | UnmarshalOnly: true, | |
1282 | }, | |
1283 | { | |
1284 | ExpectXML: `<IfaceComment><T1></T1><!--hi--><T2></T2></IfaceComment>`, | |
1285 | Value: &IfaceComment{Comment: "hi"}, | |
1286 | MarshalOnly: true, | |
1287 | }, | |
1288 | { | |
1289 | ExpectXML: `<IfaceComment><T1></T1><!--hi--><T2></T2></IfaceComment>`, | |
1290 | Value: &IfaceComment{Comment: nil}, | |
1291 | UnmarshalOnly: true, | |
1292 | }, | |
1293 | { | |
1294 | ExpectXML: `<IfaceComment><T1></T1><T2></T2></IfaceComment>`, | |
1295 | Value: &IfaceComment{Comment: nil}, | |
1296 | MarshalError: "xml: bad type for comment field of xml.IfaceComment", | |
1297 | }, | |
1298 | { | |
1299 | ExpectXML: `<IfaceComment><T1></T1><T2></T2></IfaceComment>`, | |
1300 | Value: &IfaceComment{Comment: nil}, | |
1301 | UnmarshalOnly: true, | |
1302 | }, | |
1303 | { | |
1304 | ExpectXML: `<DirectComment><T1></T1><!--hi--><T2></T2></DirectComment>`, | |
1305 | Value: &DirectComment{Comment: string("hi")}, | |
1306 | }, | |
1307 | { | |
1308 | ExpectXML: `<DirectComment><T1></T1><T2></T2></DirectComment>`, | |
1309 | Value: &DirectComment{Comment: string("")}, | |
1310 | }, | |
1311 | { | |
1312 | ExpectXML: `<IndirChardata><T1></T1>hi<T2></T2></IndirChardata>`, | |
1313 | Value: &IndirChardata{Chardata: stringptr("hi")}, | |
1314 | }, | |
1315 | { | |
1316 | ExpectXML: `<IndirChardata><T1></T1><![CDATA[hi]]><T2></T2></IndirChardata>`, | |
1317 | Value: &IndirChardata{Chardata: stringptr("hi")}, | |
1318 | UnmarshalOnly: true, // marshals without CDATA | |
1319 | }, | |
1320 | { | |
1321 | ExpectXML: `<IndirChardata><T1></T1><T2></T2></IndirChardata>`, | |
1322 | Value: &IndirChardata{Chardata: stringptr("")}, | |
1323 | }, | |
1324 | { | |
1325 | ExpectXML: `<IndirChardata><T1></T1><T2></T2></IndirChardata>`, | |
1326 | Value: &IndirChardata{Chardata: nil}, | |
1327 | MarshalOnly: true, // unmarshal leaves Chardata=stringptr("") | |
1328 | }, | |
1329 | { | |
1330 | ExpectXML: `<IfaceChardata><T1></T1>hi<T2></T2></IfaceChardata>`, | |
1331 | Value: &IfaceChardata{Chardata: string("hi")}, | |
1332 | UnmarshalError: "cannot unmarshal into interface {}", | |
1333 | }, | |
1334 | { | |
1335 | ExpectXML: `<IfaceChardata><T1></T1><![CDATA[hi]]><T2></T2></IfaceChardata>`, | |
1336 | Value: &IfaceChardata{Chardata: string("hi")}, | |
1337 | UnmarshalOnly: true, // marshals without CDATA | |
1338 | UnmarshalError: "cannot unmarshal into interface {}", | |
1339 | }, | |
1340 | { | |
1341 | ExpectXML: `<IfaceChardata><T1></T1><T2></T2></IfaceChardata>`, | |
1342 | Value: &IfaceChardata{Chardata: string("")}, | |
1343 | UnmarshalError: "cannot unmarshal into interface {}", | |
1344 | }, | |
1345 | { | |
1346 | ExpectXML: `<IfaceChardata><T1></T1><T2></T2></IfaceChardata>`, | |
1347 | Value: &IfaceChardata{Chardata: nil}, | |
1348 | UnmarshalError: "cannot unmarshal into interface {}", | |
1349 | }, | |
1350 | { | |
1351 | ExpectXML: `<DirectChardata><T1></T1>hi<T2></T2></DirectChardata>`, | |
1352 | Value: &DirectChardata{Chardata: string("hi")}, | |
1353 | }, | |
1354 | { | |
1355 | ExpectXML: `<DirectChardata><T1></T1><![CDATA[hi]]><T2></T2></DirectChardata>`, | |
1356 | Value: &DirectChardata{Chardata: string("hi")}, | |
1357 | UnmarshalOnly: true, // marshals without CDATA | |
1358 | }, | |
1359 | { | |
1360 | ExpectXML: `<DirectChardata><T1></T1><T2></T2></DirectChardata>`, | |
1361 | Value: &DirectChardata{Chardata: string("")}, | |
1362 | }, | |
1363 | { | |
1364 | ExpectXML: `<IndirCDATA><T1></T1><![CDATA[hi]]><T2></T2></IndirCDATA>`, | |
1365 | Value: &IndirCDATA{CDATA: stringptr("hi")}, | |
1366 | }, | |
1367 | { | |
1368 | ExpectXML: `<IndirCDATA><T1></T1>hi<T2></T2></IndirCDATA>`, | |
1369 | Value: &IndirCDATA{CDATA: stringptr("hi")}, | |
1370 | UnmarshalOnly: true, // marshals with CDATA | |
1371 | }, | |
1372 | { | |
1373 | ExpectXML: `<IndirCDATA><T1></T1><T2></T2></IndirCDATA>`, | |
1374 | Value: &IndirCDATA{CDATA: stringptr("")}, | |
1375 | }, | |
1376 | { | |
1377 | ExpectXML: `<IndirCDATA><T1></T1><T2></T2></IndirCDATA>`, | |
1378 | Value: &IndirCDATA{CDATA: nil}, | |
1379 | MarshalOnly: true, // unmarshal leaves CDATA=stringptr("") | |
1380 | }, | |
1381 | { | |
1382 | ExpectXML: `<IfaceCDATA><T1></T1><![CDATA[hi]]><T2></T2></IfaceCDATA>`, | |
1383 | Value: &IfaceCDATA{CDATA: string("hi")}, | |
1384 | UnmarshalError: "cannot unmarshal into interface {}", | |
1385 | }, | |
1386 | { | |
1387 | ExpectXML: `<IfaceCDATA><T1></T1>hi<T2></T2></IfaceCDATA>`, | |
1388 | Value: &IfaceCDATA{CDATA: string("hi")}, | |
1389 | UnmarshalOnly: true, // marshals with CDATA | |
1390 | UnmarshalError: "cannot unmarshal into interface {}", | |
1391 | }, | |
1392 | { | |
1393 | ExpectXML: `<IfaceCDATA><T1></T1><T2></T2></IfaceCDATA>`, | |
1394 | Value: &IfaceCDATA{CDATA: string("")}, | |
1395 | UnmarshalError: "cannot unmarshal into interface {}", | |
1396 | }, | |
1397 | { | |
1398 | ExpectXML: `<IfaceCDATA><T1></T1><T2></T2></IfaceCDATA>`, | |
1399 | Value: &IfaceCDATA{CDATA: nil}, | |
1400 | UnmarshalError: "cannot unmarshal into interface {}", | |
1401 | }, | |
1402 | { | |
1403 | ExpectXML: `<DirectCDATA><T1></T1><![CDATA[hi]]><T2></T2></DirectCDATA>`, | |
1404 | Value: &DirectCDATA{CDATA: string("hi")}, | |
1405 | }, | |
1406 | { | |
1407 | ExpectXML: `<DirectCDATA><T1></T1>hi<T2></T2></DirectCDATA>`, | |
1408 | Value: &DirectCDATA{CDATA: string("hi")}, | |
1409 | UnmarshalOnly: true, // marshals with CDATA | |
1410 | }, | |
1411 | { | |
1412 | ExpectXML: `<DirectCDATA><T1></T1><T2></T2></DirectCDATA>`, | |
1413 | Value: &DirectCDATA{CDATA: string("")}, | |
1414 | }, | |
1415 | { | |
1416 | ExpectXML: `<IndirInnerXML><T1></T1><hi/><T2></T2></IndirInnerXML>`, | |
1417 | Value: &IndirInnerXML{InnerXML: stringptr("<hi/>")}, | |
1418 | MarshalOnly: true, | |
1419 | }, | |
1420 | { | |
1421 | ExpectXML: `<IndirInnerXML><T1></T1><T2></T2></IndirInnerXML>`, | |
1422 | Value: &IndirInnerXML{InnerXML: stringptr("")}, | |
1423 | MarshalOnly: true, | |
1424 | }, | |
1425 | { | |
1426 | ExpectXML: `<IndirInnerXML><T1></T1><T2></T2></IndirInnerXML>`, | |
1427 | Value: &IndirInnerXML{InnerXML: nil}, | |
1428 | }, | |
1429 | { | |
1430 | ExpectXML: `<IndirInnerXML><T1></T1><hi/><T2></T2></IndirInnerXML>`, | |
1431 | Value: &IndirInnerXML{InnerXML: nil}, | |
1432 | UnmarshalOnly: true, | |
1433 | }, | |
1434 | { | |
1435 | ExpectXML: `<IfaceInnerXML><T1></T1><hi/><T2></T2></IfaceInnerXML>`, | |
1436 | Value: &IfaceInnerXML{InnerXML: "<hi/>"}, | |
1437 | MarshalOnly: true, | |
1438 | }, | |
1439 | { | |
1440 | ExpectXML: `<IfaceInnerXML><T1></T1><hi/><T2></T2></IfaceInnerXML>`, | |
1441 | Value: &IfaceInnerXML{InnerXML: nil}, | |
1442 | UnmarshalOnly: true, | |
1443 | }, | |
1444 | { | |
1445 | ExpectXML: `<IfaceInnerXML><T1></T1><T2></T2></IfaceInnerXML>`, | |
1446 | Value: &IfaceInnerXML{InnerXML: nil}, | |
1447 | }, | |
1448 | { | |
1449 | ExpectXML: `<IfaceInnerXML><T1></T1><T2></T2></IfaceInnerXML>`, | |
1450 | Value: &IfaceInnerXML{InnerXML: nil}, | |
1451 | UnmarshalOnly: true, | |
1452 | }, | |
1453 | { | |
1454 | ExpectXML: `<DirectInnerXML><T1></T1><hi/><T2></T2></DirectInnerXML>`, | |
1455 | Value: &DirectInnerXML{InnerXML: string("<hi/>")}, | |
1456 | MarshalOnly: true, | |
1457 | }, | |
1458 | { | |
1459 | ExpectXML: `<DirectInnerXML><T1></T1><hi/><T2></T2></DirectInnerXML>`, | |
1460 | Value: &DirectInnerXML{InnerXML: string("<T1></T1><hi/><T2></T2>")}, | |
1461 | UnmarshalOnly: true, | |
1462 | }, | |
1463 | { | |
1464 | ExpectXML: `<DirectInnerXML><T1></T1><T2></T2></DirectInnerXML>`, | |
1465 | Value: &DirectInnerXML{InnerXML: string("")}, | |
1466 | MarshalOnly: true, | |
1467 | }, | |
1468 | { | |
1469 | ExpectXML: `<DirectInnerXML><T1></T1><T2></T2></DirectInnerXML>`, | |
1470 | Value: &DirectInnerXML{InnerXML: string("<T1></T1><T2></T2>")}, | |
1471 | UnmarshalOnly: true, | |
1472 | }, | |
1473 | { | |
1474 | ExpectXML: `<IndirElement><T1></T1><Element>hi</Element><T2></T2></IndirElement>`, | |
1475 | Value: &IndirElement{Element: stringptr("hi")}, | |
1476 | }, | |
1477 | { | |
1478 | ExpectXML: `<IndirElement><T1></T1><Element></Element><T2></T2></IndirElement>`, | |
1479 | Value: &IndirElement{Element: stringptr("")}, | |
1480 | }, | |
1481 | { | |
1482 | ExpectXML: `<IndirElement><T1></T1><T2></T2></IndirElement>`, | |
1483 | Value: &IndirElement{Element: nil}, | |
1484 | }, | |
1485 | { | |
1486 | ExpectXML: `<IfaceElement><T1></T1><Element>hi</Element><T2></T2></IfaceElement>`, | |
1487 | Value: &IfaceElement{Element: "hi"}, | |
1488 | MarshalOnly: true, | |
1489 | }, | |
1490 | { | |
1491 | ExpectXML: `<IfaceElement><T1></T1><Element>hi</Element><T2></T2></IfaceElement>`, | |
1492 | Value: &IfaceElement{Element: nil}, | |
1493 | UnmarshalOnly: true, | |
1494 | }, | |
1495 | { | |
1496 | ExpectXML: `<IfaceElement><T1></T1><T2></T2></IfaceElement>`, | |
1497 | Value: &IfaceElement{Element: nil}, | |
1498 | }, | |
1499 | { | |
1500 | ExpectXML: `<IfaceElement><T1></T1><T2></T2></IfaceElement>`, | |
1501 | Value: &IfaceElement{Element: nil}, | |
1502 | UnmarshalOnly: true, | |
1503 | }, | |
1504 | { | |
1505 | ExpectXML: `<DirectElement><T1></T1><Element>hi</Element><T2></T2></DirectElement>`, | |
1506 | Value: &DirectElement{Element: string("hi")}, | |
1507 | }, | |
1508 | { | |
1509 | ExpectXML: `<DirectElement><T1></T1><Element></Element><T2></T2></DirectElement>`, | |
1510 | Value: &DirectElement{Element: string("")}, | |
1511 | }, | |
1512 | { | |
1513 | ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IndirOmitEmpty>`, | |
1514 | Value: &IndirOmitEmpty{OmitEmpty: stringptr("hi")}, | |
1515 | }, | |
1516 | { | |
1517 | // Note: Changed in Go 1.8 to include <OmitEmpty> element (because x.OmitEmpty != nil). | |
1518 | ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty></OmitEmpty><T2></T2></IndirOmitEmpty>`, | |
1519 | Value: &IndirOmitEmpty{OmitEmpty: stringptr("")}, | |
1520 | MarshalOnly: true, | |
1521 | }, | |
1522 | { | |
1523 | ExpectXML: `<IndirOmitEmpty><T1></T1><OmitEmpty></OmitEmpty><T2></T2></IndirOmitEmpty>`, | |
1524 | Value: &IndirOmitEmpty{OmitEmpty: stringptr("")}, | |
1525 | UnmarshalOnly: true, | |
1526 | }, | |
1527 | { | |
1528 | ExpectXML: `<IndirOmitEmpty><T1></T1><T2></T2></IndirOmitEmpty>`, | |
1529 | Value: &IndirOmitEmpty{OmitEmpty: nil}, | |
1530 | }, | |
1531 | { | |
1532 | ExpectXML: `<IfaceOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IfaceOmitEmpty>`, | |
1533 | Value: &IfaceOmitEmpty{OmitEmpty: "hi"}, | |
1534 | MarshalOnly: true, | |
1535 | }, | |
1536 | { | |
1537 | ExpectXML: `<IfaceOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></IfaceOmitEmpty>`, | |
1538 | Value: &IfaceOmitEmpty{OmitEmpty: nil}, | |
1539 | UnmarshalOnly: true, | |
1540 | }, | |
1541 | { | |
1542 | ExpectXML: `<IfaceOmitEmpty><T1></T1><T2></T2></IfaceOmitEmpty>`, | |
1543 | Value: &IfaceOmitEmpty{OmitEmpty: nil}, | |
1544 | }, | |
1545 | { | |
1546 | ExpectXML: `<IfaceOmitEmpty><T1></T1><T2></T2></IfaceOmitEmpty>`, | |
1547 | Value: &IfaceOmitEmpty{OmitEmpty: nil}, | |
1548 | UnmarshalOnly: true, | |
1549 | }, | |
1550 | { | |
1551 | ExpectXML: `<DirectOmitEmpty><T1></T1><OmitEmpty>hi</OmitEmpty><T2></T2></DirectOmitEmpty>`, | |
1552 | Value: &DirectOmitEmpty{OmitEmpty: string("hi")}, | |
1553 | }, | |
1554 | { | |
1555 | ExpectXML: `<DirectOmitEmpty><T1></T1><T2></T2></DirectOmitEmpty>`, | |
1556 | Value: &DirectOmitEmpty{OmitEmpty: string("")}, | |
1557 | }, | |
1558 | { | |
1559 | ExpectXML: `<IndirAny><T1></T1><Any>hi</Any><T2></T2></IndirAny>`, | |
1560 | Value: &IndirAny{Any: stringptr("hi")}, | |
1561 | }, | |
1562 | { | |
1563 | ExpectXML: `<IndirAny><T1></T1><Any></Any><T2></T2></IndirAny>`, | |
1564 | Value: &IndirAny{Any: stringptr("")}, | |
1565 | }, | |
1566 | { | |
1567 | ExpectXML: `<IndirAny><T1></T1><T2></T2></IndirAny>`, | |
1568 | Value: &IndirAny{Any: nil}, | |
1569 | }, | |
1570 | { | |
1571 | ExpectXML: `<IfaceAny><T1></T1><Any>hi</Any><T2></T2></IfaceAny>`, | |
1572 | Value: &IfaceAny{Any: "hi"}, | |
1573 | MarshalOnly: true, | |
1574 | }, | |
1575 | { | |
1576 | ExpectXML: `<IfaceAny><T1></T1><Any>hi</Any><T2></T2></IfaceAny>`, | |
1577 | Value: &IfaceAny{Any: nil}, | |
1578 | UnmarshalOnly: true, | |
1579 | }, | |
1580 | { | |
1581 | ExpectXML: `<IfaceAny><T1></T1><T2></T2></IfaceAny>`, | |
1582 | Value: &IfaceAny{Any: nil}, | |
1583 | }, | |
1584 | { | |
1585 | ExpectXML: `<IfaceAny><T1></T1><T2></T2></IfaceAny>`, | |
1586 | Value: &IfaceAny{Any: nil}, | |
1587 | UnmarshalOnly: true, | |
1588 | }, | |
1589 | { | |
1590 | ExpectXML: `<DirectAny><T1></T1><Any>hi</Any><T2></T2></DirectAny>`, | |
1591 | Value: &DirectAny{Any: string("hi")}, | |
1592 | }, | |
1593 | { | |
1594 | ExpectXML: `<DirectAny><T1></T1><Any></Any><T2></T2></DirectAny>`, | |
1595 | Value: &DirectAny{Any: string("")}, | |
1596 | }, | |
1597 | { | |
1598 | ExpectXML: `<IndirFoo><T1></T1><Foo>hi</Foo><T2></T2></IndirFoo>`, | |
1599 | Value: &IndirAny{Any: stringptr("hi")}, | |
1600 | UnmarshalOnly: true, | |
1601 | }, | |
1602 | { | |
1603 | ExpectXML: `<IndirFoo><T1></T1><Foo></Foo><T2></T2></IndirFoo>`, | |
1604 | Value: &IndirAny{Any: stringptr("")}, | |
1605 | UnmarshalOnly: true, | |
1606 | }, | |
1607 | { | |
1608 | ExpectXML: `<IndirFoo><T1></T1><T2></T2></IndirFoo>`, | |
1609 | Value: &IndirAny{Any: nil}, | |
1610 | UnmarshalOnly: true, | |
1611 | }, | |
1612 | { | |
1613 | ExpectXML: `<IfaceFoo><T1></T1><Foo>hi</Foo><T2></T2></IfaceFoo>`, | |
1614 | Value: &IfaceAny{Any: nil}, | |
1615 | UnmarshalOnly: true, | |
1616 | }, | |
1617 | { | |
1618 | ExpectXML: `<IfaceFoo><T1></T1><T2></T2></IfaceFoo>`, | |
1619 | Value: &IfaceAny{Any: nil}, | |
1620 | UnmarshalOnly: true, | |
1621 | }, | |
1622 | { | |
1623 | ExpectXML: `<IfaceFoo><T1></T1><T2></T2></IfaceFoo>`, | |
1624 | Value: &IfaceAny{Any: nil}, | |
1625 | UnmarshalOnly: true, | |
1626 | }, | |
1627 | { | |
1628 | ExpectXML: `<DirectFoo><T1></T1><Foo>hi</Foo><T2></T2></DirectFoo>`, | |
1629 | Value: &DirectAny{Any: string("hi")}, | |
1630 | UnmarshalOnly: true, | |
1631 | }, | |
1632 | { | |
1633 | ExpectXML: `<DirectFoo><T1></T1><Foo></Foo><T2></T2></DirectFoo>`, | |
1634 | Value: &DirectAny{Any: string("")}, | |
1635 | UnmarshalOnly: true, | |
1636 | }, | |
adb0401d ILT |
1637 | } |
1638 | ||
1639 | func TestMarshal(t *testing.T) { | |
1640 | for idx, test := range marshalTests { | |
df1304ee ILT |
1641 | if test.UnmarshalOnly { |
1642 | continue | |
1643 | } | |
bc998d03 ILT |
1644 | |
1645 | t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { | |
1646 | data, err := Marshal(test.Value) | |
1647 | if err != nil { | |
1648 | if test.MarshalError == "" { | |
1649 | t.Errorf("marshal(%#v): %s", test.Value, err) | |
1650 | return | |
1651 | } | |
1652 | if !strings.Contains(err.Error(), test.MarshalError) { | |
1653 | t.Errorf("marshal(%#v): %s, want %q", test.Value, err, test.MarshalError) | |
1654 | } | |
1655 | return | |
00b2a30f | 1656 | } |
bc998d03 ILT |
1657 | if test.MarshalError != "" { |
1658 | t.Errorf("Marshal succeeded, want error %q", test.MarshalError) | |
1659 | return | |
00b2a30f | 1660 | } |
bc998d03 ILT |
1661 | if got, want := string(data), test.ExpectXML; got != want { |
1662 | if strings.Contains(want, "\n") { | |
1663 | t.Errorf("marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", test.Value, got, want) | |
1664 | } else { | |
1665 | t.Errorf("marshal(%#v):\nhave %#q\nwant %#q", test.Value, got, want) | |
1666 | } | |
adb0401d | 1667 | } |
bc998d03 | 1668 | }) |
adb0401d ILT |
1669 | } |
1670 | } | |
1671 | ||
be47d6ec ILT |
1672 | type AttrParent struct { |
1673 | X string `xml:"X>Y,attr"` | |
1674 | } | |
1675 | ||
f038dae6 | 1676 | type BadAttr struct { |
c2047754 | 1677 | Name map[string]string `xml:"name,attr"` |
f038dae6 ILT |
1678 | } |
1679 | ||
adb0401d | 1680 | var marshalErrorTests = []struct { |
506cf9aa ILT |
1681 | Value interface{} |
1682 | Err string | |
1683 | Kind reflect.Kind | |
adb0401d ILT |
1684 | }{ |
1685 | { | |
506cf9aa ILT |
1686 | Value: make(chan bool), |
1687 | Err: "xml: unsupported type: chan bool", | |
1688 | Kind: reflect.Chan, | |
adb0401d ILT |
1689 | }, |
1690 | { | |
1691 | Value: map[string]string{ | |
1692 | "question": "What do you get when you multiply six by nine?", | |
1693 | "answer": "42", | |
1694 | }, | |
d5363590 | 1695 | Err: "xml: unsupported type: map[string]string", |
506cf9aa | 1696 | Kind: reflect.Map, |
adb0401d ILT |
1697 | }, |
1698 | { | |
506cf9aa | 1699 | Value: map[*Ship]bool{nil: false}, |
9690ac05 | 1700 | Err: "xml: unsupported type: map[*xml.Ship]bool", |
506cf9aa | 1701 | Kind: reflect.Map, |
adb0401d | 1702 | }, |
df1304ee ILT |
1703 | { |
1704 | Value: &Domain{Comment: []byte("f--bar")}, | |
1705 | Err: `xml: comments must not contain "--"`, | |
1706 | }, | |
be47d6ec ILT |
1707 | // Reject parent chain with attr, never worked; see golang.org/issue/5033. |
1708 | { | |
1709 | Value: &AttrParent{}, | |
1710 | Err: `xml: X>Y chain not valid with attr flag`, | |
1711 | }, | |
f038dae6 | 1712 | { |
c2047754 ILT |
1713 | Value: BadAttr{map[string]string{"X": "Y"}}, |
1714 | Err: `xml: unsupported type: map[string]string`, | |
f038dae6 | 1715 | }, |
be47d6ec ILT |
1716 | } |
1717 | ||
1718 | var marshalIndentTests = []struct { | |
1719 | Value interface{} | |
1720 | Prefix string | |
1721 | Indent string | |
1722 | ExpectXML string | |
1723 | }{ | |
1724 | { | |
1725 | Value: &SecretAgent{ | |
1726 | Handle: "007", | |
1727 | Identity: "James Bond", | |
1728 | Obfuscate: "<redacted/>", | |
1729 | }, | |
1730 | Prefix: "", | |
1731 | Indent: "\t", | |
1732 | ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"), | |
1733 | }, | |
adb0401d ILT |
1734 | } |
1735 | ||
1736 | func TestMarshalErrors(t *testing.T) { | |
1737 | for idx, test := range marshalErrorTests { | |
be47d6ec ILT |
1738 | data, err := Marshal(test.Value) |
1739 | if err == nil { | |
1740 | t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err) | |
1741 | continue | |
1742 | } | |
1743 | if err.Error() != test.Err { | |
df1304ee | 1744 | t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err) |
adb0401d | 1745 | } |
df1304ee ILT |
1746 | if test.Kind != reflect.Invalid { |
1747 | if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind { | |
1748 | t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind) | |
1749 | } | |
adb0401d ILT |
1750 | } |
1751 | } | |
1752 | } | |
1753 | ||
1754 | // Do invertibility testing on the various structures that we test | |
1755 | func TestUnmarshal(t *testing.T) { | |
1756 | for i, test := range marshalTests { | |
df1304ee | 1757 | if test.MarshalOnly { |
adb0401d ILT |
1758 | continue |
1759 | } | |
df1304ee | 1760 | if _, ok := test.Value.(*Plain); ok { |
adb0401d ILT |
1761 | continue |
1762 | } | |
af146490 ILT |
1763 | if test.ExpectXML == `<top>`+ |
1764 | `<x><b xmlns="space">b</b>`+ | |
1765 | `<b xmlns="space1">b1</b></x>`+ | |
1766 | `</top>` { | |
1767 | // TODO(rogpeppe): re-enable this test in | |
1768 | // https://go-review.googlesource.com/#/c/5910/ | |
1769 | continue | |
1770 | } | |
adb0401d | 1771 | |
df1304ee ILT |
1772 | vt := reflect.TypeOf(test.Value) |
1773 | dest := reflect.New(vt.Elem()).Interface() | |
9af4cb95 | 1774 | err := Unmarshal([]byte(test.ExpectXML), dest) |
adb0401d | 1775 | |
bc998d03 ILT |
1776 | t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { |
1777 | switch fix := dest.(type) { | |
1778 | case *Feed: | |
1779 | fix.Author.InnerXML = "" | |
1780 | for i := range fix.Entry { | |
1781 | fix.Entry[i].Author.InnerXML = "" | |
1782 | } | |
adb0401d | 1783 | } |
adb0401d | 1784 | |
bc998d03 ILT |
1785 | if err != nil { |
1786 | if test.UnmarshalError == "" { | |
1787 | t.Errorf("unmarshal(%#v): %s", test.ExpectXML, err) | |
1788 | return | |
1789 | } | |
1790 | if !strings.Contains(err.Error(), test.UnmarshalError) { | |
1791 | t.Errorf("unmarshal(%#v): %s, want %q", test.ExpectXML, err, test.UnmarshalError) | |
1792 | } | |
1793 | return | |
00b2a30f | 1794 | } |
bc998d03 ILT |
1795 | if got, want := dest, test.Value; !reflect.DeepEqual(got, want) { |
1796 | t.Errorf("unmarshal(%q):\nhave %#v\nwant %#v", test.ExpectXML, got, want) | |
00b2a30f | 1797 | } |
bc998d03 | 1798 | }) |
adb0401d ILT |
1799 | } |
1800 | } | |
1801 | ||
be47d6ec ILT |
1802 | func TestMarshalIndent(t *testing.T) { |
1803 | for i, test := range marshalIndentTests { | |
1804 | data, err := MarshalIndent(test.Value, test.Prefix, test.Indent) | |
1805 | if err != nil { | |
1806 | t.Errorf("#%d: Error: %s", i, err) | |
1807 | continue | |
1808 | } | |
1809 | if got, want := string(data), test.ExpectXML; got != want { | |
1810 | t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want) | |
1811 | } | |
1812 | } | |
1813 | } | |
1814 | ||
4ccad563 ILT |
1815 | type limitedBytesWriter struct { |
1816 | w io.Writer | |
1817 | remain int // until writes fail | |
1818 | } | |
1819 | ||
1820 | func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) { | |
1821 | if lw.remain <= 0 { | |
1822 | println("error") | |
1823 | return 0, errors.New("write limit hit") | |
1824 | } | |
1825 | if len(p) > lw.remain { | |
1826 | p = p[:lw.remain] | |
1827 | n, _ = lw.w.Write(p) | |
1828 | lw.remain = 0 | |
1829 | return n, errors.New("write limit hit") | |
1830 | } | |
1831 | n, err = lw.w.Write(p) | |
1832 | lw.remain -= n | |
1833 | return n, err | |
1834 | } | |
1835 | ||
1836 | func TestMarshalWriteErrors(t *testing.T) { | |
1837 | var buf bytes.Buffer | |
1838 | const writeCap = 1024 | |
1839 | w := &limitedBytesWriter{&buf, writeCap} | |
1840 | enc := NewEncoder(w) | |
1841 | var err error | |
1842 | var i int | |
1843 | const n = 4000 | |
1844 | for i = 1; i <= n; i++ { | |
1845 | err = enc.Encode(&Passenger{ | |
1846 | Name: []string{"Alice", "Bob"}, | |
1847 | Weight: 5, | |
1848 | }) | |
1849 | if err != nil { | |
1850 | break | |
1851 | } | |
1852 | } | |
1853 | if err == nil { | |
1854 | t.Error("expected an error") | |
1855 | } | |
1856 | if i == n { | |
1857 | t.Errorf("expected to fail before the end") | |
1858 | } | |
1859 | if buf.Len() != writeCap { | |
1860 | t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap) | |
1861 | } | |
1862 | } | |
1863 | ||
be47d6ec ILT |
1864 | func TestMarshalWriteIOErrors(t *testing.T) { |
1865 | enc := NewEncoder(errWriter{}) | |
1866 | ||
1867 | expectErr := "unwritable" | |
1868 | err := enc.Encode(&Passenger{}) | |
1869 | if err == nil || err.Error() != expectErr { | |
1870 | t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr) | |
1871 | } | |
1872 | } | |
1873 | ||
f038dae6 ILT |
1874 | func TestMarshalFlush(t *testing.T) { |
1875 | var buf bytes.Buffer | |
1876 | enc := NewEncoder(&buf) | |
1877 | if err := enc.EncodeToken(CharData("hello world")); err != nil { | |
1878 | t.Fatalf("enc.EncodeToken: %v", err) | |
1879 | } | |
1880 | if buf.Len() > 0 { | |
1881 | t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes()) | |
1882 | } | |
1883 | if err := enc.Flush(); err != nil { | |
1884 | t.Fatalf("enc.Flush: %v", err) | |
1885 | } | |
1886 | if buf.String() != "hello world" { | |
1887 | t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world") | |
1888 | } | |
1889 | } | |
1890 | ||
adb0401d | 1891 | func BenchmarkMarshal(b *testing.B) { |
af146490 | 1892 | b.ReportAllocs() |
bc998d03 ILT |
1893 | b.RunParallel(func(pb *testing.PB) { |
1894 | for pb.Next() { | |
1895 | Marshal(atomValue) | |
1896 | } | |
1897 | }) | |
adb0401d ILT |
1898 | } |
1899 | ||
1900 | func BenchmarkUnmarshal(b *testing.B) { | |
af146490 | 1901 | b.ReportAllocs() |
1a2f01ef | 1902 | xml := []byte(atomXML) |
bc998d03 ILT |
1903 | b.RunParallel(func(pb *testing.PB) { |
1904 | for pb.Next() { | |
1905 | Unmarshal(xml, &Feed{}) | |
1906 | } | |
1907 | }) | |
adb0401d | 1908 | } |
f038dae6 ILT |
1909 | |
1910 | // golang.org/issue/6556 | |
1911 | func TestStructPointerMarshal(t *testing.T) { | |
1912 | type A struct { | |
1913 | XMLName string `xml:"a"` | |
1914 | B []interface{} | |
1915 | } | |
1916 | type C struct { | |
1917 | XMLName Name | |
1918 | Value string `xml:"value"` | |
1919 | } | |
1920 | ||
1921 | a := new(A) | |
1922 | a.B = append(a.B, &C{ | |
1923 | XMLName: Name{Local: "c"}, | |
1924 | Value: "x", | |
1925 | }) | |
1926 | ||
1927 | b, err := Marshal(a) | |
1928 | if err != nil { | |
1929 | t.Fatal(err) | |
1930 | } | |
1931 | if x := string(b); x != "<a><c><value>x</value></c></a>" { | |
1932 | t.Fatal(x) | |
1933 | } | |
1934 | var v A | |
1935 | err = Unmarshal(b, &v) | |
1936 | if err != nil { | |
1937 | t.Fatal(err) | |
1938 | } | |
1939 | } | |
00d86ac9 ILT |
1940 | |
1941 | var encodeTokenTests = []struct { | |
af146490 ILT |
1942 | desc string |
1943 | toks []Token | |
00d86ac9 | 1944 | want string |
af146490 ILT |
1945 | err string |
1946 | }{{ | |
1947 | desc: "start element with name space", | |
1948 | toks: []Token{ | |
1949 | StartElement{Name{"space", "local"}, nil}, | |
1950 | }, | |
1951 | want: `<local xmlns="space">`, | |
1952 | }, { | |
1953 | desc: "start element with no name", | |
1954 | toks: []Token{ | |
1955 | StartElement{Name{"space", ""}, nil}, | |
1956 | }, | |
1957 | err: "xml: start tag with no name", | |
1958 | }, { | |
1959 | desc: "end element with no name", | |
1960 | toks: []Token{ | |
1961 | EndElement{Name{"space", ""}}, | |
1962 | }, | |
1963 | err: "xml: end tag with no name", | |
1964 | }, { | |
1965 | desc: "char data", | |
1966 | toks: []Token{ | |
1967 | CharData("foo"), | |
1968 | }, | |
1969 | want: `foo`, | |
1970 | }, { | |
1971 | desc: "char data with escaped chars", | |
1972 | toks: []Token{ | |
1973 | CharData(" \t\n"), | |
1974 | }, | |
1975 | want: " 	\n", | |
1976 | }, { | |
1977 | desc: "comment", | |
1978 | toks: []Token{ | |
1979 | Comment("foo"), | |
1980 | }, | |
1981 | want: `<!--foo-->`, | |
1982 | }, { | |
1983 | desc: "comment with invalid content", | |
1984 | toks: []Token{ | |
1985 | Comment("foo-->"), | |
1986 | }, | |
1987 | err: "xml: EncodeToken of Comment containing --> marker", | |
1988 | }, { | |
1989 | desc: "proc instruction", | |
1990 | toks: []Token{ | |
1991 | ProcInst{"Target", []byte("Instruction")}, | |
1992 | }, | |
1993 | want: `<?Target Instruction?>`, | |
1994 | }, { | |
1995 | desc: "proc instruction with empty target", | |
1996 | toks: []Token{ | |
1997 | ProcInst{"", []byte("Instruction")}, | |
1998 | }, | |
1999 | err: "xml: EncodeToken of ProcInst with invalid Target", | |
2000 | }, { | |
2001 | desc: "proc instruction with bad content", | |
2002 | toks: []Token{ | |
2003 | ProcInst{"", []byte("Instruction?>")}, | |
2004 | }, | |
2005 | err: "xml: EncodeToken of ProcInst with invalid Target", | |
2006 | }, { | |
2007 | desc: "directive", | |
2008 | toks: []Token{ | |
2009 | Directive("foo"), | |
2010 | }, | |
2011 | want: `<!foo>`, | |
2012 | }, { | |
2013 | desc: "more complex directive", | |
2014 | toks: []Token{ | |
2015 | Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"), | |
2016 | }, | |
2017 | want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`, | |
2018 | }, { | |
2019 | desc: "directive instruction with bad name", | |
2020 | toks: []Token{ | |
2021 | Directive("foo>"), | |
2022 | }, | |
2023 | err: "xml: EncodeToken of Directive containing wrong < or > markers", | |
2024 | }, { | |
2025 | desc: "end tag without start tag", | |
2026 | toks: []Token{ | |
2027 | EndElement{Name{"foo", "bar"}}, | |
2028 | }, | |
2029 | err: "xml: end tag </bar> without start tag", | |
2030 | }, { | |
2031 | desc: "mismatching end tag local name", | |
2032 | toks: []Token{ | |
2033 | StartElement{Name{"", "foo"}, nil}, | |
2034 | EndElement{Name{"", "bar"}}, | |
2035 | }, | |
2036 | err: "xml: end tag </bar> does not match start tag <foo>", | |
2037 | want: `<foo>`, | |
2038 | }, { | |
2039 | desc: "mismatching end tag namespace", | |
2040 | toks: []Token{ | |
2041 | StartElement{Name{"space", "foo"}, nil}, | |
2042 | EndElement{Name{"another", "foo"}}, | |
2043 | }, | |
2044 | err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space", | |
2045 | want: `<foo xmlns="space">`, | |
2046 | }, { | |
2047 | desc: "start element with explicit namespace", | |
2048 | toks: []Token{ | |
2049 | StartElement{Name{"space", "local"}, []Attr{ | |
2050 | {Name{"xmlns", "x"}, "space"}, | |
2051 | {Name{"space", "foo"}, "value"}, | |
2052 | }}, | |
2053 | }, | |
2054 | want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value">`, | |
2055 | }, { | |
2056 | desc: "start element with explicit namespace and colliding prefix", | |
2057 | toks: []Token{ | |
2058 | StartElement{Name{"space", "local"}, []Attr{ | |
2059 | {Name{"xmlns", "x"}, "space"}, | |
2060 | {Name{"space", "foo"}, "value"}, | |
2061 | {Name{"x", "bar"}, "other"}, | |
2062 | }}, | |
2063 | }, | |
2064 | want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value" xmlns:x="x" x:bar="other">`, | |
2065 | }, { | |
2066 | desc: "start element using previously defined namespace", | |
2067 | toks: []Token{ | |
2068 | StartElement{Name{"", "local"}, []Attr{ | |
2069 | {Name{"xmlns", "x"}, "space"}, | |
2070 | }}, | |
2071 | StartElement{Name{"space", "foo"}, []Attr{ | |
2072 | {Name{"space", "x"}, "y"}, | |
2073 | }}, | |
2074 | }, | |
2075 | want: `<local xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns:space="space" space:x="y">`, | |
2076 | }, { | |
2077 | desc: "nested name space with same prefix", | |
2078 | toks: []Token{ | |
2079 | StartElement{Name{"", "foo"}, []Attr{ | |
2080 | {Name{"xmlns", "x"}, "space1"}, | |
2081 | }}, | |
2082 | StartElement{Name{"", "foo"}, []Attr{ | |
2083 | {Name{"xmlns", "x"}, "space2"}, | |
2084 | }}, | |
2085 | StartElement{Name{"", "foo"}, []Attr{ | |
2086 | {Name{"space1", "a"}, "space1 value"}, | |
2087 | {Name{"space2", "b"}, "space2 value"}, | |
2088 | }}, | |
2089 | EndElement{Name{"", "foo"}}, | |
2090 | EndElement{Name{"", "foo"}}, | |
2091 | StartElement{Name{"", "foo"}, []Attr{ | |
2092 | {Name{"space1", "a"}, "space1 value"}, | |
2093 | {Name{"space2", "b"}, "space2 value"}, | |
2094 | }}, | |
2095 | }, | |
2096 | want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space1"><foo _xmlns:x="space2"><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value"></foo></foo><foo xmlns:space1="space1" space1:a="space1 value" xmlns:space2="space2" space2:b="space2 value">`, | |
2097 | }, { | |
2098 | desc: "start element defining several prefixes for the same name space", | |
2099 | toks: []Token{ | |
2100 | StartElement{Name{"space", "foo"}, []Attr{ | |
2101 | {Name{"xmlns", "a"}, "space"}, | |
2102 | {Name{"xmlns", "b"}, "space"}, | |
2103 | {Name{"space", "x"}, "value"}, | |
2104 | }}, | |
2105 | }, | |
2106 | want: `<foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:a="space" _xmlns:b="space" xmlns:space="space" space:x="value">`, | |
2107 | }, { | |
2108 | desc: "nested element redefines name space", | |
2109 | toks: []Token{ | |
2110 | StartElement{Name{"", "foo"}, []Attr{ | |
2111 | {Name{"xmlns", "x"}, "space"}, | |
2112 | }}, | |
2113 | StartElement{Name{"space", "foo"}, []Attr{ | |
2114 | {Name{"xmlns", "y"}, "space"}, | |
2115 | {Name{"space", "a"}, "value"}, | |
2116 | }}, | |
2117 | }, | |
2118 | want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" _xmlns:y="space" xmlns:space="space" space:a="value">`, | |
2119 | }, { | |
2120 | desc: "nested element creates alias for default name space", | |
2121 | toks: []Token{ | |
2122 | StartElement{Name{"space", "foo"}, []Attr{ | |
2123 | {Name{"", "xmlns"}, "space"}, | |
2124 | }}, | |
2125 | StartElement{Name{"space", "foo"}, []Attr{ | |
2126 | {Name{"xmlns", "y"}, "space"}, | |
2127 | {Name{"space", "a"}, "value"}, | |
2128 | }}, | |
2129 | }, | |
2130 | want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:y="space" xmlns:space="space" space:a="value">`, | |
2131 | }, { | |
2132 | desc: "nested element defines default name space with existing prefix", | |
2133 | toks: []Token{ | |
2134 | StartElement{Name{"", "foo"}, []Attr{ | |
2135 | {Name{"xmlns", "x"}, "space"}, | |
2136 | }}, | |
2137 | StartElement{Name{"space", "foo"}, []Attr{ | |
2138 | {Name{"", "xmlns"}, "space"}, | |
2139 | {Name{"space", "a"}, "value"}, | |
2140 | }}, | |
2141 | }, | |
2142 | want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns="space" xmlns:space="space" space:a="value">`, | |
2143 | }, { | |
2144 | desc: "nested element uses empty attribute name space when default ns defined", | |
2145 | toks: []Token{ | |
2146 | StartElement{Name{"space", "foo"}, []Attr{ | |
2147 | {Name{"", "xmlns"}, "space"}, | |
2148 | }}, | |
2149 | StartElement{Name{"space", "foo"}, []Attr{ | |
2150 | {Name{"", "attr"}, "value"}, | |
2151 | }}, | |
2152 | }, | |
2153 | want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" attr="value">`, | |
2154 | }, { | |
2155 | desc: "redefine xmlns", | |
2156 | toks: []Token{ | |
2157 | StartElement{Name{"", "foo"}, []Attr{ | |
2158 | {Name{"foo", "xmlns"}, "space"}, | |
2159 | }}, | |
2160 | }, | |
2161 | want: `<foo xmlns:foo="foo" foo:xmlns="space">`, | |
2162 | }, { | |
2163 | desc: "xmlns with explicit name space #1", | |
2164 | toks: []Token{ | |
2165 | StartElement{Name{"space", "foo"}, []Attr{ | |
2166 | {Name{"xml", "xmlns"}, "space"}, | |
2167 | }}, | |
2168 | }, | |
2169 | want: `<foo xmlns="space" xmlns:_xml="xml" _xml:xmlns="space">`, | |
2170 | }, { | |
2171 | desc: "xmlns with explicit name space #2", | |
2172 | toks: []Token{ | |
2173 | StartElement{Name{"space", "foo"}, []Attr{ | |
2174 | {Name{xmlURL, "xmlns"}, "space"}, | |
2175 | }}, | |
2176 | }, | |
2177 | want: `<foo xmlns="space" xml:xmlns="space">`, | |
2178 | }, { | |
2179 | desc: "empty name space declaration is ignored", | |
2180 | toks: []Token{ | |
2181 | StartElement{Name{"", "foo"}, []Attr{ | |
2182 | {Name{"xmlns", "foo"}, ""}, | |
2183 | }}, | |
2184 | }, | |
2185 | want: `<foo xmlns:_xmlns="xmlns" _xmlns:foo="">`, | |
2186 | }, { | |
2187 | desc: "attribute with no name is ignored", | |
2188 | toks: []Token{ | |
2189 | StartElement{Name{"", "foo"}, []Attr{ | |
2190 | {Name{"", ""}, "value"}, | |
2191 | }}, | |
2192 | }, | |
2193 | want: `<foo>`, | |
2194 | }, { | |
2195 | desc: "namespace URL with non-valid name", | |
2196 | toks: []Token{ | |
2197 | StartElement{Name{"/34", "foo"}, []Attr{ | |
2198 | {Name{"/34", "x"}, "value"}, | |
2199 | }}, | |
2200 | }, | |
2201 | want: `<foo xmlns="/34" xmlns:_="/34" _:x="value">`, | |
2202 | }, { | |
2203 | desc: "nested element resets default namespace to empty", | |
2204 | toks: []Token{ | |
2205 | StartElement{Name{"space", "foo"}, []Attr{ | |
2206 | {Name{"", "xmlns"}, "space"}, | |
2207 | }}, | |
2208 | StartElement{Name{"", "foo"}, []Attr{ | |
2209 | {Name{"", "xmlns"}, ""}, | |
2210 | {Name{"", "x"}, "value"}, | |
2211 | {Name{"space", "x"}, "value"}, | |
2212 | }}, | |
2213 | }, | |
2214 | want: `<foo xmlns="space" xmlns="space"><foo xmlns="" x="value" xmlns:space="space" space:x="value">`, | |
2215 | }, { | |
2216 | desc: "nested element requires empty default name space", | |
2217 | toks: []Token{ | |
2218 | StartElement{Name{"space", "foo"}, []Attr{ | |
2219 | {Name{"", "xmlns"}, "space"}, | |
2220 | }}, | |
2221 | StartElement{Name{"", "foo"}, nil}, | |
2222 | }, | |
2223 | want: `<foo xmlns="space" xmlns="space"><foo>`, | |
2224 | }, { | |
2225 | desc: "attribute uses name space from xmlns", | |
2226 | toks: []Token{ | |
2227 | StartElement{Name{"some/space", "foo"}, []Attr{ | |
2228 | {Name{"", "attr"}, "value"}, | |
2229 | {Name{"some/space", "other"}, "other value"}, | |
2230 | }}, | |
2231 | }, | |
2232 | want: `<foo xmlns="some/space" attr="value" xmlns:space="some/space" space:other="other value">`, | |
2233 | }, { | |
2234 | desc: "default name space should not be used by attributes", | |
2235 | toks: []Token{ | |
2236 | StartElement{Name{"space", "foo"}, []Attr{ | |
2237 | {Name{"", "xmlns"}, "space"}, | |
2238 | {Name{"xmlns", "bar"}, "space"}, | |
2239 | {Name{"space", "baz"}, "foo"}, | |
2240 | }}, | |
2241 | StartElement{Name{"space", "baz"}, nil}, | |
2242 | EndElement{Name{"space", "baz"}}, | |
2243 | EndElement{Name{"space", "foo"}}, | |
2244 | }, | |
2245 | want: `<foo xmlns="space" xmlns="space" xmlns:_xmlns="xmlns" _xmlns:bar="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, | |
2246 | }, { | |
2247 | desc: "default name space not used by attributes, not explicitly defined", | |
2248 | toks: []Token{ | |
2249 | StartElement{Name{"space", "foo"}, []Attr{ | |
2250 | {Name{"", "xmlns"}, "space"}, | |
2251 | {Name{"space", "baz"}, "foo"}, | |
2252 | }}, | |
2253 | StartElement{Name{"space", "baz"}, nil}, | |
2254 | EndElement{Name{"space", "baz"}}, | |
2255 | EndElement{Name{"space", "foo"}}, | |
2256 | }, | |
2257 | want: `<foo xmlns="space" xmlns="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, | |
2258 | }, { | |
2259 | desc: "impossible xmlns declaration", | |
2260 | toks: []Token{ | |
2261 | StartElement{Name{"", "foo"}, []Attr{ | |
2262 | {Name{"", "xmlns"}, "space"}, | |
2263 | }}, | |
2264 | StartElement{Name{"space", "bar"}, []Attr{ | |
2265 | {Name{"space", "attr"}, "value"}, | |
2266 | }}, | |
2267 | }, | |
2268 | want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`, | |
2269 | }} | |
00d86ac9 ILT |
2270 | |
2271 | func TestEncodeToken(t *testing.T) { | |
af146490 ILT |
2272 | loop: |
2273 | for i, tt := range encodeTokenTests { | |
00d86ac9 ILT |
2274 | var buf bytes.Buffer |
2275 | enc := NewEncoder(&buf) | |
af146490 ILT |
2276 | var err error |
2277 | for j, tok := range tt.toks { | |
2278 | err = enc.EncodeToken(tok) | |
2279 | if err != nil && j < len(tt.toks)-1 { | |
2280 | t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err) | |
2281 | continue loop | |
2282 | } | |
2283 | } | |
2284 | errorf := func(f string, a ...interface{}) { | |
2285 | t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...)) | |
2286 | } | |
00d86ac9 | 2287 | switch { |
af146490 ILT |
2288 | case tt.err != "" && err == nil: |
2289 | errorf(" expected error; got none") | |
2290 | continue | |
2291 | case tt.err == "" && err != nil: | |
2292 | errorf(" got error: %v", err) | |
2293 | continue | |
2294 | case tt.err != "" && err != nil && tt.err != err.Error(): | |
2295 | errorf(" error mismatch; got %v, want %v", err, tt.err) | |
2296 | continue | |
00d86ac9 ILT |
2297 | } |
2298 | if err := enc.Flush(); err != nil { | |
af146490 ILT |
2299 | errorf(" %v", err) |
2300 | continue | |
00d86ac9 ILT |
2301 | } |
2302 | if got := buf.String(); got != tt.want { | |
af146490 ILT |
2303 | errorf("\ngot %v\nwant %v", got, tt.want) |
2304 | continue | |
00d86ac9 ILT |
2305 | } |
2306 | } | |
2307 | } | |
2308 | ||
2309 | func TestProcInstEncodeToken(t *testing.T) { | |
2310 | var buf bytes.Buffer | |
2311 | enc := NewEncoder(&buf) | |
2312 | ||
2313 | if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil { | |
2314 | t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err) | |
2315 | } | |
2316 | ||
2317 | if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil { | |
2318 | t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst") | |
2319 | } | |
2320 | ||
2321 | if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil { | |
2322 | t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token") | |
2323 | } | |
2324 | } | |
2325 | ||
2326 | func TestDecodeEncode(t *testing.T) { | |
2327 | var in, out bytes.Buffer | |
2328 | in.WriteString(`<?xml version="1.0" encoding="UTF-8"?> | |
2329 | <?Target Instruction?> | |
2330 | <root> | |
c2047754 | 2331 | </root> |
00d86ac9 ILT |
2332 | `) |
2333 | dec := NewDecoder(&in) | |
2334 | enc := NewEncoder(&out) | |
2335 | for tok, err := dec.Token(); err == nil; tok, err = dec.Token() { | |
2336 | err = enc.EncodeToken(tok) | |
2337 | if err != nil { | |
2338 | t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err) | |
2339 | } | |
2340 | } | |
2341 | } | |
af146490 ILT |
2342 | |
2343 | // Issue 9796. Used to fail with GORACE="halt_on_error=1" -race. | |
2344 | func TestRace9796(t *testing.T) { | |
2345 | type A struct{} | |
2346 | type B struct { | |
2347 | C []A `xml:"X>Y"` | |
2348 | } | |
2349 | var wg sync.WaitGroup | |
2350 | for i := 0; i < 2; i++ { | |
2351 | wg.Add(1) | |
2352 | go func() { | |
f98dd1a3 | 2353 | Marshal(B{[]A{{}}}) |
af146490 ILT |
2354 | wg.Done() |
2355 | }() | |
2356 | } | |
2357 | wg.Wait() | |
2358 | } | |
2359 | ||
2360 | func TestIsValidDirective(t *testing.T) { | |
2361 | testOK := []string{ | |
2362 | "<>", | |
2363 | "< < > >", | |
2364 | "<!DOCTYPE '<' '>' '>' <!--nothing-->>", | |
2365 | "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>", | |
2366 | "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>", | |
2367 | "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >", | |
2368 | } | |
2369 | testKO := []string{ | |
2370 | "<", | |
2371 | ">", | |
2372 | "<!--", | |
2373 | "-->", | |
2374 | "< > > < < >", | |
2375 | "<!dummy <!-- > -->", | |
2376 | "<!DOCTYPE doc '>", | |
2377 | "<!DOCTYPE doc '>'", | |
2378 | "<!DOCTYPE doc <!--comment>", | |
2379 | } | |
2380 | for _, s := range testOK { | |
2381 | if !isValidDirective(Directive(s)) { | |
2382 | t.Errorf("Directive %q is expected to be valid", s) | |
2383 | } | |
2384 | } | |
2385 | for _, s := range testKO { | |
2386 | if isValidDirective(Directive(s)) { | |
2387 | t.Errorf("Directive %q is expected to be invalid", s) | |
2388 | } | |
2389 | } | |
2390 | } | |
2391 | ||
2392 | // Issue 11719. EncodeToken used to silently eat tokens with an invalid type. | |
2393 | func TestSimpleUseOfEncodeToken(t *testing.T) { | |
2394 | var buf bytes.Buffer | |
2395 | enc := NewEncoder(&buf) | |
2396 | if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil { | |
2397 | t.Errorf("enc.EncodeToken: pointer type should be rejected") | |
2398 | } | |
2399 | if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil { | |
2400 | t.Errorf("enc.EncodeToken: pointer type should be rejected") | |
2401 | } | |
2402 | if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil { | |
2403 | t.Errorf("enc.EncodeToken: StartElement %s", err) | |
2404 | } | |
2405 | if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil { | |
2406 | t.Errorf("enc.EncodeToken: EndElement %s", err) | |
2407 | } | |
2408 | if err := enc.EncodeToken(Universe{}); err == nil { | |
2409 | t.Errorf("enc.EncodeToken: invalid type not caught") | |
2410 | } | |
2411 | if err := enc.Flush(); err != nil { | |
2412 | t.Errorf("enc.Flush: %s", err) | |
2413 | } | |
2414 | if buf.Len() == 0 { | |
2415 | t.Errorf("enc.EncodeToken: empty buffer") | |
2416 | } | |
2417 | want := "<object2></object2>" | |
2418 | if buf.String() != want { | |
2419 | t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String()) | |
2420 | } | |
2421 | } | |
c2047754 ILT |
2422 | |
2423 | // Issue 16158. Decoder.unmarshalAttr ignores the return value of copyValue. | |
2424 | func TestIssue16158(t *testing.T) { | |
2425 | const data = `<foo b="HELLOWORLD"></foo>` | |
2426 | err := Unmarshal([]byte(data), &struct { | |
2427 | B byte `xml:"b,attr,omitempty"` | |
2428 | }{}) | |
bc998d03 ILT |
2429 | if err == nil { |
2430 | t.Errorf("Unmarshal: expected error, got nil") | |
c2047754 ILT |
2431 | } |
2432 | } | |
1a2f01ef ILT |
2433 | |
2434 | // Issue 20953. Crash on invalid XMLName attribute. | |
2435 | ||
2436 | type InvalidXMLName struct { | |
2437 | XMLName Name `xml:"error"` | |
2438 | Type struct { | |
2439 | XMLName Name `xml:"type,attr"` | |
2440 | } | |
2441 | } | |
2442 | ||
2443 | func TestInvalidXMLName(t *testing.T) { | |
2444 | var buf bytes.Buffer | |
2445 | enc := NewEncoder(&buf) | |
2446 | if err := enc.Encode(InvalidXMLName{}); err == nil { | |
2447 | t.Error("unexpected success") | |
2448 | } else if want := "invalid tag"; !strings.Contains(err.Error(), want) { | |
2449 | t.Errorf("error %q does not contain %q", err, want) | |
2450 | } | |
2451 | } |