lutaml-model 0.7.3 → 0.7.5
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/.envrc +1 -0
- data/.github/workflows/release.yml +3 -0
- data/.gitignore +6 -1
- data/.irbrc +1 -0
- data/.pryrc +1 -0
- data/.rubocop_todo.yml +25 -52
- data/README.adoc +2177 -192
- data/docs/custom_registers.adoc +228 -0
- data/docs/schema_generation.adoc +898 -0
- data/docs/schema_import.adoc +364 -0
- data/flake.lock +114 -0
- data/flake.nix +103 -0
- data/lib/lutaml/model/attribute.rb +230 -94
- data/lib/lutaml/model/choice.rb +30 -0
- data/lib/lutaml/model/collection.rb +194 -0
- data/lib/lutaml/model/comparable_model.rb +3 -3
- data/lib/lutaml/model/config.rb +26 -3
- data/lib/lutaml/model/constants.rb +2 -0
- data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
- data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
- data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
- data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
- data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
- data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
- data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
- data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
- data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
- data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
- data/lib/lutaml/model/error.rb +10 -0
- data/lib/lutaml/model/errors.rb +36 -0
- data/lib/lutaml/model/format_registry.rb +5 -2
- data/lib/lutaml/model/global_register.rb +41 -0
- data/lib/lutaml/model/{hash.rb → hash_adapter.rb} +5 -5
- data/lib/lutaml/model/jsonl/document.rb +14 -0
- data/lib/lutaml/model/jsonl/mapping.rb +19 -0
- data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
- data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
- data/lib/lutaml/model/jsonl/transform.rb +19 -0
- data/lib/lutaml/model/jsonl.rb +21 -0
- data/lib/lutaml/model/key_value_document.rb +3 -2
- data/lib/lutaml/model/mapping/key_value_mapping.rb +64 -4
- data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +4 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +8 -3
- data/lib/lutaml/model/register.rb +105 -0
- data/lib/lutaml/model/registrable.rb +6 -0
- data/lib/lutaml/model/schema/base_schema.rb +64 -0
- data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
- data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
- data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
- data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
- data/lib/lutaml/model/schema/generator/definition.rb +53 -0
- data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
- data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
- data/lib/lutaml/model/schema/generator/property.rb +110 -0
- data/lib/lutaml/model/schema/generator/ref.rb +24 -0
- data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
- data/lib/lutaml/model/schema/json_schema.rb +42 -49
- data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
- data/lib/lutaml/model/schema/renderer.rb +36 -0
- data/lib/lutaml/model/schema/shared_methods.rb +24 -0
- data/lib/lutaml/model/schema/templates/model.erb +9 -0
- data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
- data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
- data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
- data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
- data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
- data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
- data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
- data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
- data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
- data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
- data/lib/lutaml/model/schema.rb +1 -0
- data/lib/lutaml/model/sequence.rb +60 -30
- data/lib/lutaml/model/serialize.rb +175 -53
- data/lib/lutaml/model/services/base.rb +11 -0
- data/lib/lutaml/model/services/logger.rb +2 -2
- data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
- data/lib/lutaml/model/services/type/validator/number.rb +25 -0
- data/lib/lutaml/model/services/type/validator/string.rb +52 -0
- data/lib/lutaml/model/services/type/validator.rb +43 -0
- data/lib/lutaml/model/services/validator.rb +145 -0
- data/lib/lutaml/model/services.rb +3 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +60 -50
- data/lib/lutaml/model/transform/xml_transform.rb +46 -57
- data/lib/lutaml/model/transform.rb +22 -8
- data/lib/lutaml/model/type/boolean.rb +1 -1
- data/lib/lutaml/model/type/date.rb +1 -1
- data/lib/lutaml/model/type/date_time.rb +1 -1
- data/lib/lutaml/model/type/decimal.rb +11 -9
- data/lib/lutaml/model/type/float.rb +2 -1
- data/lib/lutaml/model/type/integer.rb +24 -21
- data/lib/lutaml/model/type/string.rb +4 -2
- data/lib/lutaml/model/type/time.rb +1 -1
- data/lib/lutaml/model/type/time_without_date.rb +1 -1
- data/lib/lutaml/model/type/value.rb +5 -1
- data/lib/lutaml/model/type.rb +5 -2
- data/lib/lutaml/model/utils.rb +30 -8
- data/lib/lutaml/model/validation.rb +6 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml/document.rb +37 -19
- data/lib/lutaml/model/xml/mapping.rb +74 -13
- data/lib/lutaml/model/xml/mapping_rule.rb +10 -2
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +5 -3
- data/lib/lutaml/model/xml/oga/element.rb +4 -1
- data/lib/lutaml/model/xml/oga_adapter.rb +4 -3
- data/lib/lutaml/model/xml/ox_adapter.rb +20 -6
- data/lib/lutaml/model/xml/xml_element.rb +3 -28
- data/lib/lutaml/model/xml_adapter/element.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +1 -1
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -1
- data/lib/lutaml/model/yamls/document.rb +14 -0
- data/lib/lutaml/model/yamls/mapping.rb +19 -0
- data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
- data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
- data/lib/lutaml/model/yamls/transform.rb +19 -0
- data/lib/lutaml/model/yamls.rb +21 -0
- data/lib/lutaml/model.rb +7 -31
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -5
- data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
- data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
- data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
- data/spec/fixtures/xml/product_catalog.xsd +151 -0
- data/spec/fixtures/xml/specifications_schema.xsd +38 -0
- data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
- data/spec/lutaml/model/attribute_spec.rb +41 -44
- data/spec/lutaml/model/choice_spec.rb +44 -0
- data/spec/lutaml/model/custom_collection_spec.rb +830 -0
- data/spec/lutaml/model/custom_model_spec.rb +15 -3
- data/spec/lutaml/model/defaults_spec.rb +5 -1
- data/spec/lutaml/model/global_register_spec.rb +108 -0
- data/spec/lutaml/model/group_spec.rb +9 -3
- data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
- data/spec/lutaml/model/jsonl_spec.rb +229 -0
- data/spec/lutaml/model/multiple_mapping_spec.rb +1 -1
- data/spec/lutaml/model/register/key_value_spec.rb +275 -0
- data/spec/lutaml/model/register/xml_spec.rb +187 -0
- data/spec/lutaml/model/register_spec.rb +147 -0
- data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
- data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
- data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
- data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
- data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
- data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
- data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
- data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
- data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
- data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
- data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
- data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
- data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
- data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
- data/spec/lutaml/model/sequence_spec.rb +36 -0
- data/spec/lutaml/model/serializable_spec.rb +31 -0
- data/spec/lutaml/model/type_spec.rb +8 -4
- data/spec/lutaml/model/utils_spec.rb +3 -3
- data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
- data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
- data/spec/lutaml/model/xml_adapter_spec.rb +24 -0
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +11 -4
- data/spec/lutaml/model/xml_mapping_spec.rb +1 -1
- data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
- data/spec/lutaml/model/yamls_spec.rb +294 -0
- data/spec/spec_helper.rb +1 -0
- metadata +105 -9
- data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
- /data/lib/lutaml/model/{hash → hash_adapter}/document.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/mapping.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/mapping_rule.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/standard_adapter.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/transform.rb +0 -0
data/lib/lutaml/model.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "moxml"
|
4
4
|
require_relative "model/uninitialized_class"
|
5
|
+
require_relative "model/errors"
|
5
6
|
require_relative "model/services"
|
6
7
|
require_relative "model/version"
|
7
8
|
require_relative "model/type"
|
@@ -11,13 +12,17 @@ require_relative "model/xml_adapter"
|
|
11
12
|
require_relative "model/error"
|
12
13
|
require_relative "model/constants"
|
13
14
|
require_relative "model/config"
|
15
|
+
require_relative "model/global_register"
|
16
|
+
require_relative "model/register"
|
14
17
|
require_relative "model/format_registry"
|
15
|
-
|
18
|
+
require_relative "model/collection"
|
16
19
|
require_relative "model/key_value_document"
|
17
20
|
require_relative "model/yaml"
|
21
|
+
require_relative "model/yamls"
|
18
22
|
require_relative "model/json"
|
23
|
+
require_relative "model/jsonl"
|
19
24
|
require_relative "model/toml"
|
20
|
-
require_relative "model/
|
25
|
+
require_relative "model/hash_adapter"
|
21
26
|
require_relative "model/xml"
|
22
27
|
|
23
28
|
module Lutaml
|
@@ -30,34 +35,5 @@ module Lutaml
|
|
30
35
|
|
31
36
|
class BaseModel < Serializable
|
32
37
|
end
|
33
|
-
|
34
|
-
# Register default adapters
|
35
|
-
# Lutaml::Model::FormatRegistry.register(
|
36
|
-
# :json,
|
37
|
-
# mapping_class: KeyValueMapping,
|
38
|
-
# adapter_class: JsonAdapter::StandardJsonAdapter,
|
39
|
-
# transformer: Lutaml::Model::KeyValueTransform,
|
40
|
-
# )
|
41
|
-
|
42
|
-
# Lutaml::Model::FormatRegistry.register(
|
43
|
-
# :yaml,
|
44
|
-
# mapping_class: KeyValueMapping,
|
45
|
-
# adapter_class: YamlAdapter::StandardYamlAdapter,
|
46
|
-
# transformer: Lutaml::Model::KeyValueTransform,
|
47
|
-
# )
|
48
|
-
|
49
|
-
# Lutaml::Model::FormatRegistry.register(
|
50
|
-
# :toml,
|
51
|
-
# mapping_class: KeyValueMapping,
|
52
|
-
# adapter_class: nil,
|
53
|
-
# transformer: Lutaml::Model::KeyValueTransform,
|
54
|
-
# )
|
55
|
-
|
56
|
-
# Lutaml::Model::FormatRegistry.register(
|
57
|
-
# :xml,
|
58
|
-
# mapping_class: XmlMapping,
|
59
|
-
# adapter_class: nil,
|
60
|
-
# transformer: Lutaml::Model::XmlTransform,
|
61
|
-
# )
|
62
38
|
end
|
63
39
|
end
|
@@ -5,7 +5,7 @@ require "lutaml/model/xml/oga_adapter"
|
|
5
5
|
|
6
6
|
RSpec.describe "LutaML Model Performance" do
|
7
7
|
after do
|
8
|
-
Lutaml::Model::Config.
|
8
|
+
Lutaml::Model::Config.xml_adapter_type = :nokogiri
|
9
9
|
end
|
10
10
|
|
11
11
|
let(:large_xml) do
|
@@ -26,7 +26,6 @@ RSpec.describe "LutaML Model Performance" do
|
|
26
26
|
map_attribute "id", to: :id
|
27
27
|
map_element "value", to: :value
|
28
28
|
map_element "name", to: :name
|
29
|
-
map_element "value", to: :value
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
@@ -48,12 +47,12 @@ RSpec.describe "LutaML Model Performance" do
|
|
48
47
|
end
|
49
48
|
|
50
49
|
x.report("Ox Adapter") do
|
51
|
-
Lutaml::Model::Config.
|
50
|
+
Lutaml::Model::Config.xml_adapter_type = :ox
|
52
51
|
Deserializer.from_xml(large_xml)
|
53
52
|
end
|
54
53
|
|
55
54
|
x.report("Oga Adapter") do
|
56
|
-
Lutaml::Model::Config.
|
55
|
+
Lutaml::Model::Config.xml_adapter_type = :oga
|
57
56
|
Deserializer.from_xml(large_xml)
|
58
57
|
end
|
59
58
|
|
@@ -62,7 +61,7 @@ RSpec.describe "LutaML Model Performance" do
|
|
62
61
|
|
63
62
|
thresholds = {
|
64
63
|
"Nokogiri Adapter" => 3,
|
65
|
-
"Ox Adapter" =>
|
64
|
+
"Ox Adapter" => 6,
|
66
65
|
"Oga Adapter" => 3,
|
67
66
|
}
|
68
67
|
|
@@ -0,0 +1,134 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
3
|
+
xmlns:test="http://www.example.com/test" targetNamespace="http://www.example.com/test" elementFormDefault="qualified">
|
4
|
+
|
5
|
+
<!-- Advanced Simple Types -->
|
6
|
+
<xs:simpleType name="StringOrInteger">
|
7
|
+
<xs:union memberTypes="xs:string xs:integer"/>
|
8
|
+
</xs:simpleType>
|
9
|
+
|
10
|
+
<xs:simpleType name="ColorList">
|
11
|
+
<xs:list itemType="xs:string"/>
|
12
|
+
</xs:simpleType>
|
13
|
+
|
14
|
+
<xs:simpleType name="RestrictedString">
|
15
|
+
<xs:restriction base="xs:string">
|
16
|
+
<xs:pattern value="[A-Z][a-z]+"/>
|
17
|
+
<xs:minLength value="2"/>
|
18
|
+
<xs:maxLength value="20"/>
|
19
|
+
</xs:restriction>
|
20
|
+
</xs:simpleType>
|
21
|
+
|
22
|
+
<!-- Base Complex Types -->
|
23
|
+
<xs:complexType name="BaseType">
|
24
|
+
<xs:sequence>
|
25
|
+
<xs:element name="id" type="xs:integer"/>
|
26
|
+
<xs:element name="name" type="test:RestrictedString"/>
|
27
|
+
</xs:sequence>
|
28
|
+
<xs:attribute name="version" type="xs:string" use="optional"/>
|
29
|
+
</xs:complexType>
|
30
|
+
|
31
|
+
<!-- Extension using complexContent -->
|
32
|
+
<xs:complexType name="ExtendedType">
|
33
|
+
<xs:complexContent>
|
34
|
+
<xs:extension base="test:BaseType">
|
35
|
+
<xs:sequence>
|
36
|
+
<xs:element name="description" type="xs:string"/>
|
37
|
+
<xs:element name="tags" type="test:ColorList"/>
|
38
|
+
</xs:sequence>
|
39
|
+
<xs:attribute name="priority" type="xs:integer" use="required"/>
|
40
|
+
</xs:extension>
|
41
|
+
</xs:complexContent>
|
42
|
+
</xs:complexType>
|
43
|
+
|
44
|
+
<!-- Restriction using complexContent -->
|
45
|
+
<xs:complexType name="RestrictedType">
|
46
|
+
<xs:complexContent>
|
47
|
+
<xs:restriction base="test:BaseType">
|
48
|
+
<xs:sequence>
|
49
|
+
<xs:element name="id" type="xs:positiveInteger"/>
|
50
|
+
<xs:element name="name" type="test:RestrictedString"/>
|
51
|
+
</xs:sequence>
|
52
|
+
<xs:attribute name="version" type="xs:string" fixed="1.0"/>
|
53
|
+
</xs:restriction>
|
54
|
+
</xs:complexContent>
|
55
|
+
</xs:complexType>
|
56
|
+
|
57
|
+
<!-- Mixed Content Type -->
|
58
|
+
<xs:complexType name="MixedContentType" mixed="true">
|
59
|
+
<xs:sequence>
|
60
|
+
<xs:element name="bold" type="xs:string" minOccurs="0"/>
|
61
|
+
<xs:element name="italic" type="xs:string" minOccurs="0"/>
|
62
|
+
</xs:sequence>
|
63
|
+
</xs:complexType>
|
64
|
+
|
65
|
+
<!-- Group Definitions -->
|
66
|
+
<xs:group name="CommonElements">
|
67
|
+
<xs:sequence>
|
68
|
+
<xs:element name="created" type="xs:dateTime"/>
|
69
|
+
<xs:element name="modified" type="xs:dateTime"/>
|
70
|
+
</xs:sequence>
|
71
|
+
</xs:group>
|
72
|
+
|
73
|
+
<!-- Attribute Group -->
|
74
|
+
<xs:attributeGroup name="CommonAttributes">
|
75
|
+
<xs:attribute name="id" type="xs:ID" use="required"/>
|
76
|
+
<xs:attribute name="type" type="xs:string" use="optional"/>
|
77
|
+
</xs:attributeGroup>
|
78
|
+
|
79
|
+
<!-- Complex Type with Groups -->
|
80
|
+
<xs:complexType name="GroupedType">
|
81
|
+
<xs:sequence>
|
82
|
+
<xs:group ref="test:CommonElements"/>
|
83
|
+
<xs:element name="content" type="xs:string"/>
|
84
|
+
</xs:sequence>
|
85
|
+
<xs:attributeGroup ref="test:CommonAttributes"/>
|
86
|
+
</xs:complexType>
|
87
|
+
|
88
|
+
<!-- Choice Element -->
|
89
|
+
<xs:complexType name="ChoiceType">
|
90
|
+
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
91
|
+
<xs:element name="text" type="xs:string"/>
|
92
|
+
<xs:element name="number" type="xs:integer"/>
|
93
|
+
<xs:element name="date" type="xs:date"/>
|
94
|
+
</xs:choice>
|
95
|
+
</xs:complexType>
|
96
|
+
|
97
|
+
<!-- All Element -->
|
98
|
+
<xs:complexType name="AllType">
|
99
|
+
<xs:all>
|
100
|
+
<xs:element name="first" type="xs:string"/>
|
101
|
+
<xs:element name="second" type="xs:integer"/>
|
102
|
+
<xs:element name="third" type="xs:boolean"/>
|
103
|
+
</xs:all>
|
104
|
+
</xs:complexType>
|
105
|
+
|
106
|
+
<!-- Root Elements -->
|
107
|
+
<xs:element name="TestDocument">
|
108
|
+
<xs:complexType>
|
109
|
+
<xs:sequence>
|
110
|
+
<xs:element name="base" type="test:BaseType"/>
|
111
|
+
<xs:element name="extended" type="test:ExtendedType"/>
|
112
|
+
<xs:element name="restricted" type="test:RestrictedType"/>
|
113
|
+
<xs:element name="mixed" type="test:MixedContentType"/>
|
114
|
+
<xs:element name="grouped" type="test:GroupedType"/>
|
115
|
+
<xs:element name="choice" type="test:ChoiceType"/>
|
116
|
+
<xs:element name="all" type="test:AllType"/>
|
117
|
+
</xs:sequence>
|
118
|
+
</xs:complexType>
|
119
|
+
</xs:element>
|
120
|
+
|
121
|
+
<!-- Alternative Root with Substitution Group -->
|
122
|
+
<xs:element name="AlternativeDocument" substitutionGroup="test:TestDocument">
|
123
|
+
<xs:complexType>
|
124
|
+
<xs:complexContent>
|
125
|
+
<xs:extension base="test:TestDocument">
|
126
|
+
<xs:sequence>
|
127
|
+
<xs:element name="additional" type="xs:string"/>
|
128
|
+
</xs:sequence>
|
129
|
+
</xs:extension>
|
130
|
+
</xs:complexContent>
|
131
|
+
</xs:complexType>
|
132
|
+
</xs:element>
|
133
|
+
|
134
|
+
</xs:schema>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<ProductCatalog version="1.0" lastUpdated="2024-03-20T10:00:00+00:00">
|
3
|
+
<categories>
|
4
|
+
<category>
|
5
|
+
<id>1</id>
|
6
|
+
<name>Electronics</name>
|
7
|
+
<parentCategory>0</parentCategory>
|
8
|
+
<description>Electronic devices and accessories</description>
|
9
|
+
</category>
|
10
|
+
<category>
|
11
|
+
<id>2</id>
|
12
|
+
<name>Smartphones</name>
|
13
|
+
<parentCategory>1</parentCategory>
|
14
|
+
<description>Mobile phones and accessories</description>
|
15
|
+
</category>
|
16
|
+
<category>
|
17
|
+
<id>3</id>
|
18
|
+
<name>Laptops</name>
|
19
|
+
<parentCategory>1</parentCategory>
|
20
|
+
<description>Portable computers and accessories</description>
|
21
|
+
</category>
|
22
|
+
</categories>
|
23
|
+
<products>
|
24
|
+
<product id="1" status="active">
|
25
|
+
<sku>PHN12345678</sku>
|
26
|
+
<name>Smartphone Pro</name>
|
27
|
+
<description>Latest smartphone model</description>
|
28
|
+
<category>
|
29
|
+
<id>2</id>
|
30
|
+
<name>Smartphones</name>
|
31
|
+
<parentCategory>1</parentCategory>
|
32
|
+
<description>Mobile phones and accessories</description>
|
33
|
+
</category>
|
34
|
+
<price>99.99</price>
|
35
|
+
<salePrice>89.99</salePrice>
|
36
|
+
<inventory>50</inventory>
|
37
|
+
<dimensions>
|
38
|
+
<length>14.5</length>
|
39
|
+
<width>7.0</width>
|
40
|
+
<height>0.7</height>
|
41
|
+
<weight>0.18</weight>
|
42
|
+
</dimensions>
|
43
|
+
<rating>4.5</rating>
|
44
|
+
<features>
|
45
|
+
<feature>5G Connectivity</feature>
|
46
|
+
<feature>128GB Storage</feature>
|
47
|
+
<feature>Triple Camera System</feature>
|
48
|
+
</features>
|
49
|
+
<images>
|
50
|
+
<image url="https://example.com/images/smartphone-pro-1.jpg" alt="Smartphone Pro Front View" isPrimary="true"/>
|
51
|
+
<image url="https://example.com/images/smartphone-pro-2.jpg" alt="Smartphone Pro Back View"/>
|
52
|
+
</images>
|
53
|
+
</product>
|
54
|
+
</products>
|
55
|
+
</ProductCatalog>
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<ProductCatalog version="1.0" lastUpdated="2024-03-20T10:00:00+00:00">
|
3
|
+
<categories>
|
4
|
+
<category>
|
5
|
+
<id>1</id>
|
6
|
+
<name>Electronics</name>
|
7
|
+
<parentCategory>0</parentCategory>
|
8
|
+
<description>Electronic devices and accessories</description>
|
9
|
+
</category>
|
10
|
+
</categories>
|
11
|
+
<products>
|
12
|
+
<product id="1" status="active">
|
13
|
+
<sku>ABC12345678</sku>
|
14
|
+
<name>Smartphone X</name>
|
15
|
+
<description>A high-end smartphone with advanced features</description>
|
16
|
+
<category>
|
17
|
+
<id>1</id>
|
18
|
+
<name>Electronics</name>
|
19
|
+
<parentCategory>0</parentCategory>
|
20
|
+
<description>Electronic devices and accessories</description>
|
21
|
+
</category>
|
22
|
+
<price>99.99</price>
|
23
|
+
<salePrice>89.99</salePrice>
|
24
|
+
<inventory>100</inventory>
|
25
|
+
<dimensions>
|
26
|
+
<length>15.0</length>
|
27
|
+
<width>7.5</width>
|
28
|
+
<height>0.8</height>
|
29
|
+
<weight>0.2</weight>
|
30
|
+
</dimensions>
|
31
|
+
<rating>4.5</rating>
|
32
|
+
<features>
|
33
|
+
<feature>5G Connectivity</feature>
|
34
|
+
<feature>128GB Storage</feature>
|
35
|
+
<feature>Triple Camera System</feature>
|
36
|
+
</features>
|
37
|
+
<images>
|
38
|
+
<image url="https://example.com/images/smartphone-x-1.jpg" alt="Smartphone X Front View" isPrimary="true"/>
|
39
|
+
<image url="https://example.com/images/smartphone-x-2.jpg" alt="Smartphone X Back View"/>
|
40
|
+
</images>
|
41
|
+
</product>
|
42
|
+
</products>
|
43
|
+
</ProductCatalog>
|
@@ -0,0 +1,151 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
3
|
+
xmlns:cat="http://www.example.com/catalog" targetNamespace="http://www.example.com/catalog" elementFormDefault="qualified">
|
4
|
+
|
5
|
+
<!-- Attributes -->
|
6
|
+
<xs:attribute name="id" type="xs:integer" use="required"/>
|
7
|
+
|
8
|
+
<!-- Simple Types -->
|
9
|
+
<xs:simpleType name="SKUType">
|
10
|
+
<xs:annotation>
|
11
|
+
<xs:documentation>Stock Keeping Unit - must be 8-12 alphanumeric characters</xs:documentation>
|
12
|
+
</xs:annotation>
|
13
|
+
<xs:restriction base="xs:string">
|
14
|
+
<xs:pattern value="[A-Z0-9]{8,12}"/>
|
15
|
+
</xs:restriction>
|
16
|
+
</xs:simpleType>
|
17
|
+
|
18
|
+
<xs:simpleType name="PriceType">
|
19
|
+
<xs:annotation>
|
20
|
+
<xs:documentation>Price with 2 decimal places, must be positive</xs:documentation>
|
21
|
+
</xs:annotation>
|
22
|
+
<xs:restriction base="xs:decimal">
|
23
|
+
<xs:maxExclusive value="101.00"/>
|
24
|
+
<xs:minInclusive value="0.01"/>
|
25
|
+
</xs:restriction>
|
26
|
+
</xs:simpleType>
|
27
|
+
|
28
|
+
<xs:simpleType name="QuantityTypes">
|
29
|
+
<xs:annotation>
|
30
|
+
<xs:documentation>Quantity must be non-negative</xs:documentation>
|
31
|
+
</xs:annotation>
|
32
|
+
<xs:restriction base="xs:integer">
|
33
|
+
<xs:minInclusive value="0"/>
|
34
|
+
</xs:restriction>
|
35
|
+
</xs:simpleType>
|
36
|
+
|
37
|
+
<xs:simpleType name="RatingType">
|
38
|
+
<xs:annotation>
|
39
|
+
<xs:documentation>Product rating from 1 to 5</xs:documentation>
|
40
|
+
</xs:annotation>
|
41
|
+
<xs:restriction base="xs:decimal">
|
42
|
+
<xs:minInclusive value="1"/>
|
43
|
+
<xs:maxInclusive value="5"/>
|
44
|
+
</xs:restriction>
|
45
|
+
</xs:simpleType>
|
46
|
+
|
47
|
+
<!-- Complex Types -->
|
48
|
+
<xs:complexType name="DimensionsType">
|
49
|
+
<xs:sequence>
|
50
|
+
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
51
|
+
<xs:choice maxOccurs="unbounded">
|
52
|
+
<xs:element name="length" type="xs:decimal">
|
53
|
+
<xs:annotation>
|
54
|
+
<xs:documentation>Length in centimeters</xs:documentation>
|
55
|
+
</xs:annotation>
|
56
|
+
</xs:element>
|
57
|
+
</xs:choice>
|
58
|
+
<xs:element name="width" type="xs:decimal">
|
59
|
+
<xs:annotation>
|
60
|
+
<xs:documentation>Width in centimeters</xs:documentation>
|
61
|
+
</xs:annotation>
|
62
|
+
</xs:element>
|
63
|
+
</xs:choice>
|
64
|
+
<xs:element name="height" type="xs:decimal">
|
65
|
+
<xs:annotation>
|
66
|
+
<xs:documentation>Height in centimeters</xs:documentation>
|
67
|
+
</xs:annotation>
|
68
|
+
</xs:element>
|
69
|
+
<xs:element name="weight" type="xs:decimal">
|
70
|
+
<xs:annotation>
|
71
|
+
<xs:documentation>Weight in kilograms</xs:documentation>
|
72
|
+
</xs:annotation>
|
73
|
+
</xs:element>
|
74
|
+
</xs:sequence>
|
75
|
+
</xs:complexType>
|
76
|
+
|
77
|
+
<xs:complexType name="CategoryType">
|
78
|
+
<xs:sequence>
|
79
|
+
<xs:element name="id" type="xs:integer"/>
|
80
|
+
<xs:element name="name" type="xs:string"/>
|
81
|
+
<xs:element name="parentCategory" type="xs:integer" minOccurs="0"/>
|
82
|
+
<xs:element name="description" type="xs:string" minOccurs="0"/>
|
83
|
+
</xs:sequence>
|
84
|
+
</xs:complexType>
|
85
|
+
|
86
|
+
<xs:complexType name="ProductType">
|
87
|
+
<xs:sequence>
|
88
|
+
<xs:element name="sku" type="cat:SKUType"/>
|
89
|
+
<xs:element name="name" type="xs:string"/>
|
90
|
+
<xs:element name="description" type="xs:string"/>
|
91
|
+
<xs:element name="category" type="cat:CategoryType"/>
|
92
|
+
<xs:element name="price" type="cat:PriceType"/>
|
93
|
+
<xs:element name="salePrice" type="cat:PriceType" minOccurs="0"/>
|
94
|
+
<xs:element name="inventory" type="cat:QuantityTypes"/>
|
95
|
+
<xs:element name="dimensions" type="cat:DimensionsType"/>
|
96
|
+
<xs:element name="rating" type="cat:RatingType" minOccurs="0"/>
|
97
|
+
<xs:element name="features" minOccurs="0">
|
98
|
+
<xs:complexType>
|
99
|
+
<xs:sequence>
|
100
|
+
<xs:element name="feature" type="xs:string" maxOccurs="unbounded"/>
|
101
|
+
</xs:sequence>
|
102
|
+
</xs:complexType>
|
103
|
+
</xs:element>
|
104
|
+
<xs:element name="images" minOccurs="0">
|
105
|
+
<xs:complexType>
|
106
|
+
<xs:sequence>
|
107
|
+
<xs:element name="image" maxOccurs="unbounded">
|
108
|
+
<xs:complexType>
|
109
|
+
<xs:attribute name="url" type="xs:anyURI" use="required"/>
|
110
|
+
<xs:attribute name="alt" type="xs:string" use="required"/>
|
111
|
+
<xs:attribute name="isPrimary" type="xs:boolean" default="false"/>
|
112
|
+
</xs:complexType>
|
113
|
+
</xs:element>
|
114
|
+
</xs:sequence>
|
115
|
+
</xs:complexType>
|
116
|
+
</xs:element>
|
117
|
+
</xs:sequence>
|
118
|
+
<!-- using 'ref' for development purposes only -->
|
119
|
+
<xs:attribute ref="id"/>
|
120
|
+
<xs:attribute name="status" use="required">
|
121
|
+
<xs:simpleType>
|
122
|
+
<xs:restriction base="xs:string">
|
123
|
+
<xs:enumeration value="active"/>
|
124
|
+
<xs:enumeration value="inactive"/>
|
125
|
+
<xs:enumeration value="discontinued"/>
|
126
|
+
</xs:restriction>
|
127
|
+
</xs:simpleType>
|
128
|
+
</xs:attribute>
|
129
|
+
</xs:complexType>
|
130
|
+
|
131
|
+
<xs:complexType name="ProductCatalog" mixed="true">
|
132
|
+
<xs:sequence>
|
133
|
+
<xs:element name="categories">
|
134
|
+
<xs:complexType>
|
135
|
+
<xs:sequence>
|
136
|
+
<xs:element name="category" type="cat:CategoryType" maxOccurs="unbounded"/>
|
137
|
+
</xs:sequence>
|
138
|
+
</xs:complexType>
|
139
|
+
</xs:element>
|
140
|
+
<xs:element name="products">
|
141
|
+
<xs:complexType>
|
142
|
+
<xs:sequence>
|
143
|
+
<xs:element name="product" type="cat:ProductType" maxOccurs="unbounded"/>
|
144
|
+
</xs:sequence>
|
145
|
+
</xs:complexType>
|
146
|
+
</xs:element>
|
147
|
+
</xs:sequence>
|
148
|
+
<xs:attribute name="version" type="xs:string" use="required"/>
|
149
|
+
<xs:attribute name="lastUpdated" type="xs:dateTime" use="required"/>
|
150
|
+
</xs:complexType>
|
151
|
+
</xs:schema>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/specs"
|
3
|
+
xmlns="http://example.com/specs" elementFormDefault="qualified">
|
4
|
+
<!-- ComplexType SpecBase and ShortSpec are root elements -->
|
5
|
+
|
6
|
+
<!-- Base type for a specification -->
|
7
|
+
<xs:complexType name="spec">
|
8
|
+
<xs:sequence>
|
9
|
+
<xs:element name="title" type="xs:string"/>
|
10
|
+
<xs:element name="version" type="xs:string"/>
|
11
|
+
<xs:element name="status" type="status" minOccurs="0" default="draft"/>
|
12
|
+
</xs:sequence>
|
13
|
+
<xs:attribute name="id" type="xs:ID" use="required"/>
|
14
|
+
</xs:complexType>
|
15
|
+
|
16
|
+
<!-- Restriction: A "ShortSpec" only allows title and version, not status -->
|
17
|
+
<xs:complexType name="shortSpec">
|
18
|
+
<xs:complexContent>
|
19
|
+
<xs:restriction base="spec">
|
20
|
+
<xs:sequence>
|
21
|
+
<xs:element name="title" type="xs:string"/>
|
22
|
+
<xs:element name="version" type="xs:string"/>
|
23
|
+
<xs:element name="status" type="status" minOccurs="0" maxOccurs="2"/>
|
24
|
+
</xs:sequence>
|
25
|
+
<xs:attribute name="id" type="xs:ID" use="required"/>
|
26
|
+
</xs:restriction>
|
27
|
+
</xs:complexContent>
|
28
|
+
</xs:complexType>
|
29
|
+
|
30
|
+
<!-- Enumeration for status -->
|
31
|
+
<xs:simpleType name="status">
|
32
|
+
<xs:restriction base="xs:string">
|
33
|
+
<xs:enumeration value="draft"/>
|
34
|
+
<xs:enumeration value="final"/>
|
35
|
+
<xs:enumeration value="archived"/>
|
36
|
+
</xs:restriction>
|
37
|
+
</xs:simpleType>
|
38
|
+
</xs:schema>
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model"
|
3
|
+
|
4
|
+
module AttributeCollection
|
5
|
+
class StringParts < Lutaml::Model::Collection
|
6
|
+
instances :parts, :string
|
7
|
+
|
8
|
+
def to_s
|
9
|
+
parts.join(" -- ")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class BibliographicItem < Lutaml::Model::Serializable
|
14
|
+
attribute :title_parts, :string, collection: StringParts
|
15
|
+
|
16
|
+
xml do
|
17
|
+
root "titles"
|
18
|
+
map_element "title", to: :title_parts
|
19
|
+
end
|
20
|
+
|
21
|
+
key_value do
|
22
|
+
root "titles"
|
23
|
+
map_instances to: :title_parts
|
24
|
+
end
|
25
|
+
|
26
|
+
def render_title
|
27
|
+
title_parts.to_s
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec.describe AttributeCollection do
|
33
|
+
let(:xml_data) do
|
34
|
+
<<~XML
|
35
|
+
<titles>
|
36
|
+
<title>Title One</title>
|
37
|
+
<title>Title Two</title>
|
38
|
+
<title>Title Three</title>
|
39
|
+
</titles>
|
40
|
+
XML
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:yaml_data) do
|
44
|
+
<<~YAML
|
45
|
+
---
|
46
|
+
titles:
|
47
|
+
- Title One
|
48
|
+
- Title Two
|
49
|
+
- Title Three
|
50
|
+
YAML
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "BibliographicItem with custom collection" do
|
54
|
+
let(:bib_item) { AttributeCollection::BibliographicItem.from_xml(xml_data) }
|
55
|
+
|
56
|
+
it "initializes with custom collection class" do
|
57
|
+
expect(bib_item.title_parts).to be_a(AttributeCollection::StringParts)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "contains the correct number of title parts" do
|
61
|
+
expect(bib_item.title_parts.count).to eq(3)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "contains the correct title parts" do
|
65
|
+
expect(bib_item.title_parts.parts).to eq(["Title One", "Title Two", "Title Three"])
|
66
|
+
end
|
67
|
+
|
68
|
+
it "renders title with custom separator" do
|
69
|
+
expect(bib_item.render_title).to eq("Title One -- Title Two -- Title Three")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "serializes to XML correctly" do
|
73
|
+
expect(bib_item.to_xml.strip).to eq(xml_data.strip)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "deserializes from YAML correctly" do
|
77
|
+
yaml_bib_item = AttributeCollection::BibliographicItem.from_yaml(yaml_data)
|
78
|
+
expect(yaml_bib_item.title_parts.parts).to eq(["Title One", "Title Two", "Title Three"])
|
79
|
+
end
|
80
|
+
|
81
|
+
it "serializes to YAML correctly" do
|
82
|
+
expect(bib_item.to_yaml.strip).to eq(yaml_data.strip)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "can be initialized with an array of strings" do
|
86
|
+
bib_item = AttributeCollection::BibliographicItem.new(
|
87
|
+
title_parts: AttributeCollection::StringParts.new(
|
88
|
+
["Part One", "Part Two"],
|
89
|
+
),
|
90
|
+
)
|
91
|
+
|
92
|
+
expect(bib_item.title_parts.parts).to eq(["Part One", "Part Two"])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "can be initialized with a custom collection instance" do
|
96
|
+
collection = AttributeCollection::StringParts.new(["Custom One", "Custom Two"])
|
97
|
+
bib_item = AttributeCollection::BibliographicItem.new(title_parts: collection)
|
98
|
+
expect(bib_item.title_parts.parts).to eq(["Custom One", "Custom Two"])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|