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,71 @@
1
+ module Lutaml
2
+ module Model
3
+ class Sequence
4
+ attr_reader :attributes,
5
+ :model
6
+
7
+ def initialize(model)
8
+ @attributes = []
9
+ @model = model
10
+ end
11
+
12
+ def attribute(name, type, options = {})
13
+ options[:sequence] = self
14
+ @model.attribute(name, type, options)
15
+ end
16
+
17
+ def sequence(&block)
18
+ instance_eval(&block)
19
+ end
20
+
21
+ def map_element(
22
+ name,
23
+ to: nil,
24
+ render_nil: false,
25
+ render_default: false,
26
+ with: {},
27
+ delegate: nil,
28
+ cdata: false,
29
+ namespace: nil,
30
+ prefix: nil,
31
+ transform: {}
32
+ )
33
+ @attributes << @model.map_element(
34
+ name,
35
+ to: to,
36
+ render_nil: render_nil,
37
+ render_default: render_default,
38
+ with: with,
39
+ delegate: delegate,
40
+ cdata: cdata,
41
+ namespace: namespace,
42
+ prefix: prefix,
43
+ transform: transform,
44
+ )
45
+ end
46
+
47
+ def map_attribute(*)
48
+ raise Lutaml::Model::UnknownSequenceMappingError.new("map_attribute")
49
+ end
50
+
51
+ def map_content(*)
52
+ raise Lutaml::Model::UnknownSequenceMappingError.new("map_content")
53
+ end
54
+
55
+ def map_all(*)
56
+ raise Lutaml::Model::UnknownSequenceMappingError.new("map_all")
57
+ end
58
+
59
+ def validate_content!(element_order)
60
+ defined_order = @attributes.map { |rule| rule.name.to_s }
61
+ start_index = element_order.index(defined_order.first)
62
+
63
+ defined_order.each.with_index(start_index) do |element, i|
64
+ unless element_order[i] == element
65
+ raise Lutaml::Model::IncorrectSequenceError.new(element, element_order[i])
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -12,12 +12,16 @@ require_relative "comparable_model"
12
12
  require_relative "schema_location"
13
13
  require_relative "validation"
14
14
  require_relative "error"
15
+ require_relative "choice"
16
+ require_relative "sequence"
17
+ require_relative "liquefiable"
15
18
 
16
19
  module Lutaml
17
20
  module Model
18
21
  module Serialize
19
22
  include ComparableModel
20
23
  include Validation
24
+ include Lutaml::Model::Liquefiable
21
25
 
22
26
  def self.included(base)
23
27
  base.extend(ClassMethods)
@@ -25,7 +29,9 @@ module Lutaml
25
29
  end
26
30
 
27
31
  module ClassMethods
28
- attr_accessor :attributes, :mappings
32
+ include Lutaml::Model::Liquefiable::ClassMethods
33
+
34
+ attr_accessor :attributes, :mappings, :choice_attributes
29
35
 
30
36
  def inherited(subclass)
31
37
  super
@@ -40,6 +46,7 @@ module Lutaml
40
46
  def initialize_attrs(source_class)
41
47
  @mappings = Utils.deep_dup(source_class.instance_variable_get(:@mappings)) || {}
42
48
  @attributes = Utils.deep_dup(source_class.instance_variable_get(:@attributes)) || {}
49
+ @choice_attributes = Utils.deep_dup(source_class.instance_variable_get(:@choice_attributes)) || []
43
50
  instance_variable_set(:@model, self)
44
51
  end
45
52
 
@@ -81,6 +88,12 @@ module Lutaml
81
88
  value
82
89
  end
83
90
 
91
+ def choice(min: 1, max: 1, &block)
92
+ @choice_attributes << Choice.new(self, min, max).tap do |c|
93
+ c.instance_eval(&block)
94
+ end
95
+ end
96
+
84
97
  # Define an attribute for the model
85
98
  def attribute(name, type, options = {})
