lutaml-model 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +58 -21
  3. data/Gemfile +1 -0
  4. data/README.adoc +1112 -264
  5. data/lib/lutaml/model/attribute.rb +33 -10
  6. data/lib/lutaml/model/choice.rb +56 -0
  7. data/lib/lutaml/model/config.rb +1 -0
  8. data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
  9. data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
  10. data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
  11. data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
  12. data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
  13. data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
  14. data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
  15. data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
  16. data/lib/lutaml/model/error.rb +8 -0
  17. data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
  18. data/lib/lutaml/model/key_value_mapping.rb +3 -1
  19. data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
  20. data/lib/lutaml/model/liquefiable.rb +59 -0
  21. data/lib/lutaml/model/mapping_hash.rb +1 -1
  22. data/lib/lutaml/model/mapping_rule.rb +15 -2
  23. data/lib/lutaml/model/schema/xml_compiler.rb +68 -26
  24. data/lib/lutaml/model/schema_location.rb +7 -0
  25. data/lib/lutaml/model/sequence.rb +71 -0
  26. data/lib/lutaml/model/serialize.rb +125 -35
  27. data/lib/lutaml/model/type/decimal.rb +0 -4
  28. data/lib/lutaml/model/type/time.rb +3 -3
  29. data/lib/lutaml/model/utils.rb +19 -15
  30. data/lib/lutaml/model/validation.rb +12 -1
  31. data/lib/lutaml/model/version.rb +1 -1
  32. data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
  33. data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
  34. data/lib/lutaml/model/xml_adapter/element.rb +32 -0
  35. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +8 -8
  36. data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
  37. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
  38. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
  39. data/lib/lutaml/model/xml_adapter/xml_document.rb +74 -13
  40. data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
  41. data/lib/lutaml/model/xml_mapping.rb +49 -7
  42. data/lib/lutaml/model/xml_mapping_rule.rb +8 -3
  43. data/lib/lutaml/model.rb +1 -0
  44. data/lutaml-model.gemspec +5 -0
  45. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
  46. data/spec/ceramic_spec.rb +39 -0
  47. data/spec/fixtures/ceramic.rb +23 -0
  48. data/spec/fixtures/xml/address_example_260.xsd +9 -0
  49. data/spec/fixtures/xml/user.xsd +10 -0
  50. data/spec/lutaml/model/cdata_spec.rb +4 -5
  51. data/spec/lutaml/model/choice_spec.rb +168 -0
  52. data/spec/lutaml/model/collection_spec.rb +1 -1
  53. data/spec/lutaml/model/custom_model_spec.rb +6 -7
  54. data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
  55. data/spec/lutaml/model/defaults_spec.rb +3 -1
  56. data/spec/lutaml/model/delegation_spec.rb +7 -5
  57. data/spec/lutaml/model/enum_spec.rb +35 -0
  58. data/spec/lutaml/model/group_spec.rb +160 -0
  59. data/spec/lutaml/model/inheritance_spec.rb +25 -0
  60. data/spec/lutaml/model/liquefiable_spec.rb +121 -0
  61. data/spec/lutaml/model/mixed_content_spec.rb +80 -41
  62. data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
  63. data/spec/lutaml/model/schema/xml_compiler_spec.rb +218 -25
  64. data/spec/lutaml/model/sequence_spec.rb +216 -0
  65. data/spec/lutaml/model/transformation_spec.rb +230 -0
  66. data/spec/lutaml/model/type_spec.rb +138 -31
  67. data/spec/lutaml/model/utils_spec.rb +32 -0
  68. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
  69. data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
  70. data/spec/lutaml/model/xml_mapping_spec.rb +143 -112
  71. metadata +67 -2
