lutaml-model 0.8.12 → 0.8.13
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/README.adoc +116 -103
- data/RELEASE_NOTES.adoc +3 -3
- data/benchmark/quick_benchmark.rb +2 -2
- data/benchmark/serialization_benchmark.rb +4 -4
- data/docs/_guides/advanced-mapping.adoc +1 -1
- data/docs/_guides/character-encoding.adoc +3 -3
- data/docs/_guides/missing-values-handling.adoc +6 -6
- data/docs/_guides/ooxml-examples.adoc +7 -7
- data/docs/_guides/opal.adoc +1 -1
- data/docs/_guides/value-transformations.adoc +7 -7
- data/docs/_guides/xml/namespace-presentation.adoc +1 -1
- data/docs/_guides/xml/namespace-semantics.adoc +15 -15
- data/docs/_guides/xml/type-namespaces.adoc +9 -9
- data/docs/_guides/xml-mapping.adoc +32 -26
- data/docs/_guides/xml-namespace-qualification.adoc +4 -4
- data/docs/_guides/xml-namespaces.adoc +2 -2
- data/docs/_guides/xml_mappings/04_xml_namespace_class.adoc +18 -18
- data/docs/_guides/xml_mappings/05_common_patterns.adoc +16 -16
- data/docs/_guides/xml_mappings/06_migration_guide.adoc +5 -5
- data/docs/_guides/xml_mappings/07_best_practices.adoc +11 -11
- data/docs/_migrations/0-8-0-namespace-restructuring.adoc +2 -2
- data/docs/_pages/attributes.adoc +2 -2
- data/docs/_pages/collections.adoc +26 -20
- data/docs/_pages/consolidation-mapping.adoc +4 -4
- data/docs/_pages/importable_models.adoc +14 -13
- data/docs/_pages/quick-start.adoc +1 -1
- data/docs/_pages/value_types.adoc +10 -10
- data/docs/_references/custom_registers.adoc +7 -7
- data/docs/_references/format-independent-features.adoc +4 -4
- data/docs/_references/instance-serialization.adoc +1 -1
- data/docs/_references/parent-root-context.adoc +3 -3
- data/docs/_tutorials/basic-model-definition.adoc +1 -1
- data/docs/_tutorials/first-xml-serialization.adoc +4 -4
- data/docs/_tutorials/lutaml-xml-architecture.adoc +4 -4
- data/docs/_tutorials/validation-basics.adoc +1 -1
- data/docs/_tutorials/working-with-collections.adoc +2 -2
- data/docs/_tutorials/xml-namespaces-basics.adoc +1 -1
- data/docs/_tutorials/xml-schema-primer-style-guide.adoc +29 -29
- data/docs/cli_compare.adoc +1 -1
- data/docs/index.adoc +1 -1
- data/docs/namespace-management.adoc +14 -14
- data/lib/lutaml/key_value/mapping.rb +31 -6
- data/lib/lutaml/model/collection.rb +11 -11
- data/lib/lutaml/model/error/no_root_mapping_error.rb +6 -5
- data/lib/lutaml/model/error/no_root_namespace_error.rb +6 -5
- data/lib/lutaml/model/error/type_only_mapping_error.rb +13 -0
- data/lib/lutaml/model/error/type_only_namespace_error.rb +12 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +3 -0
- data/lib/lutaml/xml/adapter/base_adapter.rb +0 -9
- data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/oga_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/ox_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/xml_serializer.rb +42 -22
- data/lib/lutaml/xml/adapter.rb +4 -0
- data/lib/lutaml/xml/builder/base.rb +64 -25
- data/lib/lutaml/xml/builder/nokogiri.rb +0 -2
- data/lib/lutaml/xml/builder/oga.rb +0 -2
- data/lib/lutaml/xml/builder/ox.rb +0 -2
- data/lib/lutaml/xml/builder/rexml.rb +0 -2
- data/lib/lutaml/xml/builder.rb +1 -0
- data/lib/lutaml/xml/configurable.rb +2 -2
- data/lib/lutaml/xml/declaration_handler.rb +3 -105
- data/lib/lutaml/xml/mapping.rb +3 -3
- data/lib/lutaml/xml/schema/xsd/documentation.rb +1 -1
- data/lib/lutaml/xml/serialization/collection_ext.rb +7 -7
- data/lib/lutaml/xml/serialization/format_conversion.rb +1 -1
- data/lib/lutaml/xml/serialization/instance_methods.rb +1 -1
- data/lib/lutaml/xml.rb +1 -2
- data/lib/tasks/memory_profile.rb +2 -2
- data/lib/tasks/performance_benchmark.rb +5 -5
- data/spec/lutaml/key_value/transformation/rule_compiler_spec.rb +1 -1
- data/spec/lutaml/key_value/transformation/value_serializer_spec.rb +1 -1
- data/spec/lutaml/model/attribute_collection_spec.rb +1 -1
- data/spec/lutaml/model/cli_spec.rb +1 -1
- data/spec/lutaml/model/collection_spec.rb +1 -1
- data/spec/lutaml/model/collection_validation_spec.rb +6 -6
- data/spec/lutaml/model/consolidation_spec.rb +8 -8
- data/spec/lutaml/model/custom_collection_spec.rb +3 -3
- data/spec/lutaml/model/default_register_spec.rb +23 -23
- data/spec/lutaml/model/delegation_spec.rb +3 -10
- data/spec/lutaml/model/derived_attribute_serialization_spec.rb +1 -1
- data/spec/lutaml/model/dynamic_attribute_spec.rb +2 -2
- data/spec/lutaml/model/enum_spec.rb +1 -1
- data/spec/lutaml/model/group_spec.rb +12 -12
- data/spec/lutaml/model/lazy_collection_spec.rb +4 -4
- data/spec/lutaml/model/mixed_content_spec.rb +2 -2
- data/spec/lutaml/model/namespace_versioning_spec.rb +4 -4
- data/spec/lutaml/model/processing_instruction_spec.rb +11 -11
- data/spec/lutaml/model/register_methods_spec.rb +2 -2
- data/spec/lutaml/model/render_empty_spec.rb +1 -1
- data/spec/lutaml/model/serialize_perf_guard_spec.rb +1 -1
- data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +1 -1
- data/spec/lutaml/model/transformation_builder_spec.rb +2 -2
- data/spec/lutaml/model/xml_decoupling_spec.rb +3 -3
- data/spec/lutaml/model/xsd_patterns_spec.rb +2 -3
- data/spec/lutaml/xml/adapter/order_spec.rb +1 -1
- data/spec/lutaml/xml/clear_parse_state_spec.rb +1 -1
- data/spec/lutaml/xml/content_model_validation_spec.rb +4 -2
- data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +5 -5
- data/spec/lutaml/xml/enhanced_mapping_spec.rb +2 -1
- data/spec/lutaml/xml/entity_fragmentation_spec.rb +5 -5
- data/spec/lutaml/xml/indent_spec.rb +109 -0
- data/spec/lutaml/xml/line_ending_spec.rb +66 -0
- data/spec/lutaml/xml/mapping_finalization_guard_spec.rb +2 -2
- data/spec/lutaml/xml/model_transform_guard_spec.rb +4 -4
- data/spec/lutaml/xml/namespace_alias_spec.rb +4 -4
- data/spec/lutaml/xml/namespace_aware_parsing_spec.rb +3 -3
- data/spec/lutaml/xml/namespace_bound_element_roundtrip_spec.rb +2 -2
- data/spec/lutaml/xml/namespace_format_preservation_spec.rb +1 -1
- data/spec/lutaml/xml/namespace_inheritance_spec.rb +3 -3
- data/spec/lutaml/xml/namespace_preservation_spec.rb +5 -5
- data/spec/lutaml/xml/opal_xml_spec.rb +8 -8
- data/spec/lutaml/xml/pipeline_integration_spec.rb +145 -0
- data/spec/lutaml/xml/schema_primer_spec.rb +5 -5
- data/spec/lutaml/xml/transformation_spec.rb +20 -20
- data/spec/lutaml/xml/type_namespace/collector_spec.rb +1 -1
- data/spec/lutaml/xml/type_namespace/planner_spec.rb +3 -3
- metadata +7 -2
|
@@ -49,6 +49,35 @@ module Lutaml
|
|
|
49
49
|
encoding = determine_encoding(options)
|
|
50
50
|
builder_options = {}
|
|
51
51
|
builder_options[:encoding] = encoding if encoding
|
|
52
|
+
builder_options[:line_ending] = options[:line_ending] if options.key?(:line_ending)
|
|
53
|
+
builder_options[:indent] = options[:indent] if options.key?(:indent)
|
|
54
|
+
|
|
55
|
+
# Pass doctype to builder for document-level insertion
|
|
56
|
+
doctype_to_use = options[:doctype] || @doctype
|
|
57
|
+
if doctype_to_use && !options[:omit_doctype]
|
|
58
|
+
builder_options[:doctype] = doctype_to_use
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Pass declaration info to builder
|
|
62
|
+
if should_include_declaration?(options)
|
|
63
|
+
builder_options[:include_declaration] = true
|
|
64
|
+
builder_options[:xml_declaration] = @xml_declaration || {}
|
|
65
|
+
if options.key?(:standalone)
|
|
66
|
+
if options[:standalone] == :preserve
|
|
67
|
+
# Keep original standalone from parsed declaration (may be nil)
|
|
68
|
+
else
|
|
69
|
+
builder_options[:xml_declaration][:standalone] = standalone_value(options[:standalone])
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
if options[:declaration].is_a?(String)
|
|
73
|
+
builder_options[:xml_declaration][:version] = options[:declaration]
|
|
74
|
+
elsif options[:declaration] == true
|
|
75
|
+
builder_options[:xml_declaration][:version] = "1.0"
|
|
76
|
+
end
|
|
77
|
+
builder_options[:xml_declaration][:encoding] = encoding if options.key?(:encoding) && encoding
|
|
78
|
+
elsif options[:encoding] && !options[:encoding].nil?
|
|
79
|
+
builder_options[:force_declaration] = true
|
|
80
|
+
end
|
|
52
81
|
|
|
53
82
|
builder = self.class::BUILDER_CLASS.build(builder_options) do |xml|
|
|
54
83
|
if root.is_a?(self.class::PARSED_ELEMENT_CLASS)
|
|
@@ -58,7 +87,7 @@ module Lutaml
|
|
|
58
87
|
end
|
|
59
88
|
end
|
|
60
89
|
|
|
61
|
-
|
|
90
|
+
builder.to_xml
|
|
62
91
|
end
|
|
63
92
|
|
|
64
93
|
def build_serializable_xml(xml, options)
|
|
@@ -95,6 +124,14 @@ module Lutaml
|
|
|
95
124
|
|
|
96
125
|
private
|
|
97
126
|
|
|
127
|
+
def standalone_value(value)
|
|
128
|
+
case value
|
|
129
|
+
when true then "yes"
|
|
130
|
+
when false then "no"
|
|
131
|
+
else value.to_s
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
98
135
|
def transformable_xml_element(options)
|
|
99
136
|
return root if root.is_a?(Lutaml::Xml::DataModel::XmlElement)
|
|
100
137
|
|
|
@@ -183,27 +220,8 @@ module Lutaml
|
|
|
183
220
|
options_with_original_ns
|
|
184
221
|
end
|
|
185
222
|
|
|
186
|
-
def finalize_adapter_xml(xml_data, encoding, options)
|
|
187
|
-
result = ""
|
|
188
|
-
if (options[:encoding] && !options[:encoding].nil?) ||
|
|
189
|
-
should_include_declaration?(options)
|
|
190
|
-
result += generate_declaration(options)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
doctype_to_use = options[:doctype] || @doctype
|
|
194
|
-
if doctype_to_use && !options[:omit_doctype]
|
|
195
|
-
result += generate_doctype_declaration(doctype_to_use)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
result += xml_data
|
|
199
|
-
if encoding && result.encoding.to_s.upcase != encoding.to_s.upcase
|
|
200
|
-
result = result.encode(encoding)
|
|
201
|
-
end
|
|
202
|
-
result
|
|
203
|
-
end
|
|
204
|
-
|
|
205
223
|
def text_content_for_xml(value)
|
|
206
|
-
::Moxml
|
|
224
|
+
::Moxml.preprocess_entities(value.to_s)
|
|
207
225
|
end
|
|
208
226
|
|
|
209
227
|
def build_plan_node(xml, xml_element, element_node, plan: nil,
|
|
@@ -212,7 +230,9 @@ module Lutaml
|
|
|
212
230
|
attributes = {}
|
|
213
231
|
|
|
214
232
|
original_ns_uris = plan&.original_namespace_uris || {}
|
|
215
|
-
element_node.hoisted_declarations.
|
|
233
|
+
element_node.hoisted_declarations.sort_by do |prefix, _uri|
|
|
234
|
+
prefix.nil? ? "" : prefix.to_s
|
|
235
|
+
end.each do |key, uri|
|
|
216
236
|
next if uri == "http://www.w3.org/XML/1998/namespace"
|
|
217
237
|
|
|
218
238
|
effective_uri = if self.class.fpi?(uri)
|
data/lib/lutaml/xml/adapter.rb
CHANGED
|
@@ -7,6 +7,10 @@ module Lutaml
|
|
|
7
7
|
autoload :AdapterHelpers, "#{__dir__}/adapter/adapter_helpers"
|
|
8
8
|
autoload :BaseAdapter, "#{__dir__}/adapter/base_adapter"
|
|
9
9
|
autoload :NamespaceData, "#{__dir__}/adapter/namespace_data"
|
|
10
|
+
autoload :XmlParser, "#{__dir__}/adapter/xml_parser"
|
|
11
|
+
autoload :XmlSerializer, "#{__dir__}/adapter/xml_serializer"
|
|
12
|
+
autoload :PlanBasedBuilder, "#{__dir__}/adapter/plan_based_builder"
|
|
13
|
+
autoload :NamespaceUriCollector, "#{__dir__}/adapter/namespace_uri_collector"
|
|
10
14
|
autoload :OgaAdapter, "#{__dir__}/adapter/oga_adapter"
|
|
11
15
|
Lutaml::Model::RuntimeCompatibility.autoload_native(
|
|
12
16
|
self,
|
|
@@ -7,15 +7,63 @@ module Lutaml
|
|
|
7
7
|
module Builder
|
|
8
8
|
# Base builder for XML construction using moxml.
|
|
9
9
|
# All adapter-specific builders inherit from this class.
|
|
10
|
+
#
|
|
11
|
+
# The builder creates XML documents through moxml's document model.
|
|
12
|
+
# Declaration, doctype, indentation, and line endings are handled
|
|
13
|
+
# by moxml — no manual string assembly.
|
|
10
14
|
class Base
|
|
11
15
|
def self.build(options = {})
|
|
12
16
|
context = Moxml.new(moxml_backend)
|
|
13
17
|
if Lutaml::Model::RuntimeCompatibility.opal?
|
|
14
18
|
context.config.namespace_validation_mode = :lenient
|
|
15
19
|
end
|
|
20
|
+
|
|
21
|
+
encoding_value = options.delete(:encoding)
|
|
22
|
+
context.config.default_indent = options.delete(:indent) if options.key?(:indent)
|
|
23
|
+
context.config.default_line_ending = options.delete(:line_ending) if options.key?(:line_ending)
|
|
24
|
+
|
|
16
25
|
doc = context.create_document
|
|
26
|
+
|
|
27
|
+
# Capture doctype — added after root to avoid Ox incompatibility
|
|
28
|
+
doctype = options.delete(:doctype)
|
|
29
|
+
|
|
17
30
|
instance = new(doc, context, options)
|
|
31
|
+
instance.encoding = encoding_value if encoding_value
|
|
18
32
|
yield(instance) if block_given?
|
|
33
|
+
|
|
34
|
+
# Add doctype before root (after build block sets root)
|
|
35
|
+
if doctype && doc.root
|
|
36
|
+
dt = doc.create_doctype(
|
|
37
|
+
doctype[:name],
|
|
38
|
+
doctype[:public_id],
|
|
39
|
+
doctype[:system_id],
|
|
40
|
+
)
|
|
41
|
+
doc.add_child(dt)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Handle declaration — configure it on the document so moxml
|
|
45
|
+
# serializes it natively (works across all adapters)
|
|
46
|
+
xml_decl = options.delete(:xml_declaration) || {}
|
|
47
|
+
include_decl = options.delete(:include_declaration)
|
|
48
|
+
force_decl = options.delete(:force_declaration)
|
|
49
|
+
|
|
50
|
+
if include_decl
|
|
51
|
+
version = xml_decl[:version] || "1.0"
|
|
52
|
+
encoding = xml_decl[:encoding]
|
|
53
|
+
encoding ||= "UTF-8" unless xml_decl[:had_declaration]
|
|
54
|
+
standalone = xml_decl[:standalone]
|
|
55
|
+
decl = doc.create_declaration(version, encoding, standalone)
|
|
56
|
+
doc.add_child(decl)
|
|
57
|
+
instance.declaration_mode = :default
|
|
58
|
+
elsif force_decl
|
|
59
|
+
decl_encoding = encoding_value || "UTF-8"
|
|
60
|
+
decl = doc.create_declaration("1.0", decl_encoding, nil)
|
|
61
|
+
doc.add_child(decl)
|
|
62
|
+
instance.declaration_mode = :default
|
|
63
|
+
else
|
|
64
|
+
instance.declaration_mode = :none
|
|
65
|
+
end
|
|
66
|
+
|
|
19
67
|
instance
|
|
20
68
|
end
|
|
21
69
|
|
|
@@ -24,13 +72,15 @@ module Lutaml
|
|
|
24
72
|
nil
|
|
25
73
|
end
|
|
26
74
|
|
|
27
|
-
attr_reader :doc
|
|
75
|
+
attr_reader :doc
|
|
76
|
+
attr_accessor :encoding, :declaration_mode
|
|
28
77
|
|
|
29
78
|
def initialize(doc, context, options = {})
|
|
30
79
|
@doc = doc
|
|
31
80
|
@context = context
|
|
32
81
|
@encoding = options[:encoding]
|
|
33
82
|
@current_stack = [doc]
|
|
83
|
+
@declaration_mode = :none
|
|
34
84
|
end
|
|
35
85
|
|
|
36
86
|
def current_element
|
|
@@ -116,33 +166,15 @@ module Lutaml
|
|
|
116
166
|
add_cdata(current_element, content)
|
|
117
167
|
end
|
|
118
168
|
|
|
119
|
-
def
|
|
169
|
+
def to_xml
|
|
120
170
|
return "" unless @doc.root
|
|
121
171
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
if has_pi_or_comment
|
|
130
|
-
# Serialize each top-level node individually
|
|
131
|
-
parts = doc_children.map do |child|
|
|
132
|
-
if child == @doc.root
|
|
133
|
-
child.to_xml(declaration: false, expand_empty: false)
|
|
134
|
-
else
|
|
135
|
-
child.to_xml
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
parts.join("\n")
|
|
139
|
-
else
|
|
140
|
-
@doc.root.to_xml(declaration: false, expand_empty: false)
|
|
141
|
-
end
|
|
142
|
-
end
|
|
172
|
+
result = if @declaration_mode == :none && !has_document_level_nodes?
|
|
173
|
+
@doc.root.to_xml(declaration: false, expand_empty: false)
|
|
174
|
+
else
|
|
175
|
+
@doc.to_xml(declaration: @declaration_mode == :default, expand_empty: false)
|
|
176
|
+
end
|
|
143
177
|
|
|
144
|
-
def to_xml
|
|
145
|
-
result = to_s
|
|
146
178
|
result = result.encode(encoding) if encoding && result.encoding.to_s != encoding
|
|
147
179
|
result
|
|
148
180
|
end
|
|
@@ -158,6 +190,13 @@ module Lutaml
|
|
|
158
190
|
|
|
159
191
|
private
|
|
160
192
|
|
|
193
|
+
def has_document_level_nodes?
|
|
194
|
+
@doc.children.any? do |child|
|
|
195
|
+
child != @doc.root &&
|
|
196
|
+
!child.is_a?(Moxml::Text)
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
161
200
|
def resolve_target(element)
|
|
162
201
|
element.is_a?(self.class) || element.is_a?(Base) ? element.current_element : element
|
|
163
202
|
end
|
data/lib/lutaml/xml/builder.rb
CHANGED
|
@@ -13,7 +13,7 @@ module Lutaml
|
|
|
13
13
|
# include Lutaml::Xml::Configurable
|
|
14
14
|
#
|
|
15
15
|
# xml do
|
|
16
|
-
#
|
|
16
|
+
# element "MyModel"
|
|
17
17
|
# namespace MyNamespace
|
|
18
18
|
# end
|
|
19
19
|
# end
|
|
@@ -51,7 +51,7 @@ module Lutaml
|
|
|
51
51
|
#
|
|
52
52
|
# @example Configuration with block
|
|
53
53
|
# xml do
|
|
54
|
-
#
|
|
54
|
+
# element "MyModel"
|
|
55
55
|
# namespace MyNamespace
|
|
56
56
|
# map_element 'name', to: :name
|
|
57
57
|
# end
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
module Lutaml
|
|
2
2
|
module Xml
|
|
3
|
-
# DeclarationHandler provides XML declaration
|
|
4
|
-
# for all XML adapter implementations.
|
|
3
|
+
# DeclarationHandler provides XML declaration extraction from input XML.
|
|
5
4
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
5
|
+
# Extraction methods detect and parse XML declarations from input strings.
|
|
6
|
+
# Generation is handled by moxml's document model — no manual string assembly.
|
|
8
7
|
module DeclarationHandler
|
|
9
8
|
# Extract XML declaration information from input string
|
|
10
9
|
#
|
|
@@ -92,126 +91,25 @@ module Lutaml
|
|
|
92
91
|
# @param xml_declaration [Hash] extracted declaration info from input
|
|
93
92
|
# @return [Boolean] true if declaration should be included
|
|
94
93
|
def should_include_declaration?(options, xml_declaration = nil)
|
|
95
|
-
# Use instance variable if not provided (for adapter instance methods)
|
|
96
94
|
xml_declaration ||= @xml_declaration
|
|
97
95
|
|
|
98
96
|
if options.key?(:declaration)
|
|
99
97
|
case options[:declaration]
|
|
100
98
|
when false
|
|
101
|
-
# Explicit false: omit declaration
|
|
102
99
|
false
|
|
103
100
|
when true
|
|
104
|
-
# Explicit true: force include
|
|
105
101
|
true
|
|
106
102
|
when :preserve
|
|
107
|
-
# Preserve mode: include if input had one
|
|
108
103
|
xml_declaration&.dig(:had_declaration) || false
|
|
109
104
|
when String
|
|
110
|
-
# Custom version string: include
|
|
111
105
|
true
|
|
112
106
|
else
|
|
113
|
-
# Default: preserve from input
|
|
114
107
|
xml_declaration&.dig(:had_declaration) || false
|
|
115
108
|
end
|
|
116
109
|
else
|
|
117
|
-
# No declaration option provided: default behavior is preserve from input
|
|
118
110
|
xml_declaration&.dig(:had_declaration) || false
|
|
119
111
|
end
|
|
120
112
|
end
|
|
121
|
-
|
|
122
|
-
# Generate XML declaration string
|
|
123
|
-
#
|
|
124
|
-
# Uses stored declaration info if available, otherwise uses defaults.
|
|
125
|
-
# Supports custom version strings, encoding, and standalone options.
|
|
126
|
-
#
|
|
127
|
-
# @param options [Hash] serialization options
|
|
128
|
-
# - :declaration => String for custom version, true for default
|
|
129
|
-
# - :encoding => String or true for UTF-8
|
|
130
|
-
# - :standalone => String ("yes"/"no"), true ("yes"), false ("no"), :preserve
|
|
131
|
-
# @param xml_declaration [Hash] extracted declaration info from input
|
|
132
|
-
# @return [String] the XML declaration (includes trailing newline)
|
|
133
|
-
def generate_declaration(options, xml_declaration = nil)
|
|
134
|
-
# Use instance variable if not provided (for adapter instance methods)
|
|
135
|
-
xml_declaration ||= @xml_declaration
|
|
136
|
-
|
|
137
|
-
# Determine version
|
|
138
|
-
# When declaration: true (force), use default 1.0 not input version
|
|
139
|
-
# When declaration: "1.x" (custom), use that string
|
|
140
|
-
# When preserving (no option or :preserve), use input version or default
|
|
141
|
-
version = if options[:declaration].is_a?(String)
|
|
142
|
-
# Custom version string
|
|
143
|
-
options[:declaration]
|
|
144
|
-
elsif options[:declaration] == true
|
|
145
|
-
# Force with default version
|
|
146
|
-
"1.0"
|
|
147
|
-
elsif xml_declaration&.dig(:version)
|
|
148
|
-
# Preserve from input
|
|
149
|
-
xml_declaration[:version]
|
|
150
|
-
else
|
|
151
|
-
# Default fallback
|
|
152
|
-
"1.0"
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# Determine encoding
|
|
156
|
-
# Priority: explicit encoding option > input encoding > none
|
|
157
|
-
encoding = if options[:encoding].is_a?(String)
|
|
158
|
-
options[:encoding]
|
|
159
|
-
elsif options[:encoding] == true
|
|
160
|
-
"UTF-8"
|
|
161
|
-
elsif xml_declaration&.dig(:encoding)
|
|
162
|
-
xml_declaration[:encoding]
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Determine standalone
|
|
166
|
-
# Priority: explicit standalone option > input standalone > none
|
|
167
|
-
# Supported values: "yes", "no", true ("yes"), false ("no"), :preserve
|
|
168
|
-
standalone = if options.key?(:standalone)
|
|
169
|
-
case options[:standalone]
|
|
170
|
-
when String
|
|
171
|
-
options[:standalone]
|
|
172
|
-
when true
|
|
173
|
-
"yes"
|
|
174
|
-
when false
|
|
175
|
-
"no"
|
|
176
|
-
when :preserve
|
|
177
|
-
xml_declaration&.dig(:standalone)
|
|
178
|
-
end
|
|
179
|
-
elsif xml_declaration&.dig(:standalone)
|
|
180
|
-
xml_declaration[:standalone]
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
declaration = "<?xml version=\"#{version}\""
|
|
184
|
-
declaration += " encoding=\"#{encoding}\"" if encoding
|
|
185
|
-
declaration += " standalone=\"#{standalone}\"" if standalone
|
|
186
|
-
declaration += "?>\n"
|
|
187
|
-
declaration
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# Generate DOCTYPE declaration from doctype hash
|
|
191
|
-
#
|
|
192
|
-
# Supports both PUBLIC and SYSTEM DTDs.
|
|
193
|
-
# Format: <!DOCTYPE name PUBLIC "public_id" "system_id">
|
|
194
|
-
# <!DOCTYPE name SYSTEM "system_id">
|
|
195
|
-
#
|
|
196
|
-
# @param doctype [Hash] the doctype information
|
|
197
|
-
# - :name => root element name
|
|
198
|
-
# - :public_id => public identifier (optional)
|
|
199
|
-
# - :system_id => system identifier (optional)
|
|
200
|
-
# @return [String, nil] the DOCTYPE declaration or nil if no doctype
|
|
201
|
-
def generate_doctype_declaration(doctype)
|
|
202
|
-
return nil unless doctype
|
|
203
|
-
|
|
204
|
-
parts = ["<!DOCTYPE #{doctype[:name]}"]
|
|
205
|
-
|
|
206
|
-
if doctype[:public_id]
|
|
207
|
-
parts << %(PUBLIC "#{doctype[:public_id]}")
|
|
208
|
-
parts << %("#{doctype[:system_id]}") if doctype[:system_id]
|
|
209
|
-
elsif doctype[:system_id]
|
|
210
|
-
parts << %(SYSTEM "#{doctype[:system_id]}")
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
"#{parts.join(' ')}>\n"
|
|
214
|
-
end
|
|
215
113
|
end
|
|
216
114
|
end
|
|
217
115
|
end
|
data/lib/lutaml/xml/mapping.rb
CHANGED
|
@@ -350,11 +350,11 @@ module Lutaml
|
|
|
350
350
|
# namespace :blank
|
|
351
351
|
#
|
|
352
352
|
# @raise [ArgumentError] if invalid arguments provided
|
|
353
|
-
# @raise [Lutaml::Model::
|
|
353
|
+
# @raise [Lutaml::Model::TypeOnlyNamespaceError] if explicitly marked as no_root
|
|
354
354
|
def namespace(ns_class_or_symbol, _deprecated_prefix = nil)
|
|
355
355
|
# Only raise error for explicitly marked no_root (using deprecated method)
|
|
356
356
|
# Type-only models (no element declared) CAN have namespaces
|
|
357
|
-
raise Lutaml::Model::
|
|
357
|
+
raise Lutaml::Model::TypeOnlyNamespaceError if @no_root
|
|
358
358
|
|
|
359
359
|
# Warn if prefix parameter is provided
|
|
360
360
|
if _deprecated_prefix
|
|
@@ -784,7 +784,7 @@ module Lutaml
|
|
|
784
784
|
#
|
|
785
785
|
# @example
|
|
786
786
|
# xml do
|
|
787
|
-
#
|
|
787
|
+
# element "rfc"
|
|
788
788
|
# map_processing_instruction "rfc", to: :pi_settings
|
|
789
789
|
# end
|
|
790
790
|
#
|
|
@@ -6,7 +6,7 @@ module Lutaml
|
|
|
6
6
|
# XML-specific overrides for Collection class methods.
|
|
7
7
|
#
|
|
8
8
|
# Prepended into Collection's singleton class when XML is loaded.
|
|
9
|
-
# Provides XML-specific
|
|
9
|
+
# Provides XML-specific unwrapped collection handling.
|
|
10
10
|
module CollectionExt
|
|
11
11
|
# XML is a structured (tree-based) format
|
|
12
12
|
def collection_structured_format?(format)
|
|
@@ -15,15 +15,15 @@ module Lutaml
|
|
|
15
15
|
true
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
# XML handles
|
|
19
|
-
def
|
|
18
|
+
# XML handles unwrapped serialization specially
|
|
19
|
+
def collection_unwrapped_to?(format)
|
|
20
20
|
return super unless format == :xml
|
|
21
21
|
|
|
22
22
|
true
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# XML
|
|
26
|
-
def
|
|
25
|
+
# XML unwrapped serialization: serialize each mapping separately
|
|
26
|
+
def collection_unwrapped_to(format, mappings, instance, options)
|
|
27
27
|
return super unless format == :xml
|
|
28
28
|
|
|
29
29
|
mappings.mappings.map do |mapping|
|
|
@@ -31,8 +31,8 @@ module Lutaml
|
|
|
31
31
|
end.join("\n")
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# XML
|
|
35
|
-
def
|
|
34
|
+
# XML unwrapped: wrap raw XML in a fake root tag before parsing
|
|
35
|
+
def wrap_unwrapped_input(format, mappings, data)
|
|
36
36
|
return super unless format == :xml
|
|
37
37
|
|
|
38
38
|
tag_name = mappings.find_by_to!(instance_name).name
|
|
@@ -94,7 +94,7 @@ module Lutaml
|
|
|
94
94
|
return super unless format == :xml
|
|
95
95
|
|
|
96
96
|
valid = root?(register) || options[:from_collection]
|
|
97
|
-
raise Lutaml::Model::
|
|
97
|
+
raise Lutaml::Model::TypeOnlyMappingError.new(self) unless valid
|
|
98
98
|
|
|
99
99
|
options[:encoding] = doc.encoding
|
|
100
100
|
if doc.is_a?(Lutaml::Xml::Document) && doc.doctype
|
|
@@ -204,7 +204,7 @@ module Lutaml
|
|
|
204
204
|
return super unless format == :xml
|
|
205
205
|
return if options[:collection] || self.class.root?(lutaml_register)
|
|
206
206
|
|
|
207
|
-
raise Lutaml::Model::
|
|
207
|
+
raise Lutaml::Model::TypeOnlyMappingError.new(self.class)
|
|
208
208
|
end
|
|
209
209
|
|
|
210
210
|
# XML-specific instance options preparation
|
data/lib/lutaml/xml.rb
CHANGED
|
@@ -134,7 +134,6 @@ module Lutaml
|
|
|
134
134
|
autoload :ElementPrefixResolver, "#{__dir__}/xml/element_prefix_resolver"
|
|
135
135
|
autoload :FormatChooser, "#{__dir__}/xml/format_chooser"
|
|
136
136
|
autoload :HoistingAlgorithm, "#{__dir__}/xml/hoisting_algorithm"
|
|
137
|
-
autoload :HoistingAlgorithm, "#{__dir__}/xml/hoisting_algorithm"
|
|
138
137
|
autoload :NamespaceInheritanceResolver,
|
|
139
138
|
"#{__dir__}/xml/namespace_inheritance_resolver"
|
|
140
139
|
autoload :NamespaceScopeConfig, "#{__dir__}/xml/namespace_scope_config"
|
|
@@ -229,7 +228,7 @@ end
|
|
|
229
228
|
Lutaml::Model::Attribute.format_specific_warn_names.push(:element_order,
|
|
230
229
|
:schema_location, :encoding, :doctype, :ordered?, :mixed?)
|
|
231
230
|
|
|
232
|
-
# Prepend XML-specific Collection overrides (
|
|
231
|
+
# Prepend XML-specific Collection overrides (unwrapped collection handling for XML)
|
|
233
232
|
require_relative "xml/serialization/collection_ext"
|
|
234
233
|
Lutaml::Model::Collection.singleton_class.prepend(
|
|
235
234
|
Lutaml::Xml::Serialization::CollectionExt,
|
data/lib/tasks/memory_profile.rb
CHANGED
|
@@ -18,7 +18,7 @@ class Address < Lutaml::Model::Serializable
|
|
|
18
18
|
attribute :country, :string
|
|
19
19
|
|
|
20
20
|
xml do
|
|
21
|
-
|
|
21
|
+
element "address"
|
|
22
22
|
map_element "street", to: :street
|
|
23
23
|
map_element "city", to: :city
|
|
24
24
|
map_element "zip", to: :zip
|
|
@@ -37,7 +37,7 @@ class Person < Lutaml::Model::Serializable
|
|
|
37
37
|
attribute :tags, :string, collection: true
|
|
38
38
|
|
|
39
39
|
xml do
|
|
40
|
-
|
|
40
|
+
element "person"
|
|
41
41
|
map_attribute "id", to: :id
|
|
42
42
|
map_element "first_name", to: :first_name
|
|
43
43
|
map_element "last_name", to: :last_name
|
|
@@ -21,7 +21,7 @@ module Lutaml
|
|
|
21
21
|
attribute :created_at, :date_time
|
|
22
22
|
|
|
23
23
|
xml do
|
|
24
|
-
|
|
24
|
+
element "simple"
|
|
25
25
|
map_attribute "id", to: :id
|
|
26
26
|
map_element "name", to: :name
|
|
27
27
|
map_element "active", to: :active
|
|
@@ -46,7 +46,7 @@ module Lutaml
|
|
|
46
46
|
attribute :country, :string
|
|
47
47
|
|
|
48
48
|
xml do
|
|
49
|
-
|
|
49
|
+
element "address"
|
|
50
50
|
map_element "street", to: :street
|
|
51
51
|
map_element "city", to: :city
|
|
52
52
|
map_element "zip", to: :zip
|
|
@@ -73,7 +73,7 @@ module Lutaml
|
|
|
73
73
|
attribute :tags, :string, collection: true
|
|
74
74
|
|
|
75
75
|
xml do
|
|
76
|
-
|
|
76
|
+
element "person"
|
|
77
77
|
map_attribute "id", to: :id
|
|
78
78
|
map_element "first_name", to: :first_name
|
|
79
79
|
map_element "last_name", to: :last_name
|
|
@@ -103,7 +103,7 @@ module Lutaml
|
|
|
103
103
|
attribute :price, :float
|
|
104
104
|
|
|
105
105
|
xml do
|
|
106
|
-
|
|
106
|
+
element "item"
|
|
107
107
|
map_attribute "product_id", to: :product_id
|
|
108
108
|
map_element "quantity", to: :quantity
|
|
109
109
|
map_element "price", to: :price
|
|
@@ -118,7 +118,7 @@ module Lutaml
|
|
|
118
118
|
attribute :status, :string
|
|
119
119
|
|
|
120
120
|
xml do
|
|
121
|
-
|
|
121
|
+
element "order"
|
|
122
122
|
map_attribute "id", to: :id
|
|
123
123
|
map_element "customer", to: :customer
|
|
124
124
|
map_element "item", to: :items
|
|
@@ -33,7 +33,7 @@ RSpec.describe Lutaml::Model::Cli do
|
|
|
33
33
|
attribute :extract_language, ExtractLanguage, collection: true
|
|
34
34
|
|
|
35
35
|
xml do
|
|
36
|
-
|
|
36
|
+
element "termium_extract"
|
|
37
37
|
namespace TermiumNamespace
|
|
38
38
|
|
|
39
39
|
map_attribute "language", to: :language, namespace: nil
|