86
99
  attr = Attribute.new(name, type, options)
@@ -103,6 +116,33 @@ module Lutaml
103
116
  instance_variable_set(:"@#{name}", attr.cast_value(value))
104
117
  end
105
118
  end
119
+
120
+ register_drop_method(name)
121
+
122
+ attr
123
+ end
124
+
125
+ def root?
126
+ mappings_for(:xml).root?
127
+ end
128
+
129
+ def import_model_attributes(model)
130
+ raise Lutaml::Model::ImportModelWithRootError.new(model) if model.root?
131
+
132
+ @attributes.merge!(model.attributes)
133
+ end
134
+
135
+ def import_model_mappings(model)
136
+ raise Lutaml::Model::ImportModelWithRootError.new(model) if model.root?
137
+
138
+ @mappings.merge!(model.mappings)
139
+ end
140
+
141
+ def import_model(model)
142
+ raise Lutaml::Model::ImportModelWithRootError.new(model) if model.root?
143
+
144
+ import_model_attributes(model)
145
+ import_model_mappings(model)
106
146
  end
107
147
 
108
148
  def add_enum_methods_to_model(klass, enum_name, values, collection: false)
@@ -140,7 +180,7 @@ module Lutaml
140
180
  enum_vals.delete(value)
141
181
  enum_vals
142
182
  else
143
- []
183
+ instance_variable_get(:"@#{enum_name}") - [value]
144
184
  end
145
185
 
146
186
  instance_variable_set(:"@#{enum_name}", enum_vals)
@@ -190,7 +230,7 @@ module Lutaml
190
230
  mappings[format] ||= klass.new
191
231
  mappings[format].instance_eval(&block)
192
232
 
193
- if format == :xml && !mappings[format].root_element
233
+ if format == :xml && !mappings[format].root_element && !mappings[format].no_root?
194
234
  mappings[format].root(model.to_s)
195
235
  end
196
236
  end
@@ -208,9 +248,10 @@ module Lutaml
208
248
  end
209
249
 
210
250
  if format == :xml
211
- doc_hash = doc.parse_element(doc.root, self, :xml)
251
+ raise Lutaml::Model::NoRootMappingError.new(self) unless root?
252
+
212
253
  options[:encoding] = doc.encoding
213
- apply_mappings(doc_hash, format, options)
254
+ apply_mappings(doc, format, options)
214
255
  else
215
256
  apply_mappings(doc.to_h, format)
216
257
  end
@@ -276,6 +317,10 @@ module Lutaml
276
317
 
277
318
  attribute = attributes[name]
278
319
 
320
+ if export_method = rule.transform[:export] || attribute.transform_export_method
321
+ value = export_method.call(value)
322
+ end
323
+
279
324
  next hash.merge!(generate_hash_from_child_mappings(attribute, value, format, rule.root_mappings)) if rule.root_mapping?
280
325
 
281
326
  value = if rule.child_mappings
@@ -429,6 +474,7 @@ module Lutaml
429
474
  return instance if Utils.blank?(doc)
430
475
 
431
476
  options[:mappings] = mappings_for(format).mappings
477
+
432
478
  return apply_xml_mapping(doc, instance, options) if format == :xml
433
479
 
434
480
  apply_hash_mapping(doc, instance, format, options)
@@ -445,25 +491,28 @@ module Lutaml
445
491
  end
446
492
  mappings = options[:mappings] || mappings_for(:xml).mappings
447
493
 
448
- if doc.is_a?(Array)
449
- raise Lutaml::Model::CollectionTrueMissingError(self, option[:caller_class])
450
- end
494
+ raise Lutaml::Model::CollectionTrueMissingError(self, option[:caller_class]) if doc.is_a?(Array)
451
495
 
452
- if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
453
- instance.element_order = doc.item_order
496
+ if instance.respond_to?(:ordered=)
497
+ instance.element_order = doc.root.order
454
498
  instance.ordered = mappings_for(:xml).ordered? || options[:ordered]
