lutaml-model 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +2 -0
  3. data/.rubocop_todo.yml +86 -23
  4. data/Gemfile +2 -0
  5. data/README.adoc +1441 -220
  6. data/lib/lutaml/model/attribute.rb +33 -10
  7. data/lib/lutaml/model/choice.rb +56 -0
  8. data/lib/lutaml/model/config.rb +1 -0
  9. data/lib/lutaml/model/constants.rb +7 -0
  10. data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
  11. data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
  12. data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
  13. data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
  14. data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
  15. data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
  16. data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
  17. data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
  18. data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
  19. data/lib/lutaml/model/error.rb +9 -0
  20. data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
  21. data/lib/lutaml/model/key_value_mapping.rb +34 -3
  22. data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
  23. data/lib/lutaml/model/liquefiable.rb +59 -0
  24. data/lib/lutaml/model/mapping_hash.rb +9 -1
  25. data/lib/lutaml/model/mapping_rule.rb +19 -2
  26. data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
  27. data/lib/lutaml/model/schema/xml_compiler.rb +762 -0
  28. data/lib/lutaml/model/schema.rb +5 -0
  29. data/lib/lutaml/model/schema_location.rb +7 -0
  30. data/lib/lutaml/model/sequence.rb +71 -0
  31. data/lib/lutaml/model/serialize.rb +139 -33
  32. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
  33. data/lib/lutaml/model/type/decimal.rb +0 -4
  34. data/lib/lutaml/model/type/hash.rb +11 -11
  35. data/lib/lutaml/model/type/time.rb +3 -3
  36. data/lib/lutaml/model/utils.rb +19 -15
  37. data/lib/lutaml/model/validation.rb +12 -1
  38. data/lib/lutaml/model/version.rb +1 -1
  39. data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
  40. data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
  41. data/lib/lutaml/model/xml_adapter/element.rb +32 -0
  42. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +13 -9
  43. data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
  44. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
  45. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
  46. data/lib/lutaml/model/xml_adapter/xml_document.rb +82 -25
  47. data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
  48. data/lib/lutaml/model/xml_mapping.rb +53 -9
  49. data/lib/lutaml/model/xml_mapping_rule.rb +8 -6
  50. data/lib/lutaml/model.rb +2 -0
  51. data/lutaml-model.gemspec +5 -0
  52. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
  53. data/spec/ceramic_spec.rb +39 -0
  54. data/spec/fixtures/ceramic.rb +23 -0
  55. data/spec/fixtures/xml/address_example_260.xsd +9 -0
  56. data/spec/fixtures/xml/invalid_math_document.xml +4 -0
  57. data/spec/fixtures/xml/math_document_schema.xsd +56 -0
  58. data/spec/fixtures/xml/test_schema.xsd +53 -0
  59. data/spec/fixtures/xml/user.xsd +10 -0
  60. data/spec/fixtures/xml/valid_math_document.xml +4 -0
  61. data/spec/lutaml/model/cdata_spec.rb +4 -5
  62. data/spec/lutaml/model/choice_spec.rb +168 -0
  63. data/spec/lutaml/model/collection_spec.rb +1 -1
  64. data/spec/lutaml/model/custom_model_spec.rb +7 -21
  65. data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
  66. data/spec/lutaml/model/defaults_spec.rb +3 -1
  67. data/spec/lutaml/model/delegation_spec.rb +7 -5
  68. data/spec/lutaml/model/enum_spec.rb +35 -0
  69. data/spec/lutaml/model/group_spec.rb +160 -0
  70. data/spec/lutaml/model/inheritance_spec.rb +25 -0
  71. data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
  72. data/spec/lutaml/model/liquefiable_spec.rb +121 -0
  73. data/spec/lutaml/model/map_all_spec.rb +188 -0
  74. data/spec/lutaml/model/mixed_content_spec.rb +95 -56
  75. data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
  76. data/spec/lutaml/model/schema/xml_compiler_spec.rb +1624 -0
  77. data/spec/lutaml/model/sequence_spec.rb +216 -0
  78. data/spec/lutaml/model/transformation_spec.rb +230 -0
  79. data/spec/lutaml/model/type_spec.rb +138 -31
  80. data/spec/lutaml/model/utils_spec.rb +32 -0
  81. data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
  82. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
  83. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
  84. data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
  85. data/spec/lutaml/model/xml_mapping_spec.rb +250 -112
  86. metadata +77 -2
