lutaml-model 0.3.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -5
  3. data/.rubocop_todo.yml +20 -101
  4. data/Gemfile +3 -18
  5. data/README.adoc +1100 -140
  6. data/lib/lutaml/model/attribute.rb +15 -2
  7. data/lib/lutaml/model/config.rb +0 -1
  8. data/lib/lutaml/model/error/invalid_value_error.rb +18 -0
  9. data/lib/lutaml/model/error.rb +8 -0
  10. data/lib/lutaml/model/json_adapter/json_document.rb +20 -0
  11. data/lib/lutaml/model/json_adapter/json_object.rb +28 -0
  12. data/lib/lutaml/model/json_adapter/{multi_json.rb → multi_json_adapter.rb} +2 -3
  13. data/lib/lutaml/model/json_adapter/{standard.rb → standard_json_adapter.rb} +2 -3
  14. data/lib/lutaml/model/json_adapter.rb +1 -31
  15. data/lib/lutaml/model/key_value_mapping.rb +9 -2
  16. data/lib/lutaml/model/key_value_mapping_rule.rb +0 -1
  17. data/lib/lutaml/model/mapping_hash.rb +0 -2
  18. data/lib/lutaml/model/mapping_rule.rb +5 -3
  19. data/lib/lutaml/model/schema/json_schema.rb +0 -1
  20. data/lib/lutaml/model/schema/relaxng_schema.rb +0 -1
  21. data/lib/lutaml/model/schema/xsd_schema.rb +0 -1
  22. data/lib/lutaml/model/schema/yaml_schema.rb +0 -1
  23. data/lib/lutaml/model/schema.rb +0 -1
  24. data/lib/lutaml/model/serializable.rb +0 -1
  25. data/lib/lutaml/model/serialize.rb +241 -153
  26. data/lib/lutaml/model/toml_adapter/toml_document.rb +20 -0
  27. data/lib/lutaml/model/toml_adapter/toml_object.rb +28 -0
  28. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +4 -5
  29. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +2 -3
  30. data/lib/lutaml/model/toml_adapter.rb +0 -31
  31. data/lib/lutaml/model/type/date_time.rb +20 -0
  32. data/lib/lutaml/model/type/json.rb +34 -0
  33. data/lib/lutaml/model/type/time_without_date.rb +4 -3
  34. data/lib/lutaml/model/type.rb +61 -124
  35. data/lib/lutaml/model/version.rb +1 -1
  36. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +20 -13
  37. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +4 -5
  38. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +24 -17
  39. data/lib/lutaml/model/xml_adapter/xml_attribute.rb +27 -0
  40. data/lib/lutaml/model/xml_adapter/xml_document.rb +184 -0
  41. data/lib/lutaml/model/xml_adapter/xml_element.rb +94 -0
  42. data/lib/lutaml/model/xml_adapter/xml_namespace.rb +49 -0
  43. data/lib/lutaml/model/xml_adapter.rb +0 -266
  44. data/lib/lutaml/model/xml_mapping.rb +1 -1
  45. data/lib/lutaml/model/xml_mapping_rule.rb +3 -4
  46. data/lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb +34 -0
  47. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +20 -0
  48. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +28 -0
  49. data/lib/lutaml/model/yaml_adapter.rb +1 -19
  50. data/lib/lutaml/model.rb +7 -5
  51. metadata +19 -5
  52. data/lib/lutaml/model/xml_namespace.rb +0 -47
data/README.adoc CHANGED
@@ -1,4 +1,4 @@
1
- = LutaML Ruby modeller: `Lutaml::Model`
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
- == What is Lutaml::Model
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," which means clay, and "ML"
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
- == Introduction to Data Modeling
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 formats
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
- == Writing a Data Model in Ruby
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
- To define a model, inherit from `Lutaml::Model::Serializable` and use the `attribute` class method to define attributes.
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,461 @@ class Kiln < Lutaml::Model::Serializable
82
96
  end
83
97
  ----
84
98
 
85
- == Translating a Data Model into Different Serialization Models
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
- Lutaml::Model allows you to translate a data model into various serialization
88
- formats including XML, JSON, YAML, and TOML.
119
+ === Supported attribute value types
89
120
 
90
- === XML: Element, Attribute, Namespaces
121
+ Lutaml::Model supports the following attribute types, they can be
122
+ referred by a string, a symbol, or their class constant.
91
123
 
92
- Define XML mappings using `map_element`, `map_attribute`, and `map_content`.
124
+ Syntax:
93
125
 
94
126
  [source,ruby]
95
127
  ----
96
- class Example < Lutaml::Model::Serializable
97
- attribute :name, Lutaml::Model::Type::String
98
- attribute :value, Lutaml::Model::Type::Integer
128
+ attribute :name_of_attribute, {symbol | string | class}
129
+ ----
99
130
 
