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