@@ -0,0 +1,188 @@
1
+ require "spec_helper"
2
+
3
+ module MapAllSpec
4
+ class Document < Lutaml::Model::Serializable
5
+ attribute :content, :string
6
+
7
+ xml do
8
+ root "document"
9
+ map_all to: :content
10
+ end
11
+
12
+ json do
13
+ map_all to: :content
14
+ end
15
+
16
+ yaml do
17
+ map_all to: :content
18
+ end
19
+
20
+ toml do
21
+ map_all to: :content
22
+ end
23
+ end
24
+
25
+ class InvalidDocument < Lutaml::Model::Serializable
26
+ attribute :content, :string
27
+ attribute :title, :string
28
+
29
+ json do
30
+ map_all to: :content
31
+ end
32
+
33
+ yaml do
34
+ map_element "title", to: :title
35
+ end
36
+ end
37
+
38
+ RSpec.describe "MapAll" do
39
+ describe "XML serialization" do
40
+ let(:xml_content) do
41
+ <<~XML
42
+ <document>
43
+ Content with <b>tags</b> and <i>formatting</i>.
44
+ <metadata>
45
+ <author>John Doe</author>
46
+ <date>2024-01-15</date>
47
+ </metadata>
48
+ </document>
49
+ XML
50
+ end
51
+
52
+ let(:sub_xml_content) do
53
+ <<~XML
54
+ Content with <b>tags</b> and <i>formatting</i>.
55
+ <metadata>
56
+ <author>John Doe</author>
57
+ <date>2024-01-15</date>
58
+ </metadata>
59
+ XML
60
+ end
61
+
62
+ it "captures all XML content" do
63
+ doc = Document.from_xml(xml_content)
64
+ expect(doc.content).to be_equivalent_to(sub_xml_content)
65
+ end
66
+
67
+ it "preserves XML content through round trip" do
68
+ doc = Document.from_xml(xml_content)
69
+ regenerated = doc.to_xml
70
+ expect(regenerated).to be_equivalent_to(xml_content)
71
+ end
72
+ end
73
+
74
+ describe "JSON serialization" do
75
+ let(:json_content) do
76
+ {
77
+ "sections" => [
78
+ { "title" => "Introduction", "text" => "Chapter 1" },
79
+ { "title" => "Conclusion", "text" => "Final chapter" },
80
+ ],
81
+ "metadata" => {
82
+ "author" => "John Doe",
83
+ "date" => "2024-01-15",
84
+ },
85
+ }.to_json
86
+ end
87
+
88
+ it "captures all JSON content" do
89
+ doc = Document.from_json(json_content)
90
+ parsed = JSON.parse(doc.content)
91
+ expect(parsed["sections"].first["title"]).to eq("Introduction")
92
+ expect(parsed["metadata"]["author"]).to eq("John Doe")
93
+ end
94
+
95
+ it "preserves JSON content through round trip" do
96
+ doc = Document.from_json(json_content)
97
+ regenerated = doc.to_json
98
+ expect(JSON.parse(regenerated)).to eq(JSON.parse(json_content))
99
+ end
100
+ end
101
+
102
+ describe "YAML serialization" do
103
+ let(:yaml_content) do
104
+ <<~YAML
105
+ sections:
106
+ - title: Introduction
107
+ text: Chapter 1
108
+ - title: Conclusion
109
+ text: Final chapter
110
+ metadata:
111
+ author: John Doe
112
+ date: 2024-01-15
113
+ YAML
114
+ end
115
+
116
+ it "captures all YAML content" do
117
+ doc = Document.from_yaml(yaml_content)
118
+ parsed = YAML.safe_load(doc.content, permitted_classes: [Date])
119
+ expect(parsed["sections"].first["title"]).to eq("Introduction")
120
+ expect(parsed["metadata"]["author"]).to eq("John Doe")
121
+ end
122
+
123
+ it "preserves YAML content through round trip" do
124
+ doc = Document.from_yaml(yaml_content)
125
+ regenerated = doc.to_yaml
126
+ expect(YAML.safe_load(regenerated, permitted_classes: [Date])).to eq(YAML.safe_load(yaml_content, permitted_classes: [Date]))
127
+ end
128
+ end
129
+
130
+ describe "TOML serialization" do
131
+ let(:toml_content) do
132
+ <<~TOML
133
+ title = "Document Title"
134
+
135
+ [metadata]
136
+ author = "John Doe"
137
+ date = "2024-01-15"
138
+
139
+ [[sections]]
140
+ title = "Introduction"
141
+ text = "Chapter 1"
142
+
143
+ [[sections]]
144
+ title = "Conclusion"
145
+ text = "Final chapter"
146
+ TOML
147
+ end
148
+
149
+ it "captures all TOML content" do
150
+ doc = Document.from_toml(toml_content)
151
+ parsed = TomlRB.parse(doc.content)
152
+ expect(parsed["sections"].first["title"]).to eq("Introduction")
153
+ expect(parsed["metadata"]["author"]).to eq("John Doe")
154
+ end
155
+
156
+ it "preserves TOML content through round trip" do
157
+ doc = Document.from_toml(toml_content)
158
+ regenerated = doc.to_toml
159
+ expect(TomlRB.parse(regenerated)).to eq(TomlRB.parse(toml_content))
160
+ end
161
+ end
162
+
163
+ describe "invalid mapping combinations" do
164
+ it "raises error when combining map_all with other mappings" do
165
+ expect do
166
+ InvalidDocument.json do
167
+ map_element "title", to: :title
168
+ end
169
+ end.to raise_error(
170
+ StandardError,
171
+ "map_all is not allowed with other mappings",
172
+ )
173
+ end
174
+
175
+ it "raises error when combining other mappings are used with map_all" do
176
+ expect do
177
+ InvalidDocument.yaml do
178
+ map_element "title", to: :title
179
+ map_all to: :content
180
+ end
181
+ end.to raise_error(
182
+ StandardError,
183
+ "map_all is not allowed with other mappings",
184
+ )
185
+ end
186
+ end
187
+ end
188
+ end
@@ -467,7 +467,9 @@ RSpec.describe "MixedContent" do
467
467
  end