100
- xml do
101
- root 'example'
102
- map_element 'name', to: :name
103
- map_attribute 'value', to: :value
104
- end
131
+ |===
132
+ | String | Symbol | Class name | Actual value class
133
+
134
+ | `String` | `:string` | `Lutaml::Model::Type::String` | `::String`
135
+ | `Integer` | `:integer` | `Lutaml::Model::Type::Integer` | `::Integer`
136
+ | `Float` | `:float` | `Lutaml::Model::Type::Float` | `::Float`
137
+ | `Date` | `:date` | `Lutaml::Model::Type::Date` | `::Date`
138
+ | `Time` | `:time` | `Lutaml::Model::Type::Time` | `::Time`
139
+ | `DateTime` | `:date_time` | `Lutaml::Model::Type::DateTime` | `::DateTime`
140
+ | `TimeWithoutDate` | `:time_without_date` | `Lutaml::Model::Type::TimeWithoutDate` | `::Time`
141
+ | `Boolean` | `:boolean` | `Lutaml::Model::Type::Boolean` | `Boolean`
142
+ | `Decimal` | `:decimal` | `Lutaml::Model::Type::Decimal` | `::BigDecimal`
143
+ | `Hash` | `:hash` | `Lutaml::Model::Type::Hash` | `::Hash`
144
+ | `Uuid` | `:uuid` | `Lutaml::Model::Type::Uuid` | `::String`
145
+ | `Symbol` | `:symbol` | `Lutaml::Model::Type::Symbol` | `Symbol`
146
+ | `Binary` | `:binary` | `Lutaml::Model::Type::Binary` | `Binary`
147
+ | `Url` | `:url` | `Lutaml::Model::Type::Url` | `::URI`
148
+ | `IpAddress` | `:ip_address` | `Lutaml::Model::Type::IpAddress` | `::IPAddr`
149
+ | `Json` | `:json` | `Lutaml::Model::Type::Json` | `::JSON`
150
+
151
+ |===
152
+
153
+
154
+ .Defining attributes with supported types via symbol, string and class
155
+ [example]
156
+ ====
157
+ [source,ruby]
158
+ ----
159
+ class Studio < Lutaml::Model::Serializable
160
+ # The following are equivalent
161
+ attribute :location, :string
162
+ attribute :potter, "String"
163
+ attribute :kiln, Lutaml::Model::Type::String
164
+ end
165
+ ----
166
+
167
+ [source,ruby]
168
+ ----
169
+ > s = Studio.new(location: 'London', potter: 'John Doe', kiln: 'Kiln 1')
170
+ > # <Studio:0x0000000104ac7240 @location="London", @potter="John Doe", @kiln="Kiln 1">
171
+ > s.location
172
+ > # "London"
173
+ > s.potter
174
+ > # "John Doe"
175
+ > s.kiln
176
+ > # "Kiln 1"
177
+ ----
178
+ ====
179
+
180
+
181
+ === Attribute as a collection
182
+
183
+ Define attributes as collections (arrays or hashes) to store multiple values
184
+ using the `collection` option.
185
+
186
+ Syntax:
187
+
188
+ [source,ruby]
189
+ ----
190
+ attribute :name_of_attribute, Type, collection: true
191
+ ----
192
+
193
+ .Using the `collection` option to define a collection attribute
194
+ [example]
195
+ ====
196
+ [source,ruby]
197
+ ----
198
+ class Studio < Lutaml::Model::Serializable
199
+ attribute :location, Lutaml::Model::Type::String
200
+ attribute :potters, Lutaml::Model::Type::String, collection: true
201
+ end
202
+ ----
203
+
204
+ [source,ruby]
205
+ ----
206
+ > Studio.new.potters
207
+ > # []
208
+ > Studio.new(potters: ['John Doe', 'Jane Doe']).potters
209
+ > # ['John Doe', 'Jane Doe']
210
+ ----
211
+ ====
212
+
213
+ === Attribute as an enumeration
214
+
215
+ An attribute can be defined as an enumeration by using the `values` directive.
216
+
217
+ The `values` directive is used to define acceptable values in an attribute. If
218
+ any other value is given, a `Lutaml::Model::InvalidValueError` will be raised.
219
+
220
+ Syntax:
221
+
222
+ [source,ruby]
223
+ ----
224
+ attribute :name_of_attribute, Type, values: [value1, value2, ...]
225
+ ----
226
+
227
+ .Using the `values` directive to define acceptable values for an attribute
228
+ [example]
229
+ ====
230
+ [source,ruby]
231
+ ----
232
+ class Ceramic < Lutaml::Model::Serializable
233
+ attribute :type, Lutaml::Model::Type::String,
234
+ values: ['Porcelain', 'Earthenware', 'Stoneware']
235
+ end
236
+ ----
237
+
238
+ [source,ruby]
239
+ ----
240
+ > Ceramic.new(type: 'Porcelain').type
241
+ > # "Porcelain"
242
+ > Ceramic.new(type: 'Earthenware').type
243
+ > # "Earthenware"
244
+ > Ceramic.new(type: 'Bone China').type
245
+ > # Lutaml::Model::InvalidValueError: Invalid value for attribute 'type'
246
+ ----
247
+ ====
248
+
249
+
250
+ === Attribute value default
251
+
252
+ Specify default values for attributes using the `default` option.
253
+ The `default` option can be set to a value or a lambda that returns a value.
254
+
255
+ Syntax:
256
+
257
+ [source,ruby]
258
+ ----
259
+ attribute :name_of_attribute, Type, default: -> { value }
260
+ ----
261
+
262
+
263
+ .Using the `default` option to set a default value for an attribute
264
+ [example]
265
+ ====
266
+ [source,ruby]
267
+ ----
268
+ class Glaze < Lutaml::Model::Serializable
269
+ attribute :color, Lutaml::Model::Type::String, default: -> { 'Clear' }
270
+ attribute :temperature, Lutaml::Model::Type::Integer, default: -> { 1050 }
105
271
  end
106
272
  ----
107
273
 
108
- NOTE: If root is not given then the class name will be used as the root.
274
+ [source,ruby]
275
+ ----
276
+ > Glaze.new.color
277
+ > # "Clear"
278
+ > Glaze.new.temperature
279
+ > # 1050
280
+ ----
281
+ ====
282
+
283
+ == Serialization model mappings
284
+
285
+ === General
109
286
 
110
- === Key Value Data Models: JSON, YAML, TOML
287
+ Lutaml::Model allows you to translate a data model into serialization models of
288
+ various serialization formats including XML, JSON, YAML, and TOML.
111
289
 
112
- Define key-value data models like JSON, YAML, and TOML using the `map` method.
290
+ Depending on the serialization format, different methods are supported for
291
+ defining serialization and deserialization mappings.
113
292
 
293
+ Serialization model mappings are defined under the `xml`, `json`, `yaml`, and
294
+ `toml` blocks.
295
+
296
+ .Using the `xml`, `json`, `yaml`, and `toml` blocks to define serialization mappings
114
297
  [source,ruby]
115
298
  ----
116
299
  class Example < Lutaml::Model::Serializable
117
- attribute :name, Lutaml::Model::Type::String
118
- attribute :value, Lutaml::Model::Type::Integer
300
+ xml do
301
+ # ...
302
+ end
119
303
 
120
304
  json do
121
- map 'name', to: :name
122
- map 'value', to: :value
305
+ # ...
123
306
  end
124
307
 
125
308
  yaml do
126
- map 'name', to: :name
127
- map 'value', to: :value
309
+ # ...
128
310
  end
129
311
 
130
312
  toml do
131
- map 'name', to: :name
132
- map 'value', to: :value
313
+ # ...
133
314
  end
134
315
  end
135
316
  ----
136
317
 
137
- == Develop Serialization and Deserialization Mappings
318
+ === XML
319
+
320
+ ==== Setting root element name
138
321
 
139
- Lutaml::Model supports various methods for defining serialization and deserialization mappings.
322
+ The `root` method sets the root element tag name of the XML document.
140
323
 
141
- === XML (`map_element`, `map_attribute`, `map_content`)
324
+ If `root` is not given, then the snake-cased class name will be used as the
325
+ root.
142
326
 
143
- Use `map_element` to map XML elements, `map_attribute` to map XML attributes, and `map_content` to map text content within an XML element.
327
+ [example]
328
+ Sets the tag name for `<example>` in XML `<example>...</example>`.
329
+
330
+ Syntax:
144
331
 
