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