455
499
  instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
456
500
  end
457
501
 
458
- if doc["attributes"]&.key?("__schema_location")
502
+ schema_location = doc.attributes.values.find do |a|
503
+ a.unprefixed_name == "schemaLocation"
504
+ end
505
+
506
+ if !schema_location.nil?
459
507
  instance.schema_location = Lutaml::Model::SchemaLocation.new(
460
- schema_location: doc["attributes"]["__schema_location"][:schema_location],
461
- prefix: doc["attributes"]["__schema_location"][:prefix],
462
- namespace: doc["attributes"]["__schema_location"][:namespace],
508
+ schema_location: schema_location.value,
509
+ prefix: schema_location.namespace_prefix,
510
+ namespace: schema_location.namespace,
463
511
  )
464
512
  end
465
513
 
466
514
  defaults_used = []
515
+ validate_sequence!(instance.element_order)
467
516
 
468
517
  mappings.each do |rule|
469
518
  raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
@@ -471,12 +520,12 @@ module Lutaml
471
520
  attr = attribute_for_rule(rule)
472
521
 
473
522
  value = if rule.raw_mapping?
474
- doc.node.inner_xml
523
+ doc.root.inner_xml
475
524
  elsif rule.content_mapping?
476
- doc[rule.content_key]
477
- elsif val = value_for_rule(doc, rule, options)
525
+ rule.cdata ? doc.cdata : doc.text
526
+ elsif val = value_for_rule(doc, rule, options, instance)
478
527
  val
479
- else
528
+ elsif instance.using_default?(rule.to) || rule.render_default
480
529
  defaults_used << rule.to
481
530
  attr&.default || rule.to_value_for(instance)
482
531
  end
@@ -492,13 +541,44 @@ module Lutaml
492
541
  instance
493
542
  end
494
543
 
495
- def value_for_rule(doc, rule, options)
544
+ def value_for_rule(doc, rule, options, instance)
496
545
  rule_names = rule.namespaced_names(options[:default_namespace])
497
- hash = rule.attribute? ? doc["attributes"] : doc["elements"]
498
- return unless hash
499
546
 
500
- value_key = rule_names.find { |name| hash.key_exist?(name) }
501
- hash.fetch(value_key) if value_key
547
+ if rule.attribute?
548
+ doc.root.find_attribute_value(rule_names)
549
+ else
550
+ attr = attribute_for_rule(rule)
551
+
552
+ children = doc.children.select do |child|
553
+ rule_names.include?(child.namespaced_name)
554
+ end
555
+
556
+ if rule.using_custom_methods? || attr.type == Lutaml::Model::Type::Hash
557
+ return children.first
558
+ end
559
+
560
+ if Utils.present?(children)
561
+ instance.value_set_for(attr.name)
562
+ end
563
+
564
+ if rule.cdata
565
+ values = children.map do |child|
566
+ child.cdata_children&.map(&:text)
567
+ end.flatten
568
+ return children.count > 1 ? values : values.first
569
+ end
570
+
571
+ values = children.map do |child|
572
+ if !rule.using_custom_methods? && attr.type <= Serialize
573
+ attr.type.apply_xml_mapping(child, attr.type.new, options.except(:mappings))
574
+ elsif attr.raw?
575
+ inner_xml_of(child)
576
+ else
577
+ child&.children&.first&.text
578
+ end
579
+ end
580
+ attr&.collection? ? values : values.first
581
+ end
502
582
  end
503
583
 
504
584
  def apply_hash_mapping(doc, instance, format, options = {})
@@ -546,18 +626,6 @@ module Lutaml
546
626
  def normalize_xml_value(value, rule, attr, options = {})
547
627
  value = [value].compact if attr&.collection? && !value.is_a?(Array)
548
628
 
