lutaml-model 0.5.2 → 0.5.4
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 +2 -0
- data/.rubocop_todo.yml +39 -13
- data/Gemfile +1 -0
- data/README.adoc +430 -52
- data/lib/lutaml/model/constants.rb +7 -0
- data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/key_value_mapping.rb +31 -2
- data/lib/lutaml/model/mapping_hash.rb +8 -0
- data/lib/lutaml/model/mapping_rule.rb +8 -0
- data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +720 -0
- data/lib/lutaml/model/schema.rb +5 -0
- data/lib/lutaml/model/serialize.rb +33 -13
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
- data/lib/lutaml/model/type/hash.rb +11 -11
- data/lib/lutaml/model/utils.rb +7 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
- data/lib/lutaml/model/xml_mapping.rb +4 -2
- data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
- data/lib/lutaml/model.rb +1 -0
- data/spec/fixtures/xml/invalid_math_document.xml +4 -0
- data/spec/fixtures/xml/math_document_schema.xsd +56 -0
- data/spec/fixtures/xml/test_schema.xsd +53 -0
- data/spec/fixtures/xml/valid_math_document.xml +4 -0
- data/spec/lutaml/model/cdata_spec.rb +2 -2
- data/spec/lutaml/model/custom_model_spec.rb +7 -20
- data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
- data/spec/lutaml/model/map_all_spec.rb +188 -0
- data/spec/lutaml/model/mixed_content_spec.rb +15 -15
- data/spec/lutaml/model/render_nil_spec.rb +29 -0
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
- data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
- metadata +12 -2
data/README.adoc
CHANGED
@@ -13,8 +13,9 @@ objects to and from various formats such as JSON, XML, YAML, and TOML. It uses
|
|
13
13
|
an adapter pattern to support multiple libraries for each format, providing
|
14
14
|
flexibility and extensibility for your data modeling needs.
|
15
15
|
|
16
|
-
NOTE: Lutaml::Model is designed to be mostly compatible
|
17
|
-
API of https://www.shalerb.org[Shale],
|
16
|
+
NOTE: The Lutaml::Model modeling Ruby API is designed to be mostly compatible
|
17
|
+
with the data modeling API of https://www.shalerb.org[Shale], a data modeller
|
18
|
+
for Ruby.
|
18
19
|
Lutaml::Model is meant to address advanced needs not currently addressed by
|
19
20
|
Shale.
|
20
21
|
|
@@ -48,12 +49,12 @@ The Lutaml::Model data modelling approach is as follows:
|
|
48
49
|
.Modeling relationships of a LutaML Model
|
49
50
|
[source]
|
50
51
|
----
|
51
|
-
|
52
|
+
LutaML Model
|
52
53
|
│
|
53
54
|
Has many attributes
|
54
55
|
│
|
55
56
|
▼
|
56
|
-
|
57
|
+
Attribute
|
57
58
|
│
|
58
59
|
Has type of
|
59
60
|
│
|
@@ -95,54 +96,144 @@ Studio (Model)
|
|
95
96
|
.Modeling relationships of a LutaML Model to serialization models
|
96
97
|
[source]
|
97
98
|
----
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
Models Value Types
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
99
|
+
╔═══════════════════════╗ ╔════════════════════════════╗
|
100
|
+
║ LutaML Core Model ║ ║ Serialization Models ║
|
101
|
+
╚═══════════════════════╝ ╚════════════════════════════╝
|
102
|
+
|
103
|
+
╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
|
104
|
+
┆ Model ┆ ┆ XML Model ┆
|
105
|
+
┆ │ ┆ ┌────────────────┐ ┆ │ ┆
|
106
|
+
┆ ┌────────┴──┐ ┆ │ │ ┆ ┌──────┴──────┐ ┆
|
107
|
+
┆ │ │ ┆ │ Model │ ┆ │ │ ┆
|
108
|
+
┆ Models Value Types ┆──►│ Transformation │ ┆ Models Value Types ┆
|
109
|
+
┆ │ │ ┆ │ & │ ┆ │ │ ┆
|
110
|
+
┆ │ │ ┆ │ Mapping Rules │ ┆ │ │ ┆
|
111
|
+
┆ │ ┌──────┴──┐ ┆ │ │ ┆ ┌────┴────┐ ┌─┴─┐ ┆
|
112
|
+
┆ │ │ │ ┆ └────────────────┘ ┆ │ │ │ │ ┆
|
113
|
+
┆ │ String Integer ┆ │ ┆ Element Value xs:string ┆
|
114
|
+
┆ │ Date Float ┆ │ ┆ Attribute Type xs:date ┆
|
115
|
+
┆ │ Time Boolean ┆ ├──────► ┆ xs:boolean ┆
|
116
|
+
┆ │ ┆ │ ┆ xs:anyURI ┆
|
117
|
+
┆ └──────┐ ┆ │ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
|
118
|
+
┆ │ ┆ │
|
119
|
+
┆ Contains ┆ │ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
|
120
|
+
┆ more Models ┆ │ ┆ JSON Model ┆
|
121
|
+
┆ (recursive) ┆ │ ┆ │ ┆
|
122
|
+
┆ ┆ │ ┆ ┌──────┴──────┐ ┆
|
123
|
+
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ └───────► ┆ │ │ ┆
|
124
|
+
┆ Models Value Types ┆
|
125
|
+
┆ │ │ ┆
|
126
|
+
┆ │ │ ┆
|
127
|
+
┆ ┌────┴───┐ ┌───┴──┐ ┆
|
128
|
+
┆ │ │ │ │ ┆
|
129
|
+
┆ object array number string ┆
|
130
|
+
┆ value boolean null ┆
|
131
|
+
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
|
132
|
+
----
|
133
|
+
|
134
|
+
.Model transformation of a LutaML Model to another LutaML Model
|
135
|
+
[source]
|
136
|
+
----
|
137
|
+
╔═══════════════════════╗ ╔══════════════════╗ ╔═══════════════════════╗
|
138
|
+
║LutaML Model Class FOO ║ ║LutaML Transformer║ ║LutaML Model Class BAR ║
|
139
|
+
╚═══════════════════════╝ ╚══════════════════╝ ╚═══════════════════════╝
|
140
|
+
|
141
|
+
╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
|
142
|
+
┆ Model ┆ ┆ Model ┆
|
143
|
+
┆ │ ┆ ┌────────────────┐ ┆ │ ┆
|
144
|
+
┆ ┌────────┴──┐ ┆ │ │ ┆ ┌────────┴──┐ ┆
|
145
|
+
┆ │ │ ┆ │ Model │ ┆ │ │ ┆
|
146
|
+
┆ Models Value Types ┆───►│ Transformation │───►┆ Models Value Types ┆
|
147
|
+
┆ │ │ ┆◄───│ & │◄───┆ │ │ ┆
|
148
|
+
┆ │ │ ┆ │ Mapping Rules │ ┆ │ │ ┆
|
149
|
+
┆ │ ┌──────┴──┐ ┆ │ │ ┆ │ ┌──────┴──┐ ┆
|
150
|
+
┆ │ │ │ ┆ └────────────────┘ ┆ │ │ │ ┆
|
151
|
+
┆ │ String Integer ┆ ┆ │ String Integer ┆
|
152
|
+
┆ │ Date Float ┆ ┆ │ Date Float ┆
|
153
|
+
┆ │ Time Boolean ┆ ┆ │ Time Boolean ┆
|
154
|
+
┆ │ ┆ ┆ │ ┆
|
155
|
+
┆ └──────┐ ┆ ┆ └──────┐ ┆
|
156
|
+
┆ │ ┆ ┆ │ ┆
|
157
|
+
┆ Contains ┆ ┆ Contains ┆
|
158
|
+
┆ more Models ┆ ┆ more Models ┆
|
159
|
+
┆ (recursive) ┆ ┆ (recursive) ┆
|
160
|
+
┆ ┆ ┆ ┆
|
161
|
+
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
|
162
|
+
----
|
163
|
+
|
164
|
+
.The `Value` class, transformation, and serialization formats
|
165
|
+
[source]
|
166
|
+
----
|
167
|
+
╔═══════════════════════╗ ╔═══════════════════════╗
|
168
|
+
║LutaML Value Class FOO ║ ║ Serialization Value ║
|
169
|
+
╚═══════════════════════╝ ╚═══════════════════════╝
|
170
|
+
╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
|
171
|
+
┆ ┌───────────────┐ ┆ ┆ ┌───────────────┐ ┆
|
172
|
+
┆ │ Value │ ┆ ┌──────────────────┐ ┆ │ XML Value │ ┆
|
173
|
+
┆ └───────────────┘ ┆──►│ Value Serializer │──►┆ └───────────────┘ ┆
|
174
|
+
┆ ┌───────────────┐ ┆ └──────────────────┘ ┆ ┌───────────────┐ ┆
|
175
|
+
┆ │Primitive Types│ ┆ ┆ │XML Value Types│ ┆
|
176
|
+
┆ └───────────────┘ ┆ ┆ └───────────────┘ ┆
|
177
|
+
┆ ┌───┘ ┆ ┆ ┌───┘ ┆
|
178
|
+
┆ ├─ string ┆ ┆ ├─ xs:string ┆
|
179
|
+
┆ ├─ integer ┆ ┆ ├─ xs:integer ┆
|
180
|
+
┆ ├─ float ┆ ┆ ├─ xs:decimal ┆
|
181
|
+
┆ ├─ boolean ┆ ┆ ├─ xs:boolean ┆
|
182
|
+
┆ ├─ date ┆ ┆ ├─ xs:date ┆
|
183
|
+
┆ ├─ time_without_date ┆ ┆ ├─ xs:time ┆
|
184
|
+
┆ ├─ date_time ┆ ┆ ├─ xs:dateTime ┆
|
185
|
+
┆ ├─ time ┆ ┆ ├─ xs:decimal ┆
|
186
|
+
┆ ├─ decimal ┆ ┆ ├─ xs:anyType ┆
|
187
|
+
┆ └─ hash ┆ ┆ └─ (complex element) ┆
|
188
|
+
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
|
189
|
+
│
|
190
|
+
▼
|
191
|
+
┌───────────────────┐
|
192
|
+
│ Value Transformer │
|
193
|
+
└───────────────────┘
|
194
|
+
│
|
195
|
+
▼
|
196
|
+
╔═══════════════════════╗
|
197
|
+
║LutaML Value Class BAR ║
|
198
|
+
╚═══════════════════════╝
|
199
|
+
╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
|
200
|
+
┆ ┌───────────────┐ ┆
|
201
|
+
┆ │ Value │ ┆
|
202
|
+
┆ └───────────────┘ ┆
|
203
|
+
┆ ┌───────────────┐ ┆
|
204
|
+
┆ │Primitive Types│ ┆
|
205
|
+
┆ └───────────────┘ ┆
|
206
|
+
┆ ┌───┘ ┆
|
207
|
+
┆ ├─ string ┆
|
208
|
+
┆ ├─ integer ┆
|
209
|
+
┆ ├─ float ┆
|
210
|
+
┆ ├─ boolean ┆
|
211
|
+
┆ ├─ date ┆
|
212
|
+
┆ ├─ time_without_date ┆
|
213
|
+
┆ ├─ date_time ┆
|
214
|
+
┆ ├─ time ┆
|
215
|
+
┆ ├─ decimal ┆
|
216
|
+
┆ └─ hash ┆
|
217
|
+
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
|
128
218
|
----
|
129
219
|
|
130
220
|
.Example of LutaML Model instance transformed into a serialization model and serialized to JSON
|
131
221
|
====
|
132
222
|
[source]
|
133
223
|
----
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
224
|
+
╔═════════════════════╗ ╔═════════════════════╗ ╔═════════════════════╗
|
225
|
+
║ Studio (Core Model) ║ ║ JSON Model ║ ║ Serialized JSON ║
|
226
|
+
╚═════════════════════╝ ╚═════════════════════╝ ╚═════════════════════╝
|
227
|
+
|
228
|
+
name: "Studio 1" ┌─► { ┌─► {
|
229
|
+
address: │ "name": "...", │ "name": "Studio 1",
|
230
|
+
├── street: "..." │ "address": { │ "address": {
|
231
|
+
└── city: "..." │ "street": "...", │ "street": "...",
|
232
|
+
kilns: ──┤ "city": "..." ──┤ "city": "..."
|
233
|
+
├── count: 3 │ }, │ },
|
234
|
+
└── temp: 1200 │ "kilnsCount": ..., │ "kilnsCount": 3,
|
235
|
+
│ "kilnsTemp": ... │ "kilnsTemp": 1200
|
236
|
+
└─► } └─► }
|
146
237
|
----
|
147
238
|
====
|
148
239
|
|
@@ -278,6 +369,7 @@ attribute :name_of_attribute, {symbol | string | class}
|
|
278
369
|
| `:time` | `Time` | `xs:dateTime` | `string` | `string` | `"2024-01-01T12:00:00+00:00"`
|
279
370
|
| `:decimal` (optional) | `BigDecimal` | `xs:decimal` | `number` | `float` | `123.45`
|
280
371
|
| `:hash` | `Hash` | complex element | object | map | `{key: "value"}`
|
372
|
+
| (nil value) | `nil` | `xs:anyType` | `null` | `null` | `null`
|
281
373
|
// | `class` | Custom class | complex element | object | map | `CustomObject`
|
282
374
|
// | `collection: true` | `Array` of type | repeated elements | array | sequence | `[obj1, obj2]`
|
283
375
|
// | `any`
|
@@ -921,10 +1013,8 @@ end
|
|
921
1013
|
----
|
922
1014
|
====
|
923
1015
|
|
924
|
-
|
925
|
-
==== Mapping all
|
926
|
-
|
927
|
-
WARNING: This feature is only applicable to XML (for now).
|
1016
|
+
[[xml-map-all]]
|
1017
|
+
==== Mapping all XML content
|
928
1018
|
|
929
1019
|
The `map_all` tag in XML mapping captures and maps all content within an XML
|
930
1020
|
element into a single attribute in the target Ruby object.
|
@@ -932,6 +1022,12 @@ element into a single attribute in the target Ruby object.
|
|
932
1022
|
The use case for `map_all` is to tell Lutaml::Model to not parse the content of
|
933
1023
|
the XML element at all, and instead handle it as an XML string.
|
934
1024
|
|
1025
|
+
NOTE: The corresponding method for key-value formats is at <<key-value-map-all>>.
|
1026
|
+
|
1027
|
+
WARNING: Notice that usage of mapping all will lead to incompatibility between
|
1028
|
+
serialization formats, i.e. the raw string content will not be portable as
|
1029
|
+
objects are across different formats.
|
1030
|
+
|
935
1031
|
This is useful in the case where the content of an XML element is not to be
|
936
1032
|
handled by a Lutaml::Model::Serializable object.
|
937
1033
|
|
@@ -944,8 +1040,9 @@ This includes:
|
|
944
1040
|
* attributes
|
945
1041
|
* text nodes
|
946
1042
|
|
947
|
-
The `map_all` tag is **exclusive** and cannot be combined with other mappings
|
948
|
-
|
1043
|
+
The `map_all` tag is **exclusive** and cannot be combined with other mappings
|
1044
|
+
(`map_element`, `map_content`) except for `map_attribute` for the same element,
|
1045
|
+
ensuring it captures the entire inner XML content.
|
949
1046
|
|
950
1047
|
NOTE: An error is raised if `map_all` is defined alongside any other mapping in
|
951
1048
|
the same XML mapping context.
|
@@ -1667,6 +1764,7 @@ end
|
|
1667
1764
|
|
1668
1765
|
// TODO: How to create mixed content from `#new`?
|
1669
1766
|
|
1767
|
+
|
1670
1768
|
[[xml-schema-location]]
|
1671
1769
|
==== Automatic support of `xsi:schemaLocation`
|
1672
1770
|
|
@@ -1926,6 +2024,102 @@ end
|
|
1926
2024
|
----
|
1927
2025
|
====
|
1928
2026
|
|
2027
|
+
[[key-value-map-all]]
|
2028
|
+
==== Mapping all key-value content
|
2029
|
+
|
2030
|
+
The `map_all` tag captures and maps all content within a serialization format
|
2031
|
+
into a single attribute in the target Ruby object.
|
2032
|
+
|
2033
|
+
The use case for `map_all` is to tell Lutaml::Model to not parse the content at
|
2034
|
+
all, and instead handle it as a raw string.
|
2035
|
+
|
2036
|
+
NOTE: The corresponding method for XML is at <<xml-map-all>>.
|
2037
|
+
|
2038
|
+
WARNING: Notice that usage of mapping all will lead to incompatibility between
|
2039
|
+
serialization formats, i.e. the raw string content will not be portable as
|
2040
|
+
objects are across different formats.
|
2041
|
+
|
2042
|
+
This is useful when the content needs to be handled as-is without parsing into
|
2043
|
+
individual attributes.
|
2044
|
+
|
2045
|
+
The `map_all` tag is **exclusive** and cannot be combined with other mappings,
|
2046
|
+
ensuring it captures the entire content.
|
2047
|
+
|
2048
|
+
NOTE: An error is raised if `map_all` is defined alongside any other mapping in
|
2049
|
+
the same mapping context.
|
2050
|
+
|
2051
|
+
Syntax:
|
2052
|
+
|
2053
|
+
[source,ruby]
|
2054
|
+
----
|
2055
|
+
json | yaml | toml | key_value do
|
2056
|
+
map_all to: :name_of_attribute
|
2057
|
+
end
|
2058
|
+
----
|
2059
|
+
|
2060
|
+
.Using `map_all` to capture all content across different formats
|
2061
|
+
[example]
|
2062
|
+
====
|
2063
|
+
[source,ruby]
|
2064
|
+
----
|
2065
|
+
class Document < Lutaml::Model::Serializable
|
2066
|
+
attribute :content, :string
|
2067
|
+
|
2068
|
+
json do
|
2069
|
+
map_all to: :content
|
2070
|
+
end
|
2071
|
+
|
2072
|
+
yaml do
|
2073
|
+
map_all to: :content
|
2074
|
+
end
|
2075
|
+
|
2076
|
+
toml do
|
2077
|
+
map_all to: :content
|
2078
|
+
end
|
2079
|
+
end
|
2080
|
+
----
|
2081
|
+
|
2082
|
+
For JSON:
|
2083
|
+
[source,json]
|
2084
|
+
----
|
2085
|
+
{
|
2086
|
+
"sections": [
|
2087
|
+
{ "title": "Introduction", "text": "Chapter 1" },
|
2088
|
+
{ "title": "Conclusion", "text": "Final chapter" }
|
2089
|
+
],
|
2090
|
+
"metadata": {
|
2091
|
+
"author": "John Doe",
|
2092
|
+
"date": "2024-01-15"
|
2093
|
+
}
|
2094
|
+
}
|
2095
|
+
----
|
2096
|
+
|
2097
|
+
For YAML:
|
2098
|
+
[source,yaml]
|
2099
|
+
----
|
2100
|
+
sections:
|
2101
|
+
- title: Introduction
|
2102
|
+
text: Chapter 1
|
2103
|
+
- title: Conclusion
|
2104
|
+
text: Final chapter
|
2105
|
+
metadata:
|
2106
|
+
author: John Doe
|
2107
|
+
date: 2024-01-15
|
2108
|
+
----
|
2109
|
+
|
2110
|
+
The content is preserved exactly as provided:
|
2111
|
+
|
2112
|
+
[source,ruby]
|
2113
|
+
----
|
2114
|
+
> doc = Document.from_json(json_content)
|
2115
|
+
> puts doc.content
|
2116
|
+
> # "{\"sections\":[{\"title\":\"Introduction\",\"text\":\"Chapter 1\"},{\"title\":\"Conclusion\",\"text\":\"Final chapter\"}],\"metadata\":{\"author\":\"John Doe\",\"date\":\"2024-01-15\"}}"
|
2117
|
+
|
2118
|
+
> doc = Document.from_yaml(yaml_content)
|
2119
|
+
> puts doc.content
|
2120
|
+
> # "sections:\n - title: Introduction\n text: Chapter 1\n - title: Conclusion\n text: Final chapter\nmetadata:\n author: John Doe\n date: 2024-01-15\n"
|
2121
|
+
----
|
2122
|
+
====
|
1929
2123
|
|
1930
2124
|
==== Nested attribute mappings
|
1931
2125
|
|
@@ -3305,6 +3499,181 @@ end
|
|
3305
3499
|
====
|
3306
3500
|
|
3307
3501
|
|
3502
|
+
== Importing data models
|
3503
|
+
|
3504
|
+
=== General
|
3505
|
+
|
3506
|
+
Lutaml::Model provides a way to import data models defined from various formats
|
3507
|
+
into the LutaML data modeling system.
|
3508
|
+
|
3509
|
+
Data model languages supported are:
|
3510
|
+
|
3511
|
+
* XSD (https://w3.org/TR/xmlschema-1/[XML Schema Definition Language])
|
3512
|
+
// * RNC (https://relaxng.org/compact-tutorial-20030326.html[RELAX NG Compact Syntax])
|
3513
|
+
// * RNG (https://relaxng.org/relaxng-compact.html[RELAX NG XML Syntax])
|
3514
|
+
// * JSON Schema (https://json-schema.org/understanding-json-schema/[JSON Schema])
|
3515
|
+
// * YAML Schema (https://yaml.org/spec/1.2/spec.html[YAML])
|
3516
|
+
// * LutaML
|
3517
|
+
|
3518
|
+
|
3519
|
+
The following figure illustrates the process of importing an XML Schema model to
|
3520
|
+
create LutaML core models. Once the LutaML core models are created, they can be
|
3521
|
+
used to parse and generate XML documents according to the imported XML Schema
|
3522
|
+
model.
|
3523
|
+
|
3524
|
+
Today, the LutaML core models are written into Ruby files, which can be used to
|
3525
|
+
parse and generate XML documents according to the imported XML Schema.
|
3526
|
+
This is to be changed so that the LutaML core models are directly loaded and
|
3527
|
+
interpreted.
|
3528
|
+
|
3529
|
+
.Importing an XML Schema model to create LutaML core models
|
3530
|
+
[source]
|
3531
|
+
----
|
3532
|
+
╔════════════════════════════╗ ╔═══════════════════════╗
|
3533
|
+
║ Serialization Models ║ ║ Core Model ║
|
3534
|
+
╚════════════════════════════╝ ╚═══════════════════════╝
|
3535
|
+
|
3536
|
+
╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
|
3537
|
+
┆ XML Schema (XSD/RNG/RNC) ┆ ┆ Model ┆
|
3538
|
+
┆ │ ┆ ┌────────────────┐ ┆ │ ┆
|
3539
|
+
┆ ┌──────┴──────┐ ┆ │ │ ┆ ┌────────┴──┐ ┆
|
3540
|
+
┆ │ │ ┆ │ Model │ ┆ │ │ ┆
|
3541
|
+
┆ Models Value Types ┆──►│ Importing │──►┆ Models Value Types ┆
|
3542
|
+
┆ │ │ ┆ │ │ ┆ │ │ ┆
|
3543
|
+
┆ │ │ ┆ └────────────────┘ ┆ │ │ ┆
|
3544
|
+
┆ ┌────┴────┐ ┌─┴─┐ ┆ │ ┆ │ ┌──────┴──┐ ┆
|
3545
|
+
┆ │ │ │ │ ┆ │ ┆ │ │ │ ┆
|
3546
|
+
┆ Element Value xs:string ┆ │ ┆ │ String Integer ┆
|
3547
|
+
┆ Attribute Type xs:date ┆ │ ┆ │ Date Float ┆
|
3548
|
+
┆ Union Complex xs:boolean ┆ │ ┆ │ Time Boolean ┆
|
3549
|
+
┆ Sequence Choice xs:anyURI ┆ │ ┆ │ ┆
|
3550
|
+
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ │ ┆ └──────┐ ┆
|
3551
|
+
│ ┆ │ ┆
|
3552
|
+
│ ┆ Contains ┆
|
3553
|
+
│ ┆ more Models ┆
|
3554
|
+
│ ┆ (recursive) ┆
|
3555
|
+
│ ┆ ┆
|
3556
|
+
│ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
|
3557
|
+
│ ┌────────────────┐
|
3558
|
+
│ │ │
|
3559
|
+
│ │ Model │
|
3560
|
+
└──────────► │ Transformation │
|
3561
|
+
│ & │
|
3562
|
+
│ Mapping Rules │
|
3563
|
+
│ │
|
3564
|
+
└────────────────┘
|
3565
|
+
----
|
3566
|
+
|
3567
|
+
|
3568
|
+
[[xml-schema-to-model-files]]
|
3569
|
+
=== XML Schema (XSD)
|
3570
|
+
|
3571
|
+
W3C XSD is a schema language designed to define the structure of XML documents,
|
3572
|
+
alongside other XML schema languages like DTD, RELAX NG, and Schematron.
|
3573
|
+
|
3574
|
+
Lutaml::Model supports the import of XSD schema files to define information
|
3575
|
+
models that can be used to parse and generate XML documents.
|
3576
|
+
|
3577
|
+
Specifically, the `Lutaml::Model::Schema#from_xml` method loads XML Schema files
|
3578
|
+
(XSD, `*.xsd`) and generates Ruby files (`*.rb`) that inherit from
|
3579
|
+
`Lutaml::Model::Serializable` that are saved to disk.
|
3580
|
+
|
3581
|
+
Syntax:
|
3582
|
+
|
3583
|
+
[source,ruby]
|
3584
|
+
----
|
3585
|
+
Lutaml::Model::Schema.from_xml(
|
3586
|
+
xsd_schema, <1>
|
3587
|
+
options: options <2>
|
3588
|
+
)
|
3589
|
+
----
|
3590
|
+
<1> The `xsd_schema` is the XML Schema string to be converted to model files.
|
3591
|
+
<2> The `options` hash is an optional argument.
|
3592
|
+
|
3593
|
+
`options`:: Optional hash containing potentially the following key-values.
|
3594
|
+
|
3595
|
+
`output_dir`::: The directory where the model files will be saved. If not
|
3596
|
+
provided, a default directory named `lutaml_models_<timestamp>` is created.
|
3597
|
+
+
|
3598
|
+
[example]
|
3599
|
+
`"path/to/directory"`
|
3600
|
+
|
3601
|
+
`namespace`::: The namespace of the schema. This will be added in the
|
3602
|
+
`Lutaml::Model::Serializable` file's `xml do` block.
|
3603
|
+
+
|
3604
|
+
[example]
|
3605
|
+
`http://example.com/namespace`
|
3606
|
+
|
3607
|
+
`prefix`::: The prefix of the namespace provided in the `namespace` option.
|
3608
|
+
+
|
3609
|
+
[example]
|
3610
|
+
`example-prefix`
|
3611
|
+
|
3612
|
+
`location`::: The URL or path of the directory containing all the files of the
|
3613
|
+
schema. For more information, refer to the
|
3614
|
+
link:https://www.w3.org/TR/xmlschema-1/#include[XML Schema specification].
|
3615
|
+
+
|
3616
|
+
[example]
|
3617
|
+
`"http://example.com/example.xsd"`
|
3618
|
+
+
|
3619
|
+
[example]
|
3620
|
+
`"path/to/schema/directory"`
|
3621
|
+
|
3622
|
+
|
3623
|
+
The generated LutaML models consists of two different kind of Ruby classes
|
3624
|
+
depending on the XSD schema:
|
3625
|
+
|
3626
|
+
XSD "SimpleTypes":: converted into classes that inherit from
|
3627
|
+
`Lutaml::Model::Type::Value`, which define the data types with restrictions and
|
3628
|
+
other validations of these values.
|
3629
|
+
|
3630
|
+
XSD "ComplexTypes":: converted into classes that inherit from
|
3631
|
+
`Lutaml::Model::Serializable` that model according to the defined structure.
|
3632
|
+
|
3633
|
+
Lutaml::Model uses the https://github.com/lutaml/lutaml-xsd[`lutaml-xsd` gem] to
|
3634
|
+
automatically resolve the `include` and `import` elements, enabling
|
3635
|
+
*Lutaml-Model* to generate the corresponding model files.
|
3636
|
+
|
3637
|
+
This auto-resolving feature allows seamless integration of these files into your
|
3638
|
+
models without the need for manual resolution of includes and imports.
|
3639
|
+
|
3640
|
+
[example]
|
3641
|
+
.Using `Lutaml::Model::Schema#from_xml` to convert an XML Schema to model files
|
3642
|
+
====
|
3643
|
+
[source,ruby]
|
3644
|
+
----
|
3645
|
+
xsd_schema = <<~XSD
|
3646
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
3647
|
+
/* your schema here */
|
3648
|
+
</xs:schema>
|
3649
|
+
XSD
|
3650
|
+
options = {
|
3651
|
+
# These are all optional:
|
3652
|
+
output_dir: 'path/to/directory',
|
3653
|
+
namespace: 'http://example.com/namespace',
|
3654
|
+
prefix: "example-prefix",
|
3655
|
+
location: "http://example.com/example.xsd"
|
3656
|
+
# or
|
3657
|
+
# location: "path/to/schema/directory"
|
3658
|
+
}
|
3659
|
+
|
3660
|
+
# generates the files in the output_dir | default_dir
|
3661
|
+
Lutaml::Model::Schema.from_xml(xsd_schema, options: options)
|
3662
|
+
----
|
3663
|
+
====
|
3664
|
+
|
3665
|
+
You could also directly load the generated Ruby files into your application by
|
3666
|
+
requiring them.
|
3667
|
+
|
3668
|
+
[example]
|
3669
|
+
.Using the generated Ruby files in your application
|
3670
|
+
====
|
3671
|
+
[source,ruby]
|
3672
|
+
----
|
3673
|
+
Lutaml::Model::Schema.from_xml(xsd_schema, options: {output_dir: 'path/to/directory'})
|
3674
|
+
require_relative 'path/to/directory/*.rb'
|
3675
|
+
----
|
3676
|
+
====
|
3308
3677
|
|
3309
3678
|
|
3310
3679
|
== Validation
|
@@ -3696,6 +4065,15 @@ attribute for every element.
|
|
3696
4065
|
| Requires manual specification on every XML element that uses it.
|
3697
4066
|
|
|
3698
4067
|
|
4068
|
+
|
4069
|
+
| Compiling XML Schema to *Lutaml::Model::Serializable* classes
|
4070
|
+
| Yes. Using <<xml-schema-to-model-files, `Lutaml::Model::Schema#from_xml`>>
|
4071
|
+
|
4072
|
+
1. *ComplexTypes* are compiled to *Lutaml::Model::Serializable* classes containing the attributes.
|
4073
|
+
2. *SimpleTypes* are compiled to *Lutaml::Model::Type::Value* classes to support XML Schema level validations.
|
4074
|
+
| Yes, Provides only an array of the classes and doesn't support `simple types` with restrictions and/or other validations.
|
4075
|
+
|
|
4076
|
+
|
3699
4077
|
4+h| Attribute features
|
3700
4078
|
|
3701
4079
|
| Attribute delegation
|
data/lib/lutaml/model/error.rb
CHANGED
@@ -36,6 +36,27 @@ module Lutaml
|
|
36
36
|
|
37
37
|
alias map_element map
|
38
38
|
|
39
|
+
def map_all(
|
40
|
+
to: nil,
|
41
|
+
render_nil: false,
|
42
|
+
render_default: false,
|
43
|
+
with: {},
|
44
|
+
delegate: nil
|
45
|
+
)
|
46
|
+
@raw_mapping = true
|
47
|
+
validate!(Constants::RAW_MAPPING_KEY, to, with)
|
48
|
+
@mappings << KeyValueMappingRule.new(
|
49
|
+
Constants::RAW_MAPPING_KEY,
|
50
|
+
to: to,
|
51
|
+
render_nil: render_nil,
|
52
|
+
render_default: render_default,
|
53
|
+
with: with,
|
54
|
+
delegate: delegate,
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
alias map_all_content map_all
|
59
|
+
|
39
60
|
def name_for_mapping(root_mappings, name)
|
40
61
|
return "root_mapping" if root_mappings
|
41
62
|
|
@@ -43,12 +64,14 @@ module Lutaml
|
|
43
64
|
end
|
44
65
|
|
45
66
|
def validate!(key, to, with)
|
46
|
-
|
67
|
+
validate_mappings!(key)
|
68
|
+
|
69
|
+
if to.nil? && with.empty? && !@raw_mapping
|
47
70
|
msg = ":to or :with argument is required for mapping '#{key}'"
|
48
71
|
raise IncorrectMappingArgumentsError.new(msg)
|
49
72
|
end
|
50
73
|
|
51
|
-
if !with.empty? && (with[:from].nil? || with[:to].nil?)
|
74
|
+
if !with.empty? && (with[:from].nil? || with[:to].nil?) && !@raw_mapping
|
52
75
|
msg = ":with argument for mapping '#{key}' requires :to and :from keys"
|
53
76
|
raise IncorrectMappingArgumentsError.new(msg)
|
54
77
|
end
|
@@ -62,6 +85,12 @@ module Lutaml
|
|
62
85
|
end
|
63
86
|
end
|
64
87
|
|
88
|
+
def validate_mappings!(_type)
|
89
|
+
if (@raw_mapping && Utils.present?(@mappings)) || (!@raw_mapping && @mappings.any?(&:raw_mapping?))
|
90
|
+
raise StandardError, "map_all is not allowed with other mappings"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
65
94
|
def deep_dup
|
66
95
|
self.class.new.tap do |new_mapping|
|
67
96
|
new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
|