github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/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 "testing" 16 "time" 17 ) 18 19 type DriveType int 20 21 const ( 22 HyperDrive DriveType = iota 23 ImprobabilityDrive 24 ) 25 26 type Passenger struct { 27 Name []string `xml:"name"` 28 Weight float32 `xml:"weight"` 29 } 30 31 type Ship struct { 32 XMLName struct{} `xml:"spaceship"` 33 34 Name string `xml:"name,attr"` 35 Pilot string `xml:"pilot,attr"` 36 Drive DriveType `xml:"drive"` 37 Age uint `xml:"age"` 38 Passenger []*Passenger `xml:"passenger"` 39 secret string 40 } 41 42 type NamedType string 43 44 type Port struct { 45 XMLName struct{} `xml:"port"` 46 Type string `xml:"type,attr,omitempty"` 47 Comment string `xml:",comment"` 48 Number string `xml:",chardata"` 49 } 50 51 type Domain struct { 52 XMLName struct{} `xml:"domain"` 53 Country string `xml:",attr,omitempty"` 54 Name []byte `xml:",chardata"` 55 Comment []byte `xml:",comment"` 56 } 57 58 type Book struct { 59 XMLName struct{} `xml:"book"` 60 Title string `xml:",chardata"` 61 } 62 63 type Event struct { 64 XMLName struct{} `xml:"event"` 65 Year int `xml:",chardata"` 66 } 67 68 type Movie struct { 69 XMLName struct{} `xml:"movie"` 70 Length uint `xml:",chardata"` 71 } 72 73 type Pi struct { 74 XMLName struct{} `xml:"pi"` 75 Approximation float32 `xml:",chardata"` 76 } 77 78 type Universe struct { 79 XMLName struct{} `xml:"universe"` 80 Visible float64 `xml:",chardata"` 81 } 82 83 type Particle struct { 84 XMLName struct{} `xml:"particle"` 85 HasMass bool `xml:",chardata"` 86 } 87 88 type Departure struct { 89 XMLName struct{} `xml:"departure"` 90 When time.Time `xml:",chardata"` 91 } 92 93 type SecretAgent struct { 94 XMLName struct{} `xml:"agent"` 95 Handle string `xml:"handle,attr"` 96 Identity string 97 Obfuscate string `xml:",innerxml"` 98 } 99 100 type NestedItems struct { 101 XMLName struct{} `xml:"result"` 102 Items []string `xml:">item"` 103 Item1 []string `xml:"Items>item1"` 104 } 105 106 type NestedOrder struct { 107 XMLName struct{} `xml:"result"` 108 Field1 string `xml:"parent>c"` 109 Field2 string `xml:"parent>b"` 110 Field3 string `xml:"parent>a"` 111 } 112 113 type MixedNested struct { 114 XMLName struct{} `xml:"result"` 115 A string `xml:"parent1>a"` 116 B string `xml:"b"` 117 C string `xml:"parent1>parent2>c"` 118 D string `xml:"parent1>d"` 119 } 120 121 type NilTest struct { 122 A interface{} `xml:"parent1>parent2>a"` 123 B interface{} `xml:"parent1>b"` 124 C interface{} `xml:"parent1>parent2>c"` 125 } 126 127 type Service struct { 128 XMLName struct{} `xml:"service"` 129 Domain *Domain `xml:"host>domain"` 130 Port *Port `xml:"host>port"` 131 Extra1 interface{} 132 Extra2 interface{} `xml:"host>extra2"` 133 } 134 135 var nilStruct *Ship 136 137 type EmbedA struct { 138 EmbedC 139 EmbedB EmbedB 140 FieldA string 141 } 142 143 type EmbedB struct { 144 FieldB string 145 *EmbedC 146 } 147 148 type EmbedC struct { 149 FieldA1 string `xml:"FieldA>A1"` 150 FieldA2 string `xml:"FieldA>A2"` 151 FieldB string 152 FieldC string 153 } 154 155 type NameCasing struct { 156 XMLName struct{} `xml:"casing"` 157 Xy string 158 XY string 159 XyA string `xml:"Xy,attr"` 160 XYA string `xml:"XY,attr"` 161 } 162 163 type NamePrecedence struct { 164 XMLName Name `xml:"Parent"` 165 FromTag XMLNameWithoutTag `xml:"InTag"` 166 FromNameVal XMLNameWithoutTag 167 FromNameTag XMLNameWithTag 168 InFieldName string 169 } 170 171 type XMLNameWithTag struct { 172 XMLName Name `xml:"InXMLNameTag"` 173 Value string `xml:",chardata"` 174 } 175 176 type XMLNameWithoutTag struct { 177 XMLName Name 178 Value string `xml:",chardata"` 179 } 180 181 type NameInField struct { 182 Foo Name `xml:"ns foo"` 183 } 184 185 type AttrTest struct { 186 Int int `xml:",attr"` 187 Named int `xml:"int,attr"` 188 Float float64 `xml:",attr"` 189 Uint8 uint8 `xml:",attr"` 190 Bool bool `xml:",attr"` 191 Str string `xml:",attr"` 192 Bytes []byte `xml:",attr"` 193 } 194 195 type OmitAttrTest struct { 196 Int int `xml:",attr,omitempty"` 197 Named int `xml:"int,attr,omitempty"` 198 Float float64 `xml:",attr,omitempty"` 199 Uint8 uint8 `xml:",attr,omitempty"` 200 Bool bool `xml:",attr,omitempty"` 201 Str string `xml:",attr,omitempty"` 202 Bytes []byte `xml:",attr,omitempty"` 203 } 204 205 type OmitFieldTest struct { 206 Int int `xml:",omitempty"` 207 Named int `xml:"int,omitempty"` 208 Float float64 `xml:",omitempty"` 209 Uint8 uint8 `xml:",omitempty"` 210 Bool bool `xml:",omitempty"` 211 Str string `xml:",omitempty"` 212 Bytes []byte `xml:",omitempty"` 213 Ptr *PresenceTest `xml:",omitempty"` 214 } 215 216 type AnyTest struct { 217 XMLName struct{} `xml:"a"` 218 Nested string `xml:"nested>value"` 219 AnyField AnyHolder `xml:",any"` 220 } 221 222 type AnyOmitTest struct { 223 XMLName struct{} `xml:"a"` 224 Nested string `xml:"nested>value"` 225 AnyField *AnyHolder `xml:",any,omitempty"` 226 } 227 228 type AnySliceTest struct { 229 XMLName struct{} `xml:"a"` 230 Nested string `xml:"nested>value"` 231 AnyField []AnyHolder `xml:",any"` 232 } 233 234 type AnyHolder struct { 235 XMLName Name 236 XML string `xml:",innerxml"` 237 } 238 239 type RecurseA struct { 240 A string 241 B *RecurseB 242 } 243 244 type RecurseB struct { 245 A *RecurseA 246 B string 247 } 248 249 type PresenceTest struct { 250 Exists *struct{} 251 } 252 253 type IgnoreTest struct { 254 PublicSecret string `xml:"-"` 255 } 256 257 type MyBytes []byte 258 259 type Data struct { 260 Bytes []byte 261 Attr []byte `xml:",attr"` 262 Custom MyBytes 263 } 264 265 type Plain struct { 266 V interface{} 267 } 268 269 type MyInt int 270 271 type EmbedInt struct { 272 MyInt 273 } 274 275 type Strings struct { 276 X []string `xml:"A>B,omitempty"` 277 } 278 279 type PointerFieldsTest struct { 280 XMLName Name `xml:"dummy"` 281 Name *string `xml:"name,attr"` 282 Age *uint `xml:"age,attr"` 283 Empty *string `xml:"empty,attr"` 284 Contents *string `xml:",chardata"` 285 } 286 287 type ChardataEmptyTest struct { 288 XMLName Name `xml:"test"` 289 Contents *string `xml:",chardata"` 290 } 291 292 type MyMarshalerTest struct { 293 } 294 295 var _ Marshaler = (*MyMarshalerTest)(nil) 296 297 func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error { 298 e.EncodeToken(start) 299 e.EncodeToken(CharData([]byte("hello world"))) 300 e.EncodeToken(EndElement{start.Name}) 301 return nil 302 } 303 304 type MyMarshalerAttrTest struct { 305 } 306 307 var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil) 308 309 func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) { 310 return Attr{name, "hello world"}, nil 311 } 312 313 type MarshalerStruct struct { 314 Foo MyMarshalerAttrTest `xml:",attr"` 315 } 316 317 type InnerStruct struct { 318 XMLName Name `xml:"testns outer"` 319 } 320 321 type OuterStruct struct { 322 InnerStruct 323 IntAttr int `xml:"int,attr"` 324 } 325 326 type OuterNamedStruct struct { 327 InnerStruct 328 XMLName Name `xml:"outerns test"` 329 IntAttr int `xml:"int,attr"` 330 } 331 332 type OuterNamedOrderedStruct struct { 333 XMLName Name `xml:"outerns test"` 334 InnerStruct 335 IntAttr int `xml:"int,attr"` 336 } 337 338 type OuterOuterStruct struct { 339 OuterStruct 340 } 341 342 func ifaceptr(x interface{}) interface{} { 343 return &x 344 } 345 346 var ( 347 nameAttr = "Sarah" 348 ageAttr = uint(12) 349 contentsAttr = "lorem ipsum" 350 ) 351 352 // Unless explicitly stated as such (or *Plain), all of the 353 // tests below are two-way tests. When introducing new tests, 354 // please try to make them two-way as well to ensure that 355 // marshalling and unmarshalling are as symmetrical as feasible. 356 var marshalTests = []struct { 357 Value interface{} 358 ExpectXML string 359 MarshalOnly bool 360 UnmarshalOnly bool 361 }{ 362 // Test nil marshals to nothing 363 {Value: nil, ExpectXML: ``, MarshalOnly: true}, 364 {Value: nilStruct, ExpectXML: ``, MarshalOnly: true}, 365 366 // Test value types 367 {Value: &Plain{true}, ExpectXML: `<Plain><V>true</V></Plain>`}, 368 {Value: &Plain{false}, ExpectXML: `<Plain><V>false</V></Plain>`}, 369 {Value: &Plain{int(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 370 {Value: &Plain{int8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 371 {Value: &Plain{int16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 372 {Value: &Plain{int32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 373 {Value: &Plain{uint(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 374 {Value: &Plain{uint8(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 375 {Value: &Plain{uint16(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 376 {Value: &Plain{uint32(42)}, ExpectXML: `<Plain><V>42</V></Plain>`}, 377 {Value: &Plain{float32(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, 378 {Value: &Plain{float64(1.25)}, ExpectXML: `<Plain><V>1.25</V></Plain>`}, 379 {Value: &Plain{uintptr(0xFFDD)}, ExpectXML: `<Plain><V>65501</V></Plain>`}, 380 {Value: &Plain{"gopher"}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, 381 {Value: &Plain{[]byte("gopher")}, ExpectXML: `<Plain><V>gopher</V></Plain>`}, 382 {Value: &Plain{"</>"}, ExpectXML: `<Plain><V></></V></Plain>`}, 383 {Value: &Plain{[]byte("</>")}, ExpectXML: `<Plain><V></></V></Plain>`}, 384 {Value: &Plain{[3]byte{'<', '/', '>'}}, ExpectXML: `<Plain><V></></V></Plain>`}, 385 {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`}, 386 {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, 387 {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, 388 {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`}, 389 390 // Test time. 391 { 392 Value: &Plain{time.Unix(1e9, 123456789).UTC()}, 393 ExpectXML: `<Plain><V>2001-09-09T01:46:40.123456789Z</V></Plain>`, 394 }, 395 396 // A pointer to struct{} may be used to test for an element's presence. 397 { 398 Value: &PresenceTest{new(struct{})}, 399 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`, 400 }, 401 { 402 Value: &PresenceTest{}, 403 ExpectXML: `<PresenceTest></PresenceTest>`, 404 }, 405 406 // A pointer to struct{} may be used to test for an element's presence. 407 { 408 Value: &PresenceTest{new(struct{})}, 409 ExpectXML: `<PresenceTest><Exists></Exists></PresenceTest>`, 410 }, 411 { 412 Value: &PresenceTest{}, 413 ExpectXML: `<PresenceTest></PresenceTest>`, 414 }, 415 416 // A []byte field is only nil if the element was not found. 417 { 418 Value: &Data{}, 419 ExpectXML: `<Data></Data>`, 420 UnmarshalOnly: true, 421 }, 422 { 423 Value: &Data{Bytes: []byte{}, Custom: MyBytes{}, Attr: []byte{}}, 424 ExpectXML: `<Data Attr=""><Bytes></Bytes><Custom></Custom></Data>`, 425 UnmarshalOnly: true, 426 }, 427 428 // Check that []byte works, including named []byte types. 429 { 430 Value: &Data{Bytes: []byte("ab"), Custom: MyBytes("cd"), Attr: []byte{'v'}}, 431 ExpectXML: `<Data Attr="v"><Bytes>ab</Bytes><Custom>cd</Custom></Data>`, 432 }, 433 434 // Test innerxml 435 { 436 Value: &SecretAgent{ 437 Handle: "007", 438 Identity: "James Bond", 439 Obfuscate: "<redacted/>", 440 }, 441 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, 442 MarshalOnly: true, 443 }, 444 { 445 Value: &SecretAgent{ 446 Handle: "007", 447 Identity: "James Bond", 448 Obfuscate: "<Identity>James Bond</Identity><redacted/>", 449 }, 450 ExpectXML: `<agent handle="007"><Identity>James Bond</Identity><redacted/></agent>`, 451 UnmarshalOnly: true, 452 }, 453 454 // Test structs 455 {Value: &Port{Type: "ssl", Number: "443"}, ExpectXML: `<port type="ssl">443</port>`}, 456 {Value: &Port{Number: "443"}, ExpectXML: `<port>443</port>`}, 457 {Value: &Port{Type: "<unix>"}, ExpectXML: `<port type="<unix>"></port>`}, 458 {Value: &Port{Number: "443", Comment: "https"}, ExpectXML: `<port><!--https-->443</port>`}, 459 {Value: &Port{Number: "443", Comment: "add space-"}, ExpectXML: `<port><!--add space- -->443</port>`, MarshalOnly: true}, 460 {Value: &Domain{Name: []byte("google.com&friends")}, ExpectXML: `<domain>google.com&friends</domain>`}, 461 {Value: &Domain{Name: []byte("google.com"), Comment: []byte(" &friends ")}, ExpectXML: `<domain>google.com<!-- &friends --></domain>`}, 462 {Value: &Book{Title: "Pride & Prejudice"}, ExpectXML: `<book>Pride & Prejudice</book>`}, 463 {Value: &Event{Year: -3114}, ExpectXML: `<event>-3114</event>`}, 464 {Value: &Movie{Length: 13440}, ExpectXML: `<movie>13440</movie>`}, 465 {Value: &Pi{Approximation: 3.14159265}, ExpectXML: `<pi>3.1415927</pi>`}, 466 {Value: &Universe{Visible: 9.3e13}, ExpectXML: `<universe>9.3e+13</universe>`}, 467 {Value: &Particle{HasMass: true}, ExpectXML: `<particle>true</particle>`}, 468 {Value: &Departure{When: ParseTime("2013-01-09T00:15:00-09:00")}, ExpectXML: `<departure>2013-01-09T00:15:00-09:00</departure>`}, 469 {Value: atomValue, ExpectXML: atomXml}, 470 { 471 Value: &Ship{ 472 Name: "Heart of Gold", 473 Pilot: "Computer", 474 Age: 1, 475 Drive: ImprobabilityDrive, 476 Passenger: []*Passenger{ 477 { 478 Name: []string{"Zaphod", "Beeblebrox"}, 479 Weight: 7.25, 480 }, 481 { 482 Name: []string{"Trisha", "McMillen"}, 483 Weight: 5.5, 484 }, 485 { 486 Name: []string{"Ford", "Prefect"}, 487 Weight: 7, 488 }, 489 { 490 Name: []string{"Arthur", "Dent"}, 491 Weight: 6.75, 492 }, 493 }, 494 }, 495 ExpectXML: `<spaceship name="Heart of Gold" pilot="Computer">` + 496 `<drive>` + strconv.Itoa(int(ImprobabilityDrive)) + `</drive>` + 497 `<age>1</age>` + 498 `<passenger>` + 499 `<name>Zaphod</name>` + 500 `<name>Beeblebrox</name>` + 501 `<weight>7.25</weight>` + 502 `</passenger>` + 503 `<passenger>` + 504 `<name>Trisha</name>` + 505 `<name>McMillen</name>` + 506 `<weight>5.5</weight>` + 507 `</passenger>` + 508 `<passenger>` + 509 `<name>Ford</name>` + 510 `<name>Prefect</name>` + 511 `<weight>7</weight>` + 512 `</passenger>` + 513 `<passenger>` + 514 `<name>Arthur</name>` + 515 `<name>Dent</name>` + 516 `<weight>6.75</weight>` + 517 `</passenger>` + 518 `</spaceship>`, 519 }, 520 521 // Test a>b 522 { 523 Value: &NestedItems{Items: nil, Item1: nil}, 524 ExpectXML: `<result>` + 525 `<Items>` + 526 `</Items>` + 527 `</result>`, 528 }, 529 { 530 Value: &NestedItems{Items: []string{}, Item1: []string{}}, 531 ExpectXML: `<result>` + 532 `<Items>` + 533 `</Items>` + 534 `</result>`, 535 MarshalOnly: true, 536 }, 537 { 538 Value: &NestedItems{Items: nil, Item1: []string{"A"}}, 539 ExpectXML: `<result>` + 540 `<Items>` + 541 `<item1>A</item1>` + 542 `</Items>` + 543 `</result>`, 544 }, 545 { 546 Value: &NestedItems{Items: []string{"A", "B"}, Item1: nil}, 547 ExpectXML: `<result>` + 548 `<Items>` + 549 `<item>A</item>` + 550 `<item>B</item>` + 551 `</Items>` + 552 `</result>`, 553 }, 554 { 555 Value: &NestedItems{Items: []string{"A", "B"}, Item1: []string{"C"}}, 556 ExpectXML: `<result>` + 557 `<Items>` + 558 `<item>A</item>` + 559 `<item>B</item>` + 560 `<item1>C</item1>` + 561 `</Items>` + 562 `</result>`, 563 }, 564 { 565 Value: &NestedOrder{Field1: "C", Field2: "B", Field3: "A"}, 566 ExpectXML: `<result>` + 567 `<parent>` + 568 `<c>C</c>` + 569 `<b>B</b>` + 570 `<a>A</a>` + 571 `</parent>` + 572 `</result>`, 573 }, 574 { 575 Value: &NilTest{A: "A", B: nil, C: "C"}, 576 ExpectXML: `<NilTest>` + 577 `<parent1>` + 578 `<parent2><a>A</a></parent2>` + 579 `<parent2><c>C</c></parent2>` + 580 `</parent1>` + 581 `</NilTest>`, 582 MarshalOnly: true, // Uses interface{} 583 }, 584 { 585 Value: &MixedNested{A: "A", B: "B", C: "C", D: "D"}, 586 ExpectXML: `<result>` + 587 `<parent1><a>A</a></parent1>` + 588 `<b>B</b>` + 589 `<parent1>` + 590 `<parent2><c>C</c></parent2>` + 591 `<d>D</d>` + 592 `</parent1>` + 593 `</result>`, 594 }, 595 { 596 Value: &Service{Port: &Port{Number: "80"}}, 597 ExpectXML: `<service><host><port>80</port></host></service>`, 598 }, 599 { 600 Value: &Service{}, 601 ExpectXML: `<service></service>`, 602 }, 603 { 604 Value: &Service{Port: &Port{Number: "80"}, Extra1: "A", Extra2: "B"}, 605 ExpectXML: `<service>` + 606 `<host><port>80</port></host>` + 607 `<Extra1>A</Extra1>` + 608 `<host><extra2>B</extra2></host>` + 609 `</service>`, 610 MarshalOnly: true, 611 }, 612 { 613 Value: &Service{Port: &Port{Number: "80"}, Extra2: "example"}, 614 ExpectXML: `<service>` + 615 `<host><port>80</port></host>` + 616 `<host><extra2>example</extra2></host>` + 617 `</service>`, 618 MarshalOnly: true, 619 }, 620 621 // Test struct embedding 622 { 623 Value: &EmbedA{ 624 EmbedC: EmbedC{ 625 FieldA1: "", // Shadowed by A.A 626 FieldA2: "", // Shadowed by A.A 627 FieldB: "A.C.B", 628 FieldC: "A.C.C", 629 }, 630 EmbedB: EmbedB{ 631 FieldB: "A.B.B", 632 EmbedC: &EmbedC{ 633 FieldA1: "A.B.C.A1", 634 FieldA2: "A.B.C.A2", 635 FieldB: "", // Shadowed by A.B.B 636 FieldC: "A.B.C.C", 637 }, 638 }, 639 FieldA: "A.A", 640 }, 641 ExpectXML: `<EmbedA>` + 642 `<FieldB>A.C.B</FieldB>` + 643 `<FieldC>A.C.C</FieldC>` + 644 `<EmbedB>` + 645 `<FieldB>A.B.B</FieldB>` + 646 `<FieldA>` + 647 `<A1>A.B.C.A1</A1>` + 648 `<A2>A.B.C.A2</A2>` + 649 `</FieldA>` + 650 `<FieldC>A.B.C.C</FieldC>` + 651 `</EmbedB>` + 652 `<FieldA>A.A</FieldA>` + 653 `</EmbedA>`, 654 }, 655 656 // Test that name casing matters 657 { 658 Value: &NameCasing{Xy: "mixed", XY: "upper", XyA: "mixedA", XYA: "upperA"}, 659 ExpectXML: `<casing Xy="mixedA" XY="upperA"><Xy>mixed</Xy><XY>upper</XY></casing>`, 660 }, 661 662 // Test the order in which the XML element name is chosen 663 { 664 Value: &NamePrecedence{ 665 FromTag: XMLNameWithoutTag{Value: "A"}, 666 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "InXMLName"}, Value: "B"}, 667 FromNameTag: XMLNameWithTag{Value: "C"}, 668 InFieldName: "D", 669 }, 670 ExpectXML: `<Parent>` + 671 `<InTag>A</InTag>` + 672 `<InXMLName>B</InXMLName>` + 673 `<InXMLNameTag>C</InXMLNameTag>` + 674 `<InFieldName>D</InFieldName>` + 675 `</Parent>`, 676 MarshalOnly: true, 677 }, 678 { 679 Value: &NamePrecedence{ 680 XMLName: Name{Local: "Parent"}, 681 FromTag: XMLNameWithoutTag{XMLName: Name{Local: "InTag"}, Value: "A"}, 682 FromNameVal: XMLNameWithoutTag{XMLName: Name{Local: "FromNameVal"}, Value: "B"}, 683 FromNameTag: XMLNameWithTag{XMLName: Name{Local: "InXMLNameTag"}, Value: "C"}, 684 InFieldName: "D", 685 }, 686 ExpectXML: `<Parent>` + 687 `<InTag>A</InTag>` + 688 `<FromNameVal>B</FromNameVal>` + 689 `<InXMLNameTag>C</InXMLNameTag>` + 690 `<InFieldName>D</InFieldName>` + 691 `</Parent>`, 692 UnmarshalOnly: true, 693 }, 694 695 // xml.Name works in a plain field as well. 696 { 697 Value: &NameInField{Name{Space: "ns", Local: "foo"}}, 698 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, 699 }, 700 { 701 Value: &NameInField{Name{Space: "ns", Local: "foo"}}, 702 ExpectXML: `<NameInField><foo xmlns="ns"><ignore></ignore></foo></NameInField>`, 703 UnmarshalOnly: true, 704 }, 705 706 // Marshaling zero xml.Name uses the tag or field name. 707 { 708 Value: &NameInField{}, 709 ExpectXML: `<NameInField><foo xmlns="ns"></foo></NameInField>`, 710 MarshalOnly: true, 711 }, 712 713 // Test attributes 714 { 715 Value: &AttrTest{ 716 Int: 8, 717 Named: 9, 718 Float: 23.5, 719 Uint8: 255, 720 Bool: true, 721 Str: "str", 722 Bytes: []byte("byt"), 723 }, 724 ExpectXML: `<AttrTest Int="8" int="9" Float="23.5" Uint8="255"` + 725 ` Bool="true" Str="str" Bytes="byt"></AttrTest>`, 726 }, 727 { 728 Value: &AttrTest{Bytes: []byte{}}, 729 ExpectXML: `<AttrTest Int="0" int="0" Float="0" Uint8="0"` + 730 ` Bool="false" Str="" Bytes=""></AttrTest>`, 731 }, 732 { 733 Value: &OmitAttrTest{ 734 Int: 8, 735 Named: 9, 736 Float: 23.5, 737 Uint8: 255, 738 Bool: true, 739 Str: "str", 740 Bytes: []byte("byt"), 741 }, 742 ExpectXML: `<OmitAttrTest Int="8" int="9" Float="23.5" Uint8="255"` + 743 ` Bool="true" Str="str" Bytes="byt"></OmitAttrTest>`, 744 }, 745 { 746 Value: &OmitAttrTest{}, 747 ExpectXML: `<OmitAttrTest></OmitAttrTest>`, 748 }, 749 750 // pointer fields 751 { 752 Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr}, 753 ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`, 754 MarshalOnly: true, 755 }, 756 757 // empty chardata pointer field 758 { 759 Value: &ChardataEmptyTest{}, 760 ExpectXML: `<test></test>`, 761 MarshalOnly: true, 762 }, 763 764 // omitempty on fields 765 { 766 Value: &OmitFieldTest{ 767 Int: 8, 768 Named: 9, 769 Float: 23.5, 770 Uint8: 255, 771 Bool: true, 772 Str: "str", 773 Bytes: []byte("byt"), 774 Ptr: &PresenceTest{}, 775 }, 776 ExpectXML: `<OmitFieldTest>` + 777 `<Int>8</Int>` + 778 `<int>9</int>` + 779 `<Float>23.5</Float>` + 780 `<Uint8>255</Uint8>` + 781 `<Bool>true</Bool>` + 782 `<Str>str</Str>` + 783 `<Bytes>byt</Bytes>` + 784 `<Ptr></Ptr>` + 785 `</OmitFieldTest>`, 786 }, 787 { 788 Value: &OmitFieldTest{}, 789 ExpectXML: `<OmitFieldTest></OmitFieldTest>`, 790 }, 791 792 // Test ",any" 793 { 794 ExpectXML: `<a><nested><value>known</value></nested><other><sub>unknown</sub></other></a>`, 795 Value: &AnyTest{ 796 Nested: "known", 797 AnyField: AnyHolder{ 798 XMLName: Name{Local: "other"}, 799 XML: "<sub>unknown</sub>", 800 }, 801 }, 802 }, 803 { 804 Value: &AnyTest{Nested: "known", 805 AnyField: AnyHolder{ 806 XML: "<unknown/>", 807 XMLName: Name{Local: "AnyField"}, 808 }, 809 }, 810 ExpectXML: `<a><nested><value>known</value></nested><AnyField><unknown/></AnyField></a>`, 811 }, 812 { 813 ExpectXML: `<a><nested><value>b</value></nested></a>`, 814 Value: &AnyOmitTest{ 815 Nested: "b", 816 }, 817 }, 818 { 819 ExpectXML: `<a><nested><value>b</value></nested><c><d>e</d></c><g xmlns="f"><h>i</h></g></a>`, 820 Value: &AnySliceTest{ 821 Nested: "b", 822 AnyField: []AnyHolder{ 823 { 824 XMLName: Name{Local: "c"}, 825 XML: "<d>e</d>", 826 }, 827 { 828 XMLName: Name{Space: "f", Local: "g"}, 829 XML: "<h>i</h>", 830 }, 831 }, 832 }, 833 }, 834 { 835 ExpectXML: `<a><nested><value>b</value></nested></a>`, 836 Value: &AnySliceTest{ 837 Nested: "b", 838 }, 839 }, 840 841 // Test recursive types. 842 { 843 Value: &RecurseA{ 844 A: "a1", 845 B: &RecurseB{ 846 A: &RecurseA{"a2", nil}, 847 B: "b1", 848 }, 849 }, 850 ExpectXML: `<RecurseA><A>a1</A><B><A><A>a2</A></A><B>b1</B></B></RecurseA>`, 851 }, 852 853 // Test ignoring fields via "-" tag 854 { 855 ExpectXML: `<IgnoreTest></IgnoreTest>`, 856 Value: &IgnoreTest{}, 857 }, 858 { 859 ExpectXML: `<IgnoreTest></IgnoreTest>`, 860 Value: &IgnoreTest{PublicSecret: "can't tell"}, 861 MarshalOnly: true, 862 }, 863 { 864 ExpectXML: `<IgnoreTest><PublicSecret>ignore me</PublicSecret></IgnoreTest>`, 865 Value: &IgnoreTest{}, 866 UnmarshalOnly: true, 867 }, 868 869 // Test escaping. 870 { 871 ExpectXML: `<a><nested><value>dquote: "; squote: '; ampersand: &; less: <; greater: >;</value></nested><empty></empty></a>`, 872 Value: &AnyTest{ 873 Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`, 874 AnyField: AnyHolder{XMLName: Name{Local: "empty"}}, 875 }, 876 }, 877 { 878 ExpectXML: `<a><nested><value>newline: 
; cr: 
; tab: 	;</value></nested><AnyField></AnyField></a>`, 879 Value: &AnyTest{ 880 Nested: "newline: \n; cr: \r; tab: \t;", 881 AnyField: AnyHolder{XMLName: Name{Local: "AnyField"}}, 882 }, 883 }, 884 { 885 ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>", 886 Value: &AnyTest{ 887 Nested: "1\n2\n3\n\n4\n5", 888 }, 889 UnmarshalOnly: true, 890 }, 891 { 892 ExpectXML: `<EmbedInt><MyInt>42</MyInt></EmbedInt>`, 893 Value: &EmbedInt{ 894 MyInt: 42, 895 }, 896 }, 897 // Test omitempty with parent chain; see golang.org/issue/4168. 898 { 899 ExpectXML: `<Strings><A></A></Strings>`, 900 Value: &Strings{}, 901 }, 902 // Custom marshalers. 903 { 904 ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`, 905 Value: &MyMarshalerTest{}, 906 }, 907 { 908 ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`, 909 Value: &MarshalerStruct{}, 910 }, 911 { 912 ExpectXML: `<outer xmlns="testns" int="10"></outer>`, 913 Value: &OuterStruct{IntAttr: 10}, 914 }, 915 { 916 ExpectXML: `<test xmlns="outerns" int="10"></test>`, 917 Value: &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, 918 }, 919 { 920 ExpectXML: `<test xmlns="outerns" int="10"></test>`, 921 Value: &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10}, 922 }, 923 { 924 ExpectXML: `<outer xmlns="testns" int="10"></outer>`, 925 Value: &OuterOuterStruct{OuterStruct{IntAttr: 10}}, 926 }, 927 } 928 929 func TestMarshal(t *testing.T) { 930 for idx, test := range marshalTests { 931 if test.UnmarshalOnly { 932 continue 933 } 934 data, err := Marshal(test.Value) 935 if err != nil { 936 t.Errorf("#%d: Error: %s", idx, err) 937 continue 938 } 939 if got, want := string(data), test.ExpectXML; got != want { 940 if strings.Contains(want, "\n") { 941 t.Errorf("#%d: marshal(%#v):\nHAVE:\n%s\nWANT:\n%s", idx, test.Value, got, want) 942 } else { 943 t.Errorf("#%d: marshal(%#v):\nhave %#q\nwant %#q", idx, test.Value, got, want) 944 } 945 } 946 } 947 } 948 949 type AttrParent struct { 950 X string `xml:"X>Y,attr"` 951 } 952 953 type BadAttr struct { 954 Name []string `xml:"name,attr"` 955 } 956 957 var marshalErrorTests = []struct { 958 Value interface{} 959 Err string 960 Kind reflect.Kind 961 }{ 962 { 963 Value: make(chan bool), 964 Err: "xml: unsupported type: chan bool", 965 Kind: reflect.Chan, 966 }, 967 { 968 Value: map[string]string{ 969 "question": "What do you get when you multiply six by nine?", 970 "answer": "42", 971 }, 972 Err: "xml: unsupported type: map[string]string", 973 Kind: reflect.Map, 974 }, 975 { 976 Value: map[*Ship]bool{nil: false}, 977 Err: "xml: unsupported type: map[*xml.Ship]bool", 978 Kind: reflect.Map, 979 }, 980 { 981 Value: &Domain{Comment: []byte("f--bar")}, 982 Err: `xml: comments must not contain "--"`, 983 }, 984 // Reject parent chain with attr, never worked; see golang.org/issue/5033. 985 { 986 Value: &AttrParent{}, 987 Err: `xml: X>Y chain not valid with attr flag`, 988 }, 989 { 990 Value: BadAttr{[]string{"X", "Y"}}, 991 Err: `xml: unsupported type: []string`, 992 }, 993 } 994 995 var marshalIndentTests = []struct { 996 Value interface{} 997 Prefix string 998 Indent string 999 ExpectXML string 1000 }{ 1001 { 1002 Value: &SecretAgent{ 1003 Handle: "007", 1004 Identity: "James Bond", 1005 Obfuscate: "<redacted/>", 1006 }, 1007 Prefix: "", 1008 Indent: "\t", 1009 ExpectXML: fmt.Sprintf("<agent handle=\"007\">\n\t<Identity>James Bond</Identity><redacted/>\n</agent>"), 1010 }, 1011 } 1012 1013 func TestMarshalErrors(t *testing.T) { 1014 for idx, test := range marshalErrorTests { 1015 data, err := Marshal(test.Value) 1016 if err == nil { 1017 t.Errorf("#%d: marshal(%#v) = [success] %q, want error %v", idx, test.Value, data, test.Err) 1018 continue 1019 } 1020 if err.Error() != test.Err { 1021 t.Errorf("#%d: marshal(%#v) = [error] %v, want %v", idx, test.Value, err, test.Err) 1022 } 1023 if test.Kind != reflect.Invalid { 1024 if kind := err.(*UnsupportedTypeError).Type.Kind(); kind != test.Kind { 1025 t.Errorf("#%d: marshal(%#v) = [error kind] %s, want %s", idx, test.Value, kind, test.Kind) 1026 } 1027 } 1028 } 1029 } 1030 1031 // Do invertibility testing on the various structures that we test 1032 func TestUnmarshal(t *testing.T) { 1033 for i, test := range marshalTests { 1034 if test.MarshalOnly { 1035 continue 1036 } 1037 if _, ok := test.Value.(*Plain); ok { 1038 continue 1039 } 1040 1041 vt := reflect.TypeOf(test.Value) 1042 dest := reflect.New(vt.Elem()).Interface() 1043 err := Unmarshal([]byte(test.ExpectXML), dest) 1044 1045 switch fix := dest.(type) { 1046 case *Feed: 1047 fix.Author.InnerXML = "" 1048 for i := range fix.Entry { 1049 fix.Entry[i].Author.InnerXML = "" 1050 } 1051 } 1052 1053 if err != nil { 1054 t.Errorf("#%d: unexpected error: %#v", i, err) 1055 } else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) { 1056 t.Errorf("#%d: unmarshal(%q):\nhave %#v\nwant %#v", i, test.ExpectXML, got, want) 1057 } 1058 } 1059 } 1060 1061 func TestMarshalIndent(t *testing.T) { 1062 for i, test := range marshalIndentTests { 1063 data, err := MarshalIndent(test.Value, test.Prefix, test.Indent) 1064 if err != nil { 1065 t.Errorf("#%d: Error: %s", i, err) 1066 continue 1067 } 1068 if got, want := string(data), test.ExpectXML; got != want { 1069 t.Errorf("#%d: MarshalIndent:\nGot:%s\nWant:\n%s", i, got, want) 1070 } 1071 } 1072 } 1073 1074 type limitedBytesWriter struct { 1075 w io.Writer 1076 remain int // until writes fail 1077 } 1078 1079 func (lw *limitedBytesWriter) Write(p []byte) (n int, err error) { 1080 if lw.remain <= 0 { 1081 println("error") 1082 return 0, errors.New("write limit hit") 1083 } 1084 if len(p) > lw.remain { 1085 p = p[:lw.remain] 1086 n, _ = lw.w.Write(p) 1087 lw.remain = 0 1088 return n, errors.New("write limit hit") 1089 } 1090 n, err = lw.w.Write(p) 1091 lw.remain -= n 1092 return n, err 1093 } 1094 1095 func TestMarshalWriteErrors(t *testing.T) { 1096 var buf bytes.Buffer 1097 const writeCap = 1024 1098 w := &limitedBytesWriter{&buf, writeCap} 1099 enc := NewEncoder(w) 1100 var err error 1101 var i int 1102 const n = 4000 1103 for i = 1; i <= n; i++ { 1104 err = enc.Encode(&Passenger{ 1105 Name: []string{"Alice", "Bob"}, 1106 Weight: 5, 1107 }) 1108 if err != nil { 1109 break 1110 } 1111 } 1112 if err == nil { 1113 t.Error("expected an error") 1114 } 1115 if i == n { 1116 t.Errorf("expected to fail before the end") 1117 } 1118 if buf.Len() != writeCap { 1119 t.Errorf("buf.Len() = %d; want %d", buf.Len(), writeCap) 1120 } 1121 } 1122 1123 func TestMarshalWriteIOErrors(t *testing.T) { 1124 enc := NewEncoder(errWriter{}) 1125 1126 expectErr := "unwritable" 1127 err := enc.Encode(&Passenger{}) 1128 if err == nil || err.Error() != expectErr { 1129 t.Errorf("EscapeTest = [error] %v, want %v", err, expectErr) 1130 } 1131 } 1132 1133 func TestMarshalFlush(t *testing.T) { 1134 var buf bytes.Buffer 1135 enc := NewEncoder(&buf) 1136 if err := enc.EncodeToken(CharData("hello world")); err != nil { 1137 t.Fatalf("enc.EncodeToken: %v", err) 1138 } 1139 if buf.Len() > 0 { 1140 t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes()) 1141 } 1142 if err := enc.Flush(); err != nil { 1143 t.Fatalf("enc.Flush: %v", err) 1144 } 1145 if buf.String() != "hello world" { 1146 t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world") 1147 } 1148 } 1149 1150 func BenchmarkMarshal(b *testing.B) { 1151 b.ReportAllocs() 1152 for i := 0; i < b.N; i++ { 1153 Marshal(atomValue) 1154 } 1155 } 1156 1157 func BenchmarkUnmarshal(b *testing.B) { 1158 b.ReportAllocs() 1159 xml := []byte(atomXml) 1160 for i := 0; i < b.N; i++ { 1161 Unmarshal(xml, &Feed{}) 1162 } 1163 } 1164 1165 // golang.org/issue/6556 1166 func TestStructPointerMarshal(t *testing.T) { 1167 type A struct { 1168 XMLName string `xml:"a"` 1169 B []interface{} 1170 } 1171 type C struct { 1172 XMLName Name 1173 Value string `xml:"value"` 1174 } 1175 1176 a := new(A) 1177 a.B = append(a.B, &C{ 1178 XMLName: Name{Local: "c"}, 1179 Value: "x", 1180 }) 1181 1182 b, err := Marshal(a) 1183 if err != nil { 1184 t.Fatal(err) 1185 } 1186 if x := string(b); x != "<a><c><value>x</value></c></a>" { 1187 t.Fatal(x) 1188 } 1189 var v A 1190 err = Unmarshal(b, &v) 1191 if err != nil { 1192 t.Fatal(err) 1193 } 1194 } 1195 1196 var encodeTokenTests = []struct { 1197 tok Token 1198 want string 1199 ok bool 1200 }{ 1201 {StartElement{Name{"space", "local"}, nil}, "<local xmlns=\"space\">", true}, 1202 {StartElement{Name{"space", ""}, nil}, "", false}, 1203 {EndElement{Name{"space", ""}}, "", false}, 1204 {CharData("foo"), "foo", true}, 1205 {Comment("foo"), "<!--foo-->", true}, 1206 {Comment("foo-->"), "", false}, 1207 {ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true}, 1208 {ProcInst{"", []byte("Instruction")}, "", false}, 1209 {ProcInst{"Target", []byte("Instruction?>")}, "", false}, 1210 {Directive("foo"), "<!foo>", true}, 1211 {Directive("foo>"), "", false}, 1212 } 1213 1214 func TestEncodeToken(t *testing.T) { 1215 for _, tt := range encodeTokenTests { 1216 var buf bytes.Buffer 1217 enc := NewEncoder(&buf) 1218 err := enc.EncodeToken(tt.tok) 1219 switch { 1220 case !tt.ok && err == nil: 1221 t.Errorf("enc.EncodeToken(%#v): expected error; got none", tt.tok) 1222 case tt.ok && err != nil: 1223 t.Fatalf("enc.EncodeToken: %v", err) 1224 case !tt.ok && err != nil: 1225 // expected error, got one 1226 } 1227 if err := enc.Flush(); err != nil { 1228 t.Fatalf("enc.EncodeToken: %v", err) 1229 } 1230 if got := buf.String(); got != tt.want { 1231 t.Errorf("enc.EncodeToken = %s; want: %s", got, tt.want) 1232 } 1233 } 1234 } 1235 1236 func TestProcInstEncodeToken(t *testing.T) { 1237 var buf bytes.Buffer 1238 enc := NewEncoder(&buf) 1239 1240 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil { 1241 t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err) 1242 } 1243 1244 if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil { 1245 t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst") 1246 } 1247 1248 if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil { 1249 t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token") 1250 } 1251 } 1252 1253 func TestDecodeEncode(t *testing.T) { 1254 var in, out bytes.Buffer 1255 in.WriteString(`<?xml version="1.0" encoding="UTF-8"?> 1256 <?Target Instruction?> 1257 <root> 1258 </root> 1259 `) 1260 dec := NewDecoder(&in) 1261 enc := NewEncoder(&out) 1262 for tok, err := dec.Token(); err == nil; tok, err = dec.Token() { 1263 err = enc.EncodeToken(tok) 1264 if err != nil { 1265 t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err) 1266 } 1267 } 1268 }