549
- value = if value.is_a?(Array)
550
- value.map do |v|
551
- text_hash?(attr, v) ? v.text : v
552
- end
553
- elsif attr&.raw? && value
554
- value.node.children.map(&:to_xml).join
555
- elsif text_hash?(attr, value)
556
- value.text
557
- else
558
- value
559
- end
560
-
561
629
  return value unless cast_value?(attr, rule)
562
630
 
563
631
  options.merge(caller_class: self, mixed_content: rule.mixed_content)
@@ -599,6 +667,26 @@ module Lutaml
599
667
  value
600
668
  end
601
669
  end
670
+
671
+ def validate_sequence!(element_order)
672
+ mapping_sequence = mappings_for(:xml).element_sequence
673
+ current_order = element_order.filter_map(&:element_tag)
674
+
675
+ mapping_sequence.each do |mapping|
676
+ mapping.validate_content!(current_order)
677
+ end
678
+ end
679
+
680
+ private
681
+
682
+ def inner_xml_of(node)
683
+ case node
684
+ when XmlAdapter::XmlElement
685
+ node.inner_xml
686
+ else
687
+ node.children.map(&:to_xml).join
688
+ end
689
+ end
602
690
  end
603
691
 
604
692
  attr_accessor :element_order, :schema_location, :encoding
@@ -719,6 +807,8 @@ module Lutaml
719
807
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
720
808
  define_method(:"to_#{format}") do |options = {}|
721
809
  adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
810
+ raise Lutaml::Model::NoRootMappingError.new(self.class) unless self.class.root?
811
+
722
812
  representation = if format == :xml
723
813
  self
724
814
  else
@@ -27,10 +27,6 @@ module Lutaml
27
27
  value.to_s("F") # Use fixed-point notation to match test expectations
28
28
  end
29
29
 
30
- def self.from_xml(value)
31
- cast(value.text)
32
- end
33
-
34
30
  def self.check_dependencies!(value)
35
31
  unless defined?(BigDecimal)
36
32
  raise TypeNotEnabledError.new("Decimal", value)
@@ -25,9 +25,9 @@ module Lutaml
25
25
  end
26
26
 
27
27
  # # xs:time format (HH:MM:SS.mmm±HH:MM)
28
- # def to_xml
29
- # value&.strftime("%H:%M:%S%:z")
30
- # end
28
+ def to_xml
29
+ value&.iso8601
30
+ end
31
31
 
32
32
  # # ISO8601 time format
33
33
  # def to_json
@@ -85,26 +85,30 @@ module Lutaml
85
85
  end
86
86
  end
87
87
 
88
- def deep_dup(hash)
89
- return hash if hash.nil?
90
-
91
- new_hash = {}
92
-
93
- hash.each do |key, value|
94
- new_hash[key] = if value.is_a?(Hash)
95
- deep_dup(value)
96
- elsif value.respond_to?(:deep_dup)
97
- value.deep_dup
98
- else
99
- value.dup
100
- end
101
- end
88
+ def deep_dup(object)
89
+ return object if object.nil?
102
90
 
103
- new_hash
91
+ case object
92
+ when Hash then deep_dup_hash(object)
93
+ when Array then deep_dup_array(object)
94
+ else deep_dup_object(object)
95
+ end
104
96
  end
105
97
 
106
98
  private
107
99
 
100
+ def deep_dup_hash(hash)
101
+ hash.transform_values { |value| deep_dup(value) }
102
+ end
103
+
104
+ def deep_dup_array(array)
105
+ array.map { |value| deep_dup(value) }
106
+ end
107
+
108
+ def deep_dup_object(object)
109
+ object.respond_to?(:deep_dup) ? object.deep_dup : object.dup
110
+ end
111
+
108
112
  def camelize_part(part)
109
113
  part.gsub(/(?:_|-|^)([a-z\d])/i) { $1.upcase }
110
114
  end
@@ -17,13 +17,24 @@ module Lutaml
17
17
  errors << e
18
18
  end
19
19
  end
20
- errors
20
+
21
+ validate_helper(errors)
21
22
  end
