lutaml-model 0.8.19 → 0.8.22
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/.github/workflows/dependent-tests.yml +1 -1
- data/.github/workflows/rake.yml +1 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +133 -72
- data/bench/bench_common.rb +8 -5
- data/bench/bench_niso.rb +1 -2
- data/bench/gate_config.rb +4 -1
- data/benchmark/serialization_benchmark.rb +4 -4
- data/docs/_guides/value-transformations.adoc +27 -0
- data/lib/compat/opal/generate_boot.rb +3 -1
- data/lib/compat/opal/lutaml_model_boot.rb +10 -7
- data/lib/compat/opal/moxml_boot.rb +6 -1
- data/lib/lutaml/hash_format/format.rb +29 -0
- data/lib/lutaml/hash_format.rb +5 -27
- data/lib/lutaml/json/format.rb +61 -0
- data/lib/lutaml/json.rb +4 -59
- data/lib/lutaml/jsonl/format.rb +22 -0
- data/lib/lutaml/jsonl.rb +5 -19
- data/lib/lutaml/key_value/format.rb +26 -0
- data/lib/lutaml/key_value/transform.rb +4 -1
- data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
- data/lib/lutaml/key_value.rb +4 -21
- data/lib/lutaml/model/attribute.rb +38 -4
- data/lib/lutaml/model/collection.rb +2 -2
- data/lib/lutaml/model/compiled_rule.rb +1 -1
- data/lib/lutaml/model/config.rb +2 -2
- data/lib/lutaml/model/consolidation_map.rb +2 -2
- data/lib/lutaml/model/consolidation_rule/dispatch_block.rb +2 -2
- data/lib/lutaml/model/errors.rb +2 -2
- data/lib/lutaml/model/global_context.rb +4 -2
- data/lib/lutaml/model/mapping/mapping_rule.rb +4 -1
- data/lib/lutaml/model/mapping/model_mapping.rb +2 -2
- data/lib/lutaml/model/runtime_compatibility.rb +2 -2
- data/lib/lutaml/model/schema/class_loader.rb +2 -1
- data/lib/lutaml/model/schema/decorators/definition_collection.rb +4 -4
- data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
- data/lib/lutaml/model/schema/file_writer.rb +5 -2
- data/lib/lutaml/model/schema/renderers/base.rb +4 -3
- data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
- data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
- data/lib/lutaml/model/schema/renderers/model.rb +18 -6
- data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
- data/lib/lutaml/model/schema/renderers.rb +2 -1
- data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
- data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
- data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
- data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
- data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
- data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +2 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
- data/lib/lutaml/model/sequence.rb +4 -4
- data/lib/lutaml/model/serialize/attribute_definition.rb +1 -0
- data/lib/lutaml/model/serialize/format_conversion.rb +2 -2
- data/lib/lutaml/model/serialize/initialization.rb +9 -1
- data/lib/lutaml/model/serialize/model_import.rb +1 -0
- data/lib/lutaml/model/serialize.rb +4 -3
- data/lib/lutaml/model/type/uninitialized_class_guard.rb +1 -1
- data/lib/lutaml/model/type.rb +9 -0
- data/lib/lutaml/model/type_registry.rb +2 -2
- data/lib/lutaml/model/uninitialized_class.rb +1 -1
- data/lib/lutaml/model/union.rb +5 -2
- data/lib/lutaml/model/utils.rb +2 -2
- data/lib/lutaml/model/value_policy.rb +27 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +17 -16
- data/lib/lutaml/rdf/mapping.rb +2 -2
- data/lib/lutaml/rdf/namespace_set.rb +2 -2
- data/lib/lutaml/rdf/transform.rb +2 -2
- data/lib/lutaml/toml/format.rb +48 -0
- data/lib/lutaml/toml.rb +5 -46
- data/lib/lutaml/xml/adapter/base_adapter.rb +2 -2
- data/lib/lutaml/xml/builder/base.rb +2 -2
- data/lib/lutaml/xml/format.rb +331 -0
- data/lib/lutaml/xml/mapping.rb +2 -2
- data/lib/lutaml/xml/mapping_rule.rb +62 -3
- data/lib/lutaml/xml/model_transform.rb +132 -55
- data/lib/lutaml/xml/namespace_collector.rb +3 -3
- data/lib/lutaml/xml/parse_session.rb +59 -0
- data/lib/lutaml/xml/parsed_namespace_set.rb +2 -2
- data/lib/lutaml/xml/schema/builder.rb +5 -5
- data/lib/lutaml/xml/schema/xsd/schema_location_mapping.rb +2 -2
- data/lib/lutaml/xml/schema/xsd.rb +0 -2
- data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
- data/lib/lutaml/xml/serialization/format_conversion.rb +3 -3
- data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
- data/lib/lutaml/xml/transformation/ordered_applier.rb +2 -1
- data/lib/lutaml/xml/xml_element.rb +2 -0
- data/lib/lutaml/xml.rb +4 -328
- data/lib/lutaml/yaml/format.rb +30 -0
- data/lib/lutaml/yaml.rb +5 -28
- data/lib/lutaml/yamls/adapter/mapping.rb +2 -2
- data/lib/lutaml/yamls/format.rb +22 -0
- data/lib/lutaml/yamls.rb +5 -19
- data/lib/tasks/benchmark_runner.rb +2 -2
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
- data/spec/lutaml/model/attribute_spec.rb +8 -3
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +2 -2
- data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +8 -8
- data/spec/lutaml/model/defaults_spec.rb +45 -0
- data/spec/lutaml/model/json_spec.rb +1 -2
- data/spec/lutaml/model/serialize/builder_spec.rb +6 -3
- data/spec/lutaml/model/store_spec.rb +3 -1
- data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
- data/spec/lutaml/model/union_attribute_spec.rb +4 -2
- data/spec/lutaml/model/value_policy_spec.rb +82 -0
- data/spec/lutaml/require_load_order_spec.rb +25 -0
- data/spec/lutaml/xml/attribute_element_namespace_disjoint_spec.rb +88 -0
- data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
- data/spec/lutaml/xml/out_of_namespace_parsing_spec.rb +123 -0
- data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
- data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
- data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
- metadata +20 -4
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Lutaml::Xml - XML serialization module for LutaML
|
|
4
|
+
#
|
|
5
|
+
# This module provides XML-specific serialization functionality.
|
|
6
|
+
# It requires lutaml/model to be loaded first for base classes.
|
|
7
|
+
|
|
8
|
+
# Ensure base model is loaded
|
|
9
|
+
|
|
10
|
+
# XML requires moxml for parsing
|
|
11
|
+
require "moxml"
|
|
12
|
+
|
|
13
|
+
# Under Opal, moxml's autoloads don't fire (Opal ignores autoload).
|
|
14
|
+
# Pull in the eager-load boot that ships at lib/compat/opal/moxml_boot.rb
|
|
15
|
+
# so Moxml::Adapter::{Oga,Rexml} etc. are actually defined. Without
|
|
16
|
+
# this, `Config.adapter = :oga` later fails with
|
|
17
|
+
# `NameError: uninitialized constant Moxml::Adapter::Oga`.
|
|
18
|
+
if Lutaml::Model.opal?
|
|
19
|
+
require "moxml_boot"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
module Lutaml
|
|
23
|
+
module Xml
|
|
24
|
+
# Error module for XML-specific errors
|
|
25
|
+
module Error
|
|
26
|
+
autoload :XmlError, "#{__dir__}/error/xml_error"
|
|
27
|
+
autoload :InvalidNamespaceError,
|
|
28
|
+
"#{__dir__}/error/invalid_namespace_error"
|
|
29
|
+
autoload :InvalidXsdTypeError,
|
|
30
|
+
"#{__dir__}/error/invalid_xsd_type_error"
|
|
31
|
+
autoload :XmlConfigurationError,
|
|
32
|
+
"#{__dir__}/error/xml_configuration_error"
|
|
33
|
+
autoload :NamespaceMismatchError,
|
|
34
|
+
"#{__dir__}/error/namespace_mismatch_error"
|
|
35
|
+
autoload :SchemaValidationError,
|
|
36
|
+
"#{__dir__}/error/schema_validation_error"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# XML Configuration modules
|
|
40
|
+
autoload :Configurable, "#{__dir__}/configurable"
|
|
41
|
+
autoload :NamespaceTypeResolver, "#{__dir__}/namespace_type_resolver"
|
|
42
|
+
|
|
43
|
+
# XML Type modules
|
|
44
|
+
module Type
|
|
45
|
+
autoload :ValueXmlMapping, "#{__dir__}/type/value_xml_mapping"
|
|
46
|
+
autoload :Configurable, "#{__dir__}/type/configurable"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# XML Serialization modules
|
|
50
|
+
autoload :Serialization, "#{__dir__}/serialization"
|
|
51
|
+
|
|
52
|
+
# XML Schema modules
|
|
53
|
+
autoload :Schema, "#{__dir__}/schema"
|
|
54
|
+
|
|
55
|
+
# Detect available XML adapter.
|
|
56
|
+
# Delegates to moxml, which is the authority on XML adapter
|
|
57
|
+
# availability and platform constraints.
|
|
58
|
+
#
|
|
59
|
+
# @return [Symbol] adapter type name
|
|
60
|
+
def self.detect_xml_adapter
|
|
61
|
+
Moxml::Config.runtime_default_adapter
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Get the current XML adapter
|
|
65
|
+
#
|
|
66
|
+
# Provides unified access to the configured XML adapter.
|
|
67
|
+
# This is a convenience method for consistent adapter access across
|
|
68
|
+
# Model and Type classes.
|
|
69
|
+
#
|
|
70
|
+
# @return [Object] the configured XML adapter instance
|
|
71
|
+
#
|
|
72
|
+
# @example Using the adapter
|
|
73
|
+
# adapter = Lutaml::Xml.adapter
|
|
74
|
+
# doc = adapter.parse(xml_string)
|
|
75
|
+
#
|
|
76
|
+
def self.adapter
|
|
77
|
+
Lutaml::Model::Config.adapter_for(:xml)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Get the current XML adapter type
|
|
81
|
+
#
|
|
82
|
+
# @return [Symbol] the configured XML adapter type (:nokogiri, :ox, etc.)
|
|
83
|
+
def self.adapter_type
|
|
84
|
+
Lutaml::Model::Config.xml_adapter_type
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Autoload core classes
|
|
88
|
+
autoload :Location, "#{__dir__}/schema_location"
|
|
89
|
+
autoload :SchemaLocation, "#{__dir__}/schema_location"
|
|
90
|
+
autoload :Namespace, "#{__dir__}/namespace"
|
|
91
|
+
autoload :Mapping, "#{__dir__}/mapping"
|
|
92
|
+
autoload :MappingRule, "#{__dir__}/mapping_rule"
|
|
93
|
+
autoload :Listener, "#{__dir__}/listener"
|
|
94
|
+
autoload :Document, "#{__dir__}/document"
|
|
95
|
+
autoload :Transformation, "#{__dir__}/transformation"
|
|
96
|
+
autoload :CustomMethodWrapper,
|
|
97
|
+
"#{__dir__}/transformation/custom_method_wrapper"
|
|
98
|
+
autoload :Transform, "#{__dir__}/transform"
|
|
99
|
+
autoload :XsdValidator, "#{__dir__}/xsd_validator"
|
|
100
|
+
autoload :Adapter, "#{__dir__}/adapter"
|
|
101
|
+
autoload :XmlElement, "#{__dir__}/xml_element"
|
|
102
|
+
autoload :AdapterElement, "#{__dir__}/adapter_element"
|
|
103
|
+
autoload :XmlAttribute, "#{__dir__}/xml_attribute"
|
|
104
|
+
autoload :Decisions, "#{__dir__}/decisions"
|
|
105
|
+
autoload :DeclarationPlan, "#{__dir__}/declaration_plan"
|
|
106
|
+
autoload :DeclarationPlanner, "#{__dir__}/declaration_planner"
|
|
107
|
+
autoload :NamespaceCollector, "#{__dir__}/namespace_collector"
|
|
108
|
+
autoload :NamespaceResolver, "#{__dir__}/namespace_resolver"
|
|
109
|
+
autoload :NamespaceDeclaration, "#{__dir__}/namespace_declaration"
|
|
110
|
+
autoload :NamespaceClassRegistry, "#{__dir__}/namespace_class_registry"
|
|
111
|
+
autoload :BlankNamespace, "#{__dir__}/blank_namespace"
|
|
112
|
+
autoload :EncodingNormalizer, "#{__dir__}/encoding_normalizer"
|
|
113
|
+
autoload :W3c, "#{__dir__}/w3c"
|
|
114
|
+
autoload :NamespaceResolutionStrategy,
|
|
115
|
+
"#{__dir__}/namespace_resolution_strategy"
|
|
116
|
+
autoload :NamespaceInheritanceStrategy,
|
|
117
|
+
"#{__dir__}/namespace_inheritance_strategy"
|
|
118
|
+
autoload :QualifiedInheritanceStrategy,
|
|
119
|
+
"#{__dir__}/qualified_inheritance_strategy"
|
|
120
|
+
autoload :UnqualifiedInheritanceStrategy,
|
|
121
|
+
"#{__dir__}/unqualified_inheritance_strategy"
|
|
122
|
+
autoload :DataModel, "#{__dir__}/data_model"
|
|
123
|
+
autoload :TransformationBuilder, "#{__dir__}/transformation_builder"
|
|
124
|
+
autoload :AdapterLoader, "#{__dir__}/adapter_loader"
|
|
125
|
+
autoload :Element, "#{__dir__}/element"
|
|
126
|
+
autoload :ModelTransform, "#{__dir__}/model_transform"
|
|
127
|
+
autoload :ParseSession, "#{__dir__}/parse_session"
|
|
128
|
+
autoload :TypeNamespaceResolver, "#{__dir__}/type_namespace_resolver"
|
|
129
|
+
autoload :NamespaceNeeds, "#{__dir__}/namespace_needs"
|
|
130
|
+
autoload :NamespaceUsage, "#{__dir__}/namespace_usage"
|
|
131
|
+
autoload :NamespaceDeclarationData,
|
|
132
|
+
"#{__dir__}/namespace_declaration_data"
|
|
133
|
+
autoload :ParsedNamespaceDeclaration,
|
|
134
|
+
"#{__dir__}/parsed_namespace_declaration"
|
|
135
|
+
autoload :ParsedNamespaceSet, "#{__dir__}/parsed_namespace_set"
|
|
136
|
+
autoload :DeclarationHandler, "#{__dir__}/declaration_handler"
|
|
137
|
+
autoload :PolymorphicValueHandler,
|
|
138
|
+
"#{__dir__}/polymorphic_value_handler"
|
|
139
|
+
autoload :AttributeNamespaceResolver,
|
|
140
|
+
"#{__dir__}/attribute_namespace_resolver"
|
|
141
|
+
autoload :BlankNamespaceHandler, "#{__dir__}/blank_namespace_handler"
|
|
142
|
+
autoload :DeclarationPlanQuery, "#{__dir__}/declaration_plan_query"
|
|
143
|
+
autoload :DocTypeExtractor, "#{__dir__}/doctype_extractor"
|
|
144
|
+
autoload :NamespaceDeclarationBuilder,
|
|
145
|
+
"#{__dir__}/namespace_declaration_builder"
|
|
146
|
+
autoload :ElementPrefixResolver, "#{__dir__}/element_prefix_resolver"
|
|
147
|
+
autoload :FormatChooser, "#{__dir__}/format_chooser"
|
|
148
|
+
autoload :HoistingAlgorithm, "#{__dir__}/hoisting_algorithm"
|
|
149
|
+
autoload :NamespaceInheritanceResolver,
|
|
150
|
+
"#{__dir__}/namespace_inheritance_resolver"
|
|
151
|
+
autoload :NamespaceScopeConfig, "#{__dir__}/namespace_scope_config"
|
|
152
|
+
autoload :Builder, "#{__dir__}/builder"
|
|
153
|
+
autoload :TypeNamespace, "#{__dir__}/type_namespace"
|
|
154
|
+
autoload :TransformationSupport, "#{__dir__}/transformation_support"
|
|
155
|
+
autoload :SharedDsl, "#{__dir__}/shared_dsl"
|
|
156
|
+
|
|
157
|
+
autoload :Oga, "#{__dir__}/oga"
|
|
158
|
+
autoload :Rexml, "#{__dir__}/rexml"
|
|
159
|
+
Lutaml::Model::RuntimeCompatibility.autoload_native(
|
|
160
|
+
self,
|
|
161
|
+
NokogiriElement: "#{__dir__}/nokogiri/element",
|
|
162
|
+
Nokogiri: "#{__dir__}/nokogiri",
|
|
163
|
+
Ox: "#{__dir__}/ox",
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Register XML format with the model's format registry
|
|
169
|
+
Lutaml::Model::FormatRegistry.register(
|
|
170
|
+
:xml,
|
|
171
|
+
mapping_class: Lutaml::Xml::Mapping,
|
|
172
|
+
adapter_class: nil,
|
|
173
|
+
transformer: Lutaml::Xml::Transform,
|
|
174
|
+
adapter_loader: Lutaml::Xml::AdapterLoader,
|
|
175
|
+
castable_type: Lutaml::Xml::XmlElement,
|
|
176
|
+
key_value: false,
|
|
177
|
+
error_types: %w[
|
|
178
|
+
Moxml::ParseError
|
|
179
|
+
Nokogiri::XML::SyntaxError
|
|
180
|
+
Ox::ParseError
|
|
181
|
+
REXML::ParseException
|
|
182
|
+
],
|
|
183
|
+
adapter_options: if Lutaml::Model.opal?
|
|
184
|
+
# Both Oga (vendored opal-oga fork, pure-Ruby lexer)
|
|
185
|
+
# and REXML (bundled stdlib gem, pure Ruby) work
|
|
186
|
+
# under Opal. Oga is the default because moxml's CI
|
|
187
|
+
# verifies it most thoroughly; REXML is selectable
|
|
188
|
+
# for callers that prefer a stdlib-only stack.
|
|
189
|
+
{
|
|
190
|
+
available: %i[oga rexml],
|
|
191
|
+
default: :oga,
|
|
192
|
+
}
|
|
193
|
+
else
|
|
194
|
+
{
|
|
195
|
+
available: %i[nokogiri ox oga rexml],
|
|
196
|
+
default: :nokogiri,
|
|
197
|
+
}
|
|
198
|
+
end,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Register XML transformation builder
|
|
202
|
+
Lutaml::Model::TransformationRegistry.register_builder(
|
|
203
|
+
:xml, Lutaml::Xml::TransformationBuilder
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Extend Type::Value with XML configuration (namespace, xsd_type, xml block)
|
|
207
|
+
Lutaml::Model::Type::Value.include(Lutaml::Xml::Type::Configurable)
|
|
208
|
+
|
|
209
|
+
# Prepend XML-specific serialization hooks into Serialize::ClassMethods
|
|
210
|
+
# Uses prepend so XML's hook overrides (pre_deserialize_hook, validate_document, etc.)
|
|
211
|
+
# take priority over core's no-op defaults.
|
|
212
|
+
Lutaml::Model::Serialize::ClassMethods.prepend(
|
|
213
|
+
Lutaml::Xml::Serialization::FormatConversion,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Prepend XML-specific ModelImport overrides (root?, ensure_format_mapping_imports!, etc.)
|
|
217
|
+
Lutaml::Model::Serialize::ModelImport.prepend(
|
|
218
|
+
Lutaml::Xml::Serialization::ModelImportExt,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Prepend XML-specific instance methods (import_declaration_plan, validate_root_mapping!, etc.)
|
|
222
|
+
Lutaml::Model::Serialize.prepend(
|
|
223
|
+
Lutaml::Xml::Serialization::InstanceMethods,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
# Opal does not propagate methods prepended into an already-included module
|
|
227
|
+
# to classes that included it before the prepend. Serializable includes
|
|
228
|
+
# Serialize during model boot, so make the XML instance API available on the
|
|
229
|
+
# concrete base class as well.
|
|
230
|
+
#
|
|
231
|
+
# Anonymous model classes (Class.new { include Serialize }) extend
|
|
232
|
+
# Serialize::ClassMethods rather than inheriting from Serializable, so the
|
|
233
|
+
# ModelImportExt override (root?) must land on ClassMethods too. The other
|
|
234
|
+
# two (FormatConversion, InstanceMethods) are already prepended unconditionally
|
|
235
|
+
# above, so Opal's no-double-prepend rule means we don't repeat them here.
|
|
236
|
+
if Lutaml::Model.opal?
|
|
237
|
+
Lutaml::Model::Serializable.singleton_class.prepend(
|
|
238
|
+
Lutaml::Xml::Serialization::ModelImportExt,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
Lutaml::Model::Serializable.prepend(
|
|
242
|
+
Lutaml::Xml::Serialization::InstanceMethods,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
Lutaml::Model::Serialize::ClassMethods.prepend(
|
|
246
|
+
Lutaml::Xml::Serialization::ModelImportExt,
|
|
247
|
+
)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# Register XML-specific attribute override warning names
|
|
251
|
+
Lutaml::Model::Attribute.format_specific_warn_names.push(:element_order,
|
|
252
|
+
:schema_location, :encoding, :doctype, :ordered?, :mixed?)
|
|
253
|
+
|
|
254
|
+
# Prepend XML-specific Collection overrides (unwrapped collection handling for XML)
|
|
255
|
+
require_relative "serialization/collection_ext"
|
|
256
|
+
Lutaml::Model::Collection.singleton_class.prepend(
|
|
257
|
+
Lutaml::Xml::Serialization::CollectionExt,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Register XML type serializers
|
|
261
|
+
require_relative "type/serializers"
|
|
262
|
+
Lutaml::Xml::Type::Serializers.register_all!
|
|
263
|
+
|
|
264
|
+
# Register XML schema methods
|
|
265
|
+
Lutaml::Model::Schema.register_method(:to_xsd) do |klass, options = {}|
|
|
266
|
+
if Lutaml::Model.opal?
|
|
267
|
+
raise NotImplementedError,
|
|
268
|
+
"XSD schema generation is not available under Opal."
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
Lutaml::Xml::Schema::XsdSchema.generate(klass, options)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
Lutaml::Model::Schema.register_method(:to_relaxng) do |klass, options = {}|
|
|
275
|
+
if Lutaml::Model.opal?
|
|
276
|
+
raise NotImplementedError,
|
|
277
|
+
"RELAX NG schema generation requires Nokogiri, " \
|
|
278
|
+
"which is not available under Opal."
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
Lutaml::Xml::Schema::RelaxngSchema.generate(klass, options)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
Lutaml::Model::Schema.register_method(:from_xml) do |xml, options = {}|
|
|
285
|
+
if Lutaml::Model.opal?
|
|
286
|
+
raise NotImplementedError,
|
|
287
|
+
"XML schema compilation is not available under Opal."
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
Lutaml::Model::Schema::XmlCompiler.to_models(xml, options)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
Lutaml::Model::Schema.register_method(:from_relaxng) do |rng, options = {}|
|
|
294
|
+
if Lutaml::Model::RuntimeCompatibility.opal?
|
|
295
|
+
raise NotImplementedError,
|
|
296
|
+
"RELAX NG schema compilation is not available under Opal."
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
Lutaml::Model::Schema::RngCompiler.to_models(rng, options)
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
Lutaml::Model::Schema.register_method(:from_rnc) do |rnc, options = {}|
|
|
303
|
+
if Lutaml::Model::RuntimeCompatibility.opal?
|
|
304
|
+
raise NotImplementedError,
|
|
305
|
+
"RNC schema compilation is not available under Opal."
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
Lutaml::Model::Schema::RncCompiler.to_models(rnc, options)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# Register XML namespace registry with GlobalContext
|
|
312
|
+
Lutaml::Model::GlobalContext.register_format_registry(
|
|
313
|
+
:xml, Lutaml::Xml::NamespaceClassRegistry.new
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Eagerly load W3C namespace definitions (has registration side effects)
|
|
317
|
+
require_relative "w3c"
|
|
318
|
+
|
|
319
|
+
# Auto-detection is now handled lazily by AdapterResolver on first use.
|
|
320
|
+
# No eager adapter selection at require time — libraries can configure
|
|
321
|
+
# their preferred adapter before any XML operations occur.
|
|
322
|
+
|
|
323
|
+
# Namespace identifier validation:
|
|
324
|
+
# - URIs (http://..., urn:...) are valid namespace names per W3C XML
|
|
325
|
+
# Namespaces (RFC 3986). URNs are first-class identifiers (RFC 8141).
|
|
326
|
+
# - Non-URI identifiers (e.g., FPIs like "-//OASIS//...") are also
|
|
327
|
+
# accepted for compatibility, though not spec-compliant.
|
|
328
|
+
# Use lenient mode to support FPIs and non-standard identifiers.
|
|
329
|
+
Moxml.configure do |config|
|
|
330
|
+
config.namespace_validation_mode = :lenient
|
|
331
|
+
end
|
data/lib/lutaml/xml/mapping.rb
CHANGED
|
@@ -818,10 +818,10 @@ module Lutaml
|
|
|
818
818
|
# @param group_class [Class] the GroupClass to instantiate (optional, resolved from Organization)
|
|
819
819
|
# @yield Builder block with gather/dispatch_by or map_element/map_content
|
|
820
820
|
# @return [ConsolidationMap]
|
|
821
|
-
def consolidate_map(by:, to:, group_class: nil, &)
|
|
821
|
+
def consolidate_map(by:, to:, group_class: nil, &block)
|
|
822
822
|
builder = ::Lutaml::Model::ConsolidationMap::Builder.new(by, to,
|
|
823
823
|
group_class)
|
|
824
|
-
builder.instance_eval(&)
|
|
824
|
+
builder.instance_eval(&block)
|
|
825
825
|
@consolidation_maps << builder.build
|
|
826
826
|
end
|
|
827
827
|
|
|
@@ -183,6 +183,34 @@ module Lutaml
|
|
|
183
183
|
result
|
|
184
184
|
end
|
|
185
185
|
|
|
186
|
+
# Match names an attribute rule consumes during parsing, mirroring how
|
|
187
|
+
# serialization qualifies the same attribute (resolve_namespace):
|
|
188
|
+
# a rule serialized with a prefix matches only attributes in its
|
|
189
|
+
# namespace; a rule serialized unprefixed matches only namespace-less
|
|
190
|
+
# attributes.
|
|
191
|
+
#
|
|
192
|
+
# Performance: memoized on the rule with zero-allocation cache hits.
|
|
193
|
+
# The inputs are constant for a given rule, register and parent model,
|
|
194
|
+
# so every element of a collection reuses the first resolution.
|
|
195
|
+
def attribute_match_names(attr, register, parent_ns_class, form_default,
|
|
196
|
+
default_namespace)
|
|
197
|
+
if defined?(@_amn_names) && @_amn_register == register &&
|
|
198
|
+
@_amn_parent_ns == parent_ns_class &&
|
|
199
|
+
@_amn_form_default == form_default &&
|
|
200
|
+
@_amn_default_ns == default_namespace
|
|
201
|
+
return @_amn_names
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
result = compute_attribute_match_names(attr, register, parent_ns_class,
|
|
205
|
+
form_default, default_namespace)
|
|
206
|
+
@_amn_register = register
|
|
207
|
+
@_amn_parent_ns = parent_ns_class
|
|
208
|
+
@_amn_form_default = form_default
|
|
209
|
+
@_amn_default_ns = default_namespace
|
|
210
|
+
@_amn_names = result
|
|
211
|
+
result
|
|
212
|
+
end
|
|
213
|
+
|
|
186
214
|
def namespaced_name(parent_namespace = nil, name = self.name)
|
|
187
215
|
if name.to_s == "lang"
|
|
188
216
|
::Lutaml::Model::Utils.blank?(prefix) ? name.to_s : "#{prefix}:#{name}"
|
|
@@ -274,6 +302,33 @@ module Lutaml
|
|
|
274
302
|
|
|
275
303
|
private
|
|
276
304
|
|
|
305
|
+
# Extracted body of attribute_match_names for caching
|
|
306
|
+
def compute_attribute_match_names(attr, register, parent_ns_class,
|
|
307
|
+
form_default, default_namespace)
|
|
308
|
+
ns_info = resolve_namespace(
|
|
309
|
+
attr: attr,
|
|
310
|
+
register: register,
|
|
311
|
+
parent_ns_class: parent_ns_class,
|
|
312
|
+
form_default: form_default,
|
|
313
|
+
)
|
|
314
|
+
uri = ns_info[:uri]
|
|
315
|
+
# Per W3C, an unprefixed attribute is in NO namespace regardless of
|
|
316
|
+
# the element's namespace. The qualified alias is only legitimate
|
|
317
|
+
# under schema-level attribute_form_default :qualified — the
|
|
318
|
+
# via_schema_default leniency (preserves ogc-gml's srsName handling
|
|
319
|
+
# under :qualified schema defaults). Without this gate, a plain
|
|
320
|
+
# attribute rule on an element-namespaced class inherits the
|
|
321
|
+
# element's namespace as default_namespace and steals a
|
|
322
|
+
# namespaced-sibling attribute whose local name matches (lutaml-model#758).
|
|
323
|
+
strict_match = uri && ns_info[:prefix] &&
|
|
324
|
+
!ns_info[:unqualified_same_ns] && !ns_info[:via_schema_default]
|
|
325
|
+
if strict_match
|
|
326
|
+
["#{uri}:#{name}"].freeze
|
|
327
|
+
else
|
|
328
|
+
namespaced_names(default_namespace)
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
277
332
|
# Extracted body of namespaced_names for caching
|
|
278
333
|
def compute_namespaced_names(parent_namespace)
|
|
279
334
|
if multiple_mappings?
|
|
@@ -413,7 +468,8 @@ form_default = :unqualified)
|
|
|
413
468
|
end
|
|
414
469
|
|
|
415
470
|
# 2. Type-level namespace
|
|
416
|
-
if attr && (type_ns_class = attr.type_namespace_class(register))
|
|
471
|
+
if attr && (type_ns_class = attr.type_namespace_class(register)) &&
|
|
472
|
+
type_ns_class != :blank
|
|
417
473
|
result = build_namespace_result_from_class(type_ns_class)
|
|
418
474
|
# CRITICAL W3C FLAG: Mark when attribute is in same namespace as parent with :unqualified
|
|
419
475
|
# Serialization code will check this to omit prefix per attributeFormDefault
|
|
@@ -427,7 +483,9 @@ form_default = :unqualified)
|
|
|
427
483
|
# When schema specifies attributeFormDefault="qualified", attributes
|
|
428
484
|
# must be qualified (prefixed) with the parent element's namespace
|
|
429
485
|
if form_default == :qualified && parent_ns_class
|
|
430
|
-
|
|
486
|
+
result = build_namespace_result_from_class(parent_ns_class)
|
|
487
|
+
result[:via_schema_default] = true
|
|
488
|
+
return result
|
|
431
489
|
end
|
|
432
490
|
|
|
433
491
|
# 4. No namespace (W3C default for unprefixed attributes)
|
|
@@ -490,7 +548,8 @@ form_default = :unqualified)
|
|
|
490
548
|
end
|
|
491
549
|
|
|
492
550
|
# 4. Type-level namespace
|
|
493
|
-
if attr && (type_ns_class = attr.type_namespace_class(register))
|
|
551
|
+
if attr && (type_ns_class = attr.type_namespace_class(register)) &&
|
|
552
|
+
type_ns_class != :blank
|
|
494
553
|
# Check if Type namespace matches parent default namespace
|
|
495
554
|
# If so, use parent's namespace with parent's actual prefix
|
|
496
555
|
if type_ns_class.uri == parent_ns_uri && parent_ns_class
|