lutaml-model 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)