22
23
 
23
24
  def validate!
24
25
  errors = validate
25
26
  raise Lutaml::Model::ValidationError.new(errors) if errors.any?
26
27
  end
28
+
29
+ def validate_helper(errors)
30
+ self.class.choice_attributes.each do |attribute|
31
+ attribute.validate_content!(self)
32
+ end
33
+ errors
34
+ rescue Lutaml::Model::ChoiceUpperBoundError,
35
+ Lutaml::Model::ChoiceLowerBoundError => e
36
+ errors << e
37
+ end
27
38
  end
28
39
  end
29
40
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.5.4"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
@@ -6,19 +6,15 @@ module Lutaml
6
6
  module Builder
7
7
  class Oga
8
8
  def self.build(options = {}, &block)
9
- if block_given?
10
- XmlAdapter::Builder::Oga.new(options, &block)
11
- else
12
- XmlAdapter::Builder::Oga.new(options)
13
- end
9
+ new(options, &block)
14
10
  end
15
11
 
16
- attr_reader :document, :current_node, :options
12
+ attr_reader :document, :current_node, :encoding
17
13
 
18
14
  def initialize(options = {})
19
15
  @document = XmlAdapter::Oga::Document.new
20
16
  @current_node = @document
21
- @options = options
17
+ @encoding = options[:encoding]
22
18
  yield(self) if block_given?
23
19
  end
24
20
 
@@ -112,9 +108,14 @@ module Lutaml
112
108
  end
113
109
 
114
110
  def add_text(element, text, cdata: false)
111
+ text = text&.encode(encoding) if encoding && text.is_a?(String)
115
112
  return add_cdata(element, text) if cdata
116
113
 
117
114
  oga_text = ::Oga::XML::Text.new(text: text.to_s)
115
+ append_text_node(element, oga_text)
116
+ end
117
+
118
+ def append_text_node(element, oga_text)
118
119
  if element.is_a?(XmlAdapter::Oga::Document)
119
120
  children = element.children
120
121
  children.empty? ? children << oga_text : children.last.children << oga_text
@@ -166,6 +167,8 @@ module Lutaml
166
167
 
167
168
  def element_attributes(oga_element, attributes)
168
169
  oga_element.attributes = attributes.map do |name, value|
