lutaml-model 0.8.32 → 0.8.34
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.yml +5 -0
- data/Gemfile +4 -0
- data/README.adoc +39 -0
- data/docs/conversion_caching.adoc +143 -0
- data/lib/compat/opal/lutaml_model_boot.rb +15 -0
- data/lib/lutaml/model/attribute.rb +89 -0
- data/lib/lutaml/model/config.rb +13 -0
- data/lib/lutaml/model/configuration.rb +23 -0
- data/lib/lutaml/model/error/fraction_digits_error.rb +20 -0
- data/lib/lutaml/model/error/length_error.rb +19 -0
- data/lib/lutaml/model/error/max_exclusive_error.rb +19 -0
- data/lib/lutaml/model/error/max_inclusive_error.rb +20 -0
- data/lib/lutaml/model/error/max_length_error.rb +20 -0
- data/lib/lutaml/model/error/min_exclusive_error.rb +19 -0
- data/lib/lutaml/model/error/min_inclusive_error.rb +20 -0
- data/lib/lutaml/model/error/min_length_error.rb +20 -0
- data/lib/lutaml/model/error/restriction_error.rb +8 -0
- data/lib/lutaml/model/error/total_digits_error.rb +20 -0
- data/lib/lutaml/model/error/type/max_exclusive_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_exclusive_error.rb +20 -0
- data/lib/lutaml/model/error/type.rb +2 -0
- data/lib/lutaml/model/restriction_validation.rb +437 -0
- data/lib/lutaml/model/schema/definitions/restricted_type.rb +4 -2
- data/lib/lutaml/model/schema/renderers/restricted_type.rb +134 -4
- data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +3 -0
- data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +9 -1
- data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +2 -0
- data/lib/lutaml/model/schema/templates.rb +2 -0
- data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +39 -7
- data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +3 -0
- data/lib/lutaml/model/serialize/attribute_definition.rb +18 -7
- data/lib/lutaml/model/serialize/builder.rb +4 -5
- data/lib/lutaml/model/serialize/conversion_caching.rb +124 -0
- data/lib/lutaml/model/serialize/enum_handling.rb +50 -11
- data/lib/lutaml/model/serialize/format_conversion.rb +46 -38
- data/lib/lutaml/model/serialize/initialization.rb +4 -2
- data/lib/lutaml/model/serialize.rb +61 -0
- data/lib/lutaml/model/services/type/validator.rb +23 -0
- data/lib/lutaml/model/type/string.rb +28 -3
- data/lib/lutaml/model/type/value.rb +231 -0
- data/lib/lutaml/model/type.rb +2 -0
- data/lib/lutaml/model/validation.rb +2 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +16 -0
- data/lib/lutaml/xml/model_transform.rb +47 -6
- data/lib/lutaml/xml/schema/xsd/max_exclusive.rb +1 -1
- data/lib/lutaml/xml/schema/xsd/min_exclusive.rb +1 -1
- data/lib/lutaml/xml/schema/xsd_schema.rb +183 -11
- data/lib/lutaml/xml/transformation/order_reconciler.rb +267 -0
- data/lib/lutaml/xml/transformation/ordered_applier.rb +80 -122
- data/lib/lutaml/xml/transformation/rule_compiler.rb +2 -0
- data/lib/lutaml/xml/transformation.rb +2 -0
- data/lib/lutaml/xml/transformation_support.rb +1 -0
- data/spec/fixtures/xml/restriction_facets.xsd +46 -0
- data/spec/lutaml/model/attribute_restriction_spec.rb +472 -0
- data/spec/lutaml/model/attribute_spec.rb +7 -2
- data/spec/lutaml/model/collection_reader_liveness_spec.rb +516 -0
- data/spec/lutaml/model/conversion_caching_spec.rb +280 -0
- data/spec/lutaml/model/lazy_collection_spec.rb +28 -3
- data/spec/lutaml/model/parsed_model_mutation_spec.rb +486 -0
- data/spec/lutaml/model/phantom_value_spec.rb +799 -0
- data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +1 -0
- data/spec/lutaml/model/schema/xsd_schema_spec.rb +340 -695
- data/spec/lutaml/model/serialize_perf_guard_spec.rb +53 -1
- data/spec/lutaml/model/type_digit_facet_spec.rb +229 -0
- data/spec/lutaml/model/type_facet_restriction_spec.rb +667 -0
- data/spec/lutaml/model/type_white_space_facet_spec.rb +132 -0
- data/spec/lutaml/xml/schema/compiler_spec.rb +302 -0
- data/spec/lutaml/xml/schema/facet_round_trip_spec.rb +153 -0
- metadata +27 -1
|
@@ -24,10 +24,14 @@ module Lutaml
|
|
|
24
24
|
# @yield Block to apply individual rules
|
|
25
25
|
def apply_rules_in_order(root, model_instance, options, compiled_rules,
|
|
26
26
|
model_class, register_id)
|
|
27
|
-
element_order =
|
|
27
|
+
element_order, fallback_rules = reconciled_element_order(
|
|
28
|
+
model_instance, compiled_rules, options
|
|
29
|
+
)
|
|
28
30
|
mapping = model_class.mappings_for(:xml, register_id)
|
|
29
31
|
|
|
30
|
-
# Track index per
|
|
32
|
+
# Track index per compiled rule for collection attributes. Keyed
|
|
33
|
+
# by rule, not by element name: one rule can appear under several
|
|
34
|
+
# names once aliases are in play.
|
|
31
35
|
element_indices = ::Hash.new(0)
|
|
32
36
|
|
|
33
37
|
# Track text node index for content-mapped attribute access.
|
|
@@ -47,13 +51,21 @@ model_class, register_id)
|
|
|
47
51
|
# in element_order. When they match, mutations to the content array
|
|
48
52
|
# are reflected in serialization. When they don't match (e.g., CDATA
|
|
49
53
|
# creates extra whitespace text nodes), we fall back to element_order.
|
|
50
|
-
text_node_count = element_order.count
|
|
51
|
-
|
|
54
|
+
text_node_count = element_order.count do |o|
|
|
55
|
+
order_entry?(o) && o.type == "Text"
|
|
56
|
+
end
|
|
57
|
+
content_value = content_rule &&
|
|
58
|
+
model_instance&.public_send(content_rule.attribute_name)
|
|
52
59
|
use_content_index = content_rule && content_value.is_a?(Array) &&
|
|
53
60
|
content_value.length == text_node_count
|
|
54
61
|
|
|
55
62
|
# Iterate through element_order to preserve original sequence
|
|
56
63
|
element_order.each do |object|
|
|
64
|
+
# element_order is a plain accessor, so entries that are not
|
|
65
|
+
# order entries (no #type) exist in the wild. Nothing can be
|
|
66
|
+
# matched or emitted for them; skip instead of crashing.
|
|
67
|
+
next unless order_entry?(object)
|
|
68
|
+
|
|
57
69
|
result = process_element_order_item(
|
|
58
70
|
object, root, model_instance, options,
|
|
59
71
|
compiled_rules, mapping, element_indices,
|
|
@@ -66,9 +78,11 @@ model_class, register_id)
|
|
|
66
78
|
processed_text_nodes = true if result == :text_node
|
|
67
79
|
end
|
|
68
80
|
|
|
69
|
-
# Apply
|
|
70
|
-
|
|
71
|
-
|
|
81
|
+
# Apply the rules element_order does not cover: attributes,
|
|
82
|
+
# content and raw.
|
|
83
|
+
apply_remaining_rules(model_instance, options, compiled_rules,
|
|
84
|
+
mapping, processed_text_nodes,
|
|
85
|
+
fallback_rules) do |action, rule, value|
|
|
72
86
|
yield(action, rule, value) if block_given?
|
|
73
87
|
end
|
|
74
88
|
end
|
|
@@ -112,6 +126,7 @@ model_class, register_id)
|
|
|
112
126
|
# @param compiled_rules [Array<CompiledRule>] The compiled rules
|
|
113
127
|
# @return [CompiledRule, nil] The matching rule or nil
|
|
114
128
|
def find_rule_for_element(object, compiled_rules)
|
|
129
|
+
return nil unless order_entry?(object)
|
|
115
130
|
return nil unless object.type == "Element"
|
|
116
131
|
|
|
117
132
|
object_ns_uri = object.namespace_uri # nil if old element_order (backward compat)
|
|
@@ -125,6 +140,18 @@ model_class, register_id)
|
|
|
125
140
|
|
|
126
141
|
private
|
|
127
142
|
|
|
143
|
+
# Whether an element_order entry is something the ordered path can
|
|
144
|
+
# interpret. element_order is a plain public accessor, so arrays
|
|
145
|
+
# holding foreign objects (Integers, strings, ...) exist in the
|
|
146
|
+
# wild. Anything without #type cannot be matched to a rule or
|
|
147
|
+
# emitted, and must be skipped rather than crash serialization.
|
|
148
|
+
#
|
|
149
|
+
# @param object [Object] An entry from element_order
|
|
150
|
+
# @return [Boolean] true when the entry is a usable order entry
|
|
151
|
+
def order_entry?(object)
|
|
152
|
+
object.respond_to?(:type)
|
|
153
|
+
end
|
|
154
|
+
|
|
128
155
|
# Match by name AND namespace when object has a namespace URI.
|
|
129
156
|
# Falls back to name-only match for backward compatibility.
|
|
130
157
|
# Also checks uri_aliases for namespace alias support (e.g., ReqIF versions).
|
|
@@ -183,8 +210,8 @@ text_node_count = 0, use_content_index = false)
|
|
|
183
210
|
|
|
184
211
|
# For collection attributes, get the specific item at the tracked index
|
|
185
212
|
if rule.collection? && value.respond_to?(:each) && !value.is_a?(String)
|
|
186
|
-
process_collection_item(
|
|
187
|
-
|
|
213
|
+
process_collection_item(rule, value,
|
|
214
|
+
element_indices) do |action, r, v, xsi_nil_flag|
|
|
188
215
|
yield(action, r, v, xsi_nil_flag) if block_given?
|
|
189
216
|
end
|
|
190
217
|
elsif block_given?
|
|
@@ -265,20 +292,21 @@ text_node_count = 0, use_content_index = false)
|
|
|
265
292
|
|
|
266
293
|
# Process a single item from a collection
|
|
267
294
|
#
|
|
268
|
-
#
|
|
295
|
+
# Indices are keyed by the compiled rule, not the element name: a
|
|
296
|
+
# rule can appear in element_order under several names, because
|
|
297
|
+
# matches_name? accepts aliases and reconciliation inserts entries
|
|
298
|
+
# under the canonical name. One rule must mean one counter.
|
|
299
|
+
#
|
|
269
300
|
# @param rule [CompiledRule] The rule
|
|
270
301
|
# @param value [Array] The collection value
|
|
271
|
-
# @param
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def process_collection_item(_root, rule, value, object, element_indices,
|
|
275
|
-
_options)
|
|
276
|
-
index = element_indices[object.name]
|
|
302
|
+
# @param element_indices [Hash] Index tracker, keyed by rule
|
|
303
|
+
def process_collection_item(rule, value, element_indices)
|
|
304
|
+
index = element_indices[rule]
|
|
277
305
|
value_length = value.respond_to?(:length) ? value.length : value.size
|
|
278
306
|
|
|
279
307
|
if index < value_length
|
|
280
308
|
single_value = value[index]
|
|
281
|
-
element_indices[
|
|
309
|
+
element_indices[rule] += 1
|
|
282
310
|
|
|
283
311
|
# Skip individual nil items when value_map says nil is omitted
|
|
284
312
|
to_map = (rule.option(:value_map) || {})[:to] || {}
|
|
@@ -286,36 +314,49 @@ _options)
|
|
|
286
314
|
yield(:apply_single, rule, single_value)
|
|
287
315
|
end
|
|
288
316
|
elsif index.zero? && value_length.zero?
|
|
289
|
-
#
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
yield(:apply_single, rule, "") if block_given?
|
|
317
|
+
# 0 items means 0 child elements, unless value_map renders the
|
|
318
|
+
# empty collection as a nil or blank element.
|
|
319
|
+
case empty_collection_render_mode(rule)
|
|
320
|
+
when :nil then yield(:apply_single, rule, nil, true) if block_given?
|
|
321
|
+
when :blank then yield(:apply_single, rule, "") if block_given?
|
|
295
322
|
end
|
|
296
|
-
# For :empty and :omitted: skip — 0 items = 0 child elements
|
|
297
323
|
end
|
|
298
324
|
end
|
|
299
325
|
|
|
300
|
-
#
|
|
301
|
-
# rules not represented in element_order).
|
|
326
|
+
# What an empty collection serializes to, per value_map.
|
|
302
327
|
#
|
|
303
|
-
#
|
|
304
|
-
#
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
#
|
|
328
|
+
# @param rule [CompiledRule] The rule
|
|
329
|
+
# @return [Symbol, nil] :nil, :blank, or the omitting mode
|
|
330
|
+
def empty_collection_render_mode(rule)
|
|
331
|
+
((rule.option(:value_map) || {})[:to] || {})[:empty]
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Whether an empty collection still produces one child element.
|
|
335
|
+
#
|
|
336
|
+
# @param rule [CompiledRule] The rule
|
|
337
|
+
# @return [Boolean]
|
|
338
|
+
def empty_collection_renders_element?(rule)
|
|
339
|
+
%i[nil blank].include?(empty_collection_render_mode(rule))
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Apply the rules element_order does not represent: attributes,
|
|
343
|
+
# content and raw.
|
|
344
|
+
#
|
|
345
|
+
# Element rules are applied here only when reconciliation handed
|
|
346
|
+
# them over. It covers every element value it can place, and
|
|
347
|
+
# emitting one of those again would duplicate it; the few it cannot
|
|
348
|
+
# place (ambiguous names, custom-method rules with no attribute)
|
|
349
|
+
# arrive as fallback_rules and would otherwise be dropped.
|
|
310
350
|
#
|
|
311
|
-
# @param root [XmlElement] Root element
|
|
312
351
|
# @param model_instance [Object] The model instance
|
|
313
352
|
# @param options [Hash] Options
|
|
314
353
|
# @param compiled_rules [Array<CompiledRule>] The compiled rules
|
|
315
354
|
# @param mapping [Xml::Mapping] The mapping
|
|
316
355
|
# @param processed_text_nodes [Boolean] Whether text nodes were processed
|
|
317
|
-
|
|
318
|
-
|
|
356
|
+
# @param fallback_rules [Array<CompiledRule>] Element rules
|
|
357
|
+
# reconciliation could not place
|
|
358
|
+
def apply_remaining_rules(model_instance, options, compiled_rules,
|
|
359
|
+
mapping, processed_text_nodes, fallback_rules)
|
|
319
360
|
attr_order = model_instance.respond_to?(:attribute_order) &&
|
|
320
361
|
model_instance.attribute_order
|
|
321
362
|
|
|
@@ -326,11 +367,11 @@ compiled_rules, mapping, processed_text_nodes)
|
|
|
326
367
|
compiled_rules
|
|
327
368
|
end
|
|
328
369
|
|
|
329
|
-
emitted_counts = element_order_coverage(model_instance,
|
|
330
|
-
compiled_rules)
|
|
331
|
-
|
|
332
370
|
rules_to_apply.each do |rule|
|
|
333
371
|
mapping_type = rule.option(:mapping_type)
|
|
372
|
+
if mapping_type == :element && !fallback_rules.include?(rule)
|
|
373
|
+
next
|
|
374
|
+
end
|
|
334
375
|
|
|
335
376
|
# Skip content/raw if mixed or text nodes were processed
|
|
336
377
|
if %i[content raw].include?(mapping_type) &&
|
|
@@ -338,95 +379,12 @@ compiled_rules, mapping, processed_text_nodes)
|
|
|
338
379
|
next
|
|
339
380
|
end
|
|
340
381
|
|
|
341
|
-
if mapping_type == :element &&
|
|
342
|
-
element_rule_already_emitted?(rule, model_instance,
|
|
343
|
-
emitted_counts)
|
|
344
|
-
next
|
|
345
|
-
end
|
|
346
|
-
|
|
347
382
|
next unless valid_mapping?(rule, options)
|
|
348
383
|
|
|
349
384
|
yield(:apply_rule, rule, nil) if block_given?
|
|
350
385
|
end
|
|
351
386
|
end
|
|
352
387
|
|
|
353
|
-
# Count, per element-typed rule, how many entries in element_order
|
|
354
|
-
# already cover it. Returns a Hash keyed by CompiledRule identity.
|
|
355
|
-
#
|
|
356
|
-
# @param model_instance [Object] The model instance
|
|
357
|
-
# @param compiled_rules [Array<CompiledRule>] The compiled rules
|
|
358
|
-
# @return [Hash<CompiledRule, Integer>] Coverage counts
|
|
359
|
-
def element_order_coverage(model_instance, compiled_rules)
|
|
360
|
-
counts = ::Hash.new(0)
|
|
361
|
-
return counts unless model_instance.respond_to?(:element_order)
|
|
362
|
-
return counts unless (order = model_instance.element_order)
|
|
363
|
-
|
|
364
|
-
element_rules = compiled_rules.select do |r|
|
|
365
|
-
r.is_a?(::Lutaml::Model::CompiledRule) &&
|
|
366
|
-
r.option(:mapping_type) == :element
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
order.each do |object|
|
|
370
|
-
next unless object.type == "Element"
|
|
371
|
-
|
|
372
|
-
object_ns_uri = object.namespace_uri
|
|
373
|
-
matched = element_rules.find do |r|
|
|
374
|
-
matches_element_rule?(r, object.name, object_ns_uri)
|
|
375
|
-
end
|
|
376
|
-
counts[matched] += 1 if matched
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
counts
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
# Whether an element-typed rule has already been fully emitted
|
|
383
|
-
# via element_order (or should not be emitted at all by the safety
|
|
384
|
-
# net). Returns true when:
|
|
385
|
-
# - the model was not constructed via builder block
|
|
386
|
-
# (`@__order_tracking__` is nil/false): parsed models trust
|
|
387
|
-
# element_order as the complete source of truth, so the safety
|
|
388
|
-
# net must not second-guess it by emitting defaults/uninitialized
|
|
389
|
-
# values that the standard path would otherwise have skipped.
|
|
390
|
-
# - the standard skip logic says the value should be skipped
|
|
391
|
-
# (handles defaults, render_nil/render_empty, value_map, etc.)
|
|
392
|
-
# - or the rule was fully covered by element_order entries
|
|
393
|
-
#
|
|
394
|
-
# The safety net targets the bug class where a builder-block
|
|
395
|
-
# construction bypassed element_order tracking (e.g. via a
|
|
396
|
-
# mutation path that forgot to call record_mutation). After
|
|
397
|
-
# Option A, all setter/getter paths record into element_order,
|
|
398
|
-
# so this branch is defense-in-depth rather than the common
|
|
399
|
-
# path.
|
|
400
|
-
#
|
|
401
|
-
# @param rule [CompiledRule] The element rule
|
|
402
|
-
# @param model_instance [Object] The model instance
|
|
403
|
-
# @param emitted_counts [Hash<CompiledRule, Integer>] Coverage map
|
|
404
|
-
# @return [Boolean]
|
|
405
|
-
def element_rule_already_emitted?(rule, model_instance,
|
|
406
|
-
emitted_counts)
|
|
407
|
-
return true unless model_order_tracking_enabled?(model_instance)
|
|
408
|
-
|
|
409
|
-
value = extract_ordered_rule_value(rule, model_instance)
|
|
410
|
-
return true if should_skip_value?(value, rule, model_instance)
|
|
411
|
-
|
|
412
|
-
emitted = emitted_counts[rule]
|
|
413
|
-
if rule.collection?
|
|
414
|
-
value_length = value.respond_to?(:length) ? value.length : 0
|
|
415
|
-
emitted >= value_length
|
|
416
|
-
else
|
|
417
|
-
emitted.positive?
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
# Whether the model was constructed via a builder block and thus
|
|
422
|
-
# has order tracking enabled. Only such models are candidates for
|
|
423
|
-
# the safety net; parsed models trust element_order as-is.
|
|
424
|
-
def model_order_tracking_enabled?(model_instance)
|
|
425
|
-
return false unless model_instance.respond_to?(:order_tracking_enabled?)
|
|
426
|
-
|
|
427
|
-
model_instance.order_tracking_enabled?
|
|
428
|
-
end
|
|
429
|
-
|
|
430
388
|
# Sort compiled rules so attribute rules follow the captured attribute_order.
|
|
431
389
|
# Non-attribute rules (content, raw) maintain their original position.
|
|
432
390
|
#
|
|
@@ -318,10 +318,12 @@ register_id, register, attr_name, custom_methods_value)
|
|
|
318
318
|
value_transformer = build_value_transformer(mapping_rule, attr)
|
|
319
319
|
value_map = mapping_rule.raw_value_map
|
|
320
320
|
rule_name = mapping_rule.multiple_mappings? ? mapping_rule.name.first : mapping_rule.name
|
|
321
|
+
alias_names = mapping_rule.multiple_mappings? ? mapping_rule.name[1..].map(&:to_s) : nil
|
|
321
322
|
|
|
322
323
|
::Lutaml::Model::CompiledRule.new(
|
|
323
324
|
attribute_name: attr_name,
|
|
324
325
|
serialized_name: rule_name.to_s,
|
|
326
|
+
alias_names: alias_names,
|
|
325
327
|
attribute_type: attr_type,
|
|
326
328
|
child_transformation: child_transformation,
|
|
327
329
|
value_transformer: value_transformer,
|
|
@@ -14,11 +14,13 @@ module Lutaml
|
|
|
14
14
|
# - TransformationSupport::ValueSerializer: Serializes values to XML strings
|
|
15
15
|
# - TransformationSupport::ElementBuilder: Creates XML elements from values
|
|
16
16
|
# - TransformationSupport::OrderedApplier: Applies rules in element order for round-trip
|
|
17
|
+
# - TransformationSupport::OrderReconciler: Aligns element_order with current values
|
|
17
18
|
# - TransformationSupport::RuleApplier: Dispatches rule application to handlers
|
|
18
19
|
class Transformation < Lutaml::Model::Transformation
|
|
19
20
|
include TransformationSupport::RuleCompiler
|
|
20
21
|
include TransformationSupport::RuleApplier
|
|
21
22
|
include TransformationSupport::OrderedApplier
|
|
23
|
+
include TransformationSupport::OrderReconciler
|
|
22
24
|
|
|
23
25
|
# Transform a model instance into XmlElement tree
|
|
24
26
|
#
|
|
@@ -8,6 +8,7 @@ module Lutaml
|
|
|
8
8
|
autoload :ValueSerializer, "#{__dir__}/transformation/value_serializer"
|
|
9
9
|
autoload :ElementBuilder, "#{__dir__}/transformation/element_builder"
|
|
10
10
|
autoload :OrderedApplier, "#{__dir__}/transformation/ordered_applier"
|
|
11
|
+
autoload :OrderReconciler, "#{__dir__}/transformation/order_reconciler"
|
|
11
12
|
autoload :RuleApplier, "#{__dir__}/transformation/rule_applier"
|
|
12
13
|
end
|
|
13
14
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
3
|
+
<xsd:simpleType name="ST_Code">
|
|
4
|
+
<xsd:restriction base="xsd:string">
|
|
5
|
+
<xsd:minLength value="2" />
|
|
6
|
+
<xsd:maxLength value="8" />
|
|
7
|
+
</xsd:restriction>
|
|
8
|
+
</xsd:simpleType>
|
|
9
|
+
<xsd:simpleType name="ST_Ratio">
|
|
10
|
+
<xsd:restriction base="xsd:integer">
|
|
11
|
+
<xsd:minExclusive value="0" />
|
|
12
|
+
<xsd:maxExclusive value="100" />
|
|
13
|
+
</xsd:restriction>
|
|
14
|
+
</xsd:simpleType>
|
|
15
|
+
<xsd:simpleType name="ST_Price">
|
|
16
|
+
<xsd:restriction base="xsd:decimal">
|
|
17
|
+
<xsd:totalDigits value="5" />
|
|
18
|
+
<xsd:fractionDigits value="2" />
|
|
19
|
+
</xsd:restriction>
|
|
20
|
+
</xsd:simpleType>
|
|
21
|
+
<xsd:simpleType name="ST_Label">
|
|
22
|
+
<xsd:restriction base="xsd:string">
|
|
23
|
+
<xsd:whiteSpace value="collapse" />
|
|
24
|
+
</xsd:restriction>
|
|
25
|
+
</xsd:simpleType>
|
|
26
|
+
<xsd:simpleType name="ST_Moment">
|
|
27
|
+
<xsd:restriction base="xsd:dateTime">
|
|
28
|
+
<xsd:minInclusive value="2020-01-01T00:00:00" />
|
|
29
|
+
<xsd:maxInclusive value="2020-12-31T23:59:59" />
|
|
30
|
+
</xsd:restriction>
|
|
31
|
+
</xsd:simpleType>
|
|
32
|
+
<xsd:simpleType name="ST_Flag">
|
|
33
|
+
<xsd:restriction base="xsd:boolean">
|
|
34
|
+
<xsd:enumeration value="true" />
|
|
35
|
+
</xsd:restriction>
|
|
36
|
+
</xsd:simpleType>
|
|
37
|
+
<xsd:complexType name="CT_Item">
|
|
38
|
+
<xsd:attribute name="code" type="ST_Code" />
|
|
39
|
+
<xsd:attribute name="ratio" type="ST_Ratio" />
|
|
40
|
+
<xsd:attribute name="price" type="ST_Price" />
|
|
41
|
+
<xsd:attribute name="label" type="ST_Label" />
|
|
42
|
+
<xsd:attribute name="moment" type="ST_Moment" />
|
|
43
|
+
<xsd:attribute name="flag" type="ST_Flag" />
|
|
44
|
+
</xsd:complexType>
|
|
45
|
+
<xsd:element name="Item" type="CT_Item" />
|
|
46
|
+
</xsd:schema>
|