@@ -0,0 +1,160 @@
1
+ require "spec_helper"
2
+ require "lutaml/model"
3
+
4
+ module GroupSpec
5
+ class Ceramic < Lutaml::Model::Serializable
6
+ attribute :type, :string
7
+ attribute :name, :string
8
+
9
+ xml do
10
+ no_root
11
+ map_element :type, to: :type
12
+ map_element :name, to: :name
13
+ end
14
+ end
15
+
16
+ class CeramicCollection < Lutaml::Model::Serializable
17
+ attribute :ceramic, Ceramic, collection: 1..2
18
+
19
+ xml do
20
+ root "collection"
21
+ map_element "ceramic", to: :ceramic
22
+ end
23
+ end
24
+
25
+ class AttributeValueType < Lutaml::Model::Type::Decimal
26
+ end
27
+
28
+ class GroupOfItems < Lutaml::Model::Serializable
29
+ attribute :name, :string
30
+ attribute :type, :string
31
+ attribute :description, :string
32
+ attribute :code, :string
33
+
34
+ xml do
35
+ no_root
36
+ sequence do
37
+ map_element "name", to: :name
38
+ map_element "type", to: :type
39
+ map_element "description", to: :description,
40
+ namespace: "http://www.sparxsystems.com/profiles/GML/1.0",
41
+ prefix: "GML"
42
+ end
43
+ map_attribute "code", to: :code, namespace: "http://www.example.com", prefix: "ex1"
44
+ end
45
+ end
46
+
47
+ class ComplexType < Lutaml::Model::Serializable
48
+ attribute :tag, AttributeValueType
49
+ attribute :content, :string
50
+ attribute :group, :string
51
+ import_model_attributes GroupOfItems
52
+
53
+ xml do
54
+ root "GroupOfItems"
55
+ map_attribute "tag", to: :tag
56
+ map_content to: :content
57
+ map_element :group, to: :group
58
+ import_model_mappings GroupOfItems
59
+ end
60
+ end
61
+
62
+ class SimpleType < Lutaml::Model::Serializable
63
+ import_model GroupOfItems
64
+ end
65
+
66
+ class GenericType < Lutaml::Model::Serializable
67
+ import_model_mappings GroupOfItems
68
+ end
69
+
70
+ class GroupWithRoot < Lutaml::Model::Serializable
71
+ attribute :name, :string
72
+
73
+ xml do
74
+ root "group"
75
+ map_element :name, to: :name
76
+ end
77
+ end
78
+ end
79
+
80
+ RSpec.describe "Group" do
81
+ context "with no_root" do
82
+ let(:mapper) { GroupSpec::CeramicCollection }
83
+
84
+ it "raises error if root-less class used directly for parsing" do
85
+ xml = <<~XML
86
+ <type>Data</type>
87
+ <name>Smith</name>
88
+ XML
89
+
90
+ expect { GroupSpec::Ceramic.from_xml(xml) }.to raise_error(
91
+ Lutaml::Model::NoRootMappingError,
92
+ "GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
93
+ )
94
+ end
95
+
96
+ it "raises error if root_less class used for deserializing" do
97
+ ceramic = GroupSpec::Ceramic.new(type: "Data", name: "Starc")
98
+
99
+ expect { ceramic.to_xml }.to raise_error(
100
+ Lutaml::Model::NoRootMappingError,
101
+ "GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
102
+ )
103
+ end
104
+
105
+ it "correctly get the element of root-less class" do
106
+ xml = <<~XML
107
+ <collection>
108
+ <ceramic>
109
+ <type>Data</type>
110
+ </ceramic>
111
+ </collection>
112
+ XML
113
+
114
+ expect { mapper.from_xml(xml) }.not_to raise_error
115
+ end
116
+ end
117
+
118
+ context "with model" do
119
+ it "import attributes" do
120
+ expect(GroupSpec::ComplexType.attributes).to include(GroupSpec::GroupOfItems.attributes)
121
+ end
122
+
123
+ it "import mappings in xml block" do
124
+ expect(GroupSpec::ComplexType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
125
+ end
126
+
127
+ it "import mappings outside xml block" do
128
+ expect(GroupSpec::GenericType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
129
+ end
130
+
131
+ it "import attributes and mappings in xml block" do
132
+ expect(GroupSpec::ComplexType.attributes).to include(GroupSpec::GroupOfItems.attributes)
133
+ expect(GroupSpec::ComplexType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
134
+ end
135
+
136
+ it "import attributes and mappings outside the xml block" do
137
+ expect(GroupSpec::SimpleType.attributes).to include(GroupSpec::GroupOfItems.attributes)
138
+ expect(GroupSpec::SimpleType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
139
+ end
140
+
141
+ it "raises error if root is defined on imported class" do
142
+ expect do
143
+ Class.new(Lutaml::Model::Serializable) do
144
+ import_model GroupSpec::GroupWithRoot
145
+ end
146
+ end.to raise_error(Lutaml::Model::ImportModelWithRootError, "Cannot import a model `GroupSpec::GroupWithRoot` with a root element")
147
+ end
148
+
149
+ it "raises error if namespace is defined with no_root" do
150
+ expect do
151
+ Class.new(Lutaml::Model::Serializable) do
152
+ xml do
153
+ no_root
154
+ namespace "http://www.omg.org/spec/XMI/20131001", "xmi"
155
+ end
156
+ end
157
+ end.to raise_error(Lutaml::Model::NoRootNamespaceError, "Cannot assign namespace to `no_root`")
158
+ end
159
+ end
160
+ end
@@ -34,6 +34,22 @@ module InheritanceSpec
34
34
  map_element "gender", to: :age
35
35
  end
36
36
  end
37
+
38
+ class ParentWithMapAll < Lutaml::Model::Serializable
39
+ attribute :id, :string
40
+ attribute :description, :string
41
+
42
+ xml do
43
+ map_attribute "id", to: :id
44
+ map_all_content to: :description
45
+ end
46
+ end
47
+
48
+ class Child < ParentWithMapAll
49
+ xml do
50
+ root "child"
51
+ end
52
+ end
37
53
  end
38
54
 
39
55
  RSpec.describe "Inheritance" do
@@ -89,4 +105,13 @@ RSpec.describe "Inheritance" do
89
105
  end
90
106
  end
91
107
  end
108
+
109
+ context "with map_all in parent" do
110
+ let(:xml) { "<child id=\"en\">Some <b>bold</b> Content</child>" }
111
+
112
+ it "round trip correctly" do
113
+ parsed = InheritanceSpec::Child.from_xml(xml)
114
+ expect(parsed.to_xml).to eq(xml)
115
+ end
116
+ end
92
117
  end
@@ -0,0 +1,121 @@
1
+ require "spec_helper"
2
+ require_relative "../../fixtures/address"
3
+
4
+ class LiquefiableClass
5
+ include Lutaml::Model::Liquefiable
6
+
7
+ attr_accessor :name, :value
8
+
9
+ def initialize(name, value)
10
+ @name = name
11
+ @value = value
12
+ end
13
+
14
+ def display_name
15
+ "#{name} (#{value})"
16
+ end
17
+ end
18
+
19
+ RSpec.describe Lutaml::Model::Liquefiable do
20
+ before do
21
+ stub_const("DummyModel", Class.new(LiquefiableClass))
22
+ end
23
+
24
+ let(:dummy) { DummyModel.new("TestName", 42) }
25
+
26
+ describe ".register_liquid_drop_class" do
27
+ context "when drop class does not exist" do
28
+ it "creates a new drop class" do
29
+ expect { dummy.class.register_liquid_drop_class }.to change {
30
+ dummy.class.const_defined?(:DummyModelDrop)
31
+ }
32
+ .from(false)
33
+ .to(true)
34
+ end
35
+ end
36
+
37
+ context "when drop class already exists" do
38
+ it "raises an error" do
39
+ dummy.class.register_liquid_drop_class
40
+ expect { dummy.class.register_liquid_drop_class }.to raise_error(RuntimeError, "DummyModelDrop Already exists!")
41
+ end
42
+ end
43
+ end
44
+
45
+ describe ".drop_class_name" do
46
+ it "returns the correct drop class name" do
47
+ expect(dummy.class.drop_class_name).to eq("DummyModelDrop")
48
+ end
49
+ end
50
+
51
+ describe ".drop_class" do
52
+ context "when drop class exists" do
53
+ it "returns the drop class" do
54
+ dummy.class.register_liquid_drop_class
55
+ expect(dummy.class.drop_class).to eq(DummyModel::DummyModelDrop)
56
+ end
57
+ end
58
+
59
+ context "when drop class does not exist" do
60
+ it "returns nil" do
61
+ expect(dummy.class.drop_class).to be_nil
62
+ end
63
+ end
64
+ end
65
+
66
+ describe ".register_drop_method" do
67
+ before do
68
+ dummy.class.register_liquid_drop_class
69
+ end
70
+
71
+ it "defines a method on the drop class" do
72
+ expect { dummy.class.register_drop_method(:display_name) }.to change {
73
+ dummy.to_liquid.respond_to?(:display_name)
74
+ }
75
+ .from(false)
76
+ .to(true)
77
+ end
78
+ end
79
+
80
+ describe ".to_liquid" do
81
+ before do
82
+ dummy.class.register_liquid_drop_class
83
+ dummy.class.register_drop_method(:display_name)
84
+ end
85
+
86
+ it "returns an instance of the drop class" do
87
+ expect(dummy.to_liquid).to be_a(dummy.class.drop_class)
88
+ end
89
+
90
+ it "allows access to registered methods via the drop class" do
91
+ expect(dummy.to_liquid.display_name).to eq("TestName (42)")
92
+ end
93
+ end
94
+
95
+ context "with serializeable classes" do
96
+ let(:address) do
97
+ Address.new(
98
+ {
99
+ country: "US",
100
+ post_code: "12345",
101
+ person: [Person.new, Person.new],
102
+ },
103
+ )
104
+ end
105
+
106
+ describe ".to_liquid" do
107
+ it "returns correct drop object" do
108
+ expect(address.to_liquid).to be_a(Address::AddressDrop)
109
+ end
110
+
111
+ it "returns array of drops for collection objects" do
112
+ person_classes = address.to_liquid.person.map(&:class)
113
+ expect(person_classes).to eq([Person::PersonDrop, Person::PersonDrop])
114
+ end
115
+
116
+ it "returns `US` for country" do
117
+ expect(address.to_liquid.country).to eq("US")
118
+ end
119
+ end
120
+ end
121
+ 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)
@@ -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)