468
468
 
469
469
  describe ".from_xml" do
470
- let(:expected_content) { "Moon&Mars Distanced©its — surface covered & processed" }
470
+ let(:expected_content) do
471
+ "Moon&Mars Distanced©its — surface covered & processed"
472
+ end
471
473
 
472
474
  it "deserializes special char mixed content correctly" do
473
475
  parsed = MixedContentSpec::SpecialCharContentWithMixedTrue.from_xml(xml)
@@ -476,7 +478,9 @@ RSpec.describe "MixedContent" do
476
478
  end
477
479
 
478
480
  describe ".to_xml" do
479
- let(:expected_xml) { "Moon&amp;Mars Distanced©its — surface covered &amp; processed" }
481
+ let(:expected_xml) do
482
+ "Moon&amp;Mars Distanced©its — surface covered &amp; processed"
483
+ end
480
484
 
481
485
  it "serializes special char mixed content correctly" do
482
486
  parsed = MixedContentSpec::SpecialCharContentWithMixedTrue.from_xml(xml)
@@ -522,9 +526,15 @@ RSpec.describe "MixedContent" do
522
526
  end
523
527
 
524
528
  describe ".from_xml" do
525
- let(:expected_nokogiri_content) { "B <p>R&amp;C</p>\n C <p>J&#x2014;C</p>\n O <p>A &amp; B </p>\n F <p>Z &#xA9;S</p>" }
526
- let(:expected_ox_content) { "B <p>R&amp;C</p>\n C <p>JC</p>\n O <p>A &amp; B </p>\n F <p>Z ©S</p>" }
527
- let(:expected_oga_content) { "B <p>R&amp;C</p>\n C <p>J—C</p>\n O <p>A &amp; B </p>\n F <p>Z ©S</p>" }
529
+ let(:expected_nokogiri_content) do
530
+ "B <p>R&amp;C</p>\n C <p>J&#x2014;C</p>\n O <p>A &amp; B </p>\n F <p>Z &#xA9;S</p>"
531
+ end
532
+ let(:expected_ox_content) do
533
+ "B <p>R&amp;C</p> C <p>J—C</p> O <p>A &amp; B </p> F <p>Z ©S</p>"
534
+ end
535
+ let(:expected_oga_content) do
536
+ "B <p>R&amp;C</p>\n C <p>J—C</p>\n O <p>A &amp; B </p>\n F <p>Z ©S</p>"
537
+ end
528
538
 
