github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/yaml/encode_test.go (about) 1 package yaml_test 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "math" 8 "reflect" 9 "strconv" 10 "testing" 11 "time" 12 13 "github.com/bingoohuang/gg/pkg/yaml" 14 "github.com/bingoohuang/gg/pkg/yaml/ast" 15 ) 16 17 var ( 18 zero = 0 19 emptyStr = "" 20 ) 21 22 func TestEncoder(t *testing.T) { 23 tests := []struct { 24 source string 25 value interface{} 26 options []yaml.EncodeOption 27 }{ 28 { 29 "null\n", 30 (*struct{})(nil), 31 nil, 32 }, 33 { 34 "v: hi\n", 35 map[string]string{"v": "hi"}, 36 nil, 37 }, 38 { 39 "v: \"true\"\n", 40 map[string]string{"v": "true"}, 41 nil, 42 }, 43 { 44 "v: \"false\"\n", 45 map[string]string{"v": "false"}, 46 nil, 47 }, 48 { 49 "v: true\n", 50 map[string]interface{}{"v": true}, 51 nil, 52 }, 53 { 54 "v: false\n", 55 map[string]bool{"v": false}, 56 nil, 57 }, 58 { 59 "v: 10\n", 60 map[string]int{"v": 10}, 61 nil, 62 }, 63 { 64 "v: -10\n", 65 map[string]int{"v": -10}, 66 nil, 67 }, 68 { 69 "v: 4294967296\n", 70 map[string]int{"v": 4294967296}, 71 nil, 72 }, 73 { 74 "v: 0.1\n", 75 map[string]interface{}{"v": 0.1}, 76 nil, 77 }, 78 { 79 "v: 0.99\n", 80 map[string]float32{"v": 0.99}, 81 nil, 82 }, 83 { 84 "v: 0.123456789\n", 85 map[string]float64{"v": 0.123456789}, 86 nil, 87 }, 88 { 89 "v: -0.1\n", 90 map[string]float64{"v": -0.1}, 91 nil, 92 }, 93 { 94 "v: 1.0\n", 95 map[string]float64{"v": 1.0}, 96 nil, 97 }, 98 { 99 "v: 1e+06\n", 100 map[string]float64{"v": 1000000}, 101 nil, 102 }, 103 { 104 "v: .inf\n", 105 map[string]interface{}{"v": math.Inf(0)}, 106 nil, 107 }, 108 { 109 "v: -.inf\n", 110 map[string]interface{}{"v": math.Inf(-1)}, 111 nil, 112 }, 113 { 114 "v: .nan\n", 115 map[string]interface{}{"v": math.NaN()}, 116 nil, 117 }, 118 { 119 "v: null\n", 120 map[string]interface{}{"v": nil}, 121 nil, 122 }, 123 { 124 "v: \"\"\n", 125 map[string]string{"v": ""}, 126 nil, 127 }, 128 { 129 "v:\n- A\n- B\n", 130 map[string][]string{"v": {"A", "B"}}, 131 nil, 132 }, 133 { 134 "v:\n - A\n - B\n", 135 map[string][]string{"v": {"A", "B"}}, 136 []yaml.EncodeOption{ 137 yaml.IndentSequence(true), 138 }, 139 }, 140 { 141 "v:\n- A\n- B\n", 142 map[string][2]string{"v": {"A", "B"}}, 143 nil, 144 }, 145 { 146 "v:\n - A\n - B\n", 147 map[string][2]string{"v": {"A", "B"}}, 148 []yaml.EncodeOption{ 149 yaml.IndentSequence(true), 150 }, 151 }, 152 { 153 "a: -\n", 154 map[string]string{"a": "-"}, 155 nil, 156 }, 157 { 158 "123\n", 159 123, 160 nil, 161 }, 162 { 163 "hello: world\n", 164 map[string]string{"hello": "world"}, 165 nil, 166 }, 167 { 168 "hello: |\n hello\n world\n", 169 map[string]string{"hello": "hello\nworld\n"}, 170 nil, 171 }, 172 { 173 "hello: |-\n hello\n world\n", 174 map[string]string{"hello": "hello\nworld"}, 175 nil, 176 }, 177 { 178 "hello: |+\n hello\n world\n\n", 179 map[string]string{"hello": "hello\nworld\n\n"}, 180 nil, 181 }, 182 { 183 "hello:\n hello: |\n hello\n world\n", 184 map[string]map[string]string{"hello": {"hello": "hello\nworld\n"}}, 185 nil, 186 }, 187 { 188 "hello: |\r hello\r world\n", 189 map[string]string{"hello": "hello\rworld\r"}, 190 nil, 191 }, 192 { 193 "hello: |\r\n hello\r\n world\n", 194 map[string]string{"hello": "hello\r\nworld\r\n"}, 195 nil, 196 }, 197 { 198 "v: |-\n username: hello\n password: hello123\n", 199 map[string]interface{}{"v": "username: hello\npassword: hello123"}, 200 []yaml.EncodeOption{ 201 yaml.UseLiteralStyleIfMultiline(true), 202 }, 203 }, 204 { 205 "v: |-\n # comment\n username: hello\n password: hello123\n", 206 map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, 207 []yaml.EncodeOption{ 208 yaml.UseLiteralStyleIfMultiline(true), 209 }, 210 }, 211 { 212 "v: \"# comment\\nusername: hello\\npassword: hello123\"\n", 213 map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, 214 []yaml.EncodeOption{ 215 yaml.UseLiteralStyleIfMultiline(false), 216 }, 217 }, 218 { 219 "v:\n- A\n- 1\n- B:\n - 2\n - 3\n", 220 map[string]interface{}{ 221 "v": []interface{}{ 222 "A", 223 1, 224 map[string][]int{ 225 "B": {2, 3}, 226 }, 227 }, 228 }, 229 nil, 230 }, 231 { 232 "v:\n - A\n - 1\n - B:\n - 2\n - 3\n - 2\n", 233 map[string]interface{}{ 234 "v": []interface{}{ 235 "A", 236 1, 237 map[string][]int{ 238 "B": {2, 3}, 239 }, 240 2, 241 }, 242 }, 243 []yaml.EncodeOption{ 244 yaml.IndentSequence(true), 245 }, 246 }, 247 { 248 "a:\n b: c\n", 249 map[string]interface{}{ 250 "a": map[string]string{ 251 "b": "c", 252 }, 253 }, 254 nil, 255 }, 256 { 257 "t2: 2018-01-09T10:40:47Z\nt4: 2098-01-09T10:40:47Z\n", 258 map[string]string{ 259 "t2": "2018-01-09T10:40:47Z", 260 "t4": "2098-01-09T10:40:47Z", 261 }, 262 nil, 263 }, 264 { 265 "a:\n b: c\n d: e\n", 266 map[string]interface{}{ 267 "a": map[string]string{ 268 "b": "c", 269 "d": "e", 270 }, 271 }, 272 nil, 273 }, 274 { 275 "a: 3s\n", 276 map[string]string{ 277 "a": "3s", 278 }, 279 nil, 280 }, 281 { 282 "a: <foo>\n", 283 map[string]string{"a": "<foo>"}, 284 nil, 285 }, 286 { 287 "a: \"1:1\"\n", 288 map[string]string{"a": "1:1"}, 289 nil, 290 }, 291 { 292 "a: 1.2.3.4\n", 293 map[string]string{"a": "1.2.3.4"}, 294 nil, 295 }, 296 { 297 "a: \"b: c\"\n", 298 map[string]string{"a": "b: c"}, 299 nil, 300 }, 301 { 302 "a: \"Hello #comment\"\n", 303 map[string]string{"a": "Hello #comment"}, 304 nil, 305 }, 306 { 307 "a: 100.5\n", 308 map[string]interface{}{ 309 "a": 100.5, 310 }, 311 nil, 312 }, 313 { 314 "a: \"\\\\0\"\n", 315 map[string]string{"a": "\\0"}, 316 nil, 317 }, 318 { 319 "a: 1\nb: 2\nc: 3\nd: 4\nsub:\n e: 5\n", 320 map[string]interface{}{ 321 "a": 1, 322 "b": 2, 323 "c": 3, 324 "d": 4, 325 "sub": map[string]int{ 326 "e": 5, 327 }, 328 }, 329 nil, 330 }, 331 { 332 "a: 1\nb: []\n", 333 struct { 334 A int 335 B []string 336 }{ 337 1, ([]string)(nil), 338 }, 339 nil, 340 }, 341 { 342 "a: 1\nb: []\n", 343 struct { 344 A int 345 B []string 346 }{ 347 1, []string{}, 348 }, 349 nil, 350 }, 351 { 352 "a: {}\n", 353 struct { 354 A map[string]interface{} 355 }{ 356 map[string]interface{}{}, 357 }, 358 nil, 359 }, 360 { 361 "a: b\nc: d\n", 362 struct { 363 A string 364 C string `yaml:"c"` 365 }{ 366 "b", "d", 367 }, 368 nil, 369 }, 370 { 371 "a: 1\n", 372 struct { 373 A int 374 B int `yaml:"-"` 375 }{ 376 1, 0, 377 }, 378 nil, 379 }, 380 { 381 "a: \"\"\n", 382 struct { 383 A string 384 }{ 385 "", 386 }, 387 nil, 388 }, 389 { 390 "a: null\n", 391 struct { 392 A *string 393 }{ 394 nil, 395 }, 396 nil, 397 }, 398 { 399 "a: \"\"\n", 400 struct { 401 A *string 402 }{ 403 &emptyStr, 404 }, 405 nil, 406 }, 407 { 408 "a: null\n", 409 struct { 410 A *int 411 }{ 412 nil, 413 }, 414 nil, 415 }, 416 { 417 "a: 0\n", 418 struct { 419 A *int 420 }{ 421 &zero, 422 }, 423 nil, 424 }, 425 426 // Conditional flag 427 { 428 "a: 1\n", 429 struct { 430 A int `yaml:"a,omitempty"` 431 B int `yaml:"b,omitempty"` 432 }{1, 0}, 433 nil, 434 }, 435 { 436 "{}\n", 437 struct { 438 A int `yaml:"a,omitempty"` 439 B int `yaml:"b,omitempty"` 440 }{0, 0}, 441 nil, 442 }, 443 444 { 445 "a:\n y: \"\"\n", 446 struct { 447 A *struct { 448 X string `yaml:"x,omitempty"` 449 Y string 450 } 451 }{&struct { 452 X string `yaml:"x,omitempty"` 453 Y string 454 }{}}, 455 nil, 456 }, 457 458 { 459 "a: {}\n", 460 struct { 461 A *struct { 462 X string `yaml:"x,omitempty"` 463 Y string `yaml:"y,omitempty"` 464 } 465 }{&struct { 466 X string `yaml:"x,omitempty"` 467 Y string `yaml:"y,omitempty"` 468 }{}}, 469 nil, 470 }, 471 472 { 473 "a: {x: 1}\n", 474 struct { 475 A *struct{ X, y int } `yaml:"a,omitempty,flow"` 476 }{&struct{ X, y int }{1, 2}}, 477 nil, 478 }, 479 480 { 481 "{}\n", 482 struct { 483 A *struct{ X, y int } `yaml:"a,omitempty,flow"` 484 }{nil}, 485 nil, 486 }, 487 488 { 489 "a: {x: 0}\n", 490 struct { 491 A *struct{ X, y int } `yaml:"a,omitempty,flow"` 492 }{&struct{ X, y int }{}}, 493 nil, 494 }, 495 496 { 497 "a: {x: 1}\n", 498 struct { 499 A struct{ X, y int } `yaml:"a,omitempty,flow"` 500 }{struct{ X, y int }{1, 2}}, 501 nil, 502 }, 503 { 504 "{}\n", 505 struct { 506 A struct{ X, y int } `yaml:"a,omitempty,flow"` 507 }{struct{ X, y int }{0, 1}}, 508 nil, 509 }, 510 { 511 "a: 1.0\n", 512 struct { 513 A float64 `yaml:"a,omitempty"` 514 B float64 `yaml:"b,omitempty"` 515 }{1, 0}, 516 nil, 517 }, 518 { 519 "a: 1\n", 520 struct { 521 A int 522 B []string `yaml:"b,omitempty"` 523 }{ 524 1, []string{}, 525 }, 526 nil, 527 }, 528 529 // Flow flag 530 { 531 "a: [1, 2]\n", 532 struct { 533 A []int `yaml:"a,flow"` 534 }{[]int{1, 2}}, 535 nil, 536 }, 537 { 538 "a: {b: c, d: e}\n", 539 &struct { 540 A map[string]string `yaml:"a,flow"` 541 }{map[string]string{"b": "c", "d": "e"}}, 542 nil, 543 }, 544 { 545 "a: {b: c, d: e}\n", 546 struct { 547 A struct { 548 B, D string 549 } `yaml:"a,flow"` 550 }{struct{ B, D string }{"c", "e"}}, 551 nil, 552 }, 553 554 // Multi bytes 555 { 556 "v: あいうえお\nv2: かきくけこ\n", 557 map[string]string{"v": "あいうえお", "v2": "かきくけこ"}, 558 nil, 559 }, 560 561 // time value 562 { 563 "v: 0001-01-01T00:00:00Z\n", 564 map[string]time.Time{"v": {}}, 565 nil, 566 }, 567 { 568 "v: 0001-01-01T00:00:00Z\n", 569 map[string]*time.Time{"v": {}}, 570 nil, 571 }, 572 { 573 "v: null\n", 574 map[string]*time.Time{"v": nil}, 575 nil, 576 }, 577 { 578 "v: 30s\n", 579 map[string]time.Duration{"v": 30 * time.Second}, 580 nil, 581 }, 582 } 583 for _, test := range tests { 584 var buf bytes.Buffer 585 enc := yaml.NewEncoder(&buf, test.options...) 586 if err := enc.Encode(test.value); err != nil { 587 t.Fatalf("%+v", err) 588 } 589 if test.source != buf.String() { 590 t.Fatalf("expect = [%s], actual = [%s]", test.source, buf.String()) 591 } 592 } 593 } 594 595 func TestEncodeStructIncludeMap(t *testing.T) { 596 type U struct { 597 M map[string]string 598 } 599 type T struct { 600 A U 601 } 602 bytes, err := yaml.Marshal(T{ 603 A: U{ 604 M: map[string]string{"x": "y"}, 605 }, 606 }) 607 if err != nil { 608 t.Fatalf("%+v", err) 609 } 610 expect := "a:\n m:\n x: y\n" 611 actual := string(bytes) 612 if actual != expect { 613 t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) 614 } 615 } 616 617 func TestEncodeDefinedTypeKeyMap(t *testing.T) { 618 type K string 619 type U struct { 620 M map[K]string 621 } 622 bytes, err := yaml.Marshal(U{ 623 M: map[K]string{K("x"): "y"}, 624 }) 625 if err != nil { 626 t.Fatalf("%+v", err) 627 } 628 expect := "m:\n x: y\n" 629 actual := string(bytes) 630 if actual != expect { 631 t.Fatalf("unexpected output. expect:[%s] actual:[%s]", expect, actual) 632 } 633 } 634 635 func TestEncodeWithAnchorAndAlias(t *testing.T) { 636 var buf bytes.Buffer 637 enc := yaml.NewEncoder(&buf) 638 type T struct { 639 A int 640 B string 641 } 642 var v struct { 643 A *T `yaml:"a,anchor=c"` 644 B *T `yaml:"b,alias=c"` 645 } 646 v.A = &T{A: 1, B: "hello"} 647 v.B = v.A 648 if err := enc.Encode(v); err != nil { 649 t.Fatalf("%+v", err) 650 } 651 expect := "a: &c\n a: 1\n b: hello\nb: *c\n" 652 if expect != buf.String() { 653 t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) 654 } 655 } 656 657 func TestEncodeWithAutoAlias(t *testing.T) { 658 var buf bytes.Buffer 659 enc := yaml.NewEncoder(&buf) 660 type T struct { 661 I int 662 S string 663 } 664 var v struct { 665 A *T `yaml:"a,anchor=a"` 666 B *T `yaml:"b,anchor=b"` 667 C *T `yaml:"c,alias"` 668 D *T `yaml:"d,alias"` 669 } 670 v.A = &T{I: 1, S: "hello"} 671 v.B = &T{I: 2, S: "world"} 672 v.C = v.A 673 v.D = v.B 674 if err := enc.Encode(v); err != nil { 675 t.Fatalf("%+v", err) 676 } 677 expect := `a: &a 678 i: 1 679 s: hello 680 b: &b 681 i: 2 682 s: world 683 c: *a 684 d: *b 685 ` 686 if expect != buf.String() { 687 t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) 688 } 689 } 690 691 func TestEncodeWithImplicitAnchorAndAlias(t *testing.T) { 692 var buf bytes.Buffer 693 enc := yaml.NewEncoder(&buf) 694 type T struct { 695 I int 696 S string 697 } 698 var v struct { 699 A *T `yaml:"a,anchor"` 700 B *T `yaml:"b,anchor"` 701 C *T `yaml:"c,alias"` 702 D *T `yaml:"d,alias"` 703 } 704 v.A = &T{I: 1, S: "hello"} 705 v.B = &T{I: 2, S: "world"} 706 v.C = v.A 707 v.D = v.B 708 if err := enc.Encode(v); err != nil { 709 t.Fatalf("%+v", err) 710 } 711 expect := `a: &a 712 i: 1 713 s: hello 714 b: &b 715 i: 2 716 s: world 717 c: *a 718 d: *b 719 ` 720 if expect != buf.String() { 721 t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) 722 } 723 } 724 725 func TestEncodeWithMerge(t *testing.T) { 726 type Person struct { 727 *Person `yaml:",omitempty,inline,alias"` 728 Name string `yaml:",omitempty"` 729 Age int `yaml:",omitempty"` 730 } 731 defaultPerson := &Person{ 732 Name: "John Smith", 733 Age: 20, 734 } 735 people := []*Person{ 736 { 737 Person: defaultPerson, 738 Name: "Ken", 739 Age: 10, 740 }, 741 { 742 Person: defaultPerson, 743 }, 744 } 745 var doc struct { 746 Default *Person `yaml:"default,anchor"` 747 People []*Person `yaml:"people"` 748 } 749 doc.Default = defaultPerson 750 doc.People = people 751 var buf bytes.Buffer 752 enc := yaml.NewEncoder(&buf) 753 if err := enc.Encode(doc); err != nil { 754 t.Fatalf("%+v", err) 755 } 756 expect := `default: &default 757 name: John Smith 758 age: 20 759 people: 760 - <<: *default 761 name: Ken 762 age: 10 763 - <<: *default 764 ` 765 if expect != buf.String() { 766 t.Fatalf("expect = [%s], actual = [%s]", expect, buf.String()) 767 } 768 } 769 770 func TestEncodeWithNestedYAML(t *testing.T) { 771 // Represents objects containing stringified YAML, and special chars 772 tests := []struct { 773 value interface{} 774 // If true, expects a different result between when using forced literal style or not 775 expectDifferent bool 776 }{ 777 { 778 value: map[string]interface{}{"v": "# comment\nname: hello\npassword: hello123\nspecial: \":ghost:\"\ntext: |\n nested multiline!"}, 779 expectDifferent: true, 780 }, 781 { 782 value: map[string]interface{}{"v": "# comment\nusername: hello\npassword: hello123"}, 783 expectDifferent: true, 784 }, 785 { 786 value: map[string]interface{}{"v": "# comment\n"}, 787 expectDifferent: true, 788 }, 789 { 790 value: map[string]interface{}{"v": "\n"}, 791 }, 792 } 793 794 for _, test := range tests { 795 yamlBytesForced, err := yaml.MarshalWithOptions(test.value, yaml.UseLiteralStyleIfMultiline(true)) 796 if err != nil { 797 t.Fatalf("%+v", err) 798 } 799 800 // Convert it back for proper equality testing 801 var unmarshaled interface{} 802 803 if err := yaml.Unmarshal(yamlBytesForced, &unmarshaled); err != nil { 804 t.Fatalf("%+v", err) 805 } 806 807 if !reflect.DeepEqual(test.value, unmarshaled) { 808 t.Fatalf("expected %v(%T). but actual %v(%T)", test.value, test.value, unmarshaled, unmarshaled) 809 } 810 811 if test.expectDifferent { 812 yamlBytesNotForced, err := yaml.MarshalWithOptions(test.value) 813 if err != nil { 814 t.Fatalf("%+v", err) 815 } 816 817 if string(yamlBytesForced) == string(yamlBytesNotForced) { 818 t.Fatalf("expected different strings when force literal style is not enabled. forced: %s, not forced: %s", string(yamlBytesForced), string(yamlBytesNotForced)) 819 } 820 } 821 } 822 } 823 824 func TestEncoder_Inline(t *testing.T) { 825 type base struct { 826 A int 827 B string 828 } 829 var buf bytes.Buffer 830 enc := yaml.NewEncoder(&buf) 831 if err := enc.Encode(struct { 832 *base `yaml:",inline"` 833 C bool 834 }{ 835 base: &base{ 836 A: 1, 837 B: "hello", 838 }, 839 C: true, 840 }); err != nil { 841 t.Fatalf("%+v", err) 842 } 843 expect := ` 844 a: 1 845 b: hello 846 c: true 847 ` 848 actual := "\n" + buf.String() 849 if expect != actual { 850 t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) 851 } 852 } 853 854 func TestEncoder_InlineAndConflictKey(t *testing.T) { 855 type base struct { 856 A int 857 B string 858 } 859 var buf bytes.Buffer 860 enc := yaml.NewEncoder(&buf) 861 if err := enc.Encode(struct { 862 *base `yaml:",inline"` 863 A int // conflict 864 C bool 865 }{ 866 base: &base{ 867 A: 1, 868 B: "hello", 869 }, 870 A: 0, // default value 871 C: true, 872 }); err != nil { 873 t.Fatalf("%+v", err) 874 } 875 expect := ` 876 b: hello 877 a: 0 878 c: true 879 ` 880 actual := "\n" + buf.String() 881 if expect != actual { 882 t.Fatalf("inline marshal error: expect=[%s] actual=[%s]", expect, actual) 883 } 884 } 885 886 func TestEncoder_Flow(t *testing.T) { 887 var buf bytes.Buffer 888 enc := yaml.NewEncoder(&buf, yaml.Flow(true)) 889 var v struct { 890 A int 891 B string 892 C struct { 893 D int 894 E string 895 } 896 F []int 897 } 898 v.A = 1 899 v.B = "hello" 900 v.C.D = 3 901 v.C.E = "world" 902 v.F = []int{1, 2} 903 if err := enc.Encode(v); err != nil { 904 t.Fatalf("%+v", err) 905 } 906 expect := ` 907 {a: 1, b: hello, c: {d: 3, e: world}, f: [1, 2]} 908 ` 909 actual := "\n" + buf.String() 910 if expect != actual { 911 t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) 912 } 913 } 914 915 func TestEncoder_FlowRecursive(t *testing.T) { 916 var v struct { 917 M map[string][]int `yaml:",flow"` 918 } 919 v.M = map[string][]int{ 920 "test": {1, 2, 3}, 921 } 922 var buf bytes.Buffer 923 if err := yaml.NewEncoder(&buf).Encode(v); err != nil { 924 t.Fatalf("%+v", err) 925 } 926 expect := ` 927 m: {test: [1, 2, 3]} 928 ` 929 actual := "\n" + buf.String() 930 if expect != actual { 931 t.Fatalf("flow style marshal error: expect=[%s] actual=[%s]", expect, actual) 932 } 933 } 934 935 func TestEncoder_JSON(t *testing.T) { 936 var buf bytes.Buffer 937 enc := yaml.NewEncoder(&buf, yaml.JSON()) 938 type st struct { 939 I int8 940 S string 941 F float32 942 } 943 if err := enc.Encode(struct { 944 I int 945 U uint 946 S string 947 F float64 948 Struct *st 949 Slice []int 950 Map map[string]interface{} 951 Time time.Time 952 Duration time.Duration 953 }{ 954 I: -10, 955 U: 10, 956 S: "hello", 957 F: 3.14, 958 Struct: &st{ 959 I: 2, 960 S: "world", 961 F: 1.23, 962 }, 963 Slice: []int{1, 2, 3, 4, 5}, 964 Map: map[string]interface{}{ 965 "a": 1, 966 "b": 1.23, 967 "c": "json", 968 }, 969 Time: time.Time{}, 970 Duration: 5 * time.Minute, 971 }); err != nil { 972 t.Fatalf("%+v", err) 973 } 974 expect := ` 975 {"i": -10, "u": 10, "s": "hello", "f": 3.14, "struct": {"i": 2, "s": "world", "f": 1.23}, "slice": [1, 2, 3, 4, 5], "map": {"a": 1, "b": 1.23, "c": "json"}, "time": "0001-01-01T00:00:00Z", "duration": "5m0s"} 976 ` 977 actual := "\n" + buf.String() 978 if expect != actual { 979 t.Fatalf("JSON style marshal error: expect=[%s] actual=[%s]", expect, actual) 980 } 981 } 982 983 func TestEncoder_MarshalAnchor(t *testing.T) { 984 type Host struct { 985 Hostname string 986 Username string 987 Password string 988 } 989 type HostDecl struct { 990 Host *Host `yaml:",anchor"` 991 } 992 type Queue struct { 993 Name string `yaml:","` 994 *Host `yaml:",alias"` 995 } 996 var doc struct { 997 Hosts []*HostDecl `yaml:"hosts"` 998 Queues []*Queue `yaml:"queues"` 999 } 1000 host1 := &Host{ 1001 Hostname: "host1.example.com", 1002 Username: "userA", 1003 Password: "pass1", 1004 } 1005 host2 := &Host{ 1006 Hostname: "host2.example.com", 1007 Username: "userB", 1008 Password: "pass2", 1009 } 1010 doc.Hosts = []*HostDecl{ 1011 { 1012 Host: host1, 1013 }, 1014 { 1015 Host: host2, 1016 }, 1017 } 1018 doc.Queues = []*Queue{ 1019 { 1020 Name: "queue", 1021 Host: host1, 1022 }, { 1023 Name: "queue2", 1024 Host: host2, 1025 }, 1026 } 1027 hostIdx := 1 1028 opt := yaml.MarshalAnchor(func(anchor *ast.AnchorNode, value interface{}) error { 1029 if _, ok := value.(*Host); ok { 1030 nameNode := anchor.Name.(*ast.StringNode) 1031 nameNode.Value = fmt.Sprintf("host%d", hostIdx) 1032 hostIdx++ 1033 } 1034 return nil 1035 }) 1036 1037 var buf bytes.Buffer 1038 if err := yaml.NewEncoder(&buf, opt).Encode(doc); err != nil { 1039 t.Fatalf("%+v", err) 1040 } 1041 expect := ` 1042 hosts: 1043 - host: &host1 1044 hostname: host1.example.com 1045 username: userA 1046 password: pass1 1047 - host: &host2 1048 hostname: host2.example.com 1049 username: userB 1050 password: pass2 1051 queues: 1052 - name: queue 1053 host: *host1 1054 - name: queue2 1055 host: *host2 1056 ` 1057 if "\n"+buf.String() != expect { 1058 t.Fatalf("unexpected output. %s", buf.String()) 1059 } 1060 } 1061 1062 type useJSONMarshalerTest struct{} 1063 1064 func (t useJSONMarshalerTest) MarshalJSON() ([]byte, error) { 1065 return []byte(`{"a":[1, 2, 3]}`), nil 1066 } 1067 1068 func TestEncoder_UseJSONMarshaler(t *testing.T) { 1069 got, err := yaml.MarshalWithOptions(useJSONMarshalerTest{}, yaml.UseJSONMarshaler()) 1070 if err != nil { 1071 t.Fatal(err) 1072 } 1073 expected := ` 1074 a: 1075 - 1 1076 - 2 1077 - 3 1078 ` 1079 if expected != "\n"+string(got) { 1080 t.Fatalf("failed to use json marshaler. expected [%q] but got [%q]", expected, string(got)) 1081 } 1082 } 1083 1084 func Example_Marshal_Node() { 1085 type T struct { 1086 Text ast.Node `yaml:"text"` 1087 } 1088 stringNode, err := yaml.ValueToNode("node example") 1089 if err != nil { 1090 panic(err) 1091 } 1092 bytes, err := yaml.Marshal(T{Text: stringNode}) 1093 if err != nil { 1094 panic(err) 1095 } 1096 fmt.Println(string(bytes)) 1097 // OUTPUT: 1098 // text: node example 1099 } 1100 1101 func Example_Marshal_ExplicitAnchorAlias() { 1102 type T struct { 1103 A int 1104 B string 1105 } 1106 var v struct { 1107 C *T `yaml:"c,anchor=x"` 1108 D *T `yaml:"d,alias=x"` 1109 } 1110 v.C = &T{A: 1, B: "hello"} 1111 v.D = v.C 1112 bytes, err := yaml.Marshal(v) 1113 if err != nil { 1114 panic(err) 1115 } 1116 fmt.Println(string(bytes)) 1117 // OUTPUT: 1118 // c: &x 1119 // a: 1 1120 // b: hello 1121 // d: *x 1122 } 1123 1124 func Example_Marshal_ImplicitAnchorAlias() { 1125 type T struct { 1126 I int 1127 S string 1128 } 1129 var v struct { 1130 A *T `yaml:"a,anchor"` 1131 B *T `yaml:"b,anchor"` 1132 C *T `yaml:"c,alias"` 1133 D *T `yaml:"d,alias"` 1134 } 1135 v.A = &T{I: 1, S: "hello"} 1136 v.B = &T{I: 2, S: "world"} 1137 v.C = v.A // C has same pointer address to A 1138 v.D = v.B // D has same pointer address to B 1139 bytes, err := yaml.Marshal(v) 1140 if err != nil { 1141 panic(err) 1142 } 1143 fmt.Println(string(bytes)) 1144 // OUTPUT: 1145 // a: &a 1146 // i: 1 1147 // s: hello 1148 // b: &b 1149 // i: 2 1150 // s: world 1151 // c: *a 1152 // d: *b 1153 } 1154 1155 type tMarshal []string 1156 1157 func (t *tMarshal) MarshalYAML() ([]byte, error) { 1158 var buf bytes.Buffer 1159 buf.WriteString("tags:\n") 1160 for i, v := range *t { 1161 if i == 0 { 1162 fmt.Fprintf(&buf, "- %s\n", v) 1163 } else { 1164 fmt.Fprintf(&buf, " %s\n", v) 1165 } 1166 } 1167 return buf.Bytes(), nil 1168 } 1169 1170 func Test_Marshaler(t *testing.T) { 1171 const expected = `- hello-world 1172 ` 1173 1174 // sanity check 1175 var l []string 1176 if err := yaml.Unmarshal([]byte(expected), &l); err != nil { 1177 t.Fatalf("failed to parse string: %s", err) 1178 } 1179 1180 buf, err := yaml.Marshal(tMarshal{"hello-world"}) 1181 if err != nil { 1182 t.Fatalf("failed to marshal: %s", err) 1183 } 1184 1185 if string(buf) != expected { 1186 t.Fatalf("expected '%s', got '%s'", expected, buf) 1187 } 1188 1189 t.Logf("%s", buf) 1190 } 1191 1192 type marshalContext struct{} 1193 1194 func (c *marshalContext) MarshalYAML(ctx context.Context) ([]byte, error) { 1195 v, ok := ctx.Value("k").(int) 1196 if !ok { 1197 return nil, fmt.Errorf("cannot get valid context") 1198 } 1199 if v != 1 { 1200 return nil, fmt.Errorf("cannot get valid context") 1201 } 1202 return []byte("1"), nil 1203 } 1204 1205 func Test_MarshalerWithContext(t *testing.T) { 1206 ctx := context.WithValue(context.Background(), "k", 1) 1207 bytes, err := yaml.MarshalContext(ctx, &marshalContext{}) 1208 if err != nil { 1209 t.Fatalf("%+v", err) 1210 } 1211 if string(bytes) != "1\n" { 1212 t.Fatalf("failed marshal: %q", string(bytes)) 1213 } 1214 } 1215 1216 type SlowMarshaler struct { 1217 A string 1218 B int 1219 } 1220 type FastMarshaler struct { 1221 A string 1222 B int 1223 } 1224 type ( 1225 TextMarshaler int64 1226 TextMarshalerContainer struct { 1227 Field TextMarshaler `yaml:"field"` 1228 } 1229 ) 1230 1231 func (v SlowMarshaler) MarshalYAML() ([]byte, error) { 1232 var buf bytes.Buffer 1233 buf.WriteString("tags:\n") 1234 buf.WriteString("- slow-marshaler\n") 1235 buf.WriteString("a: " + v.A + "\n") 1236 buf.WriteString("b: " + strconv.FormatInt(int64(v.B), 10) + "\n") 1237 return buf.Bytes(), nil 1238 } 1239 1240 func (v FastMarshaler) MarshalYAML() (interface{}, error) { 1241 return yaml.MapSlice{ 1242 {"tags", []string{"fast-marshaler"}}, 1243 {"a", v.A}, 1244 {"b", v.B}, 1245 }, nil 1246 } 1247 1248 func (t TextMarshaler) MarshalText() ([]byte, error) { 1249 return []byte(strconv.FormatInt(int64(t), 8)), nil 1250 } 1251 1252 func Example_MarshalYAML() { 1253 var slow SlowMarshaler 1254 slow.A = "Hello slow poke" 1255 slow.B = 100 1256 buf, err := yaml.Marshal(slow) 1257 if err != nil { 1258 panic(err.Error()) 1259 } 1260 1261 fmt.Println(string(buf)) 1262 1263 var fast FastMarshaler 1264 fast.A = "Hello speed demon" 1265 fast.B = 100 1266 buf, err = yaml.Marshal(fast) 1267 if err != nil { 1268 panic(err.Error()) 1269 } 1270 1271 fmt.Println(string(buf)) 1272 1273 text := TextMarshalerContainer{ 1274 Field: 11, 1275 } 1276 buf, err = yaml.Marshal(text) 1277 if err != nil { 1278 panic(err.Error()) 1279 } 1280 1281 fmt.Println(string(buf)) 1282 // OUTPUT: 1283 // tags: 1284 // - slow-marshaler 1285 // a: Hello slow poke 1286 // b: 100 1287 // 1288 // tags: 1289 // - fast-marshaler 1290 // a: Hello speed demon 1291 // b: 100 1292 // 1293 // field: 13 1294 }