145
332
  [source,ruby]
146
333
  ----
147
- class Example < Lutaml::Model::Serializable
148
- attribute :name, Lutaml::Model::Type::String
149
- attribute :description, Lutaml::Model::Type::String
334
+ xml do
335
+ root 'xml_element_name'
336
+ end
337
+ ----
150
338
 
339
+ .Setting the root element name to `example`
340
+ [example]
341
+ ====
342
+ [source,ruby]
343
+ ----
344
+ class Example < Lutaml::Model::Serializable
151
345
  xml do
152
346
  root 'example'
153
- map_element 'name', to: :name
154
- map_content to: :description
155
347
  end
156
348
  end
157
349
  ----
158
350
 
159
- === JSON (`map` method)
351
+ [source,ruby]
352
+ ----
353
+ > Example.new.to_xml
354
+ > #<example></example>
355
+ ----
356
+ ====
357
+
358
+ ==== Mapping elements
359
+
360
+ The `map_element` method maps an XML element to a data model attribute.
160
361
 
161
- Use the `map` method to define JSON mappings.
362
+ [example]
363
+ To handle the `<name>` tag in `<example><name>John Doe</name></example>`.
364
+ The value will be set to `John Doe`.
365
+
366
+ Syntax:
367
+
368
+ [source,ruby]
369
+ ----
370
+ xml do
371
+ map_element 'xml_element_name', to: :name_of_attribute
372
+ end
373
+ ----
162
374
 
375
+ .Mapping the `name` tag to the `name` attribute
376
+ [example]
377
+ ====
163
378
  [source,ruby]
164
379
  ----
165
380
  class Example < Lutaml::Model::Serializable
166
381
  attribute :name, Lutaml::Model::Type::String
167
- attribute :value, Lutaml::Model::Type::Integer
168
382
 
169
- json do
170
- map 'name', to: :name
171
- map 'value', to: :value
383
+ xml do
384
+ root 'example'
385
+ map_element 'name', to: :name
172
386
  end
173
387
  end
174
388
  ----
175
389
 
176
- === YAML
177
-
178
- Use the `map` method to define YAML mappings.
390
+ [source,xml]
391
+ ----
392
+ <example><name>John Doe</name></example>
393
+ ----
179
394
 
180
395
  [source,ruby]
181
396
  ----
182
- class Example < Lutaml::Model::Serializable
183
- attribute :name, Lutaml::Model::Type::String
184
- attribute :value, Lutaml::Model::Type::Integer
397
+ > Example.from_xml(xml)
398
+ > #<Example:0x0000000104ac7240 @name="John Doe">
399
+ > Example.new(name: "John Doe").to_xml
400
+ > #<example><name>John Doe</name></example>
401
+ ----
402
+ ====
185
403
 
186
- yaml do
187
- map 'name', to: :name
188
- map 'value', to: :value
189
- end
404
+ ==== Mapping attributes
405
+
406
+ The `map_attribute` method maps an XML attribute to a data model attribute.
407
+
408
+ Syntax:
409
+
410
+ [source,ruby]
411
+ ----
412
+ xml do
413
+ map_attribute 'xml_attribute_name', to: :name_of_attribute
190
414
  end
191
415
  ----
192
416
 
193
- === TOML
194
-
195
- Use the `map` method to define TOML mappings.
417
+ .Using `map_attribute` to map the `value` attribute
418
+ [example]
419
+ ====
420
+ The following class will parse the XML snippet below:
196
421
 
197
422
  [source,ruby]
198
423
  ----
199
424
  class Example < Lutaml::Model::Serializable
200
- attribute :name, Lutaml::Model::Type::String
201
425
  attribute :value, Lutaml::Model::Type::Integer
202
426
 
203
- toml do
204
- map 'name', to: :name
205
- map 'value', to: :value
427
+ xml do
428
+ root 'example'
429
+ map_attribute 'value', to: :value
206
430
  end
207
431
  end
208
432
  ----
209
433
 
210
- == Attribute Collections Using the `collection` Option
211
-
212
- You can define attributes as collections (arrays or hashes) to store multiple values.
434
+ [source,xml]
435
+ ----
436
+ <example value=12><name>John Doe</name></example>
437
+ ----
213
438
 
214
439
  [source,ruby]
215
440
  ----
216
- class Studio < Lutaml::Model::Serializable
217
- attribute :location, Lutaml::Model::Type::String
218
- attribute :potters, Lutaml::Model::Type::String, collection: true
219
- end
441
+ > Example.from_xml(xml)
442
+ > #<Example:0x0000000104ac7240 @value=12>
443
+ > Example.new(value: 12).to_xml
444
+ > #<example value="12"></example>
220
445
  ----
446
+ ====
221
447
 
222
- == Attribute Defaults Using the `default` Option
223
448
 
224
- Specify default values for attributes using the `default` option.
449
+ ==== Mapping content
450
+
451
+ Content represents the text inside an XML element, inclusive of whitespace.
452
+
453
+ The `map_content` method maps an XML element's content to a data model
454
+ attribute.
455
+
456
+ Syntax:
225
457
 
226
458
  [source,ruby]
227
459
  ----
228
- class Glaze < Lutaml::Model::Serializable
229
- attribute :color, Lutaml::Model::Type::String, default: -> { 'Clear' }
230
- attribute :temperature, Lutaml::Model::Type::Integer, default: -> { 1050 }
460
+ xml do
461
+ map_content to: :name_of_attribute
231
462
  end
232
463
  ----
233
464
 
234
- == Attribute Delegation Using the `delegate` Option
235
-
236
- Delegate attribute mappings to nested objects using the `delegate` option.
465
+ .Using `map_content` to map content of the `description` tag
466
+ [example]
467
+ ====
468
+ The following class will parse the XML snippet below:
237
469
 
238
470
  [source,ruby]
239
471
  ----
240
- class Ceramic < Lutaml::Model::Serializable
241
- attribute :type, Lutaml::Model::Type::String
242
- attribute :glaze, Glaze
472
+ class Example < Lutaml::Model::Serializable
473
+ attribute :description, Lutaml::Model::Type::String
243
474
 
244
- json do
245
- map 'type', to: :type
246
- map 'color', to: :color, delegate: :glaze
475
+ xml do
476
+ root 'example'
477
+ map_content to: :description
247
478
  end
248
479
  end
249
480
  ----
250
481
 