529
539
  it "deserializes special char mixed content correctly" do
530
540
  parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
@@ -539,10 +549,10 @@ RSpec.describe "MixedContent" do
539
549
  let(:expected_nokogiri_xml) do
540
550
  <<~XML
541
551
  <SpecialCharContentWithRawOptionAndMixedOption><special>
542
- B &lt;p&gt;R&amp;amp;C&lt;/p&gt;
543
- C &lt;p&gt;J&amp;#x2014;C&lt;/p&gt;
544
- O &lt;p&gt;A &amp;amp; B &lt;/p&gt;
545
- F &lt;p&gt;Z &amp;#xA9;S&lt;/p&gt;
552
+ B <p>R&amp;C</p>
553
+ C <p>JC</p>
554
+ O <p>A &amp; B </p>
555
+ F <p>Z ©S</p>
546
556
  </special></SpecialCharContentWithRawOptionAndMixedOption>
547
557
  XML
548
558
  end
@@ -550,10 +560,10 @@ RSpec.describe "MixedContent" do
550
560
  let(:expected_ox_xml) do
551
561
  <<~XML
552
562
  <SpecialCharContentWithRawOptionAndMixedOption>
553
- <special> B &lt;p&gt;R&amp;amp;C&lt;/p&gt;
554
- C &lt;p&gt;J—C&lt;/p&gt;
555
- O &lt;p&gt;A &amp;amp; B &lt;/p&gt;
556
- F &lt;p&gt;Z ©S&lt;/p&gt;
563
+ <special> B <p>R&amp;C</p>
564
+ C <p>J—C</p>
565
+ O <p>A &amp; B </p>
566
+ F <p>Z ©S</p>
557
567
  </special>
558
568
  </SpecialCharContentWithRawOptionAndMixedOption>
559
569
  XML
@@ -562,10 +572,10 @@ RSpec.describe "MixedContent" do
562
572
  let(:expected_oga_xml) do
563
573
  <<~XML
564
574
  <SpecialCharContentWithRawOptionAndMixedOption>
565
- <special> B &lt;p&gt;R&amp;C&lt;/p&gt;
566
- C &lt;p&gt;J—C&lt;/p&gt;
567
- O &lt;p&gt;A &amp; B &lt;/p&gt;
568
- F &lt;p&gt;Z ©S&lt;/p&gt;
575
+ <special> B <p>R&amp;C</p>
576
+ C <p>J—C</p>
577
+ O <p>A &amp; B </p>
578
+ F <p>Z ©S</p>
569
579
  </special>
570
580
  </SpecialCharContentWithRawOptionAndMixedOption>
571
581
  XML
@@ -604,9 +614,9 @@ RSpec.describe "MixedContent" do
604
614
  end
605
615
 
