lutaml-model 0.2.1 → 0.3.1
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/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +207 -0
- data/Gemfile +6 -2
- data/README.adoc +1054 -126
- data/lib/lutaml/model/attribute.rb +4 -0
- data/lib/lutaml/model/error/invalid_value_error.rb +18 -0
- data/lib/lutaml/model/error.rb +8 -0
- data/lib/lutaml/model/key_value_mapping.rb +9 -1
- data/lib/lutaml/model/mapping_hash.rb +42 -0
- data/lib/lutaml/model/mapping_rule.rb +24 -2
- data/lib/lutaml/model/serialize.rb +270 -189
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +2 -2
- data/lib/lutaml/model/type.rb +19 -13
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +104 -101
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +125 -85
- data/lib/lutaml/model/xml_adapter.rb +138 -7
- data/lib/lutaml/model/xml_mapping.rb +36 -6
- data/lib/lutaml/model/xml_mapping_rule.rb +6 -6
- data/lib/lutaml/model/yaml_adapter.rb +11 -3
- data/lib/lutaml/model.rb +7 -0
- metadata +6 -2
data/README.adoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= LutaML Ruby modeller
|
1
|
+
= LutaML Ruby modeller
|
2
2
|
|
3
3
|
https://github.com/lutaml/lutaml-model[image:https://img.shields.io/github/stars/lutaml/lutaml-model.svg?style=social[GitHub Stars]]
|
4
4
|
https://github.com/lutaml/lutaml-model[image:https://img.shields.io/github/forks/lutaml/lutaml-model.svg?style=social[GitHub Forks]]
|
@@ -6,39 +6,41 @@ image:https://img.shields.io/github/license/lutaml/lutaml-model.svg[License]
|
|
6
6
|
image:https://img.shields.io/github/actions/workflow/status/lutaml/lutaml-model/test.yml?branch=main[Build Status]
|
7
7
|
image:https://img.shields.io/gem/v/lutaml-model.svg[RubyGems Version]
|
8
8
|
|
9
|
-
==
|
9
|
+
== Purpose
|
10
10
|
|
11
11
|
Lutaml::Model is a lightweight library for serializing and deserializing Ruby
|
12
12
|
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
|
-
The name "LutaML" comes from the Latin word "Lutum
|
16
|
+
The name "LutaML" comes from the Latin word for clay, "Lutum", and "ML"
|
17
17
|
for Markup Language. Just as clay can be molded and modeled into beautiful and
|
18
18
|
practical end products, the Lutaml::Model gem is used for data modeling,
|
19
19
|
allowing you to shape and structure your data into useful forms.
|
20
20
|
|
21
21
|
|
22
|
+
NOTE: Lutaml::Model is designed to be compatible with the
|
23
|
+
https://www.shalerb.org[Shale] data modeling API. Shale is an amazing Ruby data
|
24
|
+
modeller. Lutaml::Model is meant to address needs that are not currently
|
25
|
+
addressed by Shale.
|
22
26
|
|
23
|
-
NOTE: Lutaml::Model is designed to be compatible with the Shale data modeling
|
24
|
-
API. Shale is an amazing Ruby data modeller. Lutaml::Model is meant to address
|
25
|
-
needs that are not currently addresed by Shale.
|
26
27
|
|
27
|
-
==
|
28
|
+
== Data modeling in a nutshell
|
28
29
|
|
29
30
|
Data modeling is the process of creating a data model for the data to be stored
|
30
31
|
in a database or used in an application. It helps in defining the structure,
|
31
32
|
relationships, and constraints of the data, making it easier to manage and use.
|
32
33
|
|
33
34
|
Lutaml::Model simplifies data modeling in Ruby by allowing you to define models
|
34
|
-
with attributes and serialize/deserialize them to/from various
|
35
|
-
seamlessly.
|
35
|
+
with attributes and serialize/deserialize them to/from various serialization
|
36
|
+
formats seamlessly.
|
37
|
+
|
36
38
|
|
37
39
|
== Features
|
38
40
|
|
39
41
|
* Define models with attributes and types
|
40
42
|
* Serialize and deserialize models to/from JSON, XML, YAML, and TOML
|
41
|
-
* Support for multiple libraries (e.g., `toml-rb`, `tomlib`)
|
43
|
+
* Support for multiple serialization libraries (e.g., `toml-rb`, `tomlib`)
|
42
44
|
* Configurable adapters for different serialization formats
|
43
45
|
* Support for collections and default values
|
44
46
|
* Custom serialization/deserialization methods
|
@@ -67,9 +69,21 @@ Or install it yourself as:
|
|
67
69
|
gem install lutaml-model
|
68
70
|
----
|
69
71
|
|
70
|
-
==
|
72
|
+
== Defining a data model class
|
73
|
+
|
74
|
+
=== General
|
75
|
+
|
76
|
+
There are two ways to define a data model in Lutaml::Model:
|
77
|
+
|
78
|
+
* Inheriting from the `Lutaml::Model::Serializable` class
|
79
|
+
* Including the `Lutaml::Model::Serialize` module
|
71
80
|
|
72
|
-
|
81
|
+
=== Definition through inheritance
|
82
|
+
|
83
|
+
The simplest way to define a model is to create a class that inherits from
|
84
|
+
`Lutaml::Model::Serializable`.
|
85
|
+
|
86
|
+
The `attribute` class method is used to define attributes.
|
73
87
|
|
74
88
|
[source,ruby]
|
75
89
|
----
|
@@ -82,201 +96,399 @@ class Kiln < Lutaml::Model::Serializable
|
|
82
96
|
end
|
83
97
|
----
|
84
98
|
|
85
|
-
|
99
|
+
=== Definition through inclusion
|
100
|
+
|
101
|
+
If the model class already has a super class that it inherits from, the model
|
102
|
+
can be extended using the `Lutaml::Model::Serialize` module.
|
103
|
+
|
104
|
+
[source,ruby]
|
105
|
+
----
|
106
|
+
require 'lutaml/model'
|
107
|
+
|
108
|
+
class Kiln < SomeSuperClass
|
109
|
+
include Lutaml::Model::Serialize
|
110
|
+
|
111
|
+
attribute :brand, Lutaml::Model::Type::String
|
112
|
+
attribute :capacity, Lutaml::Model::Type::Integer
|
113
|
+
attribute :temperature, Lutaml::Model::Type::Integer
|
114
|
+
end
|
115
|
+
----
|
116
|
+
|
117
|
+
== Defining attributes
|
86
118
|
|
87
|
-
|
88
|
-
formats including XML, JSON, YAML, and TOML.
|
119
|
+
=== Attribute as a collection
|
89
120
|
|
90
|
-
|
121
|
+
Define attributes as collections (arrays or hashes) to store multiple values
|
122
|
+
using the `collection` option.
|
91
123
|
|
92
|
-
|
124
|
+
Syntax:
|
93
125
|
|
94
126
|
[source,ruby]
|
95
127
|
----
|
96
|
-
|
97
|
-
|
98
|
-
attribute :value, Lutaml::Model::Type::Integer
|
128
|
+
attribute :name_of_attribute, Type, collection: true
|
129
|
+
----
|
99
130
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
131
|
+
.Using the `collection` option to define a collection attribute
|
132
|
+
[example]
|
133
|
+
====
|
134
|
+
[source,ruby]
|
135
|
+
----
|
136
|
+
class Studio < Lutaml::Model::Serializable
|
137
|
+
attribute :location, Lutaml::Model::Type::String
|
138
|
+
attribute :potters, Lutaml::Model::Type::String, collection: true
|
139
|
+
end
|
140
|
+
----
|
141
|
+
|
142
|
+
[source,ruby]
|
143
|
+
----
|
144
|
+
> Studio.new.potters
|
145
|
+
> # []
|
146
|
+
> Studio.new(potters: ['John Doe', 'Jane Doe']).potters
|
147
|
+
> # ['John Doe', 'Jane Doe']
|
148
|
+
----
|
149
|
+
====
|
150
|
+
|
151
|
+
=== Attribute as an enumeration
|
152
|
+
|
153
|
+
An attribute can be defined as an enumeration by using the `values` directive.
|
154
|
+
|
155
|
+
The `values` directive is used to define acceptable values in an attribute. If
|
156
|
+
any other value is given, a `Lutaml::Model::InvalidValueError` will be raised.
|
157
|
+
|
158
|
+
Syntax:
|
159
|
+
|
160
|
+
[source,ruby]
|
161
|
+
----
|
162
|
+
attribute :name_of_attribute, Type, values: [value1, value2, ...]
|
163
|
+
----
|
164
|
+
|
165
|
+
.Using the `values` directive to define acceptable values for an attribute
|
166
|
+
[example]
|
167
|
+
====
|
168
|
+
[source,ruby]
|
169
|
+
----
|
170
|
+
class Ceramic < Lutaml::Model::Serializable
|
171
|
+
attribute :type, Lutaml::Model::Type::String,
|
172
|
+
values: ['Porcelain', 'Earthenware', 'Stoneware']
|
173
|
+
end
|
174
|
+
----
|
175
|
+
|
176
|
+
[source,ruby]
|
177
|
+
----
|
178
|
+
> Ceramic.new(type: 'Porcelain').type
|
179
|
+
> # "Porcelain"
|
180
|
+
> Ceramic.new(type: 'Earthenware').type
|
181
|
+
> # "Earthenware"
|
182
|
+
> Ceramic.new(type: 'Bone China').type
|
183
|
+
> # Lutaml::Model::InvalidValueError: Invalid value for attribute 'type'
|
184
|
+
----
|
185
|
+
====
|
186
|
+
|
187
|
+
|
188
|
+
=== Attribute value default
|
189
|
+
|
190
|
+
Specify default values for attributes using the `default` option.
|
191
|
+
The `default` option can be set to a value or a lambda that returns a value.
|
192
|
+
|
193
|
+
Syntax:
|
194
|
+
|
195
|
+
[source,ruby]
|
196
|
+
----
|
197
|
+
attribute :name_of_attribute, Type, default: -> { value }
|
198
|
+
----
|
199
|
+
|
200
|
+
|
201
|
+
.Using the `default` option to set a default value for an attribute
|
202
|
+
[example]
|
203
|
+
====
|
204
|
+
[source,ruby]
|
205
|
+
----
|
206
|
+
class Glaze < Lutaml::Model::Serializable
|
207
|
+
attribute :color, Lutaml::Model::Type::String, default: -> { 'Clear' }
|
208
|
+
attribute :temperature, Lutaml::Model::Type::Integer, default: -> { 1050 }
|
105
209
|
end
|
106
210
|
----
|
107
211
|
|
108
|
-
|
212
|
+
[source,ruby]
|
213
|
+
----
|
214
|
+
> Glaze.new.color
|
215
|
+
> # "Clear"
|
216
|
+
> Glaze.new.temperature
|
217
|
+
> # 1050
|
218
|
+
----
|
219
|
+
====
|
220
|
+
|
221
|
+
== Serialization model mappings
|
222
|
+
|
223
|
+
=== General
|
224
|
+
|
225
|
+
Lutaml::Model allows you to translate a data model into serialization models of
|
226
|
+
various serialization formats including XML, JSON, YAML, and TOML.
|
227
|
+
|
228
|
+
Depending on the serialization format, different methods are supported for
|
229
|
+
defining serialization and deserialization mappings.
|
109
230
|
|
110
|
-
|
231
|
+
Serialization model mappings are defined under the `xml`, `json`, `yaml`, and
|
232
|
+
`toml` blocks.
|
111
233
|
|
234
|
+
.Using the `xml`, `json`, `yaml`, and `toml` blocks to define serialization mappings
|
112
235
|
[source,ruby]
|
113
236
|
----
|
114
237
|
class Example < Lutaml::Model::Serializable
|
115
|
-
|
116
|
-
|
238
|
+
xml do
|
239
|
+
# ...
|
240
|
+
end
|
117
241
|
|
118
242
|
json do
|
119
|
-
|
120
|
-
map 'value', to: :value
|
243
|
+
# ...
|
121
244
|
end
|
122
245
|
|
123
246
|
yaml do
|
124
|
-
|
125
|
-
map 'value', to: :value
|
247
|
+
# ...
|
126
248
|
end
|
127
249
|
|
128
250
|
toml do
|
129
|
-
|
130
|
-
map 'value', to: :value
|
251
|
+
# ...
|
131
252
|
end
|
132
253
|
end
|
133
254
|
----
|
134
255
|
|
135
|
-
|
256
|
+
=== XML
|
257
|
+
|
258
|
+
==== Setting root element name
|
259
|
+
|
260
|
+
The `root` method sets the root element tag name of the XML document.
|
136
261
|
|
137
|
-
|
262
|
+
If `root` is not given, then the snake-cased class name will be used as the
|
263
|
+
root.
|
138
264
|
|
139
|
-
|
265
|
+
[example]
|
266
|
+
Sets the tag name for `<example>` in XML `<example>...</example>`.
|
140
267
|
|
141
|
-
|
268
|
+
Syntax:
|
142
269
|
|
143
270
|
[source,ruby]
|
144
271
|
----
|
145
|
-
|
146
|
-
|
147
|
-
|
272
|
+
xml do
|
273
|
+
root 'xml_element_name'
|
274
|
+
end
|
275
|
+
----
|
148
276
|
|
277
|
+
.Setting the root element name to `example`
|
278
|
+
[example]
|
279
|
+
====
|
280
|
+
[source,ruby]
|
281
|
+
----
|
282
|
+
class Example < Lutaml::Model::Serializable
|
149
283
|
xml do
|
150
284
|
root 'example'
|
151
|
-
map_element 'name', to: :name
|
152
|
-
map_content to: :description
|
153
285
|
end
|
154
286
|
end
|
155
287
|
----
|
156
288
|
|
157
|
-
|
289
|
+
[source,ruby]
|
290
|
+
----
|
291
|
+
> Example.new.to_xml
|
292
|
+
> #<example></example>
|
293
|
+
----
|
294
|
+
====
|
295
|
+
|
296
|
+
==== Mapping elements
|
297
|
+
|
298
|
+
The `map_element` method maps an XML element to a data model attribute.
|
299
|
+
|
300
|
+
[example]
|
301
|
+
To handle the `<name>` tag in `<example><name>John Doe</name></example>`.
|
302
|
+
The value will be set to `John Doe`.
|
158
303
|
|
159
|
-
|
304
|
+
Syntax:
|
305
|
+
|
306
|
+
[source,ruby]
|
307
|
+
----
|
308
|
+
xml do
|
309
|
+
map_element 'xml_element_name', to: :name_of_attribute
|
310
|
+
end
|
311
|
+
----
|
160
312
|
|
313
|
+
.Mapping the `name` tag to the `name` attribute
|
314
|
+
[example]
|
315
|
+
====
|
161
316
|
[source,ruby]
|
162
317
|
----
|
163
318
|
class Example < Lutaml::Model::Serializable
|
164
319
|
attribute :name, Lutaml::Model::Type::String
|
165
|
-
attribute :value, Lutaml::Model::Type::Integer
|
166
320
|
|
167
|
-
|
168
|
-
|
169
|
-
|
321
|
+
xml do
|
322
|
+
root 'example'
|
323
|
+
map_element 'name', to: :name
|
170
324
|
end
|
171
325
|
end
|
172
326
|
----
|
173
327
|
|
174
|
-
|
175
|
-
|
176
|
-
|
328
|
+
[source,xml]
|
329
|
+
----
|
330
|
+
<example><name>John Doe</name></example>
|
331
|
+
----
|
177
332
|
|
178
333
|
[source,ruby]
|
179
334
|
----
|
180
|
-
|
181
|
-
|
182
|
-
|
335
|
+
> Example.from_xml(xml)
|
336
|
+
> #<Example:0x0000000104ac7240 @name="John Doe">
|
337
|
+
> Example.new(name: "John Doe").to_xml
|
338
|
+
> #<example><name>John Doe</name></example>
|
339
|
+
----
|
340
|
+
====
|
183
341
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
342
|
+
==== Mapping attributes
|
343
|
+
|
344
|
+
The `map_attribute` method maps an XML attribute to a data model attribute.
|
345
|
+
|
346
|
+
Syntax:
|
347
|
+
|
348
|
+
[source,ruby]
|
349
|
+
----
|
350
|
+
xml do
|
351
|
+
map_attribute 'xml_attribute_name', to: :name_of_attribute
|
188
352
|
end
|
189
353
|
----
|
190
354
|
|
191
|
-
|
192
|
-
|
193
|
-
|
355
|
+
.Using `map_attribute` to map the `value` attribute
|
356
|
+
[example]
|
357
|
+
====
|
358
|
+
The following class will parse the XML snippet below:
|
194
359
|
|
195
360
|
[source,ruby]
|
196
361
|
----
|
197
362
|
class Example < Lutaml::Model::Serializable
|
198
|
-
attribute :name, Lutaml::Model::Type::String
|
199
363
|
attribute :value, Lutaml::Model::Type::Integer
|
200
364
|
|
201
|
-
|
202
|
-
|
203
|
-
|
365
|
+
xml do
|
366
|
+
root 'example'
|
367
|
+
map_attribute 'value', to: :value
|
204
368
|
end
|
205
369
|
end
|
206
370
|
----
|
207
371
|
|
208
|
-
|
209
|
-
|
210
|
-
|
372
|
+
[source,xml]
|
373
|
+
----
|
374
|
+
<example value=12><name>John Doe</name></example>
|
375
|
+
----
|
211
376
|
|
212
377
|
[source,ruby]
|
213
378
|
----
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
379
|
+
> Example.from_xml(xml)
|
380
|
+
> #<Example:0x0000000104ac7240 @value=12>
|
381
|
+
> Example.new(value: 12).to_xml
|
382
|
+
> #<example value="12"></example>
|
218
383
|
----
|
384
|
+
====
|
219
385
|
|
220
|
-
== Attribute Defaults Using the `default` Option
|
221
386
|
|
222
|
-
|
387
|
+
==== Mapping content
|
388
|
+
|
389
|
+
Content represents the text inside an XML element, inclusive of whitespace.
|
390
|
+
|
391
|
+
The `map_content` method maps an XML element's content to a data model
|
392
|
+
attribute.
|
393
|
+
|
394
|
+
Syntax:
|
223
395
|
|
224
396
|
[source,ruby]
|
225
397
|
----
|
226
|
-
|
227
|
-
|
228
|
-
attribute :temperature, Lutaml::Model::Type::Integer, default: -> { 1050 }
|
398
|
+
xml do
|
399
|
+
map_content to: :name_of_attribute
|
229
400
|
end
|
230
401
|
----
|
231
402
|
|
232
|
-
|
233
|
-
|
234
|
-
|
403
|
+
.Using `map_content` to map content of the `description` tag
|
404
|
+
[example]
|
405
|
+
====
|
406
|
+
The following class will parse the XML snippet below:
|
235
407
|
|
236
408
|
[source,ruby]
|
237
409
|
----
|
238
|
-
class
|
239
|
-
attribute :
|
240
|
-
attribute :glaze, Glaze
|
410
|
+
class Example < Lutaml::Model::Serializable
|
411
|
+
attribute :description, Lutaml::Model::Type::String
|
241
412
|
|
242
|
-
|
243
|
-
|
244
|
-
|
413
|
+
xml do
|
414
|
+
root 'example'
|
415
|
+
map_content to: :description
|
245
416
|
end
|
246
417
|
end
|
247
418
|
----
|
248
419
|
|
249
|
-
|
420
|
+
[source,xml]
|
421
|
+
----
|
422
|
+
<example>John Doe is my moniker.</example>
|
423
|
+
----
|
424
|
+
|
425
|
+
[source,ruby]
|
426
|
+
----
|
427
|
+
> Example.from_xml(xml)
|
428
|
+
> #<Example:0x0000000104ac7240 @description="John Doe is my moniker.">
|
429
|
+
> Example.new(description: "John Doe is my moniker.").to_xml
|
430
|
+
> #<example>John Doe is my moniker.</example>
|
431
|
+
----
|
432
|
+
====
|
433
|
+
|
434
|
+
|
250
435
|
|
251
|
-
|
436
|
+
==== Example for mapping
|
437
|
+
|
438
|
+
[example]
|
439
|
+
====
|
440
|
+
The following class will parse the XML snippet below:
|
252
441
|
|
253
442
|
[source,ruby]
|
254
443
|
----
|
255
|
-
class
|
444
|
+
class Example < Lutaml::Model::Serializable
|
256
445
|
attribute :name, Lutaml::Model::Type::String
|
257
|
-
attribute :
|
446
|
+
attribute :description, Lutaml::Model::Type::String
|
447
|
+
attribute :value, Lutaml::Model::Type::Integer
|
258
448
|
|
259
|
-
|
260
|
-
|
261
|
-
|
449
|
+
xml do
|
450
|
+
root 'example'
|
451
|
+
map_element 'name', to: :name
|
452
|
+
map_attribute 'value', to: :value
|
453
|
+
map_content to: :description
|
262
454
|
end
|
455
|
+
end
|
456
|
+
----
|
263
457
|
|
264
|
-
|
265
|
-
|
266
|
-
|
458
|
+
[source,xml]
|
459
|
+
----
|
460
|
+
<example value=12><name>John Doe</name> is my moniker.</example>
|
461
|
+
----
|
267
462
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
463
|
+
[source,ruby]
|
464
|
+
----
|
465
|
+
> Example.from_xml(xml)
|
466
|
+
> #<Example:0x0000000104ac7240 @name="John Doe", @description=" is my moniker.", @value=12>
|
467
|
+
> Example.new(name: "John Doe", description: " is my moniker.", value: 12).to_xml
|
468
|
+
> #<example value="12"><name>John Doe</name> is my moniker.</example>
|
272
469
|
----
|
470
|
+
====
|
471
|
+
|
472
|
+
|
473
|
+
==== Namespaces
|
273
474
|
|
274
|
-
|
475
|
+
===== Namespace at root
|
275
476
|
|
276
|
-
|
477
|
+
The `namespace` method in the `xml` block sets the namespace for the root
|
478
|
+
element.
|
277
479
|
|
278
|
-
|
480
|
+
Syntax:
|
279
481
|
|
482
|
+
[source,ruby]
|
483
|
+
----
|
484
|
+
xml do
|
485
|
+
namespace 'http://example.com/namespace'
|
486
|
+
end
|
487
|
+
----
|
488
|
+
|
489
|
+
.Using the `namespace` method to set the namespace for the root element
|
490
|
+
[example]
|
491
|
+
====
|
280
492
|
[source,ruby]
|
281
493
|
----
|
282
494
|
class Ceramic < Lutaml::Model::Serializable
|
@@ -292,89 +504,805 @@ class Ceramic < Lutaml::Model::Serializable
|
|
292
504
|
end
|
293
505
|
----
|
294
506
|
|
295
|
-
|
507
|
+
[source,xml]
|
508
|
+
----
|
509
|
+
<Ceramic xmlns='http://example.com/ceramic'><Type>Porcelain</Type><Glaze>Clear</Glaze></Ceramic>
|
510
|
+
----
|
511
|
+
|
512
|
+
[source,ruby]
|
513
|
+
----
|
514
|
+
> Ceramic.from_xml(xml_file)
|
515
|
+
> #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze="Clear">
|
516
|
+
> Ceramic.new(type: "Porcelain", glaze: "Clear").to_xml
|
517
|
+
> #<Ceramic xmlns="http://example.com/ceramic"><Type>Porcelain</Type><Glaze>Clear</Glaze></Ceramic>
|
518
|
+
----
|
519
|
+
====
|
520
|
+
|
521
|
+
===== Namespace on attribute
|
522
|
+
|
523
|
+
If the namespace is defined on an XML attribute, then that will be given
|
524
|
+
priority over the one defined in the class.
|
525
|
+
|
526
|
+
Syntax:
|
527
|
+
|
528
|
+
[source,ruby]
|
529
|
+
----
|
530
|
+
xml do
|
531
|
+
map_element 'xml_element_name', to: :name_of_attribute,
|
532
|
+
namespace: 'http://example.com/namespace',
|
533
|
+
prefix: 'prefix'
|
534
|
+
end
|
535
|
+
----
|
536
|
+
|
537
|
+
`namespace`:: The XML namespace used by this element
|
538
|
+
`prefix`:: The XML namespace prefix used by this element (optional)
|
539
|
+
|
540
|
+
.Using the `namespace` option to set the namespace for an element
|
541
|
+
[example]
|
542
|
+
====
|
543
|
+
In this example, `glz` will be used for `Glaze` if it is added inside the
|
544
|
+
`Ceramic` class, and `glaze` will be used otherwise.
|
296
545
|
|
297
546
|
[source,ruby]
|
298
547
|
----
|
548
|
+
class Glaze < Lutaml::Model::Serializable
|
549
|
+
attribute :color, Lutaml::Model::Type::String
|
550
|
+
attribute :temperature, Lutaml::Model::Type::Integer
|
551
|
+
|
552
|
+
xml do
|
553
|
+
root 'Glaze'
|
554
|
+
namespace 'http://example.com/old_glaze', 'glaze'
|
555
|
+
|
556
|
+
map_element 'color', to: :color
|
557
|
+
map_element 'temperature', to: :temperature
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
299
561
|
class Ceramic < Lutaml::Model::Serializable
|
300
562
|
attribute :type, Lutaml::Model::Type::String
|
301
|
-
attribute :glaze,
|
563
|
+
attribute :glaze, Glaze
|
302
564
|
|
303
565
|
xml do
|
304
566
|
root 'Ceramic'
|
305
567
|
map_element 'Type', to: :type
|
306
|
-
map_element 'Glaze', to: :glaze
|
568
|
+
map_element 'Glaze', to: :glaze, namespace: 'http://example.com/glaze', prefix: "glz"
|
307
569
|
map_attribute 'xmlns', to: :namespace, namespace: 'http://example.com/ceramic'
|
308
570
|
end
|
309
571
|
end
|
310
572
|
----
|
311
573
|
|
312
|
-
|
574
|
+
[source,xml]
|
575
|
+
----
|
576
|
+
<Ceramic xmlns='http://example.com/ceramic'>
|
577
|
+
<Type>Porcelain</Type>
|
578
|
+
<glz:Glaze xmlns='http://example.com/glaze'>
|
579
|
+
<color>Clear</color>
|
580
|
+
<temperature>1050</temperature>
|
581
|
+
</glz:Glaze>
|
582
|
+
</Ceramic>
|
583
|
+
----
|
584
|
+
|
585
|
+
[source,ruby]
|
586
|
+
----
|
587
|
+
> Ceramic.from_xml(xml_file)
|
588
|
+
> #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=1050>>
|
589
|
+
> Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear", temperature: 1050)).to_xml
|
590
|
+
> #<Ceramic xmlns="http://example.com/ceramic"><Type>Porcelain</Type><glz:Glaze xmlns="http://example.com/glaze"><color>Clear</color><temperature>1050</temperature></glz:Glaze></Ceramic>
|
591
|
+
----
|
592
|
+
====
|
593
|
+
|
594
|
+
===== Namespace with `inherit` option
|
595
|
+
|
596
|
+
The `inherit` option is used at the element level to inherit the namespace from
|
597
|
+
the root element.
|
598
|
+
|
599
|
+
Syntax:
|
600
|
+
|
601
|
+
[source,ruby]
|
602
|
+
----
|
603
|
+
xml do
|
604
|
+
map_element 'xml_element_name', to: :name_of_attribute, namespace: :inherit
|
605
|
+
end
|
606
|
+
----
|
607
|
+
|
608
|
+
.Using the `inherit` option to inherit the namespace from the root element
|
609
|
+
[example]
|
610
|
+
====
|
611
|
+
In this example, the `Type` element will inherit the namespace from the root.
|
313
612
|
|
314
613
|
[source,ruby]
|
315
614
|
----
|
316
615
|
class Ceramic < Lutaml::Model::Serializable
|
317
616
|
attribute :type, Lutaml::Model::Type::String
|
318
617
|
attribute :glaze, Lutaml::Model::Type::String
|
618
|
+
attribute :color, Lutaml::Model::Type::String
|
319
619
|
|
320
620
|
xml do
|
321
621
|
root 'Ceramic'
|
322
622
|
namespace 'http://example.com/ceramic', prefix: 'cera'
|
323
623
|
map_element 'Type', to: :type, namespace: :inherit
|
324
624
|
map_element 'Glaze', to: :glaze
|
625
|
+
map_attribute 'color', to: :color, namespace: 'http://example.com/color', prefix: 'clr'
|
325
626
|
end
|
326
627
|
end
|
327
628
|
----
|
328
629
|
|
329
|
-
|
630
|
+
[source,xml]
|
631
|
+
----
|
632
|
+
<Ceramic
|
633
|
+
xmlns:cera='http://example.com/ceramic'
|
634
|
+
xmlns:clr='http://example.com/color'
|
635
|
+
clr:color="navy-blue">
|
636
|
+
<cera:Type>Porcelain</cera:Type>
|
637
|
+
<Glaze>Clear</Glaze>
|
638
|
+
</Ceramic>
|
639
|
+
----
|
640
|
+
|
641
|
+
[source,ruby]
|
642
|
+
----
|
643
|
+
> Ceramic.from_xml(xml_file)
|
644
|
+
> #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze="Clear", @color="navy-blue">
|
645
|
+
> Ceramic.new(type: "Porcelain", glaze: "Clear", color: "navy-blue").to_xml
|
646
|
+
> #<Ceramic xmlns:cera="http://example.com/ceramic"
|
647
|
+
# xmlns:clr='http://example.com/color'
|
648
|
+
# clr:color="navy-blue">
|
649
|
+
# <cera:Type>Porcelain</cera:Type>
|
650
|
+
# <Glaze>Clear</Glaze>
|
651
|
+
# </Ceramic>
|
652
|
+
----
|
653
|
+
====
|
654
|
+
|
655
|
+
|
656
|
+
==== Mixed content
|
657
|
+
|
658
|
+
===== General
|
659
|
+
|
660
|
+
In XML there can be tags that contain content mixed with other tags and where
|
661
|
+
whitespace is significant, such as to represent rich text.
|
662
|
+
|
663
|
+
[example]
|
664
|
+
====
|
665
|
+
[source,xml]
|
666
|
+
----
|
667
|
+
<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>
|
668
|
+
----
|
669
|
+
====
|
670
|
+
|
671
|
+
To map this to Lutaml::Model we can use the `mixed` option in either way:
|
672
|
+
|
673
|
+
* when defining the model;
|
674
|
+
* when referencing the model.
|
675
|
+
|
676
|
+
|
677
|
+
===== Specifying the `mixed` option at `root`
|
678
|
+
|
679
|
+
This will always treat the content of the element itself as mixed content.
|
680
|
+
|
681
|
+
Syntax:
|
682
|
+
|
683
|
+
[source,ruby]
|
684
|
+
----
|
685
|
+
xml do
|
686
|
+
root 'xml_element_name', mixed: true
|
687
|
+
end
|
688
|
+
----
|
689
|
+
|
690
|
+
.Applying `mixed` to treat root as mixed content
|
691
|
+
[example]
|
692
|
+
====
|
693
|
+
[source,ruby]
|
694
|
+
----
|
695
|
+
class Paragraph < Lutaml::Model::Serializable
|
696
|
+
attribute :bold, Lutaml::Model::Type::String
|
697
|
+
attribute :italic, Lutaml::Model::Type::String
|
698
|
+
|
699
|
+
xml do
|
700
|
+
root 'p', mixed: true
|
701
|
+
|
702
|
+
map_element 'bold', to: :bold
|
703
|
+
map_element 'i', to: :italic
|
704
|
+
end
|
705
|
+
end
|
706
|
+
----
|
707
|
+
|
708
|
+
[source,ruby]
|
709
|
+
----
|
710
|
+
> Paragraph.from_xml("<p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p>")
|
711
|
+
> #<Paragraph:0x0000000104ac7240 @bold="John Doe", @italic="28">
|
712
|
+
> Paragraph.new(bold: "John Doe", italic: "28").to_xml
|
713
|
+
> #<p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p>
|
714
|
+
----
|
715
|
+
====
|
716
|
+
|
717
|
+
TODO: How to create mixed content from `#new`?
|
718
|
+
|
719
|
+
|
720
|
+
===== Specifying the `mixed` option when referencing a model
|
721
|
+
|
722
|
+
This will only treat the content of the referenced model as mixed content if the
|
723
|
+
`mixed: true` is added when referencing it.
|
724
|
+
|
725
|
+
Syntax:
|
726
|
+
|
727
|
+
[source,ruby]
|
728
|
+
----
|
729
|
+
xml do
|
730
|
+
map_element 'xml_element_name', to: :name_of_attribute, mixed: true
|
731
|
+
end
|
732
|
+
----
|
733
|
+
|
734
|
+
.Applying `mixed` to treat an inner element as mixed content
|
735
|
+
[example]
|
736
|
+
====
|
737
|
+
[source,ruby]
|
738
|
+
----
|
739
|
+
class Paragraph < Lutaml::Model::Serializable
|
740
|
+
attribute :bold, Lutaml::Model::Type::String
|
741
|
+
attribute :italic, Lutaml::Model::Type::String
|
742
|
+
|
743
|
+
xml do
|
744
|
+
root 'p'
|
745
|
+
|
746
|
+
map_element 'bold', to: :bold
|
747
|
+
map_element 'i', to: :italic
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
class Description < Lutaml::Model::Serializable
|
752
|
+
attribute :paragraph, Paragraph
|
753
|
+
|
754
|
+
xml do
|
755
|
+
root 'description'
|
756
|
+
|
757
|
+
map_element 'p', to: :paragraph, mixed: true
|
758
|
+
end
|
759
|
+
end
|
760
|
+
----
|
761
|
+
|
762
|
+
[source,ruby]
|
763
|
+
----
|
764
|
+
> Description.from_xml("<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>")
|
765
|
+
> #<Description:0x0000000104ac7240 @paragraph=#<Paragraph:0x0000000104ac7240 @bold="John Doe", @italic="28">>
|
766
|
+
> Description.new(paragraph: Paragraph.new(bold: "John Doe", italic: "28")).to_xml
|
767
|
+
> #<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>
|
768
|
+
----
|
769
|
+
====
|
770
|
+
|
771
|
+
|
772
|
+
=== Key value data models
|
773
|
+
|
774
|
+
==== General
|
775
|
+
|
776
|
+
Key-value data models like JSON, YAML, and TOML all share a similar structure
|
777
|
+
where data is stored as key-value pairs.
|
778
|
+
|
779
|
+
Lutaml::Model works with these formats in a similar way.
|
780
|
+
|
781
|
+
==== Mapping
|
782
|
+
|
783
|
+
The `map` method is used to define key-value mappings.
|
784
|
+
|
785
|
+
Syntax:
|
786
|
+
|
787
|
+
[source,ruby]
|
788
|
+
----
|
789
|
+
json | yaml | toml do
|
790
|
+
map 'key_value_model_attribute_name', to: :name_of_attribute
|
791
|
+
end
|
792
|
+
----
|
793
|
+
|
794
|
+
.Using the `map` method to define key-value mappings
|
795
|
+
[example]
|
796
|
+
====
|
797
|
+
[source,ruby]
|
798
|
+
----
|
799
|
+
class Example < Lutaml::Model::Serializable
|
800
|
+
attribute :name, Lutaml::Model::Type::String
|
801
|
+
attribute :value, Lutaml::Model::Type::Integer
|
802
|
+
|
803
|
+
json do
|
804
|
+
map 'name', to: :name
|
805
|
+
map 'value', to: :value
|
806
|
+
end
|
807
|
+
|
808
|
+
yaml do
|
809
|
+
map 'name', to: :name
|
810
|
+
map 'value', to: :value
|
811
|
+
end
|
812
|
+
|
813
|
+
toml do
|
814
|
+
map 'name', to: :name
|
815
|
+
map 'value', to: :value
|
816
|
+
end
|
817
|
+
end
|
818
|
+
----
|
819
|
+
|
820
|
+
[source,json]
|
821
|
+
----
|
822
|
+
{
|
823
|
+
"name": "John Doe",
|
824
|
+
"value": 28
|
825
|
+
}
|
826
|
+
----
|
827
|
+
|
828
|
+
[source,ruby]
|
829
|
+
----
|
830
|
+
> Example.from_json(json)
|
831
|
+
> #<Example:0x0000000104ac7240 @name="John Doe", @value=28>
|
832
|
+
> Example.new(name: "John Doe", value: 28).to_json
|
833
|
+
> #{"name"=>"John Doe", "value"=>28}
|
834
|
+
----
|
835
|
+
====
|
836
|
+
|
837
|
+
|
838
|
+
==== Nested attribute mappings
|
839
|
+
|
840
|
+
The `map` method can also be used to map nested key-value data models
|
841
|
+
by referring to a Lutaml::Model class as an attribute class.
|
842
|
+
|
843
|
+
[example]
|
844
|
+
====
|
845
|
+
[source,ruby]
|
846
|
+
----
|
847
|
+
class Glaze < Lutaml::Model::Serializable
|
848
|
+
attribute :color, Lutaml::Model::Type::String
|
849
|
+
attribute :temperature, Lutaml::Model::Type::Integer
|
330
850
|
|
331
|
-
|
851
|
+
json do
|
852
|
+
map 'color', to: :color
|
853
|
+
map 'temperature', to: :temperature
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
class Ceramic < Lutaml::Model::Serializable
|
858
|
+
attribute :type, Lutaml::Model::Type::String
|
859
|
+
attribute :glaze, Glaze
|
860
|
+
|
861
|
+
json do
|
862
|
+
map 'type', to: :type
|
863
|
+
map 'glaze', to: :glaze
|
864
|
+
end
|
865
|
+
end
|
866
|
+
----
|
332
867
|
|
333
|
-
|
868
|
+
[source,json]
|
869
|
+
----
|
870
|
+
{
|
871
|
+
"type": "Porcelain",
|
872
|
+
"glaze": {
|
873
|
+
"color": "Clear",
|
874
|
+
"temperature": 1050
|
875
|
+
}
|
876
|
+
}
|
877
|
+
----
|
334
878
|
|
879
|
+
[source,ruby]
|
880
|
+
----
|
881
|
+
> Ceramic.from_json(json)
|
882
|
+
> #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=1050>>
|
883
|
+
> Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear", temperature: 1050)).to_json
|
884
|
+
> #{"type"=>"Porcelain", "glaze"=>{"color"=>"Clear", "temperature"=>1050}}
|
885
|
+
----
|
886
|
+
====
|
887
|
+
|
888
|
+
=== Advanced attribute mapping
|
889
|
+
|
890
|
+
==== Attribute mapping delegation
|
891
|
+
|
892
|
+
Delegate attribute mappings to nested objects using the `delegate` option.
|
893
|
+
|
894
|
+
Syntax:
|
895
|
+
|
896
|
+
[source,ruby]
|
897
|
+
----
|
898
|
+
xml | json | yaml | toml do
|
899
|
+
map 'key_value_model_attribute_name', to: :name_of_attribute, delegate: :model_to_delegate_to
|
900
|
+
end
|
901
|
+
----
|
902
|
+
|
903
|
+
.Using the `delegate` option to map attributes to nested objects
|
904
|
+
[example]
|
905
|
+
====
|
906
|
+
The following class will parse the JSON snippet below:
|
907
|
+
|
908
|
+
[source,ruby]
|
909
|
+
----
|
910
|
+
class Glaze < Lutaml::Model::Serializable
|
911
|
+
attribute :color, Lutaml::Model::Type::String
|
912
|
+
attribute :temperature, Lutaml::Model::Type::Integer
|
913
|
+
|
914
|
+
json do
|
915
|
+
map 'color', to: :color
|
916
|
+
map 'temperature', to: :temperature
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
class Ceramic < Lutaml::Model::Serializable
|
921
|
+
attribute :type, Lutaml::Model::Type::String
|
922
|
+
attribute :glaze, Glaze
|
923
|
+
|
924
|
+
json do
|
925
|
+
map 'type', to: :type
|
926
|
+
map 'color', to: :color, delegate: :glaze
|
927
|
+
end
|
928
|
+
end
|
929
|
+
----
|
930
|
+
|
931
|
+
[source,json]
|
932
|
+
----
|
933
|
+
{
|
934
|
+
"type": "Porcelain",
|
935
|
+
"color": "Clear"
|
936
|
+
}
|
937
|
+
----
|
938
|
+
|
939
|
+
[source,ruby]
|
940
|
+
----
|
941
|
+
> Ceramic.from_json(json)
|
942
|
+
> #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=nil>>
|
943
|
+
> Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear")).to_json
|
944
|
+
> #{"type"=>"Porcelain", "color"=>"Clear"}
|
945
|
+
----
|
946
|
+
====
|
947
|
+
|
948
|
+
|
949
|
+
==== Attribute serialization with custom methods
|
950
|
+
|
951
|
+
Define custom methods for specific attribute mappings using the `with:` key for
|
952
|
+
each serialization mapping block for `from` and `to`.
|
953
|
+
|
954
|
+
Syntax:
|
955
|
+
|
956
|
+
[source,ruby]
|
957
|
+
----
|
958
|
+
xml | json | yaml | toml do
|
959
|
+
map 'key_value_model_attribute_name', to: :name_of_attribute, with: {
|
960
|
+
to: :method_name_to_serialize,
|
961
|
+
from: :method_name_to_deserialize
|
962
|
+
}
|
963
|
+
end
|
964
|
+
----
|
965
|
+
|
966
|
+
.Using the `with:` key to define custom serialization methods
|
967
|
+
[example]
|
968
|
+
====
|
969
|
+
The following class will parse the JSON snippet below:
|
970
|
+
|
971
|
+
[source,ruby]
|
972
|
+
----
|
973
|
+
class CustomCeramic < Lutaml::Model::Serializable
|
974
|
+
attribute :name, Lutaml::Model::Type::String
|
975
|
+
attribute :size, Lutaml::Model::Type::Integer
|
976
|
+
|
977
|
+
json do
|
978
|
+
map 'name', to: :name, with: { to: :name_to_json, from: :name_from_json }
|
979
|
+
map 'size', to: :size
|
980
|
+
end
|
981
|
+
|
982
|
+
def name_to_json(model, value)
|
983
|
+
"Masterpiece: #{value}"
|
984
|
+
end
|
985
|
+
|
986
|
+
def name_from_json(model, doc)
|
987
|
+
doc['name'].sub(/^Masterpiece: /, '')
|
988
|
+
end
|
989
|
+
end
|
990
|
+
----
|
991
|
+
|
992
|
+
[source,json]
|
993
|
+
----
|
994
|
+
{
|
995
|
+
"name": "Masterpiece: Vase",
|
996
|
+
"size": 12
|
997
|
+
}
|
998
|
+
----
|
999
|
+
|
1000
|
+
[source,ruby]
|
1001
|
+
----
|
1002
|
+
> CustomCeramic.from_json(json)
|
1003
|
+
> #<CustomCeramic:0x0000000104ac7240 @name="Vase", @size=12>
|
1004
|
+
> CustomCeramic.new(name: "Vase", size: 12).to_json
|
1005
|
+
> #{"name"=>"Masterpiece: Vase", "size"=>12}
|
1006
|
+
----
|
1007
|
+
====
|
1008
|
+
|
1009
|
+
|
1010
|
+
|
1011
|
+
==== Attribute extraction
|
1012
|
+
|
1013
|
+
NOTE: This feature is for key-value data model serialization only.
|
1014
|
+
|
1015
|
+
The `child_mappings` option is used to extract results from a key-value data
|
1016
|
+
model (JSON, YAML, TOML) into a `Lutaml::Model` collection.
|
1017
|
+
|
1018
|
+
The values are extracted from the key-value data model using the list of keys
|
1019
|
+
provided.
|
1020
|
+
|
1021
|
+
Syntax:
|
1022
|
+
|
1023
|
+
[source,ruby]
|
1024
|
+
----
|
1025
|
+
json | yaml | toml do
|
1026
|
+
map 'key_value_model_attribute_name', to: :name_of_attribute,
|
1027
|
+
child_mappings: {
|
1028
|
+
key_attribute_name_1: <1>
|
1029
|
+
{path_to_value_1}, <2>
|
1030
|
+
key_attribute_name_2:
|
1031
|
+
{path_to_value_2},
|
1032
|
+
# ...
|
1033
|
+
}
|
1034
|
+
end
|
1035
|
+
----
|
1036
|
+
<1> The `key_attribute_name_1` is the attribute name in the model. The value of
|
1037
|
+
this attribute will be assigned the key of the hash in the key-value data model.
|
1038
|
+
|
1039
|
+
<2> The `path_to_value_1` is an array of keys that represent the path to the
|
1040
|
+
value in the key-value data model. The keys are used to extract the value from
|
1041
|
+
the key-value data model and assign it to the attribute in the model.
|
1042
|
+
|
1043
|
+
The `path_to_value` is in a nested array format with each value a symbol, where
|
1044
|
+
each symbol represents a key to traverse down. The last key in the path is the
|
1045
|
+
value to be extracted.
|
1046
|
+
|
1047
|
+
.Determining the path to value in a key-value data model
|
1048
|
+
[example]
|
1049
|
+
====
|
1050
|
+
The following JSON contains 2 keys in schema named `engine` and `gearbox`.
|
1051
|
+
|
1052
|
+
[source,json]
|
1053
|
+
----
|
1054
|
+
{
|
1055
|
+
"components": {
|
1056
|
+
"engine": {
|
1057
|
+
"manufacturer": "Ford",
|
1058
|
+
"model": "V8"
|
1059
|
+
},
|
1060
|
+
"gearbox": {
|
1061
|
+
"manufacturer": "Toyota",
|
1062
|
+
"model": "4-speed"
|
1063
|
+
}
|
1064
|
+
}
|
1065
|
+
}
|
1066
|
+
----
|
1067
|
+
|
1068
|
+
The path to value for the `engine` schema is `[:components, :engine]` and for
|
1069
|
+
the `gearbox` schema is `[:components, :gearbox]`.
|
1070
|
+
====
|
1071
|
+
|
1072
|
+
In `path_to_value`, the `:key` and `:value` are reserved instructions used to
|
1073
|
+
assign the key or value of the serialization data respectively as the value to
|
1074
|
+
the attribute.
|
1075
|
+
|
1076
|
+
[example]
|
1077
|
+
====
|
1078
|
+
In the following JSON content, the `path_to_value` for the object keys named
|
1079
|
+
`engine` and `gearbox` will utilize the `:key` keyword to assign the key of the
|
1080
|
+
object as the value of a designated attribute.
|
1081
|
+
|
1082
|
+
[source,json]
|
1083
|
+
----
|
1084
|
+
{
|
1085
|
+
"components": {
|
1086
|
+
"engine": { /*...*/ },
|
1087
|
+
"gearbox": { /*...*/ }
|
1088
|
+
}
|
1089
|
+
}
|
1090
|
+
----
|
1091
|
+
====
|
1092
|
+
|
1093
|
+
If a specified value path is not found, the corresponding attribute in the model
|
1094
|
+
will be assigned a `nil` value.
|
1095
|
+
|
1096
|
+
.Attribute values set to `nil` when the `path_to_value` is not found
|
1097
|
+
[example]
|
1098
|
+
====
|
1099
|
+
In the following JSON content, the `path_to_value` of `[:extras, :sunroof]` and
|
1100
|
+
`[:extras, :drinks_cooler]` at the object `"gearbox"` would be set to `nil`.
|
1101
|
+
|
1102
|
+
[source,json]
|
1103
|
+
----
|
1104
|
+
{
|
1105
|
+
"components": {
|
1106
|
+
"engine": {
|
1107
|
+
"manufacturer": "Ford",
|
1108
|
+
"extras": {
|
1109
|
+
"sunroof": true,
|
1110
|
+
"drinks_cooler": true
|
1111
|
+
}
|
1112
|
+
},
|
1113
|
+
"gearbox": {
|
1114
|
+
"manufacturer": "Toyota"
|
1115
|
+
}
|
1116
|
+
}
|
1117
|
+
}
|
1118
|
+
----
|
1119
|
+
====
|
1120
|
+
|
1121
|
+
|
1122
|
+
.Using the `child_mappings` option to extract values from a key-value data model
|
1123
|
+
[example]
|
1124
|
+
====
|
1125
|
+
The following JSON contains 2 keys in schema named `foo` and `bar`.
|
1126
|
+
|
1127
|
+
[source,json]
|
1128
|
+
----
|
1129
|
+
{
|
1130
|
+
"schemas": {
|
1131
|
+
"foo": { <1>
|
1132
|
+
"path": { <2>
|
1133
|
+
"link": "link one",
|
1134
|
+
"name": "one"
|
1135
|
+
}
|
1136
|
+
},
|
1137
|
+
"bar": { <1>
|
1138
|
+
"path": { <2>
|
1139
|
+
"link": "link two",
|
1140
|
+
"name": "two"
|
1141
|
+
}
|
1142
|
+
}
|
1143
|
+
}
|
1144
|
+
}
|
1145
|
+
----
|
1146
|
+
<1> The keys `foo` and `bar` are to be mapped to the `id` attribute.
|
1147
|
+
<2> The nested `path.link` and `path.name` keys are used as the `link` and
|
1148
|
+
`name` attributes, respectively.
|
1149
|
+
|
1150
|
+
A model can be defined for this JSON as follows:
|
1151
|
+
|
1152
|
+
[source,ruby]
|
1153
|
+
----
|
1154
|
+
class Schema < Lutaml::Model::Serializable
|
1155
|
+
attribute :id, Lutaml::Model::Type::String
|
1156
|
+
attribute :link, Lutaml::Model::Type::String
|
1157
|
+
attribute :name, Lutaml::Model::Type::String
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
class ChildMappingClass < Lutaml::Model::Serializable
|
1161
|
+
attribute :schemas, Schema, collection: true
|
1162
|
+
|
1163
|
+
json do
|
1164
|
+
map "schemas", to: :schemas,
|
1165
|
+
child_mappings: {
|
1166
|
+
id: :key,
|
1167
|
+
link: %i[path link],
|
1168
|
+
name: %i[path name],
|
1169
|
+
}
|
1170
|
+
end
|
1171
|
+
end
|
1172
|
+
----
|
1173
|
+
|
1174
|
+
The output becomes:
|
1175
|
+
|
1176
|
+
[source,ruby]
|
1177
|
+
----
|
1178
|
+
> ChildMappingClass.from_json(json)
|
1179
|
+
> #<ChildMappingClass:0x0000000104ac7240
|
1180
|
+
@schemas=
|
1181
|
+
[#<Schema:0x0000000104ac6e30 @id="foo", @link="link one", @name="one">,
|
1182
|
+
#<Schema:0x0000000104ac58f0 @id="bar", @link="link two", @name="two">]>
|
1183
|
+
> ChildMappingClass.new(schemas: [Schema.new(id: "foo", link: "link one", name: "one"), Schema.new(id: "bar", link: "link two", name: "two")]).to_json
|
1184
|
+
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}
|
1185
|
+
----
|
1186
|
+
|
1187
|
+
In this example:
|
1188
|
+
|
1189
|
+
* The `key` of each schema (`foo` and `bar`) is mapped to the `id` attribute.
|
1190
|
+
|
1191
|
+
* The nested `path.link` and `path.name` keys are mapped to the `link` and
|
1192
|
+
`name` attributes, respectively.
|
1193
|
+
====
|
1194
|
+
|
1195
|
+
|
1196
|
+
== Adapters
|
1197
|
+
|
1198
|
+
=== General
|
1199
|
+
|
1200
|
+
Lutaml::Model uses an adapter pattern to support multiple libraries for each
|
1201
|
+
serialization format.
|
1202
|
+
|
1203
|
+
=== XML
|
1204
|
+
|
1205
|
+
Lutaml::Model supports the following XML adapters:
|
1206
|
+
|
1207
|
+
* Nokogiri (default)
|
1208
|
+
* Oga (optional, plain Ruby suitable for Opal/JS)
|
1209
|
+
* Ox (optional)
|
1210
|
+
|
1211
|
+
.Using the Nokogiri XML adapter
|
335
1212
|
[source,ruby]
|
336
1213
|
----
|
337
1214
|
require 'lutaml/model'
|
338
|
-
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
339
|
-
require 'lutaml/model/xml_adapter/ox_adapter'
|
340
|
-
require 'lutaml/model/xml_adapter/oga_adapter'
|
341
1215
|
|
342
1216
|
Lutaml::Model::Config.configure do |config|
|
1217
|
+
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
343
1218
|
config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
|
344
|
-
# Or use OxAdapter or OgaAdapter
|
345
1219
|
end
|
346
1220
|
----
|
347
1221
|
|
348
|
-
|
1222
|
+
.Using the Oga XML adapter
|
1223
|
+
[source,ruby]
|
1224
|
+
----
|
1225
|
+
require 'lutaml/model'
|
1226
|
+
|
1227
|
+
Lutaml::Model::Config.configure do |config|
|
1228
|
+
require 'lutaml/model/xml_adapter/oga_adapter'
|
1229
|
+
config.xml_adapter = Lutaml::Model::XmlAdapter::OgaAdapter
|
1230
|
+
end
|
1231
|
+
----
|
1232
|
+
|
1233
|
+
.Using the Ox XML adapter
|
1234
|
+
[source,ruby]
|
1235
|
+
----
|
1236
|
+
require 'lutaml/model'
|
1237
|
+
|
1238
|
+
Lutaml::Model::Config.configure do |config|
|
1239
|
+
require 'lutaml/model/xml_adapter/ox_adapter'
|
1240
|
+
config.xml_adapter = Lutaml::Model::XmlAdapter::OxAdapter
|
1241
|
+
end
|
1242
|
+
----
|
1243
|
+
|
1244
|
+
|
1245
|
+
=== JSON
|
1246
|
+
|
1247
|
+
Lutaml::Model supports the following JSON adapters:
|
1248
|
+
|
1249
|
+
* JSON (default)
|
1250
|
+
* MultiJson (optional)
|
349
1251
|
|
1252
|
+
.Using the JSON adapter
|
350
1253
|
[source,ruby]
|
351
1254
|
----
|
352
1255
|
require 'lutaml/model'
|
353
|
-
require 'lutaml/model/json_adapter/standard'
|
354
|
-
require 'lutaml/model/json_adapter/multi_json'
|
355
1256
|
|
356
1257
|
Lutaml::Model::Config.configure do |config|
|
1258
|
+
require 'lutaml/model/json_adapter/standard'
|
357
1259
|
config.json_adapter = Lutaml::Model::JsonAdapter::StandardDocument
|
358
|
-
# Or use MultiJsonDocument
|
359
1260
|
end
|
360
1261
|
----
|
361
1262
|
|
362
|
-
|
1263
|
+
.Using the MultiJson adapter
|
1264
|
+
[source,ruby]
|
1265
|
+
----
|
1266
|
+
require 'lutaml/model'
|
1267
|
+
|
1268
|
+
Lutaml::Model::Config.configure do |config|
|
1269
|
+
require 'lutaml/model/json_adapter/multi_json'
|
1270
|
+
config.json_adapter = Lutaml::Model::JsonAdapter::MultiJsonDocument
|
1271
|
+
end
|
1272
|
+
----
|
1273
|
+
|
1274
|
+
=== TOML
|
1275
|
+
|
1276
|
+
Lutaml::Model supports the following TOML adapters:
|
363
1277
|
|
1278
|
+
* Toml-rb (default)
|
1279
|
+
* Tomlib (optional)
|
1280
|
+
|
1281
|
+
.Using the Toml-rb adapter
|
364
1282
|
[source,ruby]
|
365
1283
|
----
|
366
1284
|
require 'lutaml/model'
|
367
|
-
require 'lutaml/model/toml_adapter/toml_rb_adapter'
|
368
|
-
require 'lutaml/model/toml_adapter/tomlib_adapter'
|
369
1285
|
|
370
1286
|
Lutaml::Model::Config.configure do |config|
|
1287
|
+
require 'lutaml/model/toml_adapter/toml_rb_adapter'
|
371
1288
|
config.toml_adapter = Lutaml::Model::TomlAdapter::TomlRbDocument
|
372
|
-
|
1289
|
+
end
|
1290
|
+
----
|
1291
|
+
|
1292
|
+
.Using the Tomlib adapter
|
1293
|
+
[source,ruby]
|
1294
|
+
----
|
1295
|
+
require 'lutaml/model'
|
1296
|
+
|
1297
|
+
Lutaml::Model::Config.configure do |config|
|
1298
|
+
config.toml_adapter = Lutaml::Model::TomlAdapter::TomlibDocument
|
1299
|
+
require 'lutaml/model/toml_adapter/tomlib_adapter'
|
373
1300
|
end
|
374
1301
|
----
|
375
1302
|
|
376
1303
|
== License and Copyright
|
377
1304
|
|
378
|
-
This project is licensed under the BSD 2-clause License
|
1305
|
+
This project is licensed under the BSD 2-clause License.
|
1306
|
+
See the LICENSE file for details.
|
379
1307
|
|
380
|
-
|
1308
|
+
Copyright Ribose.
|