lutaml-model 0.5.4 → 0.6.1
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.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +58 -21
- data/Gemfile +1 -0
- data/README.adoc +1112 -264
- data/lib/lutaml/model/attribute.rb +37 -15
- data/lib/lutaml/model/choice.rb +56 -0
- data/lib/lutaml/model/config.rb +1 -0
- data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
- data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
- data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
- data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
- data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
- data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
- data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
- data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
- data/lib/lutaml/model/error.rb +8 -0
- data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
- data/lib/lutaml/model/key_value_mapping.rb +3 -1
- data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
- data/lib/lutaml/model/liquefiable.rb +59 -0
- data/lib/lutaml/model/mapping_hash.rb +1 -1
- data/lib/lutaml/model/mapping_rule.rb +15 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +68 -26
- data/lib/lutaml/model/schema_location.rb +7 -0
- data/lib/lutaml/model/sequence.rb +71 -0
- data/lib/lutaml/model/serialize.rb +126 -38
- data/lib/lutaml/model/type/decimal.rb +0 -4
- data/lib/lutaml/model/type/time.rb +3 -3
- data/lib/lutaml/model/utils.rb +19 -15
- data/lib/lutaml/model/validation.rb +12 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
- data/lib/lutaml/model/xml_adapter/element.rb +32 -0
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +8 -8
- data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
- data/lib/lutaml/model/xml_adapter/xml_document.rb +74 -13
- data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
- data/lib/lutaml/model/xml_mapping.rb +49 -7
- data/lib/lutaml/model/xml_mapping_rule.rb +8 -3
- data/lib/lutaml/model.rb +1 -0
- data/lutaml-model.gemspec +5 -0
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
- data/spec/ceramic_spec.rb +39 -0
- data/spec/fixtures/ceramic.rb +23 -0
- data/spec/fixtures/xml/address_example_260.xsd +9 -0
- data/spec/fixtures/xml/user.xsd +10 -0
- data/spec/lutaml/model/cdata_spec.rb +4 -5
- data/spec/lutaml/model/choice_spec.rb +168 -0
- data/spec/lutaml/model/collection_spec.rb +1 -1
- data/spec/lutaml/model/custom_model_spec.rb +55 -8
- data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
- data/spec/lutaml/model/defaults_spec.rb +3 -1
- data/spec/lutaml/model/delegation_spec.rb +7 -5
- data/spec/lutaml/model/enum_spec.rb +35 -0
- data/spec/lutaml/model/group_spec.rb +160 -0
- data/spec/lutaml/model/inheritance_spec.rb +25 -0
- data/spec/lutaml/model/liquefiable_spec.rb +121 -0
- data/spec/lutaml/model/mixed_content_spec.rb +80 -41
- data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +218 -25
- data/spec/lutaml/model/sequence_spec.rb +216 -0
- data/spec/lutaml/model/transformation_spec.rb +230 -0
- data/spec/lutaml/model/type_spec.rb +138 -31
- data/spec/lutaml/model/utils_spec.rb +32 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +167 -112
- 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
|
-
|
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
|
-
|
251
|
+
raise Lutaml::Model::NoRootMappingError.new(self) unless root?
|
252
|
+
|
212
253
|
options[:encoding] = doc.encoding
|
213
|
-
apply_mappings(
|
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,29 @@ 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
|
-
|
453
|
-
|
496
|
+
doc_order = doc.root.order
|
497
|
+
if instance.respond_to?(:ordered=)
|
498
|
+
instance.element_order = doc_order
|
454
499
|
instance.ordered = mappings_for(:xml).ordered? || options[:ordered]
|
455
500
|
instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
|
456
501
|
end
|
457
502
|
|
458
|
-
|
503
|
+
schema_location = doc.attributes.values.find do |a|
|
504
|
+
a.unprefixed_name == "schemaLocation"
|
505
|
+
end
|
506
|
+
|
507
|
+
if !schema_location.nil?
|
459
508
|
instance.schema_location = Lutaml::Model::SchemaLocation.new(
|
460
|
-
schema_location:
|
461
|
-
prefix:
|
462
|
-
namespace:
|
509
|
+
schema_location: schema_location.value,
|
510
|
+
prefix: schema_location.namespace_prefix,
|
511
|
+
namespace: schema_location.namespace,
|
463
512
|
)
|
464
513
|
end
|
465
514
|
|
466
515
|
defaults_used = []
|
516
|
+
validate_sequence!(doc_order)
|
467
517
|
|
468
518
|
mappings.each do |rule|
|
469
519
|
raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
|
@@ -471,12 +521,12 @@ module Lutaml
|
|
471
521
|
attr = attribute_for_rule(rule)
|
472
522
|
|
473
523
|
value = if rule.raw_mapping?
|
474
|
-
doc.
|
524
|
+
doc.root.inner_xml
|
475
525
|
elsif rule.content_mapping?
|
476
|
-
|
477
|
-
elsif val = value_for_rule(doc, rule, options)
|
526
|
+
rule.cdata ? doc.cdata : doc.text
|
527
|
+
elsif val = value_for_rule(doc, rule, options, instance)
|
478
528
|
val
|
479
|
-
|
529
|
+
elsif instance.using_default?(rule.to) || rule.render_default
|
480
530
|
defaults_used << rule.to
|
481
531
|
attr&.default || rule.to_value_for(instance)
|
482
532
|
end
|
@@ -485,20 +535,48 @@ module Lutaml
|
|
485
535
|
rule.deserialize(instance, value, attributes, self)
|
486
536
|
end
|
487
537
|
|
488
|
-
defaults_used.each
|
489
|
-
instance.using_default_for(attribute_name)
|
490
|
-
end
|
538
|
+
defaults_used.each { |attr_name| instance.using_default_for(attr_name) }
|
491
539
|
|
492
540
|
instance
|
493
541
|
end
|
494
542
|
|
495
|
-
def value_for_rule(doc, rule, options)
|
543
|
+
def value_for_rule(doc, rule, options, instance)
|
496
544
|
rule_names = rule.namespaced_names(options[:default_namespace])
|
497
|
-
hash = rule.attribute? ? doc["attributes"] : doc["elements"]
|
498
|
-
return unless hash
|
499
545
|
|
500
|
-
|
501
|
-
|
546
|
+
if rule.attribute?
|
547
|
+
doc.root.find_attribute_value(rule_names)
|
548
|
+
else
|
549
|
+
attr = attribute_for_rule(rule)
|
550
|
+
children = doc.children.select do |child|
|
551
|
+
rule_names.include?(child.namespaced_name) && !child.text?
|
552
|
+
end
|
553
|
+
|
554
|
+
if rule.using_custom_methods? || attr.type == Lutaml::Model::Type::Hash
|
555
|
+
return children.first
|
556
|
+
end
|
557
|
+
|
558
|
+
if Utils.present?(children)
|
559
|
+
instance.value_set_for(attr.name)
|
560
|
+
end
|
561
|
+
|
562
|
+
if rule.cdata
|
563
|
+
values = children.map do |child|
|
564
|
+
child.cdata_children&.map(&:text)
|
565
|
+
end.flatten
|
566
|
+
return children.count > 1 ? values : values.first
|
567
|
+
end
|
568
|
+
|
569
|
+
values = children.map do |child|
|
570
|
+
if !rule.using_custom_methods? && attr.type <= Serialize
|
571
|
+
attr.cast(child, :xml, options.except(:mappings))
|
572
|
+
elsif attr.raw?
|
573
|
+
inner_xml_of(child)
|
574
|
+
else
|
575
|
+
child&.children&.first&.text
|
576
|
+
end
|
577
|
+
end
|
578
|
+
attr&.collection? ? values : values.first
|
579
|
+
end
|
502
580
|
end
|
503
581
|
|
504
582
|
def apply_hash_mapping(doc, instance, format, options = {})
|
@@ -546,18 +624,6 @@ module Lutaml
|
|
546
624
|
def normalize_xml_value(value, rule, attr, options = {})
|
547
625
|
value = [value].compact if attr&.collection? && !value.is_a?(Array)
|
548
626
|
|
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
627
|
return value unless cast_value?(attr, rule)
|
562
628
|
|
563
629
|
options.merge(caller_class: self, mixed_content: rule.mixed_content)
|
@@ -599,6 +665,26 @@ module Lutaml
|
|
599
665
|
value
|
600
666
|
end
|
601
667
|
end
|
668
|
+
|
669
|
+
def validate_sequence!(element_order)
|
670
|
+
mapping_sequence = mappings_for(:xml).element_sequence
|
671
|
+
current_order = element_order.filter_map(&:element_tag)
|
672
|
+
|
673
|
+
mapping_sequence.each do |mapping|
|
674
|
+
mapping.validate_content!(current_order)
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
private
|
679
|
+
|
680
|
+
def inner_xml_of(node)
|
681
|
+
case node
|
682
|
+
when XmlAdapter::XmlElement
|
683
|
+
node.inner_xml
|
684
|
+
else
|
685
|
+
node.children.map(&:to_xml).join
|
686
|
+
end
|
687
|
+
end
|
602
688
|
end
|
603
689
|
|
604
690
|
attr_accessor :element_order, :schema_location, :encoding
|
@@ -719,6 +805,8 @@ module Lutaml
|
|
719
805
|
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
720
806
|
define_method(:"to_#{format}") do |options = {}|
|
721
807
|
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
808
|
+
raise Lutaml::Model::NoRootMappingError.new(self.class) unless self.class.root?
|
809
|
+
|
722
810
|
representation = if format == :xml
|
723
811
|
self
|
724
812
|
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)
|
data/lib/lutaml/model/utils.rb
CHANGED
@@ -85,26 +85,30 @@ module Lutaml
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
def deep_dup(
|
89
|
-
return
|
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
|
-
|
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
|
-
|
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
|
data/lib/lutaml/model/version.rb
CHANGED
@@ -6,19 +6,15 @@ module Lutaml
|
|
6
6
|
module Builder
|
7
7
|
class Oga
|
8
8
|
def self.build(options = {}, &block)
|
9
|
-
|
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, :
|
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
|
-
@
|
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 =
|
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
|
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 |
|
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 &&
|
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)
|