606
616
  describe ".to_xml" do
607
- let(:expected_nokogiri_xml) { "B &lt;p&gt;R&lt;/p&gt;" }
608
- let(:expected_oga_xml) { "B &lt;p&gt;R&amp;C&lt;/p&gt;" }
609
- let(:expected_ox_xml) { "B &lt;p&gt;R&amp;amp;C&lt;/p&gt;" }
617
+ let(:expected_nokogiri_xml) { "B <p>R</p>" }
618
+ let(:expected_oga_xml) { "B <p>R&amp;C</p>" }
619
+ let(:expected_ox_xml) { "B <p>R&amp;C</p>" }
610
620
 
611
621
  it "serializes special char mixed content correctly" do
612
622
  parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
@@ -627,7 +637,9 @@ RSpec.describe "MixedContent" do
627
637
  end
628
638
 
629
639
  describe ".from_xml" do
630
- let(:expected_content) { "<computer security> type of operation specified by an access right" }
640
+ let(:expected_content) do
641
+ "<computer security> type of operation specified by an access right"
642
+ end
631
643
 
632
644
  it "deserializes special char mixed content correctly" do
633
645
  parsed = MixedContentSpec::TextualSupport.from_xml(xml)
@@ -637,8 +649,12 @@ RSpec.describe "MixedContent" do
637
649
  end
638
650
 
639
651
  describe ".to_xml" do
640
- let(:expected_xml) { "<TextualSupport>\n <value>&lt;computer security&gt; type of operation specified by an access right</value>\n</TextualSupport>" }
641
- let(:expected_oga_xml) { "<TextualSupport><value>&lt;computer security&gt; type of operation specified by an access right</value></TextualSupport>" }
652
+ let(:expected_xml) do
653
+ "<TextualSupport>\n <value>&lt;computer security&gt; type of operation specified by an access right</value>\n</TextualSupport>"
654
+ end
655
+ let(:expected_oga_xml) do
656
+ "<TextualSupport><value>&lt;computer security&gt; type of operation specified by an access right</value></TextualSupport>"
657
+ end
642
658
 
643
659
  it "serializes special char mixed content correctly" do
644
660
  parsed = MixedContentSpec::TextualSupport.from_xml(xml)
@@ -659,7 +675,9 @@ RSpec.describe "MixedContent" do
659
675
  end
660
676
 
661
677
  describe ".from_xml" do
662
- let(:expected_content) { "∑computer security∏ type of ​ operation specified µ by an access right" }
678
+ let(:expected_content) do
679
+ "∑computer security∏ type of ​ operation specified µ by an access right"
680
+ end
663
681
 
664
682
  it "deserializes special char mixed content correctly" do
665
683
  parsed = MixedContentSpec::HexCode.from_xml(xml)
@@ -670,7 +688,9 @@ RSpec.describe "MixedContent" do
670
688
 
671
689
  describe ".to_xml" do
672
690
  context "when default encoding xml" do
673
- let(:expected_default_encoding_xml) { "∑computer security∏ type of ​ operation specified µ by an access right" }
691
+ let(:expected_default_encoding_xml) do
692
+ "∑computer security∏ type of ​ operation specified µ by an access right"
693
+ end
674
694
 
675
695
  it "serializes special char mixed content correctly with default encoding: UTF-8" do
676
696
  parsed = MixedContentSpec::HexCode.from_xml(xml)
@@ -681,20 +701,36 @@ RSpec.describe "MixedContent" do
681
701
  end
682
702
 
683
703
  context "when encoding: nil xml" do
