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