golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/webdav/internal/xml/read_test.go (about) 1 // Copyright 2009 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 "fmt" 10 "io" 11 "reflect" 12 "strings" 13 "testing" 14 "time" 15 ) 16 17 // Stripped down Atom feed data structures. 18 19 func TestUnmarshalFeed(t *testing.T) { 20 var f Feed 21 if err := Unmarshal([]byte(atomFeedString), &f); err != nil { 22 t.Fatalf("Unmarshal: %s", err) 23 } 24 if !reflect.DeepEqual(f, atomFeed) { 25 t.Fatalf("have %#v\nwant %#v", f, atomFeed) 26 } 27 } 28 29 // hget http://codereview.appspot.com/rss/mine/rsc 30 const atomFeedString = ` 31 <?xml version="1.0" encoding="utf-8"?> 32 <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us" updated="2009-10-04T01:35:58+00:00"><title>Code Review - My issues</title><link href="http://codereview.appspot.com/" rel="alternate"></link><link href="http://codereview.appspot.com/rss/mine/rsc" rel="self"></link><id>http://codereview.appspot.com/</id><author><name>rietveld<></name></author><entry><title>rietveld: an attempt at pubsubhubbub 33 </title><link href="http://codereview.appspot.com/126085" rel="alternate"></link><updated>2009-10-04T01:35:58+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:134d9179c41f806be79b3a5f7877d19a</id><summary type="html"> 34 An attempt at adding pubsubhubbub support to Rietveld. 35 http://code.google.com/p/pubsubhubbub 36 http://code.google.com/p/rietveld/issues/detail?id=155 37 38 The server side of the protocol is trivial: 39 1. add a &lt;link rel=&quot;hub&quot; href=&quot;hub-server&quot;&gt; tag to all 40 feeds that will be pubsubhubbubbed. 41 2. every time one of those feeds changes, tell the hub 42 with a simple POST request. 43 44 I have tested this by adding debug prints to a local hub 45 server and checking that the server got the right publish 46 requests. 47 48 I can&#39;t quite get the server to work, but I think the bug 49 is not in my code. I think that the server expects to be 50 able to grab the feed and see the feed&#39;s actual URL in 51 the link rel=&quot;self&quot;, but the default value for that drops 52 the :port from the URL, and I cannot for the life of me 53 figure out how to get the Atom generator deep inside 54 django not to do that, or even where it is doing that, 55 or even what code is running to generate the Atom feed. 56 (I thought I knew but I added some assert False statements 57 and it kept running!) 58 59 Ignoring that particular problem, I would appreciate 60 feedback on the right way to get the two values at 61 the top of feeds.py marked NOTE(rsc). 62 63 64 </summary></entry><entry><title>rietveld: correct tab handling 65 </title><link href="http://codereview.appspot.com/124106" rel="alternate"></link><updated>2009-10-03T23:02:17+00:00</updated><author><name>email-address-removed</name></author><id>urn:md5:0a2a4f19bb815101f0ba2904aed7c35a</id><summary type="html"> 66 This fixes the buggy tab rendering that can be seen at 67 http://codereview.appspot.com/116075/diff/1/2 68 69 The fundamental problem was that the tab code was 70 not being told what column the text began in, so it 71 didn&#39;t know where to put the tab stops. Another problem 72 was that some of the code assumed that string byte 73 offsets were the same as column offsets, which is only 74 true if there are no tabs. 75 76 In the process of fixing this, I cleaned up the arguments 77 to Fold and ExpandTabs and renamed them Break and 78 _ExpandTabs so that I could be sure that I found all the 79 call sites. I also wanted to verify that ExpandTabs was 80 not being used from outside intra_region_diff.py. 81 82 83 </summary></entry></feed> ` 84 85 type Feed struct { 86 XMLName Name `xml:"http://www.w3.org/2005/Atom feed"` 87 Title string `xml:"title"` 88 Id string `xml:"id"` 89 Link []Link `xml:"link"` 90 Updated time.Time `xml:"updated,attr"` 91 Author Person `xml:"author"` 92 Entry []Entry `xml:"entry"` 93 } 94 95 type Entry struct { 96 Title string `xml:"title"` 97 Id string `xml:"id"` 98 Link []Link `xml:"link"` 99 Updated time.Time `xml:"updated"` 100 Author Person `xml:"author"` 101 Summary Text `xml:"summary"` 102 } 103 104 type Link struct { 105 Rel string `xml:"rel,attr,omitempty"` 106 Href string `xml:"href,attr"` 107 } 108 109 type Person struct { 110 Name string `xml:"name"` 111 URI string `xml:"uri"` 112 Email string `xml:"email"` 113 InnerXML string `xml:",innerxml"` 114 } 115 116 type Text struct { 117 Type string `xml:"type,attr,omitempty"` 118 Body string `xml:",chardata"` 119 } 120 121 var atomFeed = Feed{ 122 XMLName: Name{"http://www.w3.org/2005/Atom", "feed"}, 123 Title: "Code Review - My issues", 124 Link: []Link{ 125 {Rel: "alternate", Href: "http://codereview.appspot.com/"}, 126 {Rel: "self", Href: "http://codereview.appspot.com/rss/mine/rsc"}, 127 }, 128 Id: "http://codereview.appspot.com/", 129 Updated: ParseTime("2009-10-04T01:35:58+00:00"), 130 Author: Person{ 131 Name: "rietveld<>", 132 InnerXML: "<name>rietveld<></name>", 133 }, 134 Entry: []Entry{ 135 { 136 Title: "rietveld: an attempt at pubsubhubbub\n", 137 Link: []Link{ 138 {Rel: "alternate", Href: "http://codereview.appspot.com/126085"}, 139 }, 140 Updated: ParseTime("2009-10-04T01:35:58+00:00"), 141 Author: Person{ 142 Name: "email-address-removed", 143 InnerXML: "<name>email-address-removed</name>", 144 }, 145 Id: "urn:md5:134d9179c41f806be79b3a5f7877d19a", 146 Summary: Text{ 147 Type: "html", 148 Body: ` 149 An attempt at adding pubsubhubbub support to Rietveld. 150 http://code.google.com/p/pubsubhubbub 151 http://code.google.com/p/rietveld/issues/detail?id=155 152 153 The server side of the protocol is trivial: 154 1. add a <link rel="hub" href="hub-server"> tag to all 155 feeds that will be pubsubhubbubbed. 156 2. every time one of those feeds changes, tell the hub 157 with a simple POST request. 158 159 I have tested this by adding debug prints to a local hub 160 server and checking that the server got the right publish 161 requests. 162 163 I can't quite get the server to work, but I think the bug 164 is not in my code. I think that the server expects to be 165 able to grab the feed and see the feed's actual URL in 166 the link rel="self", but the default value for that drops 167 the :port from the URL, and I cannot for the life of me 168 figure out how to get the Atom generator deep inside 169 django not to do that, or even where it is doing that, 170 or even what code is running to generate the Atom feed. 171 (I thought I knew but I added some assert False statements 172 and it kept running!) 173 174 Ignoring that particular problem, I would appreciate 175 feedback on the right way to get the two values at 176 the top of feeds.py marked NOTE(rsc). 177 178 179 `, 180 }, 181 }, 182 { 183 Title: "rietveld: correct tab handling\n", 184 Link: []Link{ 185 {Rel: "alternate", Href: "http://codereview.appspot.com/124106"}, 186 }, 187 Updated: ParseTime("2009-10-03T23:02:17+00:00"), 188 Author: Person{ 189 Name: "email-address-removed", 190 InnerXML: "<name>email-address-removed</name>", 191 }, 192 Id: "urn:md5:0a2a4f19bb815101f0ba2904aed7c35a", 193 Summary: Text{ 194 Type: "html", 195 Body: ` 196 This fixes the buggy tab rendering that can be seen at 197 http://codereview.appspot.com/116075/diff/1/2 198 199 The fundamental problem was that the tab code was 200 not being told what column the text began in, so it 201 didn't know where to put the tab stops. Another problem 202 was that some of the code assumed that string byte 203 offsets were the same as column offsets, which is only 204 true if there are no tabs. 205 206 In the process of fixing this, I cleaned up the arguments 207 to Fold and ExpandTabs and renamed them Break and 208 _ExpandTabs so that I could be sure that I found all the 209 call sites. I also wanted to verify that ExpandTabs was 210 not being used from outside intra_region_diff.py. 211 212 213 `, 214 }, 215 }, 216 }, 217 } 218 219 const pathTestString = ` 220 <Result> 221 <Before>1</Before> 222 <Items> 223 <Item1> 224 <Value>A</Value> 225 </Item1> 226 <Item2> 227 <Value>B</Value> 228 </Item2> 229 <Item1> 230 <Value>C</Value> 231 <Value>D</Value> 232 </Item1> 233 <_> 234 <Value>E</Value> 235 </_> 236 </Items> 237 <After>2</After> 238 </Result> 239 ` 240 241 type PathTestItem struct { 242 Value string 243 } 244 245 type PathTestA struct { 246 Items []PathTestItem `xml:">Item1"` 247 Before, After string 248 } 249 250 type PathTestB struct { 251 Other []PathTestItem `xml:"Items>Item1"` 252 Before, After string 253 } 254 255 type PathTestC struct { 256 Values1 []string `xml:"Items>Item1>Value"` 257 Values2 []string `xml:"Items>Item2>Value"` 258 Before, After string 259 } 260 261 type PathTestSet struct { 262 Item1 []PathTestItem 263 } 264 265 type PathTestD struct { 266 Other PathTestSet `xml:"Items"` 267 Before, After string 268 } 269 270 type PathTestE struct { 271 Underline string `xml:"Items>_>Value"` 272 Before, After string 273 } 274 275 var pathTests = []interface{}{ 276 &PathTestA{Items: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"}, 277 &PathTestB{Other: []PathTestItem{{"A"}, {"D"}}, Before: "1", After: "2"}, 278 &PathTestC{Values1: []string{"A", "C", "D"}, Values2: []string{"B"}, Before: "1", After: "2"}, 279 &PathTestD{Other: PathTestSet{Item1: []PathTestItem{{"A"}, {"D"}}}, Before: "1", After: "2"}, 280 &PathTestE{Underline: "E", Before: "1", After: "2"}, 281 } 282 283 func TestUnmarshalPaths(t *testing.T) { 284 for _, pt := range pathTests { 285 v := reflect.New(reflect.TypeOf(pt).Elem()).Interface() 286 if err := Unmarshal([]byte(pathTestString), v); err != nil { 287 t.Fatalf("Unmarshal: %s", err) 288 } 289 if !reflect.DeepEqual(v, pt) { 290 t.Fatalf("have %#v\nwant %#v", v, pt) 291 } 292 } 293 } 294 295 type BadPathTestA struct { 296 First string `xml:"items>item1"` 297 Other string `xml:"items>item2"` 298 Second string `xml:"items"` 299 } 300 301 type BadPathTestB struct { 302 Other string `xml:"items>item2>value"` 303 First string `xml:"items>item1"` 304 Second string `xml:"items>item1>value"` 305 } 306 307 type BadPathTestC struct { 308 First string 309 Second string `xml:"First"` 310 } 311 312 type BadPathTestD struct { 313 BadPathEmbeddedA 314 BadPathEmbeddedB 315 } 316 317 type BadPathEmbeddedA struct { 318 First string 319 } 320 321 type BadPathEmbeddedB struct { 322 Second string `xml:"First"` 323 } 324 325 var badPathTests = []struct { 326 v, e interface{} 327 }{ 328 {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items"}}, 329 {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}}, 330 {&BadPathTestC{}, &TagPathError{reflect.TypeOf(BadPathTestC{}), "First", "", "Second", "First"}}, 331 {&BadPathTestD{}, &TagPathError{reflect.TypeOf(BadPathTestD{}), "First", "", "Second", "First"}}, 332 } 333 334 func TestUnmarshalBadPaths(t *testing.T) { 335 for _, tt := range badPathTests { 336 err := Unmarshal([]byte(pathTestString), tt.v) 337 if !reflect.DeepEqual(err, tt.e) { 338 t.Fatalf("Unmarshal with %#v didn't fail properly:\nhave %#v,\nwant %#v", tt.v, err, tt.e) 339 } 340 } 341 } 342 343 const OK = "OK" 344 const withoutNameTypeData = ` 345 <?xml version="1.0" charset="utf-8"?> 346 <Test3 Attr="OK" />` 347 348 type TestThree struct { 349 XMLName Name `xml:"Test3"` 350 Attr string `xml:",attr"` 351 } 352 353 func TestUnmarshalWithoutNameType(t *testing.T) { 354 var x TestThree 355 if err := Unmarshal([]byte(withoutNameTypeData), &x); err != nil { 356 t.Fatalf("Unmarshal: %s", err) 357 } 358 if x.Attr != OK { 359 t.Fatalf("have %v\nwant %v", x.Attr, OK) 360 } 361 } 362 363 func TestUnmarshalAttr(t *testing.T) { 364 type ParamVal struct { 365 Int int `xml:"int,attr"` 366 } 367 368 type ParamPtr struct { 369 Int *int `xml:"int,attr"` 370 } 371 372 type ParamStringPtr struct { 373 Int *string `xml:"int,attr"` 374 } 375 376 x := []byte(`<Param int="1" />`) 377 378 p1 := &ParamPtr{} 379 if err := Unmarshal(x, p1); err != nil { 380 t.Fatalf("Unmarshal: %s", err) 381 } 382 if p1.Int == nil { 383 t.Fatalf("Unmarshal failed in to *int field") 384 } else if *p1.Int != 1 { 385 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p1.Int, 1) 386 } 387 388 p2 := &ParamVal{} 389 if err := Unmarshal(x, p2); err != nil { 390 t.Fatalf("Unmarshal: %s", err) 391 } 392 if p2.Int != 1 { 393 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p2.Int, 1) 394 } 395 396 p3 := &ParamStringPtr{} 397 if err := Unmarshal(x, p3); err != nil { 398 t.Fatalf("Unmarshal: %s", err) 399 } 400 if p3.Int == nil { 401 t.Fatalf("Unmarshal failed in to *string field") 402 } else if *p3.Int != "1" { 403 t.Fatalf("Unmarshal with %s failed:\nhave %#v,\n want %#v", x, p3.Int, 1) 404 } 405 } 406 407 type Tables struct { 408 HTable string `xml:"http://www.w3.org/TR/html4/ table"` 409 FTable string `xml:"http://www.w3schools.com/furniture table"` 410 } 411 412 var tables = []struct { 413 xml string 414 tab Tables 415 ns string 416 }{ 417 { 418 xml: `<Tables>` + 419 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` + 420 `<table xmlns="http://www.w3schools.com/furniture">world</table>` + 421 `</Tables>`, 422 tab: Tables{"hello", "world"}, 423 }, 424 { 425 xml: `<Tables>` + 426 `<table xmlns="http://www.w3schools.com/furniture">world</table>` + 427 `<table xmlns="http://www.w3.org/TR/html4/">hello</table>` + 428 `</Tables>`, 429 tab: Tables{"hello", "world"}, 430 }, 431 { 432 xml: `<Tables xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/">` + 433 `<f:table>world</f:table>` + 434 `<h:table>hello</h:table>` + 435 `</Tables>`, 436 tab: Tables{"hello", "world"}, 437 }, 438 { 439 xml: `<Tables>` + 440 `<table>bogus</table>` + 441 `</Tables>`, 442 tab: Tables{}, 443 }, 444 { 445 xml: `<Tables>` + 446 `<table>only</table>` + 447 `</Tables>`, 448 tab: Tables{HTable: "only"}, 449 ns: "http://www.w3.org/TR/html4/", 450 }, 451 { 452 xml: `<Tables>` + 453 `<table>only</table>` + 454 `</Tables>`, 455 tab: Tables{FTable: "only"}, 456 ns: "http://www.w3schools.com/furniture", 457 }, 458 { 459 xml: `<Tables>` + 460 `<table>only</table>` + 461 `</Tables>`, 462 tab: Tables{}, 463 ns: "something else entirely", 464 }, 465 } 466 467 func TestUnmarshalNS(t *testing.T) { 468 for i, tt := range tables { 469 var dst Tables 470 var err error 471 if tt.ns != "" { 472 d := NewDecoder(strings.NewReader(tt.xml)) 473 d.DefaultSpace = tt.ns 474 err = d.Decode(&dst) 475 } else { 476 err = Unmarshal([]byte(tt.xml), &dst) 477 } 478 if err != nil { 479 t.Errorf("#%d: Unmarshal: %v", i, err) 480 continue 481 } 482 want := tt.tab 483 if dst != want { 484 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want) 485 } 486 } 487 } 488 489 func TestRoundTrip(t *testing.T) { 490 // From issue 7535 491 const s = `<ex:element xmlns:ex="http://example.com/schema"></ex:element>` 492 in := bytes.NewBufferString(s) 493 for i := 0; i < 10; i++ { 494 out := &bytes.Buffer{} 495 d := NewDecoder(in) 496 e := NewEncoder(out) 497 498 for { 499 t, err := d.Token() 500 if err == io.EOF { 501 break 502 } 503 if err != nil { 504 fmt.Println("failed:", err) 505 return 506 } 507 e.EncodeToken(t) 508 } 509 e.Flush() 510 in = out 511 } 512 if got := in.String(); got != s { 513 t.Errorf("have: %q\nwant: %q\n", got, s) 514 } 515 } 516 517 func TestMarshalNS(t *testing.T) { 518 dst := Tables{"hello", "world"} 519 data, err := Marshal(&dst) 520 if err != nil { 521 t.Fatalf("Marshal: %v", err) 522 } 523 want := `<Tables><table xmlns="http://www.w3.org/TR/html4/">hello</table><table xmlns="http://www.w3schools.com/furniture">world</table></Tables>` 524 str := string(data) 525 if str != want { 526 t.Errorf("have: %q\nwant: %q\n", str, want) 527 } 528 } 529 530 type TableAttrs struct { 531 TAttr TAttr 532 } 533 534 type TAttr struct { 535 HTable string `xml:"http://www.w3.org/TR/html4/ table,attr"` 536 FTable string `xml:"http://www.w3schools.com/furniture table,attr"` 537 Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"` 538 Other1 string `xml:"http://golang.org/xml/ other,attr,omitempty"` 539 Other2 string `xml:"http://golang.org/xmlfoo/ other,attr,omitempty"` 540 Other3 string `xml:"http://golang.org/json/ other,attr,omitempty"` 541 Other4 string `xml:"http://golang.org/2/json/ other,attr,omitempty"` 542 } 543 544 var tableAttrs = []struct { 545 xml string 546 tab TableAttrs 547 ns string 548 }{ 549 { 550 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` + 551 `h:table="hello" f:table="world" ` + 552 `/></TableAttrs>`, 553 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}}, 554 }, 555 { 556 xml: `<TableAttrs><TAttr xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` + 557 `h:table="hello" f:table="world" ` + 558 `/></TableAttrs>`, 559 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}}, 560 }, 561 { 562 xml: `<TableAttrs><TAttr ` + 563 `h:table="hello" f:table="world" xmlns:f="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/" ` + 564 `/></TableAttrs>`, 565 tab: TableAttrs{TAttr{HTable: "hello", FTable: "world"}}, 566 }, 567 { 568 // Default space does not apply to attribute names. 569 xml: `<TableAttrs xmlns="http://www.w3schools.com/furniture" xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` + 570 `h:table="hello" table="world" ` + 571 `/></TableAttrs>`, 572 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}}, 573 }, 574 { 575 // Default space does not apply to attribute names. 576 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr xmlns="http://www.w3.org/TR/html4/" ` + 577 `table="hello" f:table="world" ` + 578 `/></TableAttrs>`, 579 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}}, 580 }, 581 { 582 xml: `<TableAttrs><TAttr ` + 583 `table="bogus" ` + 584 `/></TableAttrs>`, 585 tab: TableAttrs{}, 586 }, 587 { 588 // Default space does not apply to attribute names. 589 xml: `<TableAttrs xmlns:h="http://www.w3.org/TR/html4/"><TAttr ` + 590 `h:table="hello" table="world" ` + 591 `/></TableAttrs>`, 592 tab: TableAttrs{TAttr{HTable: "hello", FTable: ""}}, 593 ns: "http://www.w3schools.com/furniture", 594 }, 595 { 596 // Default space does not apply to attribute names. 597 xml: `<TableAttrs xmlns:f="http://www.w3schools.com/furniture"><TAttr ` + 598 `table="hello" f:table="world" ` + 599 `/></TableAttrs>`, 600 tab: TableAttrs{TAttr{HTable: "", FTable: "world"}}, 601 ns: "http://www.w3.org/TR/html4/", 602 }, 603 { 604 xml: `<TableAttrs><TAttr ` + 605 `table="bogus" ` + 606 `/></TableAttrs>`, 607 tab: TableAttrs{}, 608 ns: "something else entirely", 609 }, 610 } 611 612 func TestUnmarshalNSAttr(t *testing.T) { 613 for i, tt := range tableAttrs { 614 var dst TableAttrs 615 var err error 616 if tt.ns != "" { 617 d := NewDecoder(strings.NewReader(tt.xml)) 618 d.DefaultSpace = tt.ns 619 err = d.Decode(&dst) 620 } else { 621 err = Unmarshal([]byte(tt.xml), &dst) 622 } 623 if err != nil { 624 t.Errorf("#%d: Unmarshal: %v", i, err) 625 continue 626 } 627 want := tt.tab 628 if dst != want { 629 t.Errorf("#%d: dst=%+v, want %+v", i, dst, want) 630 } 631 } 632 } 633 634 func TestMarshalNSAttr(t *testing.T) { 635 src := TableAttrs{TAttr{"hello", "world", "en_US", "other1", "other2", "other3", "other4"}} 636 data, err := Marshal(&src) 637 if err != nil { 638 t.Fatalf("Marshal: %v", err) 639 } 640 want := `<TableAttrs><TAttr xmlns:json_1="http://golang.org/2/json/" xmlns:json="http://golang.org/json/" xmlns:_xmlfoo="http://golang.org/xmlfoo/" xmlns:_xml="http://golang.org/xml/" xmlns:furniture="http://www.w3schools.com/furniture" xmlns:html4="http://www.w3.org/TR/html4/" html4:table="hello" furniture:table="world" xml:lang="en_US" _xml:other="other1" _xmlfoo:other="other2" json:other="other3" json_1:other="other4"></TAttr></TableAttrs>` 641 str := string(data) 642 if str != want { 643 t.Errorf("Marshal:\nhave: %#q\nwant: %#q\n", str, want) 644 } 645 646 var dst TableAttrs 647 if err := Unmarshal(data, &dst); err != nil { 648 t.Errorf("Unmarshal: %v", err) 649 } 650 651 if dst != src { 652 t.Errorf("Unmarshal = %q, want %q", dst, src) 653 } 654 } 655 656 type MyCharData struct { 657 body string 658 } 659 660 func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error { 661 for { 662 t, err := d.Token() 663 if err == io.EOF { // found end of element 664 break 665 } 666 if err != nil { 667 return err 668 } 669 if char, ok := t.(CharData); ok { 670 m.body += string(char) 671 } 672 } 673 return nil 674 } 675 676 var _ Unmarshaler = (*MyCharData)(nil) 677 678 func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error { 679 panic("must not call") 680 } 681 682 type MyAttr struct { 683 attr string 684 } 685 686 func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error { 687 m.attr = attr.Value 688 return nil 689 } 690 691 var _ UnmarshalerAttr = (*MyAttr)(nil) 692 693 type MyStruct struct { 694 Data *MyCharData 695 Attr *MyAttr `xml:",attr"` 696 697 Data2 MyCharData 698 Attr2 MyAttr `xml:",attr"` 699 } 700 701 func TestUnmarshaler(t *testing.T) { 702 xml := `<?xml version="1.0" encoding="utf-8"?> 703 <MyStruct Attr="attr1" Attr2="attr2"> 704 <Data>hello <!-- comment -->world</Data> 705 <Data2>howdy <!-- comment -->world</Data2> 706 </MyStruct> 707 ` 708 709 var m MyStruct 710 if err := Unmarshal([]byte(xml), &m); err != nil { 711 t.Fatal(err) 712 } 713 714 if m.Data == nil || m.Attr == nil || m.Data.body != "hello world" || m.Attr.attr != "attr1" || m.Data2.body != "howdy world" || m.Attr2.attr != "attr2" { 715 t.Errorf("m=%#+v\n", m) 716 } 717 } 718 719 type Pea struct { 720 Cotelydon string 721 } 722 723 type Pod struct { 724 Pea interface{} `xml:"Pea"` 725 } 726 727 // https://golang.org/issue/6836 728 func TestUnmarshalIntoInterface(t *testing.T) { 729 pod := new(Pod) 730 pod.Pea = new(Pea) 731 xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>` 732 err := Unmarshal([]byte(xml), pod) 733 if err != nil { 734 t.Fatalf("failed to unmarshal %q: %v", xml, err) 735 } 736 pea, ok := pod.Pea.(*Pea) 737 if !ok { 738 t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea) 739 } 740 have, want := pea.Cotelydon, "Green stuff" 741 if have != want { 742 t.Errorf("failed to unmarshal into interface, have %q want %q", have, want) 743 } 744 }