251
- == Attribute Serialization with Custom `from` and `to` Methods
482
+ [source,xml]
483
+ ----
484
+ <example>John Doe is my moniker.</example>
485
+ ----
486
+
487
+ [source,ruby]
488
+ ----
489
+ > Example.from_xml(xml)
490
+ > #<Example:0x0000000104ac7240 @description="John Doe is my moniker.">
491
+ > Example.new(description: "John Doe is my moniker.").to_xml
492
+ > #<example>John Doe is my moniker.</example>
493
+ ----
494
+ ====
495
+
496
+
252
497
 
253
- Define custom methods for specific attribute mappings using the `with:` key for each serialization mapping block.
498
+ ==== Example for mapping
499
+
500
+ [example]
501
+ ====
502
+ The following class will parse the XML snippet below:
254
503
 
255
504
  [source,ruby]
256
505
  ----
257
- class CustomCeramic < Lutaml::Model::Serializable
506
+ class Example < Lutaml::Model::Serializable
258
507
  attribute :name, Lutaml::Model::Type::String
259
- attribute :size, Lutaml::Model::Type::Integer
508
+ attribute :description, Lutaml::Model::Type::String
509
+ attribute :value, Lutaml::Model::Type::Integer
260
510
 
261
- json do
262
- map 'name', to: :name, with: { to: :name_to_json, from: :name_from_json }
263
- map 'size', to: :size
511
+ xml do
512
+ root 'example'
513
+ map_element 'name', to: :name
514
+ map_attribute 'value', to: :value
515
+ map_content to: :description
264
516
  end
517
+ end
518
+ ----
265
519
 
266
- def name_to_json(model, value)
267
- "Masterpiece: #{value}"
268
- end
520
+ [source,xml]
521
+ ----
522
+ <example value=12><name>John Doe</name> is my moniker.</example>
523
+ ----
269
524
 
270
- def name_from_json(model, doc)
271
- doc['name'].sub('Masterpiece: ', '')
272
- end
273
- end
525
+ [source,ruby]
526
+ ----
527
+ > Example.from_xml(xml)
528
+ > #<Example:0x0000000104ac7240 @name="John Doe", @description=" is my moniker.", @value=12>
529
+ > Example.new(name: "John Doe", description: " is my moniker.", value: 12).to_xml
530
+ > #<example value="12"><name>John Doe</name> is my moniker.</example>
274
531
  ----
532
+ ====
275
533
 
276
- == Using XML Namespaces
277
534
 
278
- Define XML namespaces for your models to handle namespaced XML elements.
535
+ ==== Namespaces
279
536
 
280
- === XML Namespace on Element
537
+ ===== Namespace at root
281
538
 
539
+ The `namespace` method in the `xml` block sets the namespace for the root
540
+ element.
541
+
542
+ Syntax:
543
+
544
+ [source,ruby]
545
+ ----
546
+ xml do
547
+ namespace 'http://example.com/namespace'
548
+ end
549
+ ----
550
+
551
+ .Using the `namespace` method to set the namespace for the root element
552
+ [example]
553
+ ====
282
554
  [source,ruby]
283
555
  ----
284
556
  class Ceramic < Lutaml::Model::Serializable
@@ -294,9 +566,44 @@ class Ceramic < Lutaml::Model::Serializable
294
566
  end
295
567
  ----
296
568
 
297
- === XML Namespace on Attribute
569
+ [source,xml]
570
+ ----
571
+ <Ceramic xmlns='http://example.com/ceramic'><Type>Porcelain</Type><Glaze>Clear</Glaze></Ceramic>
572
+ ----
573
+
574
+ [source,ruby]
575
+ ----
576
+ > Ceramic.from_xml(xml_file)
577
+ > #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze="Clear">
578
+ > Ceramic.new(type: "Porcelain", glaze: "Clear").to_xml
579
+ > #<Ceramic xmlns="http://example.com/ceramic"><Type>Porcelain</Type><Glaze>Clear</Glaze></Ceramic>
580
+ ----
581
+ ====
582
+
583
+ ===== Namespace on attribute
584
+
585
+ If the namespace is defined on an XML attribute, then that will be given
586
+ priority over the one defined in the class.
587
+
588
+ Syntax:
589
+
590
+ [source,ruby]
591
+ ----
592
+ xml do
593
+ map_element 'xml_element_name', to: :name_of_attribute,
594
+ namespace: 'http://example.com/namespace',
595
+ prefix: 'prefix'
596
+ end
597
+ ----
598
+
599
+ `namespace`:: The XML namespace used by this element
600
+ `prefix`:: The XML namespace prefix used by this element (optional)
298
601
 
299
- If the namespace is defined on an attribute then that will be given priority over the one defined in the class. In the example below `glz` will be used for `Glaze` if it is added inside the `Ceramic` class, and `glaze` will be used otherwise.
602
+ .Using the `namespace` option to set the namespace for an element
603
+ [example]
604
+ ====
605
+ In this example, `glz` will be used for `Glaze` if it is added inside the
606
+ `Ceramic` class, and `glaze` will be used otherwise.
300
607
 
301
608
  [source,ruby]
302
609
  ----
@@ -326,33 +633,125 @@ class Ceramic < Lutaml::Model::Serializable
326
633
  end
327
634
  ----
328
635
 
329
- === XML Namespace with `inherit` Option
636
+ [source,xml]
637
+ ----
638
+ <Ceramic xmlns='http://example.com/ceramic'>
639
+ <Type>Porcelain</Type>
640
+ <glz:Glaze xmlns='http://example.com/glaze'>
641
+ <color>Clear</color>
642
+ <temperature>1050</temperature>
643
+ </glz:Glaze>
644
+ </Ceramic>
645
+ ----
330
646
 
331
647
  [source,ruby]
332
648
  ----
333
- class Ceramic < Lutaml::Model::Serializable
334
- attribute :type, Lutaml::Model::Type::String
335
- attribute :glaze, Lutaml::Model::Type::String
649
+ > Ceramic.from_xml(xml_file)
650
+ > #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=1050>>
651
+ > Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear", temperature: 1050)).to_xml
652
+ > #<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>
653
+ ----
654
+ ====
336
655
 
