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