684
- let(:expected_encoding_nil_nokogiri_xml) { "&#x2211;computer security&#x220F; type of &#x200B; operation specified &#xB5; by an access right" }
685
- let(:expected_encoding_nil_ox_xml) { "computer security type of operation specified µ by an access right" }
704
+ let(:expected_encoding_nil_nokogiri_xml) do
705
+ "&#x2211;computer security&#x220F; type of &#x200B; operation specified &#xB5; by an access right"
706
+ end
707
+ let(:expected_encoding_nil_ox_xml) do
708
+ "<HexCode> \xE2\x88\x91computer security\xE2\x88\x8F type of \xE2\x80\x8B operation specified \xC2\xB5 by an access right </HexCode>\n".force_encoding("ASCII-8BIT")
709
+ end
710
+ let(:expected_encoding_nil_oga_xml) do
711
+ "<HexCode>\n ∑computer security∏ type of ​ operation specified µ by an access right\n</HexCode>"
712
+ end
686
713
 
687
714
  it "serializes special char mixed content correctly with encoding: nil to get hexcode" do
688
- parsed = MixedContentSpec::HexCode.from_xml(xml)
715
+ parsed = MixedContentSpec::HexCode.from_xml(xml, encoding: nil)
689
716
  serialized = parsed.to_xml(encoding: nil)
690
717
 
691
718
  expected_output = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
692
719
  expected_encoding_nil_nokogiri_xml
693
- else
720
+ elsif adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
694
721
  expected_encoding_nil_ox_xml
722
+ else
723
+ expected_encoding_nil_oga_xml
695
724
  end
696
725
 
697
- expect(serialized.strip).to include(expected_output)
726
+ expect(parsed.encoding).to be_nil
727
+ expect(serialized.strip).to include(expected_output.strip)
728
+
729
+ if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
730
+ expect(serialized.encoding.to_s).to eq("ASCII-8BIT")
731
+ else
732
+ expect(serialized.encoding.to_s).to eq("UTF-8")
733
+ end
698
734
  end
699
735
  end
700
736
  end
@@ -702,7 +738,9 @@ RSpec.describe "MixedContent" do
702
738
 
703
739
  context "when use encoding in parsing" do
704
740
  context "when use SHIFT-JIS encoding" do
705
- let(:fixture) { File.read(fixture_path("xml/shift_jis.xml"), encoding: "Shift_JIS") }
741
+ let(:fixture) do
742
+ File.read(fixture_path("xml/shift_jis.xml"), encoding: "Shift_JIS")
743
+ end
706
744
 
707
745
  describe ".from_xml" do
708
746
  it "verifies the encoding of file read" do
@@ -712,10 +750,10 @@ RSpec.describe "MixedContent" do
712
750
  it "deserializes SHIFT encoded content correctly with explicit encoding option" do
713
751
  parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
714
752
 
715
- expected_content = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
716
- "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
717
- else
753
+ expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
718
754
  "手書き英字1"
755
+ else
756
+ "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
719
757
  end
720
758
 
721
759
  expect(parsed.field).to include(expected_content)
@@ -725,13 +763,12 @@ RSpec.describe "MixedContent" do
725
763
  parsed = MixedContentSpec::Shift.from_xml(fixture)
726
764
 
727
765
  expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
728
- "�菑���p���P"
729
- elsif adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
730
766
  "手書き英字1"
731
767
  else
732
- "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("UTF-8")
768
+ "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
733
769
  end
734
770
 
771
+ expect(parsed.encoding).to eq("Shift_JIS")
735
772
  expect(parsed.field).to include(expected_content)
736
773
  end
737
774
  end
@@ -748,13 +785,13 @@ RSpec.describe "MixedContent" do
748
785
  parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
749
786
  serialized = parsed.to_xml(encoding: "UTF-8")
750
787
 
751
- expected_xml = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
752
- "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
753
- else
754
- "手書き英字1"
755
- end
788
+ parsed_xml = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
789
+ "手書き英字1"
790
+ else
791
+ "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
792
+ end
756
793
 
757
- expect(parsed.field).to include(expected_xml)
794
+ expect(parsed.field).to include(parsed_xml)
758
795
  expect(parsed.encoding).to eq("Shift_JIS")
759
796
 
760
797
  expect(serialized).to include("手書き英字1")
