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