337
- xml do
338
- root 'Ceramic'
339
- namespace 'http://example.com/ceramic', prefix: 'cera'
340
- map_element 'Type', to: :type, namespace: :inherit
656
+ ===== Namespace with `inherit` option
657
+
658
+ The `inherit` option is used at the element level to inherit the namespace from
659
+ the root element.
660
+
661
+ Syntax:
662
+
663
+ [source,ruby]
664
+ ----
665
+ xml do
666
+ map_element 'xml_element_name', to: :name_of_attribute, namespace: :inherit
667
+ end
668
+ ----
669
+
670
+ .Using the `inherit` option to inherit the namespace from the root element
671
+ [example]
672
+ ====
673
+ In this example, the `Type` element will inherit the namespace from the root.
674
+
675
+ [source,ruby]
676
+ ----
677
+ class Ceramic < Lutaml::Model::Serializable
678
+ attribute :type, Lutaml::Model::Type::String
679
+ attribute :glaze, Lutaml::Model::Type::String
680
+ attribute :color, Lutaml::Model::Type::String
681
+
682
+ xml do
683
+ root 'Ceramic'
684
+ namespace 'http://example.com/ceramic', prefix: 'cera'
685
+ map_element 'Type', to: :type, namespace: :inherit
341
686
  map_element 'Glaze', to: :glaze
687
+ map_attribute 'color', to: :color, namespace: 'http://example.com/color', prefix: 'clr'
342
688
  end
343
689
  end
344
690
  ----
345
691
 
346
- == Using XML `mixed` option
692
+ [source,xml]
693
+ ----
694
+ <Ceramic
695
+ xmlns:cera='http://example.com/ceramic'
696
+ xmlns:clr='http://example.com/color'
697
+ clr:color="navy-blue">
698
+ <cera:Type>Porcelain</cera:Type>
699
+ <Glaze>Clear</Glaze>
700
+ </Ceramic>
701
+ ----
702
+
703
+ [source,ruby]
704
+ ----
705
+ > Ceramic.from_xml(xml_file)
706
+ > #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze="Clear", @color="navy-blue">
707
+ > Ceramic.new(type: "Porcelain", glaze: "Clear", color: "navy-blue").to_xml
708
+ > #<Ceramic xmlns:cera="http://example.com/ceramic"
709
+ # xmlns:clr='http://example.com/color'
710
+ # clr:color="navy-blue">
711
+ # <cera:Type>Porcelain</cera:Type>
712
+ # <Glaze>Clear</Glaze>
713
+ # </Ceramic>
714
+ ----
715
+ ====
716
+
347
717
 
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>`
718
+ ==== Mixed content
719
+
720
+ ===== General
721
+
722
+ In XML there can be tags that contain content mixed with other tags and where
723
+ whitespace is significant, such as to represent rich text.
724
+
725
+ [example]
726
+ ====
727
+ [source,xml]
728
+ ----
729
+ <description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>
730
+ ----
731
+ ====
349
732
 
350
- To map this to Lutaml::Model we can use the mixed option when defining the model or when referencing it.
733
+ To map this to Lutaml::Model we can use the `mixed` option in either way:
351
734
 
352
- === Adding mixed option when defining a Model
735
+ * when defining the model;
736
+ * when referencing the model.
353
737
 
354
- This will always treat the content of `<p>` tag as mixed content.
355
738
 
739
+ ===== Specifying the `mixed` option at `root`
740
+
741
+ This will always treat the content of the element itself as mixed content.
742
+
743
+ Syntax:
744
+
745
+ [source,ruby]
746
+ ----
747
+ xml do
748
+ root 'xml_element_name', mixed: true
749
+ end
750
+ ----
751
+
752
+ .Applying `mixed` to treat root as mixed content
753
+ [example]
754
+ ====
356
755
  [source,ruby]
357
756
  ----
358
757
  class Paragraph < Lutaml::Model::Serializable
@@ -368,10 +767,35 @@ class Paragraph < Lutaml::Model::Serializable
368
767
  end
369
768
  ----
370
769
 
371
- === Adding mixed option when referencing a Model
770
+ [source,ruby]
771
+ ----
772
+ > Paragraph.from_xml("<p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p>")
773
+ > #<Paragraph:0x0000000104ac7240 @bold="John Doe", @italic="28">
774
+ > Paragraph.new(bold: "John Doe", italic: "28").to_xml
775
+ > #<p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p>
776
+ ----
777
+ ====
778
+
779
+ TODO: How to create mixed content from `#new`?
780
+
372
781
 
373
- This will only treat the content of `<p>` tag as mixed content if the `mixed: true` is added when referencing it.
782
+ ===== Specifying the `mixed` option when referencing a model
783
+
784
+ This will only treat the content of the referenced model as mixed content if the
785
+ `mixed: true` is added when referencing it.
786
+
787
+ Syntax:
374
788
 
789
+ [source,ruby]
790
+ ----
791
+ xml do
792
+ map_element 'xml_element_name', to: :name_of_attribute, mixed: true
793
+ end
794
+ ----
795
+
796
+ .Applying `mixed` to treat an inner element as mixed content
797
+ [example]
798
+ ====
375
799
  [source,ruby]
376
800
  ----
377
801
  class Paragraph < Lutaml::Model::Serializable
@@ -397,55 +821,591 @@ class Description < Lutaml::Model::Serializable
397
821
  end
398
822
  ----
399
823
 