@@ -765,10 +802,10 @@ RSpec.describe "MixedContent" do
765
802
  parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
766
803
  serialized = parsed.to_xml(encoding: "Shift_JIS")
767
804
 
768
- expected_xml = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
769
- "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
770
- else
805
+ expected_xml = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
771
806
  "手書き英字1"
807
+ else
808
+ "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
772
809
  end
773
810
 
774
811
  expect(parsed.field).to include(expected_xml)
@@ -788,16 +825,18 @@ RSpec.describe "MixedContent" do
788
825
  expect(serialized.encoding.to_s).to eq("Shift_JIS")
789
826
  end
790
827
 
791
- it "serializes SHIFT-JIS content incorrectly bcz no encoding provided during parsing" do
828
+ it "serializes SHIFT-JIS content correctly bcz xml.encoding used during parsing" do
792
829
  parsed = MixedContentSpec::Shift.from_xml(fixture)
793
830
  serialized = parsed.to_xml(encoding: "Shift_JIS")
831
+
794
832
  expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
795
- "<root>\n <FieldName>&#65533;&#33745;&#65533;&#65533;&#65533;p&#65533;&#65533;&#65533;P</FieldName>\n <FieldName>123456</FieldName>\n</root>"
833
+ "<root>\n <FieldName>手書き英字1</FieldName>\n <FieldName>123456</FieldName>\n</root>".encode("Shift_JIS")
796
834
  elsif adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
797
- "<root>\n <FieldName>\x8E菑\x82\xAB\x89p\x8E\x9A\x82P</FieldName>\n <FieldName>123456</FieldName>\n</root>\n"
835
+ "<root>\n <FieldName>手書き英字1</FieldName>\n <FieldName>123456</FieldName>\n</root>\n".encode("Shift_JIS")
798
836
  else
799
837
  "<root><FieldName>手書き英字1</FieldName><FieldName>123456</FieldName></root>".encode("Shift_JIS")
800
838
  end
839
+
801
840
  expect(serialized).to eq(expected_content)
802
841
  end
803
842
 
@@ -829,27 +868,27 @@ RSpec.describe "MixedContent" do
829
868
  it "deserializes latin encoded content correctly" do
830
869
  parsed = MixedContentSpec::Latin.from_xml(fixture, encoding: "ISO-8859-1")
831
870
 
832
- expected_content = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
833
- ["M\xFCller".force_encoding("ISO-8859-1"), "Jos\xE9".force_encoding("ISO-8859-1")]
834
- else
871
+ expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
835
872
  ["Müller", "José"]
873
+ else
874
+ ["M\xFCller".force_encoding("ISO-8859-1"), "Jos\xE9".force_encoding("ISO-8859-1")]
836
875
  end
837
876
 
877
+ expect(parsed.encoding).to eq("ISO-8859-1")
838
878
  expect(parsed.from).to eq(expected_content[0])
839
879
  expect(parsed.the).to eq(expected_content[1])
840
880
  end
841
881
 
842
- it "deserializes latin encoded content incorrectly" do
882
+ it "deserializes latin encoded content correctly, bcz xml.encoding used for parsing" do
843
883
  parsed = MixedContentSpec::Latin.from_xml(fixture)
844
884
 
845
885
  expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
846
- ["M�ller", "Jos�"]
847
- elsif adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
848
886
  ["Müller", "José"]
849
887
  else
850
- ["M\xFCller", "Jos\xE9"]
888
+ ["M\xFCller".force_encoding("ISO-8859-1"), "Jos\xE9".force_encoding("ISO-8859-1")]
851
889
  end
852
890
 
891
+ expect(parsed.encoding).to eq("ISO-8859-1")
853
892
  expect(parsed.from).to eq(expected_content[0])
854
893
  expect(parsed.the).to eq(expected_content[1])
855
894
  end
@@ -106,7 +106,7 @@ module MultipleMapping
106
106
  end
107
107
 
108
108
  def name_from_xml(model, value)