170
+ value = value.uri unless value.is_a?(String)
171
+
169
172
  ::Oga::XML::Attribute.new(
170
173
  name: name,
171
174
  value: value,
@@ -6,17 +6,18 @@ module Lutaml
6
6
  def self.build(options = {})
7
7
  if block_given?
8
8
  ::Ox::Builder.new(options) do |xml|
9
- yield(new(xml))
9
+ yield(new(xml, options))
10
10
  end
11
11
  else
12
- new(::Ox::Builder.new(options))
12
+ new(::Ox::Builder.new(options), options)
13
13
  end
14
14
  end
15
15
 
16
- attr_reader :xml
16
+ attr_reader :xml, :encoding
17
17
 
18
- def initialize(xml)
18
+ def initialize(xml, options = {})
19
19
  @xml = xml
20
+ @encoding = options[:encoding]
20
21
  @current_namespace = nil
21
22
  end
22
23
 
@@ -27,7 +28,7 @@ module Lutaml
27
28
 
28
29
  if block_given?
29
30
  xml.element(name, attributes) do |element|
30
- yield(self.class.new(element))
31
+ yield(self.class.new(element, { encoding: encoding }))
31
32
  end
32
33
  else
33
34
  xml.element(name, attributes)
@@ -44,17 +45,11 @@ module Lutaml
44
45
 
45
46
  def create_and_add_element(element_name, prefix: nil, attributes: {})
46
47
  element_name = element_name.first if element_name.is_a?(Array)
47
- prefixed_name = if prefix
48
- "#{prefix}:#{element_name}"
49
- elsif @current_namespace && !element_name.start_with?("#{@current_namespace}:")
50
- "#{@current_namespace}:#{element_name}"
51
- else
52
- element_name
53
- end
48
+ prefixed_name = set_prefixed_name(element_name, prefix)
54
49
 
55
50
  if block_given?
56
51
  xml.element(prefixed_name, attributes) do |element|
57
- yield(self.class.new(element))
52
+ yield(self.class.new(element, { encoding: encoding }))
58
53
  end
59
54
  else
60
55
  xml.element(prefixed_name, attributes)
@@ -63,6 +58,16 @@ module Lutaml
63
58
  @current_namespace = nil
64
59
  end
65
60
 
61
+ def set_prefixed_name(element_name, prefix)
62
+ if prefix
63
+ "#{prefix}:#{element_name}"
64
+ elsif @current_namespace && !element_name.start_with?("#{@current_namespace}:")
65
+ "#{@current_namespace}:#{element_name}"
66
+ else
67
+ element_name
68
+ end
69
+ end
70
+
66
71
  def <<(text)
67
72
  xml.text(text)
68
73
  end
@@ -72,6 +77,8 @@ module Lutaml
72
77
  end
73
78
 
74
79
  def add_text(element, text, cdata: false)
80
+ text = text&.encode(encoding) if encoding && text.is_a?(String)
81
+
75
82
  return element.cdata(text) if cdata
76
83
 
77
84
  element.text(text)
@@ -0,0 +1,32 @@
1
+ module Lutaml
2
+ module Model
3
+ module XmlAdapter
4
+ class Element
5
+ attr_reader :type, :name
6
+
7
+ def initialize(type, name)
8
+ @type = type
9
+ @name = name
10
+ end
11
+
12
+ def text?
13
+ @type == "Text" && @name != "#cdata-section"
14
+ end
15
+
16
+ def element_tag
17
+ @name unless text?
18
+ end
19
+
20
+ def eql?(other)
21
+ return false unless other.is_a?(self.class)
22
+
23
+ instance_variables.all? do |var|
24
+ instance_variable_get(var) == other.instance_variable_get(var)
25
+ end
26
+ end
27
+
28
+ alias == eql?
29
+ end
30
+ end
31
+ end
32
+ end
@@ -7,9 +7,9 @@ module Lutaml
7
7
  module XmlAdapter
8
8
  class NokogiriAdapter < XmlDocument
9
9
  def self.parse(xml, options = {})
10
- parsed = Nokogiri::XML(xml, nil, options[:encoding])
11
- root = NokogiriElement.new(parsed.root)
12
- new(root, parsed.encoding)
10
+ parsed = Nokogiri::XML(xml, nil, encoding(xml, options))
11
+ @root = NokogiriElement.new(parsed.root)
12
+ new(@root, parsed.encoding)
13
13
  end
14
14
 
15
15
  def to_xml(options = {})
@@ -71,11 +71,11 @@ module Lutaml
71
71
  index_hash = {}
72
72
  content = []
73
73
 
74
- element.element_order.each do |name|
75
- index_hash[name] ||= -1
76
- curr_index = index_hash[name] += 1
74
+ element.element_order.each do |object|
75
+ index_hash[object.name] ||= -1
76
+ curr_index = index_hash[object.name] += 1
77
77
 
78
- element_rule = xml_mapping.find_by_name(name)
78
+ element_rule = xml_mapping.find_by_name(object.name)
79
79
  next if element_rule.nil?
80
80
 
81
81
  attribute_def = attribute_definition_for(element, element_rule,
@@ -83,7 +83,7 @@ module Lutaml
83
83
  value = attribute_value_for(element, element_rule)
84
84
 
85
85
  if element_rule == xml_mapping.content_mapping
86
- next if element_rule.cdata && name == "text"
86
+ next if element_rule.cdata && object.text?
87
87
 
88
88
  text = xml_mapping.content_mapping.serialize(element)
89
89
  text = text[curr_index] if text.is_a?(Array)