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.
- 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 +33 -10
- 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 +125 -35
- 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 +6 -7
- 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 +143 -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,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=)
|
453
|
-
instance.element_order = doc.
|
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
|
-
|
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:
|
461
|
-
prefix:
|
462
|
-
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.
|
523
|
+
doc.root.inner_xml
|
475
524
|
elsif rule.content_mapping?
|
476
|
-
|
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
|
-
|
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
|
-
|
501
|
-
|
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)
|
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)
|