lutaml-model 0.4.0 → 0.5.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 +34 -18
- data/README.adoc +166 -8
- data/lib/lutaml/model/key_value_mapping.rb +0 -1
- data/lib/lutaml/model/key_value_mapping_rule.rb +3 -1
- data/lib/lutaml/model/mapping_rule.rb +14 -2
- data/lib/lutaml/model/serialize.rb +67 -52
- data/lib/lutaml/model/type/decimal.rb +5 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +1 -0
- data/lib/lutaml/model/xml_adapter/builder/oga.rb +180 -0
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +1 -0
- data/lib/lutaml/model/xml_adapter/oga/document.rb +20 -0
- data/lib/lutaml/model/xml_adapter/oga/element.rb +117 -0
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +77 -44
- data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -9
- data/lib/lutaml/model/xml_mapping.rb +0 -1
- data/lib/lutaml/model/xml_mapping_rule.rb +16 -4
- data/spec/address_spec.rb +1 -0
- data/spec/fixtures/sample_model.rb +7 -0
- data/spec/lutaml/model/custom_model_spec.rb +47 -1
- data/spec/lutaml/model/included_spec.rb +192 -0
- data/spec/lutaml/model/mixed_content_spec.rb +48 -32
- data/spec/lutaml/model/multiple_mapping_spec.rb +329 -0
- data/spec/lutaml/model/ordered_content_spec.rb +1 -1
- data/spec/lutaml/model/render_nil_spec.rb +3 -0
- data/spec/lutaml/model/serializable_spec.rb +1 -1
- data/spec/lutaml/model/type/boolean_spec.rb +62 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -11
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +1 -1
- data/spec/lutaml/model/xml_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_mapping_spec.rb +24 -9
- data/spec/sample_model_spec.rb +114 -0
- metadata +8 -2
@@ -0,0 +1,180 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module XmlAdapter
|
6
|
+
module Builder
|
7
|
+
class Oga
|
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
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :document, :current_node, :options
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
@document = XmlAdapter::Oga::Document.new
|
20
|
+
@current_node = @document
|
21
|
+
@options = options
|
22
|
+
yield(self) if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_element(name, attributes = {}, &block)
|
26
|
+
if @current_namespace && !name.start_with?("#{@current_namespace}:")
|
27
|
+
name = "#{@current_namespace}:#{name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
element(name, attributes, &block)
|
32
|
+
else
|
33
|
+
element(name, attributes)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def element(name, attributes = {})
|
38
|
+
oga_element = ::Oga::XML::Element.new(name: name)
|
39
|
+
if block_given?
|
40
|
+
element_attributes(oga_element, attributes)
|
41
|
+
@current_node.children << oga_element
|
42
|
+
# Save previous node to reset the pointer for the rest of the iteration
|
43
|
+
previous_node = @current_node
|
44
|
+
# Set current node to new element as pointer for the block
|
45
|
+
@current_node = oga_element
|
46
|
+
yield(self)
|
47
|
+
# Reset the pointer for the rest of the iterations
|
48
|
+
@current_node = previous_node
|
49
|
+
end
|
50
|
+
oga_element
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_element(oga_element, child)
|
54
|
+
if child.is_a?(String)
|
55
|
+
current_element = oga_element.is_a?(XmlAdapter::Oga::Document) ? current_node : oga_element
|
56
|
+
add_xml_fragment(current_element, child)
|
57
|
+
elsif oga_element.is_a?(XmlAdapter::Oga::Document)
|
58
|
+
oga_element.children.last.children << child
|
59
|
+
else
|
60
|
+
oga_element.children << child
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_attribute(element, name, value)
|
65
|
+
attribute = ::Oga::XML::Attribute.new(
|
66
|
+
name: name,
|
67
|
+
value: value.to_s,
|
68
|
+
)
|
69
|
+
if element.is_a?(XmlAdapter::Oga::Document)
|
70
|
+
element.children.last.attributes << attribute
|
71
|
+
else
|
72
|
+
element.attributes << attribute
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_and_add_element(
|
77
|
+
element_name,
|
78
|
+
prefix: (prefix_unset = true
|
79
|
+
nil),
|
80
|
+
attributes: {},
|
81
|
+
&block
|
82
|
+
)
|
83
|
+
@current_namespace = nil if prefix.nil? && !prefix_unset
|
84
|
+
prefixed_name = if prefix
|
85
|
+
"#{prefix}:#{element_name}"
|
86
|
+
elsif @current_namespace && !element_name.start_with?("#{@current_namespace}:")
|
87
|
+
"#{@current_namespace}:#{element_name}"
|
88
|
+
else
|
89
|
+
element_name
|
90
|
+
end
|
91
|
+
|
92
|
+
if block_given?
|
93
|
+
element(prefixed_name, attributes, &block)
|
94
|
+
else
|
95
|
+
element(prefixed_name, attributes)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def <<(text)
|
100
|
+
@current_node.text(text)
|
101
|
+
end
|
102
|
+
|
103
|
+
def add_xml_fragment(element, content)
|
104
|
+
fragment = "<fragment>#{content}</fragment>"
|
105
|
+
parsed_fragment = ::Oga.parse_xml(fragment)
|
106
|
+
parsed_children = parsed_fragment.children.first.children
|
107
|
+
if element.is_a?(XmlAdapter::Oga::Document)
|
108
|
+
element.children.last.children += parsed_children
|
109
|
+
else
|
110
|
+
element.children += parsed_children
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def add_text(element, text, cdata: false)
|
115
|
+
return add_cdata(element, text) if cdata
|
116
|
+
|
117
|
+
oga_text = ::Oga::XML::Text.new(text: text.to_s)
|
118
|
+
if element.is_a?(XmlAdapter::Oga::Document)
|
119
|
+
children = element.children
|
120
|
+
children.empty? ? children << oga_text : children.last.children << oga_text
|
121
|
+
else
|
122
|
+
element.children << oga_text
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def add_cdata(element, value)
|
127
|
+
oga_cdata = ::Oga::XML::CData.new(text: value.to_s)
|
128
|
+
if element.is_a?(XmlAdapter::Oga::Document)
|
129
|
+
element.children.last.children << oga_cdata
|
130
|
+
else
|
131
|
+
element.children << oga_cdata
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def add_namespace_prefix(prefix)
|
136
|
+
@current_namespace = prefix
|
137
|
+
self
|
138
|
+
end
|
139
|
+
|
140
|
+
def parent
|
141
|
+
@document
|
142
|
+
end
|
143
|
+
|
144
|
+
def text(value = nil)
|
145
|
+
return @current_node.inner_text if value.nil?
|
146
|
+
|
147
|
+
str = value.is_a?(Array) ? value.join : value
|
148
|
+
@current_node.children << ::Oga::XML::Text.new(text: str)
|
149
|
+
end
|
150
|
+
|
151
|
+
def method_missing(method_name, *args)
|
152
|
+
if block_given?
|
153
|
+
@current_node.public_send(method_name, *args) do
|
154
|
+
yield(self)
|
155
|
+
end
|
156
|
+
else
|
157
|
+
@current_node.public_send(method_name, *args)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def respond_to_missing?(method_name, include_private = false)
|
162
|
+
@current_node.respond_to?(method_name) || super
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
|
167
|
+
def element_attributes(oga_element, attributes)
|
168
|
+
oga_element.attributes = attributes.map do |name, value|
|
169
|
+
::Oga::XML::Attribute.new(
|
170
|
+
name: name,
|
171
|
+
value: value,
|
172
|
+
element: oga_element,
|
173
|
+
)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -43,6 +43,7 @@ module Lutaml
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def create_and_add_element(element_name, prefix: nil, attributes: {})
|
46
|
+
element_name = element_name.first if element_name.is_a?(Array)
|
46
47
|
prefixed_name = if prefix
|
47
48
|
"#{prefix}:#{element_name}"
|
48
49
|
elsif @current_namespace && !element_name.start_with?("#{@current_namespace}:")
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module XmlAdapter
|
6
|
+
module Oga
|
7
|
+
class Document < ::Oga::XML::Document
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def text(value = nil)
|
13
|
+
children << ::Oga::XML::Text.new(text: value)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module XmlAdapter
|
6
|
+
module Oga
|
7
|
+
class Element < XmlElement
|
8
|
+
def initialize(node, parent: nil)
|
9
|
+
name = case node
|
10
|
+
when ::Oga::XML::Element
|
11
|
+
namespace_name = node.namespace_name
|
12
|
+
add_namespaces(node)
|
13
|
+
children = parse_children(node)
|
14
|
+
attributes = node_attributes(node)
|
15
|
+
node.name
|
16
|
+
when ::Oga::XML::Text
|
17
|
+
"text"
|
18
|
+
end
|
19
|
+
super(
|
20
|
+
name,
|
21
|
+
Hash(attributes),
|
22
|
+
Array(children),
|
23
|
+
node.text,
|
24
|
+
parent_document: parent,
|
25
|
+
namespace_prefix: namespace_name,
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def text?
|
30
|
+
children.empty? && text&.length&.positive?
|
31
|
+
end
|
32
|
+
|
33
|
+
def text
|
34
|
+
@text
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_xml(builder = Builder::Oga.build)
|
38
|
+
build_xml(builder).to_xml
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_xml(builder = Builder::Oga.build)
|
42
|
+
if name == "text"
|
43
|
+
builder.add_text(builder.current_node, @text)
|
44
|
+
else
|
45
|
+
builder.create_element(name, build_attributes(self)) do |xml|
|
46
|
+
children.each { |child| child.build_xml(xml) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
builder
|
51
|
+
end
|
52
|
+
|
53
|
+
def inner_xml
|
54
|
+
children.map(&:to_xml).join
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def node_attributes(node)
|
60
|
+
node.attributes.each_with_object({}) do |attr, hash|
|
61
|
+
next if attr_is_namespace?(attr)
|
62
|
+
|
63
|
+
name = if attr.namespace
|
64
|
+
"#{attr.namespace.name}:#{attr.name}"
|
65
|
+
else
|
66
|
+
attr.name
|
67
|
+
end
|
68
|
+
hash[name] = XmlAttribute.new(
|
69
|
+
name,
|
70
|
+
attr.value,
|
71
|
+
namespace: attr.namespace&.uri,
|
72
|
+
namespace_prefix: attr.namespace&.name,
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_children(node)
|
78
|
+
node.children.map { |child| self.class.new(child, parent: self) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def add_namespaces(node)
|
82
|
+
node.namespaces.each_value do |namespace|
|
83
|
+
add_namespace(XmlNamespace.new(namespace.uri, namespace.name))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def attr_is_namespace?(attr)
|
88
|
+
attribute_is_namespace?(attr.name) ||
|
89
|
+
namespaces[attr.name]&.uri == attr.value
|
90
|
+
end
|
91
|
+
|
92
|
+
def build_attributes(node, _options = {})
|
93
|
+
attrs = node.attributes.transform_values(&:value)
|
94
|
+
|
95
|
+
attrs.merge(build_namespace_attributes(node))
|
96
|
+
end
|
97
|
+
|
98
|
+
def build_namespace_attributes(node)
|
99
|
+
namespace_attrs = {}
|
100
|
+
|
101
|
+
node.own_namespaces.each_value do |namespace|
|
102
|
+
namespace_attrs[namespace.attr_name] = namespace.uri
|
103
|
+
end
|
104
|
+
|
105
|
+
node.children.each do |child|
|
106
|
+
namespace_attrs = namespace_attrs.merge(
|
107
|
+
build_namespace_attributes(child),
|
108
|
+
)
|
109
|
+
end
|
110
|
+
|
111
|
+
namespace_attrs
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -1,69 +1,102 @@
|
|
1
1
|
require "oga"
|
2
2
|
require_relative "xml_document"
|
3
|
+
require_relative "oga/document"
|
4
|
+
require_relative "oga/element"
|
5
|
+
require_relative "builder/oga"
|
3
6
|
|
4
7
|
module Lutaml
|
5
8
|
module Model
|
6
9
|
module XmlAdapter
|
7
10
|
class OgaAdapter < XmlDocument
|
8
|
-
def self.parse(xml,
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def to_h
|
15
|
-
{ @root.name => parse_element(@root) }
|
11
|
+
def self.parse(xml, options = {})
|
12
|
+
encoding = options[:encoding] || xml.encoding.to_s
|
13
|
+
xml = xml.encode("UTF-16").encode("UTF-8") if encoding && encoding != "UTF-8"
|
14
|
+
parsed = ::Oga.parse_xml(xml)
|
15
|
+
@root = Oga::Element.new(parsed.children.first)
|
16
|
+
new(@root, encoding)
|
16
17
|
end
|
17
18
|
|
18
19
|
def to_xml(options = {})
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
builder_options = {}
|
21
|
+
|
22
|
+
builder_options[:encoding] = if options.key?(:encoding)
|
23
|
+
options[:encoding] || "UTF-8"
|
24
|
+
elsif options.key?(:parse_encoding)
|
25
|
+
options[:parse_encoding]
|
26
|
+
else
|
27
|
+
"UTF-8"
|
28
|
+
end
|
29
|
+
builder = Builder::Oga.build(options) do |xml|
|
30
|
+
if @root.is_a?(Oga::Element)
|
31
|
+
@root.build_xml(xml)
|
32
|
+
else
|
33
|
+
build_element(xml, @root, options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
xml_data = builder.to_xml.encode!(builder_options[:encoding])
|
22
37
|
options[:declaration] ? declaration(options) + xml_data : xml_data
|
38
|
+
rescue Encoding::ConverterNotFoundError
|
39
|
+
invalid_encoding!(builder_options[:encoding])
|
23
40
|
end
|
24
41
|
|
25
42
|
private
|
26
43
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
build_element(builder, child, options)
|
32
|
-
end
|
33
|
-
builder.text(element.text) if element.text
|
34
|
-
end
|
35
|
-
end
|
44
|
+
def build_ordered_element(builder, element, options = {})
|
45
|
+
mapper_class = options[:mapper_class] || element.class
|
46
|
+
xml_mapping = mapper_class.mappings_for(:xml)
|
47
|
+
return xml unless xml_mapping
|
36
48
|
|
37
|
-
|
38
|
-
attributes.transform_values(&:value)
|
39
|
-
end
|
49
|
+
attributes = build_attributes(element, xml_mapping).compact
|
40
50
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
51
|
+
tag_name = options[:tag_name] || xml_mapping.root_element
|
52
|
+
builder.create_and_add_element(tag_name,
|
53
|
+
attributes: attributes) do |el|
|
54
|
+
index_hash = {}
|
55
|
+
content = []
|
45
56
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
57
|
+
element.element_order.each do |name|
|
58
|
+
index_hash[name] ||= -1
|
59
|
+
curr_index = index_hash[name] += 1
|
60
|
+
|
61
|
+
element_rule = xml_mapping.find_by_name(name)
|
62
|
+
next if element_rule.nil?
|
63
|
+
|
64
|
+
attribute_def = attribute_definition_for(element, element_rule,
|
65
|
+
mapper_class: mapper_class)
|
66
|
+
value = attribute_value_for(element, element_rule)
|
67
|
+
|
68
|
+
next if element_rule == xml_mapping.content_mapping && element_rule.cdata && name == "text"
|
69
|
+
|
70
|
+
if element_rule == xml_mapping.content_mapping
|
71
|
+
text = xml_mapping.content_mapping.serialize(element)
|
72
|
+
text = text[curr_index] if text.is_a?(Array)
|
73
|
+
|
74
|
+
next el.add_text(el, text, cdata: element_rule.cdata) if element.mixed?
|
52
75
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
76
|
+
content << text
|
77
|
+
elsif !value.nil? || element_rule.render_nil?
|
78
|
+
value = value[curr_index] if attribute_def.collection?
|
79
|
+
|
80
|
+
add_to_xml(
|
81
|
+
el,
|
82
|
+
element,
|
83
|
+
nil,
|
84
|
+
value,
|
85
|
+
options.merge(
|
86
|
+
attribute: attribute_def,
|
87
|
+
rule: element_rule,
|
88
|
+
mapper_class: mapper_class,
|
89
|
+
),
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
el.add_text(el, content.join)
|
57
95
|
end
|
58
|
-
super(node.name, attributes, parse_children(node), node.text)
|
59
96
|
end
|
60
97
|
|
61
|
-
|
62
|
-
|
63
|
-
def parse_children(node)
|
64
|
-
node.children.select do |child|
|
65
|
-
child.is_a?(Oga::XML::Element)
|
66
|
-
end.map { |child| OgaElement.new(child) }
|
98
|
+
def invalid_encoding!(encoding)
|
99
|
+
raise Error, "unknown encoding name - #{encoding}"
|
67
100
|
end
|
68
101
|
end
|
69
102
|
end
|
@@ -233,20 +233,16 @@ module Lutaml
|
|
233
233
|
end
|
234
234
|
|
235
235
|
process_content_mapping(element, xml_mapping.content_mapping,
|
236
|
-
prefixed_xml)
|
236
|
+
prefixed_xml, mapper_class)
|
237
237
|
end
|
238
238
|
end
|
239
239
|
|
240
|
-
def process_content_mapping(element, content_rule, xml)
|
240
|
+
def process_content_mapping(element, content_rule, xml, mapper_class)
|
241
241
|
return unless content_rule
|
242
242
|
|
243
243
|
if content_rule.custom_methods[:to]
|
244
|
-
|
245
|
-
|
246
|
-
element,
|
247
|
-
xml.parent,
|
248
|
-
xml,
|
249
|
-
)
|
244
|
+
mapper_class.new.send(content_rule.custom_methods[:to], element,
|
245
|
+
xml.parent, xml)
|
250
246
|
else
|
251
247
|
text = content_rule.serialize(element)
|
252
248
|
text = text.join if text.is_a?(Array)
|
@@ -340,7 +336,9 @@ module Lutaml
|
|
340
336
|
next if options[:except]&.include?(mapping_rule.to)
|
341
337
|
next if mapping_rule.custom_methods[:to]
|
342
338
|
|
343
|
-
|
339
|
+
mapping_rule_name = mapping_rule.multiple_mappings? ? mapping_rule.name.first : mapping_rule.name
|
340
|
+
|
341
|
+
if mapping_rule.namespace && mapping_rule.prefix && mapping_rule_name != "lang"
|
344
342
|
hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
|
345
343
|
end
|
346
344
|
|
@@ -378,6 +376,10 @@ module Lutaml
|
|
378
376
|
key = ["xmlns", xml_mapping.namespace_prefix].compact.join(":")
|
379
377
|
{ key => xml_mapping.namespace_uri }
|
380
378
|
end
|
379
|
+
|
380
|
+
def self.type
|
381
|
+
Utils.snake_case(self).split("/").last.split("_").first
|
382
|
+
end
|
381
383
|
end
|
382
384
|
end
|
383
385
|
end
|
@@ -19,7 +19,8 @@ module Lutaml
|
|
19
19
|
namespace_set: false,
|
20
20
|
prefix_set: false,
|
21
21
|
attribute: false,
|
22
|
-
default_namespace: nil
|
22
|
+
default_namespace: nil,
|
23
|
+
id: nil
|
23
24
|
)
|
24
25
|
super(
|
25
26
|
name,
|
@@ -29,6 +30,7 @@ module Lutaml
|
|
29
30
|
with: with,
|
30
31
|
delegate: delegate,
|
31
32
|
attribute: attribute,
|
33
|
+
id: id
|
32
34
|
)
|
33
35
|
|
34
36
|
@namespace = if namespace.to_s == "inherit"
|
@@ -45,6 +47,7 @@ module Lutaml
|
|
45
47
|
|
46
48
|
@namespace_set = namespace_set
|
47
49
|
@prefix_set = prefix_set
|
50
|
+
@id = id
|
48
51
|
end
|
49
52
|
|
50
53
|
def namespace_set?
|
@@ -72,14 +75,23 @@ module Lutaml
|
|
72
75
|
end
|
73
76
|
|
74
77
|
def prefixed_name
|
78
|
+
rule_name = multiple_mappings? ? name.first : name
|
75
79
|
if prefix
|
76
|
-
"#{prefix}:#{
|
80
|
+
"#{prefix}:#{rule_name}"
|
81
|
+
else
|
82
|
+
rule_name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def namespaced_names(parent_namespace = nil)
|
87
|
+
if multiple_mappings?
|
88
|
+
name.map { |rule_name| namespaced_name(parent_namespace, rule_name) }
|
77
89
|
else
|
78
|
-
|
90
|
+
[namespaced_name(parent_namespace)]
|
79
91
|
end
|
80
92
|
end
|
81
93
|
|
82
|
-
def namespaced_name(parent_namespace = nil)
|
94
|
+
def namespaced_name(parent_namespace = nil, name = self.name)
|
83
95
|
if name == "lang"
|
84
96
|
"#{prefix}:#{name}"
|
85
97
|
elsif namespace_set? || @attribute
|
data/spec/address_spec.rb
CHANGED
@@ -100,6 +100,7 @@ RSpec.describe Address do
|
|
100
100
|
expect(address_from_json.post_code).to eq("01001")
|
101
101
|
expect(address_from_json.person.first.first_name).to eq("Tom")
|
102
102
|
expect(address_from_json.person.last.first_name).to eq("Jack")
|
103
|
+
expect(address_from_json.person.last.active).to be(false)
|
103
104
|
end
|
104
105
|
|
105
106
|
it "serializes to XML with a collection of persons" do
|
@@ -36,5 +36,12 @@ class SampleModel < Lutaml::Model::Serializable
|
|
36
36
|
yaml do
|
37
37
|
map "name", to: :name
|
38
38
|
map "age", to: :age
|
39
|
+
map "balance", to: :balance
|
40
|
+
map "tags", to: :tags
|
41
|
+
map "preferences", to: :preferences
|
42
|
+
map "status", to: :status
|
43
|
+
map "large_number", to: :large_number
|
44
|
+
map "email", to: :email
|
45
|
+
map "role", to: :role
|
39
46
|
end
|
40
47
|
end
|
@@ -78,7 +78,7 @@ module CustomModelSpecs
|
|
78
78
|
end
|
79
79
|
|
80
80
|
class Id
|
81
|
-
attr_accessor :id
|
81
|
+
attr_accessor :id, :prefix
|
82
82
|
end
|
83
83
|
|
84
84
|
class Docid < Lutaml::Model::Serializable
|
@@ -147,6 +147,28 @@ module CustomModelSpecs
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
end
|
150
|
+
|
151
|
+
class CustomId < Lutaml::Model::Serializable
|
152
|
+
model Id
|
153
|
+
attribute :id, :string
|
154
|
+
attribute :prefix, :string
|
155
|
+
|
156
|
+
xml do
|
157
|
+
root "custom-id"
|
158
|
+
map_attribute "prefix", to: :prefix
|
159
|
+
map_content with: { to: :id_to_xml, from: :id_from_xml }
|
160
|
+
end
|
161
|
+
|
162
|
+
def id_to_xml(model, _parent, doc)
|
163
|
+
content = "ABC-#{model.id}"
|
164
|
+
doc.add_text(doc, content)
|
165
|
+
end
|
166
|
+
|
167
|
+
def id_from_xml(model, value)
|
168
|
+
id = value.split("-").last
|
169
|
+
model.id = id.to_i
|
170
|
+
end
|
171
|
+
end
|
150
172
|
end
|
151
173
|
|
152
174
|
RSpec.describe "CustomModel" do
|
@@ -407,4 +429,28 @@ RSpec.describe "CustomModel" do
|
|
407
429
|
end
|
408
430
|
end
|
409
431
|
end
|
432
|
+
|
433
|
+
context "with custom methods" do
|
434
|
+
describe ".xml serialization" do
|
435
|
+
it "handles custom content mapping methods" do
|
436
|
+
xml = '<custom-id prefix="ABC">ABC-123</custom-id>'
|
437
|
+
|
438
|
+
instance = CustomModelSpecs::Id.new
|
439
|
+
instance.id = 123
|
440
|
+
instance.prefix = "ABC"
|
441
|
+
result_xml = CustomModelSpecs::CustomId.to_xml(instance)
|
442
|
+
expect(result_xml).to eq(xml)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
describe ".xml deserialization" do
|
447
|
+
it "handles custom content mapping methods" do
|
448
|
+
xml = '<custom-id prefix="ABC">ABC-123</custom-id>'
|
449
|
+
instance = CustomModelSpecs::CustomId.from_xml(xml)
|
450
|
+
|
451
|
+
expect(instance.id).to eq(123)
|
452
|
+
expect(instance.prefix).to eq("ABC")
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
410
456
|
end
|