lutaml-model 0.3.0 → 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/README.adoc +995 -138
- 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_rule.rb +5 -2
- data/lib/lutaml/model/serialize.rb +220 -150
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +2 -2
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +15 -7
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +16 -8
- data/lib/lutaml/model/xml_adapter.rb +31 -12
- data/lib/lutaml/model/yaml_adapter.rb +11 -3
- data/lib/lutaml/model.rb +7 -0
- metadata +4 -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,203 +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
|
109
224
|
|
110
|
-
|
225
|
+
Lutaml::Model allows you to translate a data model into serialization models of
|
226
|
+
various serialization formats including XML, JSON, YAML, and TOML.
|
111
227
|
|
112
|
-
|
228
|
+
Depending on the serialization format, different methods are supported for
|
229
|
+
defining serialization and deserialization mappings.
|
113
230
|
|
231
|
+
Serialization model mappings are defined under the `xml`, `json`, `yaml`, and
|
232
|
+
`toml` blocks.
|
233
|
+
|
234
|
+
.Using the `xml`, `json`, `yaml`, and `toml` blocks to define serialization mappings
|
114
235
|
[source,ruby]
|
115
236
|
----
|
116
237
|
class Example < Lutaml::Model::Serializable
|
117
|
-
|
118
|
-
|
238
|
+
xml do
|
239
|
+
# ...
|
240
|
+
end
|
119
241
|
|
120
242
|
json do
|
121
|
-
|
122
|
-
map 'value', to: :value
|
243
|
+
# ...
|
123
244
|
end
|
124
245
|
|
125
246
|
yaml do
|
126
|
-
|
127
|
-
map 'value', to: :value
|
247
|
+
# ...
|
128
248
|
end
|
129
249
|
|
130
250
|
toml do
|
131
|
-
|
132
|
-
map 'value', to: :value
|
251
|
+
# ...
|
133
252
|
end
|
134
253
|
end
|
135
254
|
----
|
136
255
|
|
137
|
-
|
256
|
+
=== XML
|
257
|
+
|
258
|
+
==== Setting root element name
|
138
259
|
|
139
|
-
|
260
|
+
The `root` method sets the root element tag name of the XML document.
|
140
261
|
|
141
|
-
|
262
|
+
If `root` is not given, then the snake-cased class name will be used as the
|
263
|
+
root.
|
142
264
|
|
143
|
-
|
265
|
+
[example]
|
266
|
+
Sets the tag name for `<example>` in XML `<example>...</example>`.
|
267
|
+
|
268
|
+
Syntax:
|
144
269
|
|
145
270
|
[source,ruby]
|
146
271
|
----
|
147
|
-
|
148
|
-
|
149
|
-
|
272
|
+
xml do
|
273
|
+
root 'xml_element_name'
|
274
|
+
end
|
275
|
+
----
|
150
276
|
|
277
|
+
.Setting the root element name to `example`
|
278
|
+
[example]
|
279
|
+
====
|
280
|
+
[source,ruby]
|
281
|
+
----
|
282
|
+
class Example < Lutaml::Model::Serializable
|
151
283
|
xml do
|
152
284
|
root 'example'
|
153
|
-
map_element 'name', to: :name
|
154
|
-
map_content to: :description
|
155
285
|
end
|
156
286
|
end
|
157
287
|
----
|
158
288
|
|
159
|
-
|
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`.
|
303
|
+
|
304
|
+
Syntax:
|
160
305
|
|
161
|
-
|
306
|
+
[source,ruby]
|
307
|
+
----
|
308
|
+
xml do
|
309
|
+
map_element 'xml_element_name', to: :name_of_attribute
|
310
|
+
end
|
311
|
+
----
|
162
312
|
|
313
|
+
.Mapping the `name` tag to the `name` attribute
|
314
|
+
[example]
|
315
|
+
====
|
163
316
|
[source,ruby]
|
164
317
|
----
|
165
318
|
class Example < Lutaml::Model::Serializable
|
166
319
|
attribute :name, Lutaml::Model::Type::String
|
167
|
-
attribute :value, Lutaml::Model::Type::Integer
|
168
320
|
|
169
|
-
|
170
|
-
|
171
|
-
|
321
|
+
xml do
|
322
|
+
root 'example'
|
323
|
+
map_element 'name', to: :name
|
172
324
|
end
|
173
325
|
end
|
174
326
|
----
|
175
327
|
|
176
|
-
|
177
|
-
|
178
|
-
|
328
|
+
[source,xml]
|
329
|
+
----
|
330
|
+
<example><name>John Doe</name></example>
|
331
|
+
----
|
179
332
|
|
180
333
|
[source,ruby]
|
181
334
|
----
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
+
====
|
185
341
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
190
352
|
end
|
191
353
|
----
|
192
354
|
|
193
|
-
|
194
|
-
|
195
|
-
|
355
|
+
.Using `map_attribute` to map the `value` attribute
|
356
|
+
[example]
|
357
|
+
====
|
358
|
+
The following class will parse the XML snippet below:
|
196
359
|
|
197
360
|
[source,ruby]
|
198
361
|
----
|
199
362
|
class Example < Lutaml::Model::Serializable
|
200
|
-
attribute :name, Lutaml::Model::Type::String
|
201
363
|
attribute :value, Lutaml::Model::Type::Integer
|
202
364
|
|
203
|
-
|
204
|
-
|
205
|
-
|
365
|
+
xml do
|
366
|
+
root 'example'
|
367
|
+
map_attribute 'value', to: :value
|
206
368
|
end
|
207
369
|
end
|
208
370
|
----
|
209
371
|
|
210
|
-
|
211
|
-
|
212
|
-
|
372
|
+
[source,xml]
|
373
|
+
----
|
374
|
+
<example value=12><name>John Doe</name></example>
|
375
|
+
----
|
213
376
|
|
214
377
|
[source,ruby]
|
215
378
|
----
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
379
|
+
> Example.from_xml(xml)
|
380
|
+
> #<Example:0x0000000104ac7240 @value=12>
|
381
|
+
> Example.new(value: 12).to_xml
|
382
|
+
> #<example value="12"></example>
|
220
383
|
----
|
384
|
+
====
|
221
385
|
|
222
|
-
== Attribute Defaults Using the `default` Option
|
223
386
|
|
224
|
-
|
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:
|
225
395
|
|
226
396
|
[source,ruby]
|
227
397
|
----
|
228
|
-
|
229
|
-
|
230
|
-
attribute :temperature, Lutaml::Model::Type::Integer, default: -> { 1050 }
|
398
|
+
xml do
|
399
|
+
map_content to: :name_of_attribute
|
231
400
|
end
|
232
401
|
----
|
233
402
|
|
234
|
-
|
235
|
-
|
236
|
-
|
403
|
+
.Using `map_content` to map content of the `description` tag
|
404
|
+
[example]
|
405
|
+
====
|
406
|
+
The following class will parse the XML snippet below:
|
237
407
|
|
238
408
|
[source,ruby]
|
239
409
|
----
|
240
|
-
class
|
241
|
-
attribute :
|
242
|
-
attribute :glaze, Glaze
|
410
|
+
class Example < Lutaml::Model::Serializable
|
411
|
+
attribute :description, Lutaml::Model::Type::String
|
243
412
|
|
244
|
-
|
245
|
-
|
246
|
-
|
413
|
+
xml do
|
414
|
+
root 'example'
|
415
|
+
map_content to: :description
|
247
416
|
end
|
248
417
|
end
|
249
418
|
----
|
250
419
|
|
251
|
-
|
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
|
+
|
435
|
+
|
436
|
+
==== Example for mapping
|
252
437
|
|
253
|
-
|
438
|
+
[example]
|
439
|
+
====
|
440
|
+
The following class will parse the XML snippet below:
|
254
441
|
|
255
442
|
[source,ruby]
|
256
443
|
----
|
257
|
-
class
|
444
|
+
class Example < Lutaml::Model::Serializable
|
258
445
|
attribute :name, Lutaml::Model::Type::String
|
259
|
-
attribute :
|
446
|
+
attribute :description, Lutaml::Model::Type::String
|
447
|
+
attribute :value, Lutaml::Model::Type::Integer
|
260
448
|
|
261
|
-
|
262
|
-
|
263
|
-
|
449
|
+
xml do
|
450
|
+
root 'example'
|
451
|
+
map_element 'name', to: :name
|
452
|
+
map_attribute 'value', to: :value
|
453
|
+
map_content to: :description
|
264
454
|
end
|
455
|
+
end
|
456
|
+
----
|
265
457
|
|
266
|
-
|
267
|
-
|
268
|
-
|
458
|
+
[source,xml]
|
459
|
+
----
|
460
|
+
<example value=12><name>John Doe</name> is my moniker.</example>
|
461
|
+
----
|
269
462
|
|
270
|
-
|
271
|
-
doc['name'].sub('Masterpiece: ', '')
|
272
|
-
end
|
273
|
-
end
|
463
|
+
[source,ruby]
|
274
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>
|
469
|
+
----
|
470
|
+
====
|
471
|
+
|
472
|
+
|
473
|
+
==== Namespaces
|
474
|
+
|
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
|
|
280
|
-
|
482
|
+
[source,ruby]
|
483
|
+
----
|
484
|
+
xml do
|
485
|
+
namespace 'http://example.com/namespace'
|
486
|
+
end
|
487
|
+
----
|
281
488
|
|
489
|
+
.Using the `namespace` method to set the namespace for the root element
|
490
|
+
[example]
|
491
|
+
====
|
282
492
|
[source,ruby]
|
283
493
|
----
|
284
494
|
class Ceramic < Lutaml::Model::Serializable
|
@@ -294,9 +504,44 @@ class Ceramic < Lutaml::Model::Serializable
|
|
294
504
|
end
|
295
505
|
----
|
296
506
|
|
297
|
-
|
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)
|
298
539
|
|
299
|
-
|
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.
|
300
545
|
|
301
546
|
[source,ruby]
|
302
547
|
----
|
@@ -326,41 +571,133 @@ class Ceramic < Lutaml::Model::Serializable
|
|
326
571
|
end
|
327
572
|
----
|
328
573
|
|
329
|
-
|
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.
|
330
612
|
|
331
613
|
[source,ruby]
|
332
614
|
----
|
333
615
|
class Ceramic < Lutaml::Model::Serializable
|
334
616
|
attribute :type, Lutaml::Model::Type::String
|
335
617
|
attribute :glaze, Lutaml::Model::Type::String
|
618
|
+
attribute :color, Lutaml::Model::Type::String
|
336
619
|
|
337
620
|
xml do
|
338
621
|
root 'Ceramic'
|
339
622
|
namespace 'http://example.com/ceramic', prefix: 'cera'
|
340
623
|
map_element 'Type', to: :type, namespace: :inherit
|
341
624
|
map_element 'Glaze', to: :glaze
|
625
|
+
map_attribute 'color', to: :color, namespace: 'http://example.com/color', prefix: 'clr'
|
342
626
|
end
|
343
627
|
end
|
344
628
|
----
|
345
629
|
|
346
|
-
|
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
|
+
====
|
347
654
|
|
348
|
-
In XML there can be some tags that containg content mixed with other tags for example `<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>`
|
349
655
|
|
350
|
-
|
656
|
+
==== Mixed content
|
351
657
|
|
352
|
-
|
658
|
+
===== General
|
353
659
|
|
354
|
-
|
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.
|
355
662
|
|
356
|
-
[
|
663
|
+
[example]
|
664
|
+
====
|
665
|
+
[source,xml]
|
357
666
|
----
|
358
|
-
|
359
|
-
|
360
|
-
|
667
|
+
<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>
|
668
|
+
----
|
669
|
+
====
|
361
670
|
|
362
|
-
|
363
|
-
|
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
|
364
701
|
|
365
702
|
map_element 'bold', to: :bold
|
366
703
|
map_element 'i', to: :italic
|
@@ -368,10 +705,35 @@ class Paragraph < Lutaml::Model::Serializable
|
|
368
705
|
end
|
369
706
|
----
|
370
707
|
|
371
|
-
|
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`?
|
372
718
|
|
373
|
-
|
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:
|
374
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
|
+
====
|
375
737
|
[source,ruby]
|
376
738
|
----
|
377
739
|
class Paragraph < Lutaml::Model::Serializable
|
@@ -397,55 +759,550 @@ class Description < Lutaml::Model::Serializable
|
|
397
759
|
end
|
398
760
|
----
|
399
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
|
850
|
+
|
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
|
+
----
|
867
|
+
|
868
|
+
[source,json]
|
869
|
+
----
|
870
|
+
{
|
871
|
+
"type": "Porcelain",
|
872
|
+
"glaze": {
|
873
|
+
"color": "Clear",
|
874
|
+
"temperature": 1050
|
875
|
+
}
|
876
|
+
}
|
877
|
+
----
|
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
|
+
|
400
1196
|
== Adapters
|
401
1197
|
|
402
|
-
|
1198
|
+
=== General
|
1199
|
+
|
1200
|
+
Lutaml::Model uses an adapter pattern to support multiple libraries for each
|
1201
|
+
serialization format.
|
1202
|
+
|
1203
|
+
=== XML
|
403
1204
|
|
404
|
-
|
1205
|
+
Lutaml::Model supports the following XML adapters:
|
405
1206
|
|
1207
|
+
* Nokogiri (default)
|
1208
|
+
* Oga (optional, plain Ruby suitable for Opal/JS)
|
1209
|
+
* Ox (optional)
|
1210
|
+
|
1211
|
+
.Using the Nokogiri XML adapter
|
406
1212
|
[source,ruby]
|
407
1213
|
----
|
408
1214
|
require 'lutaml/model'
|
409
|
-
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
410
|
-
require 'lutaml/model/xml_adapter/ox_adapter'
|
411
|
-
require 'lutaml/model/xml_adapter/oga_adapter'
|
412
1215
|
|
413
1216
|
Lutaml::Model::Config.configure do |config|
|
1217
|
+
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
414
1218
|
config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
|
415
|
-
# Or use OxAdapter or OgaAdapter
|
416
1219
|
end
|
417
1220
|
----
|
418
1221
|
|
419
|
-
|
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
|
420
1246
|
|
1247
|
+
Lutaml::Model supports the following JSON adapters:
|
1248
|
+
|
1249
|
+
* JSON (default)
|
1250
|
+
* MultiJson (optional)
|
1251
|
+
|
1252
|
+
.Using the JSON adapter
|
421
1253
|
[source,ruby]
|
422
1254
|
----
|
423
1255
|
require 'lutaml/model'
|
424
|
-
require 'lutaml/model/json_adapter/standard'
|
425
|
-
require 'lutaml/model/json_adapter/multi_json'
|
426
1256
|
|
427
1257
|
Lutaml::Model::Config.configure do |config|
|
1258
|
+
require 'lutaml/model/json_adapter/standard'
|
428
1259
|
config.json_adapter = Lutaml::Model::JsonAdapter::StandardDocument
|
429
|
-
# Or use MultiJsonDocument
|
430
1260
|
end
|
431
1261
|
----
|
432
1262
|
|
433
|
-
|
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:
|
1277
|
+
|
1278
|
+
* Toml-rb (default)
|
1279
|
+
* Tomlib (optional)
|
434
1280
|
|
1281
|
+
.Using the Toml-rb adapter
|
435
1282
|
[source,ruby]
|
436
1283
|
----
|
437
1284
|
require 'lutaml/model'
|
438
|
-
require 'lutaml/model/toml_adapter/toml_rb_adapter'
|
439
|
-
require 'lutaml/model/toml_adapter/tomlib_adapter'
|
440
1285
|
|
441
1286
|
Lutaml::Model::Config.configure do |config|
|
1287
|
+
require 'lutaml/model/toml_adapter/toml_rb_adapter'
|
442
1288
|
config.toml_adapter = Lutaml::Model::TomlAdapter::TomlRbDocument
|
443
|
-
|
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'
|
444
1300
|
end
|
445
1301
|
----
|
446
1302
|
|
447
1303
|
== License and Copyright
|
448
1304
|
|
449
|
-
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.
|
450
1307
|
|
451
|
-
|
1308
|
+
Copyright Ribose.
|