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 ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "reflect" 13 "strconv" 14 "strings" 15 "sync" 16 "testing" 17 "time" 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 { 33 XMLName struct{} `xml:"spaceship"` 34 35 Name string `xml:"name,attr"` 36 Pilot string `xml:"pilot,attr"` 37 Drive DriveType `xml:"drive"` 38 Age uint `xml:"age"` 39 Passenger []*Passenger `xml:"passenger"` 40 secret string 41 } 42 43 type NamedType string 44 45 type Port struct { 46 XMLName struct{} `xml:"port"` 47 Type string `xml:"type,attr,omitempty"` 48 Comment string `xml:",comment"` 49 Number string `xml:",chardata"` 50 } 51 52 type Domain struct { 53 XMLName struct{} `xml:"domain"` 54 Country string `xml:",attr,omitempty"` 55 Name []byte `xml:",chardata"` 56 Comment []byte `xml:",comment"` 57 } 58 59 type Book struct { 60 XMLName struct{} `xml:"book"` 61 Title string `xml:",chardata"` 62 } 63 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 94 type SecretAgent struct { 95 XMLName struct{} `xml:"agent"` 96 Handle string `xml:"handle,attr"` 97 Identity string 98 Obfuscate string `xml:",innerxml"` 99 } 100 101 type NestedItems struct { 102 XMLName struct{} `xml:"result"` 103 Items []string `xml:">item"` 104 Item1 []string `xml:"Items>item1"` 105 } 106 107 type NestedOrder struct { 108 XMLName struct{} `xml:"result"` 109 Field1 string `xml:"parent>c"` 110 Field2 string `xml:"parent>b"` 111 Field3 string `xml:"parent>a"` 112 } 113 114 type MixedNested struct { 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"` 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 { 129 XMLName struct{} `xml:"service"` 130 Domain *Domain `xml:"host>domain"` 131 Port *Port `xml:"host>port"` 132 Extra1 interface{} 133 Extra2 interface{} `xml:"host>extra2"` 134 } 135 136 var nilStruct *Ship 137 138 type EmbedA struct { 139 EmbedC 140 EmbedB EmbedB 141 FieldA string 142 } 143 144 type EmbedB struct { 145 FieldB string 146 *EmbedC 147 } 148 149 type EmbedC struct { 150 FieldA1 string `xml:"FieldA>A1"` 151 FieldA2 string `xml:"FieldA>A2"` 152 FieldB string 153 FieldC string 154 } 155 156 type 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 164 type NamePrecedence struct { 165 XMLName Name `xml:"Parent"` 166 FromTag XMLNameWithoutTag `xml:"InTag"` 167 FromNameVal XMLNameWithoutTag 168 FromNameTag XMLNameWithTag 169 InFieldName string 170 } 171 172 type XMLNameWithTag struct { 173 XMLName Name `xml:"InXMLNameTag"` 174 Value string `xml:",chardata"` 175 } 176 177 type XMLNameWithoutTag struct { 178 XMLName Name 179 Value string `xml:",chardata"` 180 } 181 182 type NameInField struct { 183 Foo Name `xml:"ns foo"` 184 } 185 186 type AttrTest struct { 187 Int int `xml:",attr"` 188 Named int `xml:"int,attr"` 189 Float float64 `xml:",attr"` 190 Uint8 uint8 `xml:",attr"` 191 Bool bool `xml:",attr"` 192 Str string `xml:",attr"` 193 Bytes []byte `xml:",attr"` 194 } 195 196 type 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 206 type 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"` 215 } 216 217 type AnyTest struct { 218 XMLName struct{} `xml:"a"` 219 Nested string `xml:"nested>value"` 220 AnyField AnyHolder `xml:",any"` 221 } 222 223 type AnyOmitTest struct { 224 XMLName struct{} `xml:"a"` 225 Nested string `xml:"nested>value"` 226 AnyField *AnyHolder `xml:",any,omitempty"` 227 } 228 229 type AnySliceTest struct { 230 XMLName struct{} `xml:"a"` 231 Nested string `xml:"nested>value"` 232 AnyField []AnyHolder `xml:",any"` 233 } 234 235 type AnyHolder struct { 236 XMLName Name 237 XML string `xml:",innerxml"` 238 } 239 240 type RecurseA struct { 241 A string 242 B *RecurseB 243 } 244 245 type RecurseB struct { 246 A *RecurseA 247 B string 248 } 249 250 type PresenceTest struct { 251 Exists *struct{} 252 } 253 254 type IgnoreTest struct { 255 PublicSecret string `xml:"-"` 256 } 257 258 type MyBytes []byte 259 260 type Data struct { 261 Bytes []byte 262 Attr []byte `xml:",attr"` 263 Custom MyBytes 264 } 265 266 type Plain struct { 267 V interface{} 268 } 269 270 type MyInt int 271 272 type EmbedInt struct { 273 MyInt 274 } 275 276 type Strings struct { 277 X []string `xml:"A>B,omitempty"` 278 } 279 280 type 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 288 type ChardataEmptyTest struct { 289 XMLName Name `xml:"test"` 290 Contents *string `xml:",chardata"` 291 } 292 293 type MyMarshalerTest struct { 294 } 295 296 var _ Marshaler = (*MyMarshalerTest)(nil) 297 298 func (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 305 type MyMarshalerAttrTest struct { 306 } 307 308 var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil) 309 310 func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) { 311 return Attr{name, "hello world"}, nil 312 } 313 314 type MarshalerStruct struct { 315 Foo MyMarshalerAttrTest `xml:",attr"` 316 } 317 318 type InnerStruct struct { 319 XMLName Name `xml:"testns outer"` 320 } 321 322 type OuterStruct struct { 323 InnerStruct 324 IntAttr int `xml:"int,attr"` 325 } 326 327 type OuterNamedStruct struct { 328 InnerStruct 329 XMLName Name `xml:"outerns test"` 330 IntAttr int `xml:"int,attr"` 331 } 332 333 type OuterNamedOrderedStruct struct { 334 XMLName Name `xml:"outerns test"` 335 InnerStruct 336 IntAttr int `xml:"int,attr"` 337 } 338 339 type OuterOuterStruct struct { 340 OuterStruct 341 } 342 343 type NestedAndChardata struct { 344 AB []string `xml:"A>B"` 345 Chardata string `xml:",chardata"` 346 } 347 348 type NestedAndComment struct { 349 AB []string `xml:"A>B"` 350 Comment string `xml:",comment"` 351 } 352 353 func ifaceptr(x interface{}) interface{} { 354 return &x 355 } 356 357 var ( 358 nameAttr = "Sarah" 359 ageAttr = uint(12) 360 contentsAttr = "lorem ipsum" 361 ) 362 363 // Unless explicitly stated as such (or *Plain), all of the 364 // tests below are two-way tests. When introducing new tests, 365 // please try to make them two-way as well to ensure that 366 // marshalling and unmarshalling are as symmetrical as feasible. 367 var marshalTests = []struct { 368 Value interface{} 369 ExpectXML string 370 MarshalOnly bool 371 UnmarshalOnly bool 372 }{ 373 // Test nil marshals to nothing 374 {Value: nil, ExpectXML: ``, MarshalOnly: true}, 375 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true}, 376 377 // Test value types 378 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`}, 379 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`}, 380 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 381 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 382 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 383 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 384 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 385 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 386 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 387 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 388 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, 389 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, 390 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`}, 391 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, 392 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, 393 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`}, 394 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`}, 395 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`}, 396 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`}, 397 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, 398 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, 399 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`}, 400 401 // Test time. 402 { 403 Value: &Plain{time.Unix(1e9, 123456789).UTC()}, 404 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`, 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 pointer to struct{} may be used to test for an element's presence. 418 { 419 Value: &PresenceTest{new(struct{})}, 420 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`, 421 }, 422 { 423 Value: &PresenceTest{}, 424 ExpectXML: `<PresenceTest></PresenceTest>`, 425 }, 426 427 // A []byte field is only nil if the element was not found. 428 { 429 Value: &Data{}, 430 ExpectXML: `<Data></Data>`, 431 UnmarshalOnly: true, 432 }, 433 { 434 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}}, 435 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`, 436 UnmarshalOnly: true, 437 }, 438 439 // Check that []byte works, including named []byte types. 440 { 441 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}}, 442 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`, 443 }, 444 445 // Test innerxml 446 { 447 Value: &SecretAgent{ 448 Handle: "007", 449 Identity: "James Bond", 450 Obfuscate: "<redacted/>", 451 }, 452 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, 453 MarshalOnly: true, 454 }, 455 { 456 Value: &SecretAgent{ 457 Handle: "007", 458 Identity: "James Bond", 459 Obfuscate: "<Identity>James Bond</Identity><redacted/>", 460 }, 461 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, 462 UnmarshalOnly: true, 463 }, 464 465 // Test structs 466 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`}, 467 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`}, 468 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`}, 469 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`}, 470 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true}, 471 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`}, 472 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`}, 473 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`}, 474 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`}, 475 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`}, 476 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`}, 477 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`}, 478 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`}, 479 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`}, 480 {Value: atomValue, ExpectXML: atomXml}, 481 { 482 Value: &Ship{ 483 Name: "Heart of Gold", 484 Pilot: "Computer", 485 Age: 1, 486 Drive: ImprobabilityDrive, 487 Passenger: []*Passenger{ 488 { 489 Name: []string{"Zaphod", "Beeblebrox"}, 490 Weight: 7.25, 491 }, 492 { 493 Name: []string{"Trisha", "McMillen"}, 494 Weight: 5.5, 495 }, 496 { 497 Name: []string{"Ford", "Prefect"}, 498 Weight: 7, 499 }, 500 { 501 Name: []string{"Arthur", "Dent"}, 502 Weight: 6.75, 503 }, 504 }, 505 }, 506 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` + 507 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` + 508 `<age>1</age>` + 509 `<passenger>` + 510 `<name>Zaphod</name>` + 511 `<name>Beeblebrox</name>` + 512 `<weight>7.25</weight>` + 513 `</passenger>` + 514 `<passenger>` + 515 `<name>Trisha</name>` + 516 `<name>McMillen</name>` + 517 `<weight>5.5</weight>` + 518 `</passenger>` + 519 `<passenger>` + 520 `<name>Ford</name>` + 521 `<name>Prefect</name>` + 522 `<weight>7</weight>` + 523 `</passenger>` + 524 `<passenger>` + 525 `<name>Arthur</name>` + 526 `<name>Dent</name>` + 527 `<weight>6.75</weight>` + 528 `</passenger>` + 529 `</spaceship>`, 530 }, 531 532 // Test a>b 533 { 534 Value: &NestedItems{Items: nil, Item1: nil}, 535 ExpectXML: `<result>` + 536 `<Items>` + 537 `</Items>` + 538 `</result>`, 539 }, 540 { 541 Value: &NestedItems{Items: []string{}, Item1: []string{}}, 542 ExpectXML: `<result>` + 543 `<Items>` + 544 `</Items>` + 545 `</result>`, 546 MarshalOnly: true, 547 }, 548 { 549 Value: &NestedItems{Items: nil, Item1: []string{"A"}}, 550 ExpectXML: `<result>` + 551 `<Items>` + 552 `<item1>A</item1>` + 553 `</Items>` + 554 `</result>`, 555 }, 556 { 557 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil}, 558 ExpectXML: `<result>` + 559 `<Items>` + 560 `<item>A</item>` + 561 `<item>B</item>` + 562 `</Items>` + 563 `</result>`, 564 }, 565 { 566 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}}, 567 ExpectXML: `<result>` + 568 `<Items>` + 569 `<item>A</item>` + 570 `<item>B</item>` + 571 `<item1>C</item1>` + 572 `</Items>` + 573 `</result>`, 574 }, 575 { 576 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"}, 577 ExpectXML: `<result>` + 578 `<parent>` + 579 `<c>C</c>` + 580 `<b>B</b>` + 581 `<a>A</a>` + 582 `</parent>` + 583 `</result>`, 584 }, 585 { 586 Value: &NilTest{A: "A", B: nil, C: "C"}, 587 ExpectXML: `<NilTest>` + 588 `<parent1>` + 589 `<parent2><a>A</a></parent2>` + 590 `<parent2><c>C</c></parent2>` + 591 `</parent1>` + 592 `</NilTest>`, 593 MarshalOnly: true, // Uses interface{} 594 }, 595 { 596 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"}, 597 ExpectXML: `<result>` + 598 `<parent1><a>A</a></parent1>` + 599 `<b>B</b>` + 600 `<parent1>` + 601 `<parent2><c>C</c></parent2>` + 602 `<d>D</d>` + 603 `</parent1>` + 604 `</result>`, 605 }, 606 { 607 Value: &Service{Port: &Port{Number: "80"}}, 608 ExpectXML: `<service><host><port>80</port></host></service>`, 609 }, 610 { 611 Value: &Service{}, 612 ExpectXML: `<service></service>`, 613 }, 614 { 615 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"}, 616 ExpectXML: `<service>` + 617 `<host><port>80</port></host>` + 618 `<Extra1>A</Extra1>` + 619 `<host><extra2>B</extra2></host>` + 620 `</service>`, 621 MarshalOnly: true, 622 }, 623 { 624 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"}, 625 ExpectXML: `<service>` + 626 `<host><port>80</port></host>` + 627 `<host><extra2>example</extra2></host>` + 628 `</service>`, 629 MarshalOnly: true, 630 }, 631 { 632 Value: &struct { 633 XMLName struct{} `xml:"space top"` 634 A string `xml:"x>a"` 635 B string `xml:"x>b"` 636 C string `xml:"space x>c"` 637 C1 string `xml:"space1 x>c"` 638 D1 string `xml:"space1 x>d"` 639 }{ 640 A: "a", 641 B: "b", 642 C: "c", 643 C1: "c1", 644 D1: "d1", 645 }, 646 ExpectXML: `<top xmlns="space">` + 647 `<x><a>a</a><b>b</b><c xmlns="space">c</c>` + 648 `<c xmlns="space1">c1</c>` + 649 `<d xmlns="space1">d1</d>` + 650 `</x>` + 651 `</top>`, 652 }, 653 { 654 Value: &struct { 655 XMLName Name 656 A string `xml:"x>a"` 657 B string `xml:"x>b"` 658 C string `xml:"space x>c"` 659 C1 string `xml:"space1 x>c"` 660 D1 string `xml:"space1 x>d"` 661 }{ 662 XMLName: Name{ 663 Space: "space0", 664 Local: "top", 665 }, 666 A: "a", 667 B: "b", 668 C: "c", 669 C1: "c1", 670 D1: "d1", 671 }, 672 ExpectXML: `<top xmlns="space0">` + 673 `<x><a>a</a><b>b</b>` + 674 `<c xmlns="space">c</c>` + 675 `<c xmlns="space1">c1</c>` + 676 `<d xmlns="space1">d1</d>` + 677 `</x>` + 678 `</top>`, 679 }, 680 { 681 Value: &struct { 682 XMLName struct{} `xml:"top"` 683 B string `xml:"space x>b"` 684 B1 string `xml:"space1 x>b"` 685 }{ 686 B: "b", 687 B1: "b1", 688 }, 689 ExpectXML: `<top>` + 690 `<x><b xmlns="space">b</b>` + 691 `<b xmlns="space1">b1</b></x>` + 692 `</top>`, 693 }, 694 695 // Test struct embedding 696 { 697 Value: &EmbedA{ 698 EmbedC: EmbedC{ 699 FieldA1: "", // Shadowed by A.A 700 FieldA2: "", // Shadowed by A.A 701 FieldB: "A.C.B", 702 FieldC: "A.C.C", 703 }, 704 EmbedB: EmbedB{ 705 FieldB: "A.B.B", 706 EmbedC: &EmbedC{ 707 FieldA1: "A.B.C.A1", 708 FieldA2: "A.B.C.A2", 709 FieldB: "", // Shadowed by A.B.B 710 FieldC: "A.B.C.C", 711 }, 712 }, 713 FieldA: "A.A", 714 }, 715 ExpectXML: `<EmbedA>` + 716 `<FieldB>A.C.B</FieldB>` + 717 `<FieldC>A.C.C</FieldC>` + 718 `<EmbedB>` + 719 `<FieldB>A.B.B</FieldB>` + 720 `<FieldA>` + 721 `<A1>A.B.C.A1</A1>` + 722 `<A2>A.B.C.A2</A2>` + 723 `</FieldA>` + 724 `<FieldC>A.B.C.C</FieldC>` + 725 `</EmbedB>` + 726 `<FieldA>A.A</FieldA>` + 727 `</EmbedA>`, 728 }, 729 730 // Test that name casing matters 731 { 732 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"}, 733 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`, 734 }, 735 736 // Test the order in which the XML element name is chosen 737 { 738 Value: &NamePrecedence{ 739 FromTag: XMLNameWithoutTag{Value: "A"}, 740 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"}, 741 FromNameTag: XMLNameWithTag{Value: "C"}, 742 InFieldName: "D", 743 }, 744 ExpectXML: `<Parent>` + 745 `<InTag>A</InTag>` + 746 `<InXMLName>B</InXMLName>` + 747 `<InXMLNameTag>C</InXMLNameTag>` + 748 `<InFieldName>D</InFieldName>` + 749 `</Parent>`, 750 MarshalOnly: true, 751 }, 752 { 753 Value: &NamePrecedence{ 754 XMLName: Name{Local: "Parent"}, 755 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"}, 756 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"}, 757 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"}, 758 InFieldName: "D", 759 }, 760 ExpectXML: `<Parent>` + 761 `<InTag>A</InTag>` + 762 `<FromNameVal>B</FromNameVal>` + 763 `<InXMLNameTag>C</InXMLNameTag>` + 764 `<InFieldName>D</InFieldName>` + 765 `</Parent>`, 766 UnmarshalOnly: true, 767 }, 768 769 // xml.Name works in a plain field as well. 770 { 771 Value: &NameInField{Name{Space: "ns", Local: "foo"}}, 772 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, 773 }, 774 { 775 Value: &NameInField{Name{Space: "ns", Local: "foo"}}, 776 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`, 777 UnmarshalOnly: true, 778 }, 779 780 // Marshaling zero xml.Name uses the tag or field name. 781 { 782 Value: &NameInField{}, 783 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, 784 MarshalOnly: true, 785 }, 786 787 // Test attributes 788 { 789 Value: &AttrTest{ 790 Int: 8, 791 Named: 9, 792 Float: 23.5, 793 Uint8: 255, 794 Bool: true, 795 Str: "str", 796 Bytes: []byte("byt"), 797 }, 798 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` + 799 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`, 800 }, 801 { 802 Value: &AttrTest{Bytes: []byte{}}, 803 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` + 804 ` Bool="false" Str="" Bytes=""></AttrTest>`, 805 }, 806 { 807 Value: &OmitAttrTest{ 808 Int: 8, 809 Named: 9, 810 Float: 23.5, 811 Uint8: 255, 812 Bool: true, 813 Str: "str", 814 Bytes: []byte("byt"), 815 }, 816 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` + 817 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`, 818 }, 819 { 820 Value: &OmitAttrTest{}, 821 ExpectXML: `<OmitAttrTest></OmitAttrTest>`, 822 }, 823 824 // pointer fields 825 { 826 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr}, 827 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`, 828 MarshalOnly: true, 829 }, 830 831 // empty chardata pointer field 832 { 833 Value: &ChardataEmptyTest{}, 834 ExpectXML: `<test></test>`, 835 MarshalOnly: true, 836 }, 837 838 // omitempty on fields 839 { 840 Value: &OmitFieldTest{ 841 Int: 8, 842 Named: 9, 843 Float: 23.5, 844 Uint8: 255, 845 Bool: true, 846 Str: "str", 847 Bytes: []byte("byt"), 848 Ptr: &PresenceTest{}, 849 }, 850 ExpectXML: `<OmitFieldTest>` + 851 `<Int>8</Int>` + 852 `<int>9</int>` + 853 `<Float>23.5</Float>` + 854 `<Uint8>255</Uint8>` + 855 `<Bool>true</Bool>` + 856 `<Str>str</Str>` + 857 `<Bytes>byt</Bytes>` + 858 `<Ptr></Ptr>` + 859 `</OmitFieldTest>`, 860 }, 861 { 862 Value: &OmitFieldTest{}, 863 ExpectXML: `<OmitFieldTest></OmitFieldTest>`, 864 }, 865 866 // Test ",any" 867 { 868 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`, 869 Value: &AnyTest{ 870 Nested: "known", 871 AnyField: AnyHolder{ 872 XMLName: Name{Local: "other"}, 873 XML: "<sub>unknown</sub>", 874 }, 875 }, 876 }, 877 { 878 Value: &AnyTest{Nested: "known", 879 AnyField: AnyHolder{ 880 XML: "<unknown/>", 881 XMLName: Name{Local: "AnyField"}, 882 }, 883 }, 884 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`, 885 }, 886 { 887 ExpectXML: `<a><nested><value>b</value></nested></a>`, 888 Value: &AnyOmitTest{ 889 Nested: "b", 890 }, 891 }, 892 { 893 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`, 894 Value: &AnySliceTest{ 895 Nested: "b", 896 AnyField: []AnyHolder{ 897 { 898 XMLName: Name{Local: "c"}, 899 XML: "<d>e</d>", 900 }, 901 { 902 XMLName: Name{Space: "f", Local: "g"}, 903 XML: "<h>i</h>", 904 }, 905 }, 906 }, 907 }, 908 { 909 ExpectXML: `<a><nested><value>b</value></nested></a>`, 910 Value: &AnySliceTest{ 911 Nested: "b", 912 }, 913 }, 914 915 // Test recursive types. 916 { 917 Value: &RecurseA{ 918 A: "a1", 919 B: &RecurseB{ 920 A: &RecurseA{"a2", nil}, 921 B: "b1", 922 }, 923 }, 924 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`, 925 }, 926 927 // Test ignoring fields via "-" tag 928 { 929 ExpectXML: `<IgnoreTest></IgnoreTest>`, 930 Value: &IgnoreTest{}, 931 }, 932 { 933 ExpectXML: `<IgnoreTest></IgnoreTest>`, 934 Value: &IgnoreTest{PublicSecret: "can't tell"}, 935 MarshalOnly: true, 936 }, 937 { 938 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`, 939 Value: &IgnoreTest{}, 940 UnmarshalOnly: true, 941 }, 942 943 // Test escaping. 944 { 945 ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`, 946 Value: &AnyTest{ 947 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`, 948 AnyField: AnyHolder{XMLName: Name{Local: "empty"}}, 949 }, 950 }, 951 { 952 ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`, 953 Value: &AnyTest{ 954 Nested: "newline: \n; cr: \r; tab: \t;", 955 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}}, 956 }, 957 }, 958 { 959 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>", 960 Value: &AnyTest{ 961 Nested: "1\n2\n3\n\n4\n5", 962 }, 963 UnmarshalOnly: true, 964 }, 965 { 966 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`, 967 Value: &EmbedInt{ 968 MyInt: 42, 969 }, 970 }, 971 // Test omitempty with parent chain; see golang.org/issue/4168. 972 { 973 ExpectXML: `<Strings><A></A></Strings>`, 974 Value: &Strings{}, 975 }, 976 // Custom marshalers. 977 { 978 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`, 979 Value: &MyMarshalerTest{}, 980 }, 981 { 982 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`, 983 Value: &MarshalerStruct{}, 984 }, 985 { 986 ExpectXML: `<outer xmlns="testns" int="10"></outer>`, 987 Value: &OuterStruct{IntAttr: 10}, 988 }, 989 { 990 ExpectXML: `<test xmlns="outerns" int="10"></test>`, 991 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, 992 }, 993 { 994 ExpectXML: `<test xmlns="outerns" int="10"></test>`, 995 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, 996 }, 997 { 998 ExpectXML: `<outer xmlns="testns" int="10"></outer>`, 999 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}}, 1000 }, 1001 { 1002 ExpectXML: `<NestedAndChardata><A><B></B><B></B></A>test</NestedAndChardata>`, 1003 Value: &NestedAndChardata{AB: make([]string, 2), Chardata: "test"}, 1004 }, 1005 { 1006 ExpectXML: `<NestedAndComment><A><B></B><B></B></A><!--test--></NestedAndComment>`, 1007 Value: &NestedAndComment{AB: make([]string, 2), Comment: "test"}, 1008 }, 1009 } 1010 1011 func TestMarshal(t *testing.T) { 1012 for idx, test := range marshalTests { 1013 if test.UnmarshalOnly { 1014 continue 1015 } 1016 data, err := Marshal(test.Value) 1017 if err != nil { 1018 t.Errorf("#%d: marshal(%#v): %s", idx, test.Value, err) 1019 continue 1020 } 1021 if got, want := string(data), test.ExpectXML; got != want { 1022 if strings.Contains(want, "\n") { 1023 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want) 1024 } else { 1025 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want) 1026 } 1027 } 1028 } 1029 } 1030 1031 type AttrParent struct { 1032 X string `xml:"X>Y,attr"` 1033 } 1034 1035 type BadAttr struct { 1036 Name []string `xml:"name,attr"` 1037 } 1038 1039 var marshalErrorTests = []struct { 1040 Value interface{} 1041 Err string 1042 Kind reflect.Kind 1043 }{ 1044 { 1045 Value: make(chan bool), 1046 Err: "xml: unsupported type: chan bool", 1047 Kind: reflect.Chan, 1048 }, 1049 { 1050 Value: map[string]string{ 1051 "question": "What do you get when you multiply six by nine?", 1052 "answer": "42", 1053 }, 1054 Err: "xml: unsupported type: map[string]string", 1055 Kind: reflect.Map, 1056 }, 1057 { 1058 Value: map[*Ship]bool{nil: false}, 1059 Err: "xml: unsupported type: map[*xml.Ship]bool", 1060 Kind: reflect.Map, 1061 }, 1062 { 1063 Value: &Domain{Comment: []byte("f--bar")}, 1064 Err: `xml: comments must not contain "--"`, 1065 }, 1066 // Reject parent chain with attr, never worked; see golang.org/issue/5033. 1067 { 1068 Value: &AttrParent{}, 1069 Err: `xml: X>Y chain not valid with attr flag`, 1070 }, 1071 { 1072 Value: BadAttr{[]string{"X", "Y"}}, 1073 Err: `xml: unsupported type: []string`, 1074 }, 1075 } 1076 1077 var marshalIndentTests = []struct { 1078 Value interface{} 1079 Prefix string 1080 Indent string 1081 ExpectXML string 1082 }{ 1083 { 1084 Value: &SecretAgent{ 1085 Handle: "007", 1086 Identity: "James Bond", 1087 Obfuscate: "<redacted/>", 1088 }, 1089 Prefix: "", 1090 Indent: "\t", 1091 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"), 1092 }, 1093 } 1094 1095 func TestMarshalErrors(t *testing.T) { 1096 for idx, test := range marshalErrorTests { 1097 data, err := Marshal(test.Value) 1098 if err == nil { 1099 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err) 1100 continue 1101 } 1102 if err.Error() != test.Err { 1103 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err) 1104 } 1105 if test.Kind != reflect.Invalid { 1106 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind { 1107 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind) 1108 } 1109 } 1110 } 1111 } 1112 1113 // Do invertibility testing on the various structures that we test 1114 func TestUnmarshal(t *testing.T) { 1115 for i, test := range marshalTests { 1116 if test.MarshalOnly { 1117 continue 1118 } 1119 if _, ok := test.Value.(*Plain); ok { 1120 continue 1121 } 1122 if test.ExpectXML == `<top>`+ 1123 `<x><b xmlns="space">b</b>`+ 1124 `<b xmlns="space1">b1</b></x>`+ 1125 `</top>` { 1126 // TODO(rogpeppe): re-enable this test in 1127 // https://go-review.googlesource.com/#/c/5910/ 1128 continue 1129 } 1130 1131 vt := reflect.TypeOf(test.Value) 1132 dest := reflect.New(vt.Elem()).Interface() 1133 err := Unmarshal([]byte(test.ExpectXML), dest) 1134 1135 switch fix := dest.(type) { 1136 case *Feed: 1137 fix.Author.InnerXML = "" 1138 for i := range fix.Entry { 1139 fix.Entry[i].Author.InnerXML = "" 1140 } 1141 } 1142 1143 if err != nil { 1144 t.Errorf("#%d: unexpected error: %#v", i, err) 1145 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) { 1146 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want) 1147 } 1148 } 1149 } 1150 1151 func TestMarshalIndent(t *testing.T) { 1152 for i, test := range marshalIndentTests { 1153 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent) 1154 if err != nil { 1155 t.Errorf("#%d: Error: %s", i, err) 1156 continue 1157 } 1158 if got, want := string(data), test.ExpectXML; got != want { 1159 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want) 1160 } 1161 } 1162 } 1163 1164 type limitedBytesWriter struct { 1165 w io.Writer 1166 remain int // until writes fail 1167 } 1168 1169 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) { 1170 if lw.remain <= 0 { 1171 println("error") 1172 return 0, errors.New("write limit hit") 1173 } 1174 if len(p) > lw.remain { 1175 p = p[:lw.remain] 1176 n, _ = lw.w.Write(p) 1177 lw.remain = 0 1178 return n, errors.New("write limit hit") 1179 } 1180 n, err = lw.w.Write(p) 1181 lw.remain -= n 1182 return n, err 1183 } 1184 1185 func TestMarshalWriteErrors(t *testing.T) { 1186 var buf bytes.Buffer 1187 const writeCap = 1024 1188 w := &limitedBytesWriter{&buf, writeCap} 1189 enc := NewEncoder(w) 1190 var err error 1191 var i int 1192 const n = 4000 1193 for i = 1; i <= n; i++ { 1194 err = enc.Encode(&Passenger{ 1195 Name: []string{"Alice", "Bob"}, 1196 Weight: 5, 1197 }) 1198 if err != nil { 1199 break 1200 } 1201 } 1202 if err == nil { 1203 t.Error("expected an error") 1204 } 1205 if i == n { 1206 t.Errorf("expected to fail before the end") 1207 } 1208 if buf.Len() != writeCap { 1209 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap) 1210 } 1211 } 1212 1213 func TestMarshalWriteIOErrors(t *testing.T) { 1214 enc := NewEncoder(errWriter{}) 1215 1216 expectErr := "unwritable" 1217 err := enc.Encode(&Passenger{}) 1218 if err == nil || err.Error() != expectErr { 1219 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr) 1220 } 1221 } 1222 1223 func TestMarshalFlush(t *testing.T) { 1224 var buf bytes.Buffer 1225 enc := NewEncoder(&buf) 1226 if err := enc.EncodeToken(CharData("hello world")); err != nil { 1227 t.Fatalf("enc.EncodeToken: %v", err) 1228 } 1229 if buf.Len() > 0 { 1230 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes()) 1231 } 1232 if err := enc.Flush(); err != nil { 1233 t.Fatalf("enc.Flush: %v", err) 1234 } 1235 if buf.String() != "hello world" { 1236 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world") 1237 } 1238 } 1239 1240 func BenchmarkMarshal(b *testing.B) { 1241 b.ReportAllocs() 1242 for i := 0; i < b.N; i++ { 1243 Marshal(atomValue) 1244 } 1245 } 1246 1247 func BenchmarkUnmarshal(b *testing.B) { 1248 b.ReportAllocs() 1249 xml := []byte(atomXml) 1250 for i := 0; i < b.N; i++ { 1251 Unmarshal(xml, &Feed{}) 1252 } 1253 } 1254 1255 // golang.org/issue/6556 1256 func TestStructPointerMarshal(t *testing.T) { 1257 type A struct { 1258 XMLName string `xml:"a"` 1259 B []interface{} 1260 } 1261 type C struct { 1262 XMLName Name 1263 Value string `xml:"value"` 1264 } 1265 1266 a := new(A) 1267 a.B = append(a.B, &C{ 1268 XMLName: Name{Local: "c"}, 1269 Value: "x", 1270 }) 1271 1272 b, err := Marshal(a) 1273 if err != nil { 1274 t.Fatal(err) 1275 } 1276 if x := string(b); x != "<a><c><value>x</value></c></a>" { 1277 t.Fatal(x) 1278 } 1279 var v A 1280 err = Unmarshal(b, &v) 1281 if err != nil { 1282 t.Fatal(err) 1283 } 1284 } 1285 1286 var encodeTokenTests = []struct { 1287 desc string 1288 toks []Token 1289 want string 1290 err string 1291 }{{ 1292 desc: "start element with name space", 1293 toks: []Token{ 1294 StartElement{Name{"space", "local"}, nil}, 1295 }, 1296 want: `<local xmlns="space">`, 1297 }, { 1298 desc: "start element with no name", 1299 toks: []Token{ 1300 StartElement{Name{"space", ""}, nil}, 1301 }, 1302 err: "xml: start tag with no name", 1303 }, { 1304 desc: "end element with no name", 1305 toks: []Token{ 1306 EndElement{Name{"space", ""}}, 1307 }, 1308 err: "xml: end tag with no name", 1309 }, { 1310 desc: "char data", 1311 toks: []Token{ 1312 CharData("foo"), 1313 }, 1314 want: `foo`, 1315 }, { 1316 desc: "char data with escaped chars", 1317 toks: []Token{ 1318 CharData(" \t\n"), 1319 }, 1320 want: " 	\n", 1321 }, { 1322 desc: "comment", 1323 toks: []Token{ 1324 Comment("foo"), 1325 }, 1326 want: `<!--foo-->`, 1327 }, { 1328 desc: "comment with invalid content", 1329 toks: []Token{ 1330 Comment("foo-->"), 1331 }, 1332 err: "xml: EncodeToken of Comment containing --> marker", 1333 }, { 1334 desc: "proc instruction", 1335 toks: []Token{ 1336 ProcInst{"Target", []byte("Instruction")}, 1337 }, 1338 want: `<?Target Instruction?>`, 1339 }, { 1340 desc: "proc instruction with empty target", 1341 toks: []Token{ 1342 ProcInst{"", []byte("Instruction")}, 1343 }, 1344 err: "xml: EncodeToken of ProcInst with invalid Target", 1345 }, { 1346 desc: "proc instruction with bad content", 1347 toks: []Token{ 1348 ProcInst{"", []byte("Instruction?>")}, 1349 }, 1350 err: "xml: EncodeToken of ProcInst with invalid Target", 1351 }, { 1352 desc: "directive", 1353 toks: []Token{ 1354 Directive("foo"), 1355 }, 1356 want: `<!foo>`, 1357 }, { 1358 desc: "more complex directive", 1359 toks: []Token{ 1360 Directive("DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]"), 1361 }, 1362 want: `<!DOCTYPE doc [ <!ELEMENT doc '>'> <!-- com>ment --> ]>`, 1363 }, { 1364 desc: "directive instruction with bad name", 1365 toks: []Token{ 1366 Directive("foo>"), 1367 }, 1368 err: "xml: EncodeToken of Directive containing wrong < or > markers", 1369 }, { 1370 desc: "end tag without start tag", 1371 toks: []Token{ 1372 EndElement{Name{"foo", "bar"}}, 1373 }, 1374 err: "xml: end tag </bar> without start tag", 1375 }, { 1376 desc: "mismatching end tag local name", 1377 toks: []Token{ 1378 StartElement{Name{"", "foo"}, nil}, 1379 EndElement{Name{"", "bar"}}, 1380 }, 1381 err: "xml: end tag </bar> does not match start tag <foo>", 1382 want: `<foo>`, 1383 }, { 1384 desc: "mismatching end tag namespace", 1385 toks: []Token{ 1386 StartElement{Name{"space", "foo"}, nil}, 1387 EndElement{Name{"another", "foo"}}, 1388 }, 1389 err: "xml: end tag </foo> in namespace another does not match start tag <foo> in namespace space", 1390 want: `<foo xmlns="space">`, 1391 }, { 1392 desc: "start element with explicit namespace", 1393 toks: []Token{ 1394 StartElement{Name{"space", "local"}, []Attr{ 1395 {Name{"xmlns", "x"}, "space"}, 1396 {Name{"space", "foo"}, "value"}, 1397 }}, 1398 }, 1399 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value">`, 1400 }, { 1401 desc: "start element with explicit namespace and colliding prefix", 1402 toks: []Token{ 1403 StartElement{Name{"space", "local"}, []Attr{ 1404 {Name{"xmlns", "x"}, "space"}, 1405 {Name{"space", "foo"}, "value"}, 1406 {Name{"x", "bar"}, "other"}, 1407 }}, 1408 }, 1409 want: `<local xmlns="space" xmlns:_xmlns="xmlns" _xmlns:x="space" xmlns:space="space" space:foo="value" xmlns:x="x" x:bar="other">`, 1410 }, { 1411 desc: "start element using previously defined namespace", 1412 toks: []Token{ 1413 StartElement{Name{"", "local"}, []Attr{ 1414 {Name{"xmlns", "x"}, "space"}, 1415 }}, 1416 StartElement{Name{"space", "foo"}, []Attr{ 1417 {Name{"space", "x"}, "y"}, 1418 }}, 1419 }, 1420 want: `<local xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns:space="space" space:x="y">`, 1421 }, { 1422 desc: "nested name space with same prefix", 1423 toks: []Token{ 1424 StartElement{Name{"", "foo"}, []Attr{ 1425 {Name{"xmlns", "x"}, "space1"}, 1426 }}, 1427 StartElement{Name{"", "foo"}, []Attr{ 1428 {Name{"xmlns", "x"}, "space2"}, 1429 }}, 1430 StartElement{Name{"", "foo"}, []Attr{ 1431 {Name{"space1", "a"}, "space1 value"}, 1432 {Name{"space2", "b"}, "space2 value"}, 1433 }}, 1434 EndElement{Name{"", "foo"}}, 1435 EndElement{Name{"", "foo"}}, 1436 StartElement{Name{"", "foo"}, []Attr{ 1437 {Name{"space1", "a"}, "space1 value"}, 1438 {Name{"space2", "b"}, "space2 value"}, 1439 }}, 1440 }, 1441 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">`, 1442 }, { 1443 desc: "start element defining several prefixes for the same name space", 1444 toks: []Token{ 1445 StartElement{Name{"space", "foo"}, []Attr{ 1446 {Name{"xmlns", "a"}, "space"}, 1447 {Name{"xmlns", "b"}, "space"}, 1448 {Name{"space", "x"}, "value"}, 1449 }}, 1450 }, 1451 want: `<foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:a="space" _xmlns:b="space" xmlns:space="space" space:x="value">`, 1452 }, { 1453 desc: "nested element redefines name space", 1454 toks: []Token{ 1455 StartElement{Name{"", "foo"}, []Attr{ 1456 {Name{"xmlns", "x"}, "space"}, 1457 }}, 1458 StartElement{Name{"space", "foo"}, []Attr{ 1459 {Name{"xmlns", "y"}, "space"}, 1460 {Name{"space", "a"}, "value"}, 1461 }}, 1462 }, 1463 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" _xmlns:y="space" xmlns:space="space" space:a="value">`, 1464 }, { 1465 desc: "nested element creates alias for default name space", 1466 toks: []Token{ 1467 StartElement{Name{"space", "foo"}, []Attr{ 1468 {Name{"", "xmlns"}, "space"}, 1469 }}, 1470 StartElement{Name{"space", "foo"}, []Attr{ 1471 {Name{"xmlns", "y"}, "space"}, 1472 {Name{"space", "a"}, "value"}, 1473 }}, 1474 }, 1475 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" xmlns:_xmlns="xmlns" _xmlns:y="space" xmlns:space="space" space:a="value">`, 1476 }, { 1477 desc: "nested element defines default name space with existing prefix", 1478 toks: []Token{ 1479 StartElement{Name{"", "foo"}, []Attr{ 1480 {Name{"xmlns", "x"}, "space"}, 1481 }}, 1482 StartElement{Name{"space", "foo"}, []Attr{ 1483 {Name{"", "xmlns"}, "space"}, 1484 {Name{"space", "a"}, "value"}, 1485 }}, 1486 }, 1487 want: `<foo xmlns:_xmlns="xmlns" _xmlns:x="space"><foo xmlns="space" xmlns="space" xmlns:space="space" space:a="value">`, 1488 }, { 1489 desc: "nested element uses empty attribute name space when default ns defined", 1490 toks: []Token{ 1491 StartElement{Name{"space", "foo"}, []Attr{ 1492 {Name{"", "xmlns"}, "space"}, 1493 }}, 1494 StartElement{Name{"space", "foo"}, []Attr{ 1495 {Name{"", "attr"}, "value"}, 1496 }}, 1497 }, 1498 want: `<foo xmlns="space" xmlns="space"><foo xmlns="space" attr="value">`, 1499 }, { 1500 desc: "redefine xmlns", 1501 toks: []Token{ 1502 StartElement{Name{"", "foo"}, []Attr{ 1503 {Name{"foo", "xmlns"}, "space"}, 1504 }}, 1505 }, 1506 want: `<foo xmlns:foo="foo" foo:xmlns="space">`, 1507 }, { 1508 desc: "xmlns with explicit name space #1", 1509 toks: []Token{ 1510 StartElement{Name{"space", "foo"}, []Attr{ 1511 {Name{"xml", "xmlns"}, "space"}, 1512 }}, 1513 }, 1514 want: `<foo xmlns="space" xmlns:_xml="xml" _xml:xmlns="space">`, 1515 }, { 1516 desc: "xmlns with explicit name space #2", 1517 toks: []Token{ 1518 StartElement{Name{"space", "foo"}, []Attr{ 1519 {Name{xmlURL, "xmlns"}, "space"}, 1520 }}, 1521 }, 1522 want: `<foo xmlns="space" xml:xmlns="space">`, 1523 }, { 1524 desc: "empty name space declaration is ignored", 1525 toks: []Token{ 1526 StartElement{Name{"", "foo"}, []Attr{ 1527 {Name{"xmlns", "foo"}, ""}, 1528 }}, 1529 }, 1530 want: `<foo xmlns:_xmlns="xmlns" _xmlns:foo="">`, 1531 }, { 1532 desc: "attribute with no name is ignored", 1533 toks: []Token{ 1534 StartElement{Name{"", "foo"}, []Attr{ 1535 {Name{"", ""}, "value"}, 1536 }}, 1537 }, 1538 want: `<foo>`, 1539 }, { 1540 desc: "namespace URL with non-valid name", 1541 toks: []Token{ 1542 StartElement{Name{"/34", "foo"}, []Attr{ 1543 {Name{"/34", "x"}, "value"}, 1544 }}, 1545 }, 1546 want: `<foo xmlns="/34" xmlns:_="/34" _:x="value">`, 1547 }, { 1548 desc: "nested element resets default namespace to empty", 1549 toks: []Token{ 1550 StartElement{Name{"space", "foo"}, []Attr{ 1551 {Name{"", "xmlns"}, "space"}, 1552 }}, 1553 StartElement{Name{"", "foo"}, []Attr{ 1554 {Name{"", "xmlns"}, ""}, 1555 {Name{"", "x"}, "value"}, 1556 {Name{"space", "x"}, "value"}, 1557 }}, 1558 }, 1559 want: `<foo xmlns="space" xmlns="space"><foo xmlns="" x="value" xmlns:space="space" space:x="value">`, 1560 }, { 1561 desc: "nested element requires empty default name space", 1562 toks: []Token{ 1563 StartElement{Name{"space", "foo"}, []Attr{ 1564 {Name{"", "xmlns"}, "space"}, 1565 }}, 1566 StartElement{Name{"", "foo"}, nil}, 1567 }, 1568 want: `<foo xmlns="space" xmlns="space"><foo>`, 1569 }, { 1570 desc: "attribute uses name space from xmlns", 1571 toks: []Token{ 1572 StartElement{Name{"some/space", "foo"}, []Attr{ 1573 {Name{"", "attr"}, "value"}, 1574 {Name{"some/space", "other"}, "other value"}, 1575 }}, 1576 }, 1577 want: `<foo xmlns="some/space" attr="value" xmlns:space="some/space" space:other="other value">`, 1578 }, { 1579 desc: "default name space should not be used by attributes", 1580 toks: []Token{ 1581 StartElement{Name{"space", "foo"}, []Attr{ 1582 {Name{"", "xmlns"}, "space"}, 1583 {Name{"xmlns", "bar"}, "space"}, 1584 {Name{"space", "baz"}, "foo"}, 1585 }}, 1586 StartElement{Name{"space", "baz"}, nil}, 1587 EndElement{Name{"space", "baz"}}, 1588 EndElement{Name{"space", "foo"}}, 1589 }, 1590 want: `<foo xmlns="space" xmlns="space" xmlns:_xmlns="xmlns" _xmlns:bar="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, 1591 }, { 1592 desc: "default name space not used by attributes, not explicitly defined", 1593 toks: []Token{ 1594 StartElement{Name{"space", "foo"}, []Attr{ 1595 {Name{"", "xmlns"}, "space"}, 1596 {Name{"space", "baz"}, "foo"}, 1597 }}, 1598 StartElement{Name{"space", "baz"}, nil}, 1599 EndElement{Name{"space", "baz"}}, 1600 EndElement{Name{"space", "foo"}}, 1601 }, 1602 want: `<foo xmlns="space" xmlns="space" xmlns:space="space" space:baz="foo"><baz xmlns="space"></baz></foo>`, 1603 }, { 1604 desc: "impossible xmlns declaration", 1605 toks: []Token{ 1606 StartElement{Name{"", "foo"}, []Attr{ 1607 {Name{"", "xmlns"}, "space"}, 1608 }}, 1609 StartElement{Name{"space", "bar"}, []Attr{ 1610 {Name{"space", "attr"}, "value"}, 1611 }}, 1612 }, 1613 want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`, 1614 }} 1615 1616 func TestEncodeToken(t *testing.T) { 1617 loop: 1618 for i, tt := range encodeTokenTests { 1619 var buf bytes.Buffer 1620 enc := NewEncoder(&buf) 1621 var err error 1622 for j, tok := range tt.toks { 1623 err = enc.EncodeToken(tok) 1624 if err != nil && j < len(tt.toks)-1 { 1625 t.Errorf("#%d %s token #%d: %v", i, tt.desc, j, err) 1626 continue loop 1627 } 1628 } 1629 errorf := func(f string, a ...interface{}) { 1630 t.Errorf("#%d %s token #%d:%s", i, tt.desc, len(tt.toks)-1, fmt.Sprintf(f, a...)) 1631 } 1632 switch { 1633 case tt.err != "" && err == nil: 1634 errorf(" expected error; got none") 1635 continue 1636 case tt.err == "" && err != nil: 1637 errorf(" got error: %v", err) 1638 continue 1639 case tt.err != "" && err != nil && tt.err != err.Error(): 1640 errorf(" error mismatch; got %v, want %v", err, tt.err) 1641 continue 1642 } 1643 if err := enc.Flush(); err != nil { 1644 errorf(" %v", err) 1645 continue 1646 } 1647 if got := buf.String(); got != tt.want { 1648 errorf("\ngot %v\nwant %v", got, tt.want) 1649 continue 1650 } 1651 } 1652 } 1653 1654 func TestProcInstEncodeToken(t *testing.T) { 1655 var buf bytes.Buffer 1656 enc := NewEncoder(&buf) 1657 1658 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil { 1659 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err) 1660 } 1661 1662 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil { 1663 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst") 1664 } 1665 1666 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil { 1667 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token") 1668 } 1669 } 1670 1671 func TestDecodeEncode(t *testing.T) { 1672 var in, out bytes.Buffer 1673 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?> 1674 <?Target Instruction?> 1675 <root> 1676 </root> 1677 `) 1678 dec := NewDecoder(&in) 1679 enc := NewEncoder(&out) 1680 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() { 1681 err = enc.EncodeToken(tok) 1682 if err != nil { 1683 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err) 1684 } 1685 } 1686 } 1687 1688 // Issue 9796. Used to fail with GORACE="halt_on_error=1" -race. 1689 func TestRace9796(t *testing.T) { 1690 type A struct{} 1691 type B struct { 1692 C []A `xml:"X>Y"` 1693 } 1694 var wg sync.WaitGroup 1695 for i := 0; i < 2; i++ { 1696 wg.Add(1) 1697 go func() { 1698 Marshal(B{[]A{A{}}}) 1699 wg.Done() 1700 }() 1701 } 1702 wg.Wait() 1703 } 1704 1705 func TestIsValidDirective(t *testing.T) { 1706 testOK := []string{ 1707 "<>", 1708 "< < > >", 1709 "<!DOCTYPE '<' '>' '>' <!--nothing-->>", 1710 "<!DOCTYPE doc [ <!ELEMENT doc ANY> <!ELEMENT doc ANY> ]>", 1711 "<!DOCTYPE doc [ <!ELEMENT doc \"ANY> '<' <!E\" LEMENT '>' doc ANY> ]>", 1712 "<!DOCTYPE doc <!-- just>>>> a < comment --> [ <!ITEM anything> ] >", 1713 } 1714 testKO := []string{ 1715 "<", 1716 ">", 1717 "<!--", 1718 "-->", 1719 "< > > < < >", 1720 "<!dummy <!-- > -->", 1721 "<!DOCTYPE doc '>", 1722 "<!DOCTYPE doc '>'", 1723 "<!DOCTYPE doc <!--comment>", 1724 } 1725 for _, s := range testOK { 1726 if !isValidDirective(Directive(s)) { 1727 t.Errorf("Directive %q is expected to be valid", s) 1728 } 1729 } 1730 for _, s := range testKO { 1731 if isValidDirective(Directive(s)) { 1732 t.Errorf("Directive %q is expected to be invalid", s) 1733 } 1734 } 1735 } 1736 1737 // Issue 11719. EncodeToken used to silently eat tokens with an invalid type. 1738 func TestSimpleUseOfEncodeToken(t *testing.T) { 1739 var buf bytes.Buffer 1740 enc := NewEncoder(&buf) 1741 if err := enc.EncodeToken(&StartElement{Name: Name{"", "object1"}}); err == nil { 1742 t.Errorf("enc.EncodeToken: pointer type should be rejected") 1743 } 1744 if err := enc.EncodeToken(&EndElement{Name: Name{"", "object1"}}); err == nil { 1745 t.Errorf("enc.EncodeToken: pointer type should be rejected") 1746 } 1747 if err := enc.EncodeToken(StartElement{Name: Name{"", "object2"}}); err != nil { 1748 t.Errorf("enc.EncodeToken: StartElement %s", err) 1749 } 1750 if err := enc.EncodeToken(EndElement{Name: Name{"", "object2"}}); err != nil { 1751 t.Errorf("enc.EncodeToken: EndElement %s", err) 1752 } 1753 if err := enc.EncodeToken(Universe{}); err == nil { 1754 t.Errorf("enc.EncodeToken: invalid type not caught") 1755 } 1756 if err := enc.Flush(); err != nil { 1757 t.Errorf("enc.Flush: %s", err) 1758 } 1759 if buf.Len() == 0 { 1760 t.Errorf("enc.EncodeToken: empty buffer") 1761 } 1762 want := "<object2></object2>" 1763 if buf.String() != want { 1764 t.Errorf("enc.EncodeToken: expected %q; got %q", want, buf.String()) 1765 } 1766 } 1767