824
+ [source,ruby]
825
+ ----
826
+ > Description.from_xml("<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>")
827
+ > #<Description:0x0000000104ac7240 @paragraph=#<Paragraph:0x0000000104ac7240 @bold="John Doe", @italic="28">>
828
+ > Description.new(paragraph: Paragraph.new(bold: "John Doe", italic: "28")).to_xml
829
+ > #<description><p>My name is <bold>John Doe</bold>, and I'm <i>28</i> years old</p></description>
830
+ ----
831
+ ====
832
+
833
+
834
+ === Key value data models
835
+
836
+ ==== General
837
+
838
+ Key-value data models like JSON, YAML, and TOML all share a similar structure
839
+ where data is stored as key-value pairs.
840
+
841
+ Lutaml::Model works with these formats in a similar way.
842
+
843
+ ==== Mapping
844
+
845
+ The `map` method is used to define key-value mappings.
846
+
847
+ Syntax:
848
+
849
+ [source,ruby]
850
+ ----
851
+ json | yaml | toml do
852
+ map 'key_value_model_attribute_name', to: :name_of_attribute
853
+ end
854
+ ----
855
+
856
+ .Using the `map` method to define key-value mappings
857
+ [example]
858
+ ====
859
+ [source,ruby]
860
+ ----
861
+ class Example < Lutaml::Model::Serializable
862
+ attribute :name, Lutaml::Model::Type::String
863
+ attribute :value, Lutaml::Model::Type::Integer
864
+
865
+ json do
866
+ map 'name', to: :name
867
+ map 'value', to: :value
868
+ end
869
+
870
+ yaml do
871
+ map 'name', to: :name
872
+ map 'value', to: :value
873
+ end
874
+
875
+ toml do
876
+ map 'name', to: :name
877
+ map 'value', to: :value
878
+ end
879
+ end
880
+ ----
881
+
882
+ [source,json]
883
+ ----
884
+ {
885
+ "name": "John Doe",
886
+ "value": 28
887
+ }
888
+ ----
889
+
890
+ [source,ruby]
891
+ ----
892
+ > Example.from_json(json)
893
+ > #<Example:0x0000000104ac7240 @name="John Doe", @value=28>
894
+ > Example.new(name: "John Doe", value: 28).to_json
895
+ > #{"name"=>"John Doe", "value"=>28}
896
+ ----
897
+ ====
898
+
899
+
900
+ ==== Nested attribute mappings
901
+
902
+ The `map` method can also be used to map nested key-value data models
903
+ by referring to a Lutaml::Model class as an attribute class.
904
+
905
+ [example]
906
+ ====
907
+ [source,ruby]
908
+ ----
909
+ class Glaze < Lutaml::Model::Serializable
910
+ attribute :color, Lutaml::Model::Type::String
911
+ attribute :temperature, Lutaml::Model::Type::Integer
912
+
913
+ json do
914
+ map 'color', to: :color
915
+ map 'temperature', to: :temperature
916
+ end
917
+ end
918
+
919
+ class Ceramic < Lutaml::Model::Serializable
920
+ attribute :type, Lutaml::Model::Type::String
921
+ attribute :glaze, Glaze
922
+
923
+ json do
924
+ map 'type', to: :type
925
+ map 'glaze', to: :glaze
926
+ end
927
+ end
928
+ ----
929
+
930
+ [source,json]
931
+ ----
932
+ {
933
+ "type": "Porcelain",
934
+ "glaze": {
935
+ "color": "Clear",
936
+ "temperature": 1050
937
+ }
938
+ }
939
+ ----
940
+
941
+ [source,ruby]
942
+ ----
943
+ > Ceramic.from_json(json)
944
+ > #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=1050>>
945
+ > Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear", temperature: 1050)).to_json
946
+ > #{"type"=>"Porcelain", "glaze"=>{"color"=>"Clear", "temperature"=>1050}}
947
+ ----
948
+ ====
949
+
950
+ === Advanced attribute mapping
951
+
952
+ ==== Attribute mapping delegation
953
+
954
+ Delegate attribute mappings to nested objects using the `delegate` option.
955
+
956
+ Syntax:
957
+
958
+ [source,ruby]
959
+ ----
960
+ xml | json | yaml | toml do
961
+ map 'key_value_model_attribute_name', to: :name_of_attribute, delegate: :model_to_delegate_to
962
+ end
963
+ ----
964
+
965
+ .Using the `delegate` option to map attributes to nested objects
966
+ [example]
967
+ ====
968
+ The following class will parse the JSON snippet below:
969
+
970
+ [source,ruby]
971
+ ----
972
+ class Glaze < Lutaml::Model::Serializable
973
+ attribute :color, Lutaml::Model::Type::String
974
+ attribute :temperature, Lutaml::Model::Type::Integer
975
+
976
+ json do
977
+ map 'color', to: :color
978
+ map 'temperature', to: :temperature
979
+ end
980
+ end
981
+
982
+ class Ceramic < Lutaml::Model::Serializable
983
+ attribute :type, Lutaml::Model::Type::String
984
+ attribute :glaze, Glaze
985
+
986
+ json do
987
+ map 'type', to: :type
988
+ map 'color', to: :color, delegate: :glaze
989
+ end
990
+ end
991
+ ----
992
+
993
+ [source,json]
994
+ ----
995
+ {
996
+ "type": "Porcelain",
997
+ "color": "Clear"
998
+ }
999
+ ----
1000
+
1001
+ [source,ruby]
1002
+ ----
1003
+ > Ceramic.from_json(json)
1004
+ > #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=nil>>
1005
+ > Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear")).to_json
1006
+ > #{"type"=>"Porcelain", "color"=>"Clear"}
1007
+ ----
1008
+ ====
1009
+
1010
+
1011
+ ==== Attribute serialization with custom methods
1012
+
1013
+ Define custom methods for specific attribute mappings using the `with:` key for
1014
+ each serialization mapping block for `from` and `to`.
1015
+
1016
+ Syntax:
1017
+
1018
+ [source,ruby]
1019
+ ----
1020
+ xml | json | yaml | toml do
1021
+ map 'key_value_model_attribute_name', to: :name_of_attribute, with: {
1022
+ to: :method_name_to_serialize,
1023
+ from: :method_name_to_deserialize
1024
+ }
1025
+ end
1026
+ ----
1027
+
1028
+ .Using the `with:` key to define custom serialization methods
1029
+ [example]
1030
+ ====
1031
+ The following class will parse the JSON snippet below:
1032
+
1033
+ [source,ruby]
1034
+ ----
1035
+ class CustomCeramic < Lutaml::Model::Serializable
1036
+ attribute :name, Lutaml::Model::Type::String
1037
+ attribute :size, Lutaml::Model::Type::Integer
1038
+
1039
+ json do
1040
+ map 'name', to: :name, with: { to: :name_to_json, from: :name_from_json }
1041
+ map 'size', to: :size
1042
+ end
1043
+
1044
+ def name_to_json(model, value)
1045
+ "Masterpiece: #{value}"
1046
+ end
1047
+
1048
+ def name_from_json(model, doc)
1049
+ doc['name'].sub(/^Masterpiece: /, '')
1050
+ end
1051
+ end
1052
+ ----
1053
+
1054
+ [source,json]
1055
+ ----
1056
+ {
1057
+ "name": "Masterpiece: Vase",
1058
+ "size": 12
1059
+ }
1060
+ ----
1061
+
1062
+ [source,ruby]
1063
+ ----
1064
+ > CustomCeramic.from_json(json)
1065
+ > #<CustomCeramic:0x0000000104ac7240 @name="Vase", @size=12>
1066
+ > CustomCeramic.new(name: "Vase", size: 12).to_json
1067
+ > #{"name"=>"Masterpiece: Vase", "size"=>12}
1068
+ ----
1069
+ ====
1070
+
1071
+
1072
+
1073
+ ==== Attribute extraction
1074
+
1075
+ NOTE: This feature is for key-value data model serialization only.
1076
+
1077
+ The `child_mappings` option is used to extract results from a key-value data
1078
+ model (JSON, YAML, TOML) into a `Lutaml::Model` collection.
1079
+
1080
+ The values are extracted from the key-value data model using the list of keys
1081
+ provided.
1082
+
1083
+ Syntax:
1084
+
1085
+ [source,ruby]
1086
+ ----
1087
+ json | yaml | toml do
1088
+ map 'key_value_model_attribute_name', to: :name_of_attribute,
1089
+ child_mappings: {
1090
+ key_attribute_name_1: <1>
1091
+ {path_to_value_1}, <2>
1092
+ key_attribute_name_2:
1093
+ {path_to_value_2},
1094
+ # ...
1095
+ }
1096
+ end
1097
+ ----
1098
+ <1> The `key_attribute_name_1` is the attribute name in the model. The value of
1099
+ this attribute will be assigned the key of the hash in the key-value data model.
1100
+
1101
+ <2> The `path_to_value_1` is an array of keys that represent the path to the
1102
+ value in the key-value data model. The keys are used to extract the value from
1103
+ the key-value data model and assign it to the attribute in the model.
1104
+
1105
+ The `path_to_value` is in a nested array format with each value a symbol, where
1106
+ each symbol represents a key to traverse down. The last key in the path is the
1107
+ value to be extracted.
1108
+
1109
+ .Determining the path to value in a key-value data model
1110
+ [example]
1111
+ ====
1112
+ The following JSON contains 2 keys in schema named `engine` and `gearbox`.
1113
+
1114
+ [source,json]
1115
+ ----
1116
+ {
1117
+ "components": {
1118
+ "engine": {
1119
+ "manufacturer": "Ford",
1120
+ "model": "V8"
1121
+ },
1122
+ "gearbox": {
1123
+ "manufacturer": "Toyota",
1124
+ "model": "4-speed"
1125
+ }
1126
+ }
1127
+ }
1128
+ ----
1129
+
1130
+ The path to value for the `engine` schema is `[:components, :engine]` and for
1131
+ the `gearbox` schema is `[:components, :gearbox]`.
1132
+ ====
1133
+
1134
+ In `path_to_value`, the `:key` and `:value` are reserved instructions used to
1135
+ assign the key or value of the serialization data respectively as the value to
1136
+ the attribute.
1137
+
1138
+ [example]
1139
+ ====
1140
+ In the following JSON content, the `path_to_value` for the object keys named
1141
+ `engine` and `gearbox` will utilize the `:key` keyword to assign the key of the
1142
+ object as the value of a designated attribute.
1143
+
1144
+ [source,json]
1145
+ ----
1146
+ {
1147
+ "components": {
1148
+ "engine": { /*...*/ },
1149
+ "gearbox": { /*...*/ }
1150
+ }
1151
+ }
1152
+ ----
1153
+ ====
1154
+
1155
+ If a specified value path is not found, the corresponding attribute in the model
1156
+ will be assigned a `nil` value.
1157
+
1158
+ .Attribute values set to `nil` when the `path_to_value` is not found
1159
+ [example]
1160
+ ====
1161
+ In the following JSON content, the `path_to_value` of `[:extras, :sunroof]` and
1162
+ `[:extras, :drinks_cooler]` at the object `"gearbox"` would be set to `nil`.
1163
+
1164
+ [source,json]
1165
+ ----
1166
+ {
1167
+ "components": {
1168
+ "engine": {
1169
+ "manufacturer": "Ford",
1170
+ "extras": {
1171
+ "sunroof": true,
1172
+ "drinks_cooler": true
1173
+ }
1174
+ },
1175
+ "gearbox": {
1176
+ "manufacturer": "Toyota"
1177
+ }
1178
+ }
1179
+ }
1180
+ ----
1181
+ ====
1182
+
1183
+
1184
+ .Using the `child_mappings` option to extract values from a key-value data model
1185
+ [example]
1186
+ ====
1187
+ The following JSON contains 2 keys in schema named `foo` and `bar`.
1188
+
1189
+ [source,json]
1190
+ ----
1191
+ {
1192
+ "schemas": {
1193
+ "foo": { <1>
1194
+ "path": { <2>
1195
+ "link": "link one",
1196
+ "name": "one"
1197
+ }
1198
+ },
1199
+ "bar": { <1>
1200
+ "path": { <2>
1201
+ "link": "link two",
1202
+ "name": "two"
1203
+ }
1204
+ }
1205
+ }
1206
+ }
1207
+ ----
1208
+ <1> The keys `foo` and `bar` are to be mapped to the `id` attribute.
1209
+ <2> The nested `path.link` and `path.name` keys are used as the `link` and
1210
+ `name` attributes, respectively.
1211
+
1212
+ A model can be defined for this JSON as follows:
1213
+
1214
+ [source,ruby]
1215
+ ----
1216
+ class Schema < Lutaml::Model::Serializable
1217
+ attribute :id, Lutaml::Model::Type::String
1218
+ attribute :link, Lutaml::Model::Type::String
1219
+ attribute :name, Lutaml::Model::Type::String
1220
+ end
1221
+
1222
+ class ChildMappingClass < Lutaml::Model::Serializable
1223
+ attribute :schemas, Schema, collection: true
1224
+
1225
+ json do
1226
+ map "schemas", to: :schemas,
1227
+ child_mappings: {
1228
+ id: :key,
1229
+ link: %i[path link],
1230
+ name: %i[path name],
1231
+ }
1232
+ end
1233
+ end
1234
+ ----
1235
+
1236
+ The output becomes:
1237
+
1238
+ [source,ruby]
1239
+ ----
1240
+ > ChildMappingClass.from_json(json)
1241
+ > #<ChildMappingClass:0x0000000104ac7240
1242
+ @schemas=
1243
+ [#<Schema:0x0000000104ac6e30 @id="foo", @link="link one", @name="one">,
1244
+ #<Schema:0x0000000104ac58f0 @id="bar", @link="link two", @name="two">]>
1245
+ > ChildMappingClass.new(schemas: [Schema.new(id: "foo", link: "link one", name: "one"), Schema.new(id: "bar", link: "link two", name: "two")]).to_json
1246
+ > #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}
1247
+ ----
1248
+
1249
+ In this example:
1250
+
1251
+ * The `key` of each schema (`foo` and `bar`) is mapped to the `id` attribute.
1252
+
1253
+ * The nested `path.link` and `path.name` keys are mapped to the `link` and
1254
+ `name` attributes, respectively.
1255
+ ====
1256
+
1257
+
400
1258
  == Adapters
401
1259
 
402
- Lutaml::Model uses an adapter pattern to support multiple libraries for each serialization format.
1260
+ === General
1261
+
1262
+ Lutaml::Model uses an adapter pattern to support multiple libraries for each
1263
+ serialization format.
403
1264
 
404
- === XML: Nokogiri, Oga, Ox
1265
+ You will need to specify the configuration for the adapter you want to use. The
1266
+ easiest way is to copy and paste the following configuration into your code.
1267
+
1268
+ The default configuration is as follows:
405
1269
 
406
1270
  [source,ruby]
407
1271
  ----
408
1272
  require 'lutaml/model'
409
1273
  require 'lutaml/model/xml_adapter/nokogiri_adapter'
410
- require 'lutaml/model/xml_adapter/ox_adapter'
411
- require 'lutaml/model/xml_adapter/oga_adapter'
1274
+ require 'lutaml/model/json_adapter/standard_json_adapter'
1275
+ require 'lutaml/model/toml_adapter/toml_rb_adapter'
1276
+ require 'lutaml/model/yaml_adapter/standard_yaml_adapter'
412
1277
 
413
1278
  Lutaml::Model::Config.configure do |config|
414
1279
  config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
415
- # Or use OxAdapter or OgaAdapter
1280
+ config.yaml_adapter = Lutaml::Model::YamlAdapter::StandardYamlAdapter
1281
+ config.json_adapter = Lutaml::Model::JsonAdapter::StandardJsonAdapter
1282
+ config.toml_adapter = Lutaml::Model::TomlAdapter::TomlRbAdapter
416
1283
  end
417
1284
  ----
418
1285
 
419
- === JSON: `JSON` and `MultiJson`
420
1286
 
1287
+ === XML
1288
+
1289
+ Lutaml::Model supports the following XML adapters:
1290
+
1291
+ * Nokogiri (default)
1292
+ * Oga (optional, plain Ruby suitable for Opal/JS)
1293
+ * Ox (optional)
1294
+
1295
+ .Using the Nokogiri XML adapter
421
1296
  [source,ruby]
422
1297
  ----
423
1298
  require 'lutaml/model'
424
- require 'lutaml/model/json_adapter/standard'
425
- require 'lutaml/model/json_adapter/multi_json'
426
1299
 
427
1300
  Lutaml::Model::Config.configure do |config|
428
- config.json_adapter = Lutaml::Model::JsonAdapter::StandardDocument
429
- # Or use MultiJsonDocument
1301
+ require 'lutaml/model/xml_adapter/nokogiri_adapter'
1302
+ config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
430
1303
  end
431
1304
  ----
432
1305
 
433
- === TOML: `Tomlib` and `Toml-rb`
1306
+ .Using the Oga XML adapter
1307
+ [source,ruby]
1308
+ ----
1309
+ require 'lutaml/model'
1310
+
1311
+ Lutaml::Model::Config.configure do |config|
1312
+ require 'lutaml/model/xml_adapter/oga_adapter'
1313
+ config.xml_adapter = Lutaml::Model::XmlAdapter::OgaAdapter
1314
+ end
1315
+ ----
434
1316
 
1317
+ .Using the Ox XML adapter
1318
+ [source,ruby]
1319
+ ----
1320
+ require 'lutaml/model'
1321
+
1322
+ Lutaml::Model::Config.configure do |config|
1323
+ require 'lutaml/model/xml_adapter/ox_adapter'
1324
+ config.xml_adapter = Lutaml::Model::XmlAdapter::OxAdapter
1325
+ end
1326
+ ----
1327
+
1328
+
1329
+ === YAML
1330
+
1331
+ Lutaml::Model supports only one YAML adapter.
1332
+
1333
+ * YAML (default)
1334
+
1335
+ .Using the YAML adapter
1336
+ [source,ruby]
1337
+ ----
1338
+ require 'lutaml/model'
1339
+
1340
+ Lutaml::Model::Config.configure do |config|
1341
+ require 'lutaml/model/yaml_adapter/standard_yaml_adapter'
1342
+ config.yaml_adapter = Lutaml::Model::YamlAdapter::StandardYamlAdapter
1343
+ end
1344
+ ----
1345
+
1346
+
1347
+
1348
+ === JSON
1349
+
1350
+ Lutaml::Model supports the following JSON adapters:
1351
+
1352
+ * JSON (default)
1353
+ * MultiJson (optional)
1354
+
1355
+ .Using the JSON adapter
1356
+ [source,ruby]
1357
+ ----
1358
+ require 'lutaml/model'
1359
+
1360
+ Lutaml::Model::Config.configure do |config|
1361
+ require 'lutaml/model/json_adapter/standard_json_adapter'
1362
+ config.json_adapter = Lutaml::Model::JsonAdapter::StandardJsonAdapter
1363
+ end
1364
+ ----
1365
+
1366
+ .Using the MultiJson adapter
1367
+ [source,ruby]
1368
+ ----
1369
+ require 'lutaml/model'
1370
+
1371
+ Lutaml::Model::Config.configure do |config|
1372
+ require 'lutaml/model/json_adapter/multi_json_adapter'
1373
+ config.json_adapter = Lutaml::Model::JsonAdapter::MultiJsonAdapter
1374
+ end
1375
+ ----
1376
+
1377
+ === TOML
1378
+
1379
+ Lutaml::Model supports the following TOML adapters:
1380
+
1381
+ * Toml-rb (default)
1382
+ * Tomlib (optional)
1383
+
1384
+ .Using the Toml-rb adapter
1385
+ [source,ruby]
1386
+ ----
1387
+ require 'lutaml/model'
1388
+
1389
+ Lutaml::Model::Config.configure do |config|
1390
+ require 'lutaml/model/toml_adapter/toml_rb_adapter'
1391
+ config.toml_adapter = Lutaml::Model::TomlAdapter::TomlRbAdapter
1392
+ end
1393
+ ----
1394
+
1395
+ .Using the Tomlib adapter
435
1396
  [source,ruby]
436
1397
  ----
437
1398
  require 'lutaml/model'
438
- require 'lutaml/model/toml_adapter/toml_rb_adapter'
439
- require 'lutaml/model/toml_adapter/tomlib_adapter'
440
1399
 
441
1400
  Lutaml::Model::Config.configure do |config|
442
- config.toml_adapter = Lutaml::Model::TomlAdapter::TomlRbDocument
443
- # Or use TomlibDocument
1401
+ config.toml_adapter = Lutaml::Model::TomlAdapter::TomlibAdapter
1402
+ require 'lutaml/model/toml_adapter/tomlib_adapter'
444
1403
  end
445
1404
  ----
446
1405
 
447
1406
  == License and Copyright
448
1407
 
449
- This project is licensed under the BSD 2-clause License - see the LICENSE file for details.
1408
+ This project is licensed under the BSD 2-clause License.
1409
+ See the LICENSE file for details.
450
1410
 
451
- This project is maintained by Ribose.
1411
+ Copyright Ribose.