109
- model.full_name = value.sub(/^XML Model: /, "")
109
+ model.full_name = value.text.sub(/^XML Model: /, "")
110
110
  end
111
111
 
112
112
  def color_to_xml(model, parent, doc)
@@ -116,7 +116,7 @@ module MultipleMapping
116
116
  end
117
117
 
118
118
  def color_from_xml(model, value)
119
- model.color = value.downcase
119
+ model.color = value.text.downcase
120
120
  end
121
121
 
122
122
  def size_to_xml(model, parent, doc)
@@ -126,7 +126,7 @@ module MultipleMapping
126
126
  end
127
127
 
128
128
  def size_from_xml(model, value)
129
- model.size = (value.to_i || 0) - 10
129
+ model.size = (value.text.to_i || 0) - 10
130
130
  end
131
131
 
132
132
  def desc_to_xml(model, parent, doc)
@@ -136,7 +136,7 @@ module MultipleMapping
136
136
  end
137
137
 
138
138
  def desc_from_xml(model, value)
139
- model.description = value.sub(/^XML Description: /, "")
139
+ model.description = value.text.sub(/^XML Description: /, "")
140
140
  end
141
141
  end
142
142
  end
@@ -144,8 +144,12 @@ end
144
144
  RSpec.describe MultipleMapping do
145
145
  context "with key-value formats" do
146
146
  context "with YAML format" do
147
- let(:yaml_with_name) { "product_name: Coffee Maker\ndescription: Premium coffee maker" }
148
- let(:yaml_with_desc) { "---\nname: Coffee Maker\ndesc: Premium coffee maker\n" }
147
+ let(:yaml_with_name) do
148
+ "product_name: Coffee Maker\ndescription: Premium coffee maker"
149
+ end
150
+ let(:yaml_with_desc) do
151
+ "---\nname: Coffee Maker\ndesc: Premium coffee maker\n"
152
+ end
149
153
 
150
154
  it "handles bidirectional conversion" do
151
155
  product1 = MultipleMapping::Product.from_yaml(yaml_with_name)
@@ -161,8 +165,12 @@ RSpec.describe MultipleMapping do
161
165
  end
162
166
 
163
167
  context "with JSON format" do
164
- let(:json_with_name) { '{"product_name":"Coffee Maker","description":"Premium coffee maker"}' }
165
- let(:json_with_desc) { '{"name":"Coffee Maker","desc":"Premium coffee maker"}' }
168
+ let(:json_with_name) do
169
+ '{"product_name":"Coffee Maker","description":"Premium coffee maker"}'
170
+ end
171
+ let(:json_with_desc) do
172
+ '{"name":"Coffee Maker","desc":"Premium coffee maker"}'
173
+ end
166
174
 
167
175
  it "handles bidirectional conversion" do
168
176
  product1 = MultipleMapping::Product.from_json(json_with_name)
@@ -254,8 +262,12 @@ RSpec.describe MultipleMapping do
254
262
 
255
263
  context "with CustomModel" do
256
264
  context "with JSON format" do
257
- let(:json_with_alternate) { '{"custom_name":"JSON Model: Vase","shade":"BLUE","dimension":22,"description":"JSON Description: A beautiful ceramic vase"}' }
258
- let(:json_with_standard) { '{"name":"JSON Model: Vase","color":"BLUE","size":22,"desc":"JSON Description: A beautiful ceramic vase"}' }
265
+ let(:json_with_alternate) do
266
+ '{"custom_name":"JSON Model: Vase","shade":"BLUE","dimension":22,"description":"JSON Description: A beautiful ceramic vase"}'
267
+ end
268
+ let(:json_with_standard) do
269
+ '{"name":"JSON Model: Vase","color":"BLUE","size":22,"desc":"JSON Description: A beautiful ceramic vase"}'
270
+ end
259
271
 
260
272
  it "handles bidirectional conversion with custom methods" do
261
273
  model1 = MultipleMapping::CustomModel.from_json(json_with_alternate)