lutaml-model 0.7.1 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.envrc +1 -0
- data/.github/workflows/dependent-tests.yml +4 -0
- data/.github/workflows/rake.yml +12 -0
- data/.github/workflows/release.yml +3 -0
- data/.gitignore +6 -1
- data/.irbrc +1 -0
- data/.pryrc +1 -0
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +58 -84
- data/Gemfile +4 -1
- data/README.adoc +3115 -365
- data/RELEASE_NOTES.adoc +346 -0
- data/docs/custom_adapters.adoc +144 -0
- data/docs/custom_registers.adoc +228 -0
- data/docs/schema_generation.adoc +898 -0
- data/docs/schema_import.adoc +364 -0
- data/flake.lock +114 -0
- data/flake.nix +103 -0
- data/lib/lutaml/model/attribute.rb +242 -100
- data/lib/lutaml/model/choice.rb +30 -0
- data/lib/lutaml/model/collection.rb +212 -0
- data/lib/lutaml/model/comparable_model.rb +3 -3
- data/lib/lutaml/model/config.rb +71 -42
- data/lib/lutaml/model/constants.rb +2 -0
- data/lib/lutaml/model/error/element_count_out_of_range_error.rb +29 -0
- data/lib/lutaml/model/error/invalid_attribute_name_error.rb +15 -0
- data/lib/lutaml/model/error/invalid_attribute_options_error.rb +16 -0
- data/lib/lutaml/model/error/invalid_choice_range_error.rb +3 -5
- data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
- data/lib/lutaml/model/error/register/not_registrable_class_error.rb +11 -0
- data/lib/lutaml/model/error/type/invalid_value_error.rb +5 -3
- data/lib/lutaml/model/error/type/max_bound_error.rb +20 -0
- data/lib/lutaml/model/error/type/max_length_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_bound_error.rb +20 -0
- data/lib/lutaml/model/error/type/min_length_error.rb +20 -0
- data/lib/lutaml/model/error/type/pattern_not_matched_error.rb +18 -0
- data/lib/lutaml/model/error/validation_failed_error.rb +9 -0
- data/lib/lutaml/model/error.rb +10 -0
- data/lib/lutaml/model/errors.rb +36 -0
- data/lib/lutaml/model/format_registry.rb +44 -0
- data/lib/lutaml/model/global_register.rb +41 -0
- data/lib/lutaml/model/hash_adapter/document.rb +11 -0
- data/lib/lutaml/model/hash_adapter/mapping.rb +19 -0
- data/lib/lutaml/model/hash_adapter/mapping_rule.rb +9 -0
- data/lib/lutaml/model/hash_adapter/standard_adapter.rb +17 -0
- data/lib/lutaml/model/hash_adapter/transform.rb +8 -0
- data/lib/lutaml/model/hash_adapter.rb +21 -0
- data/lib/lutaml/model/json/document.rb +11 -0
- data/lib/lutaml/model/json/mapping.rb +19 -0
- data/lib/lutaml/model/json/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
- data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
- data/lib/lutaml/model/json/transform.rb +8 -0
- data/lib/lutaml/model/json.rb +21 -0
- data/lib/lutaml/model/jsonl/document.rb +14 -0
- data/lib/lutaml/model/jsonl/mapping.rb +19 -0
- data/lib/lutaml/model/jsonl/mapping_rule.rb +9 -0
- data/lib/lutaml/model/jsonl/standard_adapter.rb +33 -0
- data/lib/lutaml/model/jsonl/transform.rb +19 -0
- data/lib/lutaml/model/jsonl.rb +21 -0
- data/lib/lutaml/model/key_value_document.rb +28 -0
- data/lib/lutaml/model/mapping/key_value_mapping.rb +85 -13
- data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +5 -0
- data/lib/lutaml/model/mapping/mapping.rb +13 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +15 -9
- data/lib/lutaml/model/register.rb +105 -0
- data/lib/lutaml/model/{toml_adapter.rb → registrable.rb} +1 -1
- data/lib/lutaml/model/schema/base_schema.rb +64 -0
- data/lib/lutaml/model/schema/decorators/attribute.rb +114 -0
- data/lib/lutaml/model/schema/decorators/choices.rb +31 -0
- data/lib/lutaml/model/schema/decorators/class_definition.rb +85 -0
- data/lib/lutaml/model/schema/decorators/definition_collection.rb +97 -0
- data/lib/lutaml/model/schema/generator/definition.rb +53 -0
- data/lib/lutaml/model/schema/generator/definitions_collection.rb +81 -0
- data/lib/lutaml/model/schema/generator/properties_collection.rb +63 -0
- data/lib/lutaml/model/schema/generator/property.rb +110 -0
- data/lib/lutaml/model/schema/generator/ref.rb +24 -0
- data/lib/lutaml/model/schema/helpers/template_helper.rb +49 -0
- data/lib/lutaml/model/schema/json_schema.rb +42 -49
- data/lib/lutaml/model/schema/relaxng_schema.rb +14 -10
- data/lib/lutaml/model/schema/renderer.rb +36 -0
- data/lib/lutaml/model/schema/shared_methods.rb +24 -0
- data/lib/lutaml/model/schema/templates/model.erb +9 -0
- data/lib/lutaml/model/schema/xml_compiler/attribute.rb +85 -0
- data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +45 -0
- data/lib/lutaml/model/schema/xml_compiler/choice.rb +65 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +27 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +34 -0
- data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +136 -0
- data/lib/lutaml/model/schema/xml_compiler/element.rb +104 -0
- data/lib/lutaml/model/schema/xml_compiler/group.rb +97 -0
- data/lib/lutaml/model/schema/xml_compiler/restriction.rb +101 -0
- data/lib/lutaml/model/schema/xml_compiler/sequence.rb +50 -0
- data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +36 -0
- data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +189 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +231 -587
- data/lib/lutaml/model/schema/xsd_schema.rb +12 -8
- data/lib/lutaml/model/schema/yaml_schema.rb +41 -35
- data/lib/lutaml/model/schema.rb +1 -0
- data/lib/lutaml/model/sequence.rb +60 -30
- data/lib/lutaml/model/serialization_adapter.rb +22 -0
- data/lib/lutaml/model/serialize.rb +296 -548
- data/lib/lutaml/model/services/base.rb +11 -0
- data/lib/lutaml/model/services/logger.rb +54 -0
- data/lib/lutaml/model/services/rule_value_extractor.rb +92 -0
- data/lib/lutaml/model/services/transformer.rb +48 -0
- data/lib/lutaml/model/services/type/validator/number.rb +25 -0
- data/lib/lutaml/model/services/type/validator/string.rb +52 -0
- data/lib/lutaml/model/services/type/validator.rb +43 -0
- data/lib/lutaml/model/services/validator.rb +145 -0
- data/lib/lutaml/model/services.rb +5 -0
- data/lib/lutaml/model/toml/document.rb +11 -0
- data/lib/lutaml/model/toml/mapping.rb +27 -0
- data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
- data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
- data/lib/lutaml/model/toml/transform.rb +8 -0
- data/lib/lutaml/model/toml.rb +30 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +297 -0
- data/lib/lutaml/model/transform/xml_transform.rb +228 -0
- data/lib/lutaml/model/transform.rb +93 -0
- data/lib/lutaml/model/type/boolean.rb +1 -1
- data/lib/lutaml/model/type/date.rb +1 -1
- data/lib/lutaml/model/type/date_time.rb +1 -1
- data/lib/lutaml/model/type/decimal.rb +11 -9
- data/lib/lutaml/model/type/float.rb +2 -1
- data/lib/lutaml/model/type/integer.rb +24 -21
- data/lib/lutaml/model/type/string.rb +4 -2
- data/lib/lutaml/model/type/time.rb +1 -1
- data/lib/lutaml/model/type/time_without_date.rb +1 -1
- data/lib/lutaml/model/type/value.rb +11 -10
- data/lib/lutaml/model/type.rb +5 -2
- data/lib/lutaml/model/uninitialized_class.rb +1 -1
- data/lib/lutaml/model/utils.rb +56 -4
- data/lib/lutaml/model/validation.rb +6 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
- data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
- data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
- data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +42 -25
- data/lib/lutaml/model/xml/element.rb +32 -0
- data/lib/lutaml/model/xml/mapping.rb +471 -0
- data/lib/lutaml/model/xml/mapping_rule.rb +149 -0
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +234 -0
- data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +7 -2
- data/lib/lutaml/model/xml/oga_adapter.rb +172 -0
- data/lib/lutaml/model/xml/ox_adapter.rb +229 -0
- data/lib/lutaml/model/xml/transform.rb +8 -0
- data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
- data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +9 -31
- data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
- data/lib/lutaml/model/xml.rb +31 -0
- data/lib/lutaml/model/xml_adapter/element.rb +11 -25
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
- data/lib/lutaml/model/yaml/document.rb +10 -0
- data/lib/lutaml/model/yaml/mapping.rb +19 -0
- data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
- data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
- data/lib/lutaml/model/yaml/transform.rb +8 -0
- data/lib/lutaml/model/yaml.rb +21 -0
- data/lib/lutaml/model/yamls/document.rb +14 -0
- data/lib/lutaml/model/yamls/mapping.rb +19 -0
- data/lib/lutaml/model/yamls/mapping_rule.rb +9 -0
- data/lib/lutaml/model/yamls/standard_adapter.rb +34 -0
- data/lib/lutaml/model/yamls/transform.rb +19 -0
- data/lib/lutaml/model/yamls.rb +21 -0
- data/lib/lutaml/model.rb +15 -4
- data/lutaml-model.gemspec +0 -4
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +5 -6
- data/spec/fixtures/xml/advanced_test_schema.xsd +134 -0
- data/spec/fixtures/xml/examples/nested_categories.xml +55 -0
- data/spec/fixtures/xml/examples/valid_catalog.xml +43 -0
- data/spec/fixtures/xml/product_catalog.xsd +151 -0
- data/spec/fixtures/xml/specifications_schema.xsd +38 -0
- data/spec/lutaml/model/attribute_collection_spec.rb +101 -0
- data/spec/lutaml/model/attribute_spec.rb +41 -44
- data/spec/lutaml/model/cdata_spec.rb +7 -7
- data/spec/lutaml/model/choice_spec.rb +44 -0
- data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
- data/spec/lutaml/model/custom_collection_spec.rb +830 -0
- data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
- data/spec/lutaml/model/global_register_spec.rb +108 -0
- data/spec/lutaml/model/group_spec.rb +27 -10
- data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
- data/spec/lutaml/model/json_adapter_spec.rb +6 -6
- data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
- data/spec/lutaml/model/jsonl_spec.rb +229 -0
- data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
- data/spec/lutaml/model/mixed_content_spec.rb +24 -24
- data/spec/lutaml/model/multiple_mapping_spec.rb +6 -6
- data/spec/lutaml/model/ordered_content_spec.rb +6 -6
- data/spec/lutaml/model/polymorphic_spec.rb +178 -0
- data/spec/lutaml/model/register/key_value_spec.rb +275 -0
- data/spec/lutaml/model/register/xml_spec.rb +185 -0
- data/spec/lutaml/model/register_spec.rb +147 -0
- data/spec/lutaml/model/root_mappings_spec.rb +3 -3
- data/spec/lutaml/model/rule_value_extractor_spec.rb +162 -0
- data/spec/lutaml/model/schema/generator/definitions_collection_spec.rb +120 -0
- data/spec/lutaml/model/schema/json_schema_spec.rb +412 -51
- data/spec/lutaml/model/schema/json_schema_to_models_spec.rb +383 -0
- data/spec/lutaml/model/schema/xml_compiler/attribute_group_spec.rb +65 -0
- data/spec/lutaml/model/schema/xml_compiler/attribute_spec.rb +63 -0
- data/spec/lutaml/model/schema/xml_compiler/choice_spec.rb +71 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_content_restriction_spec.rb +55 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_content_spec.rb +37 -0
- data/spec/lutaml/model/schema/xml_compiler/complex_type_spec.rb +173 -0
- data/spec/lutaml/model/schema/xml_compiler/element_spec.rb +63 -0
- data/spec/lutaml/model/schema/xml_compiler/group_spec.rb +86 -0
- data/spec/lutaml/model/schema/xml_compiler/restriction_spec.rb +76 -0
- data/spec/lutaml/model/schema/xml_compiler/sequence_spec.rb +59 -0
- data/spec/lutaml/model/schema/xml_compiler/simple_content_spec.rb +55 -0
- data/spec/lutaml/model/schema/xml_compiler/simple_type_spec.rb +181 -0
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +503 -1804
- data/spec/lutaml/model/schema/yaml_schema_spec.rb +249 -26
- data/spec/lutaml/model/sequence_spec.rb +36 -0
- data/spec/lutaml/model/serializable_spec.rb +201 -94
- data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
- data/spec/lutaml/model/toml_spec.rb +51 -0
- data/spec/lutaml/model/transformation_spec.rb +72 -15
- data/spec/lutaml/model/type_spec.rb +8 -4
- data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
- data/spec/lutaml/model/utils_spec.rb +3 -3
- data/spec/lutaml/model/xml/derived_attributes_spec.rb +1 -1
- data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
- data/spec/lutaml/model/xml/root_mappings/nested_child_mappings_spec.rb +217 -0
- data/spec/lutaml/model/xml/xml_element_spec.rb +7 -1
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +12 -12
- data/spec/lutaml/model/xml_adapter_spec.rb +30 -6
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +14 -7
- data/spec/lutaml/model/xml_mapping_spec.rb +27 -15
- data/spec/lutaml/model/xml_spec.rb +63 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
- data/spec/lutaml/model/yamls/standard_adapter_spec.rb +183 -0
- data/spec/lutaml/model/yamls_spec.rb +294 -0
- data/spec/spec_helper.rb +4 -3
- metadata +162 -60
- data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
- data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
- data/lib/lutaml/model/loggable.rb +0 -15
- data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
- data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
- data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
- data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
- data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
- data/lib/lutaml/model/mapping.rb +0 -14
- data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
- data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
- data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
- data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
- data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
- data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
- data/lib/lutaml/model/yaml_adapter.rb +0 -8
data/README.adoc
CHANGED
|
@@ -17,7 +17,7 @@ for:
|
|
|
17
17
|
|
|
18
18
|
It provides simple, flexible and comprehensive mechanisms for defining
|
|
19
19
|
information models with attributes and types, and the serialization of them
|
|
20
|
-
to/from serialization formats including JSON, XML, YAML, and TOML.
|
|
20
|
+
to/from serialization formats including Hash, JSON, XML, YAML, and TOML.
|
|
21
21
|
|
|
22
22
|
For serialization formats, it uses an adapter pattern to support multiple
|
|
23
23
|
libraries for each format, providing flexibility and extensibility for your data
|
|
@@ -33,12 +33,17 @@ Lutaml::Model are provided in <<migrate-from-shale>>.
|
|
|
33
33
|
== Features
|
|
34
34
|
|
|
35
35
|
* Define models with attributes and types
|
|
36
|
-
* Serialize and deserialize models to/from JSON, XML, YAML, and TOML
|
|
36
|
+
* Serialize and deserialize models to/from Hash, JSON, XML, YAML, and TOML
|
|
37
37
|
* Support for multiple serialization libraries (e.g., `toml-rb`, `tomlib`)
|
|
38
38
|
* Configurable adapters for different serialization formats
|
|
39
39
|
* Support for collections and default values
|
|
40
40
|
* Custom serialization/deserialization methods
|
|
41
41
|
* XML namespaces and mappings
|
|
42
|
+
* Generate serialization schemas from model definitions (<<schema-generation>>)
|
|
43
|
+
* Import serialization schemas to define models (<<schema-import>>)
|
|
44
|
+
* Create custom adapters for additional data formats (see <<custom-adapters>>)
|
|
45
|
+
* Dynamically modify model attribute types using registers (see <<custom-registers>>)
|
|
46
|
+
|
|
42
47
|
|
|
43
48
|
|
|
44
49
|
== Data modeling in a nutshell
|
|
@@ -269,10 +274,26 @@ Or install it yourself as:
|
|
|
269
274
|
gem install lutaml-model
|
|
270
275
|
----
|
|
271
276
|
|
|
277
|
+
|
|
278
|
+
== Components
|
|
279
|
+
|
|
280
|
+
LutaML provides the following set of components to model information in a
|
|
281
|
+
structured way.
|
|
282
|
+
|
|
283
|
+
* <<model-definition,Basic models>>
|
|
284
|
+
* <<attribute-definition,Attributes in models>>
|
|
285
|
+
* <<value-definition,Values assigned to attributes>>
|
|
286
|
+
* <<collection-definition,Collections of models>>
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
[[model-definition]]
|
|
272
290
|
== Model
|
|
273
291
|
|
|
274
292
|
=== General
|
|
275
293
|
|
|
294
|
+
A LutaML model is used to represent a class of information, of which a model
|
|
295
|
+
instance is a set of information representing a coherent concept.
|
|
296
|
+
|
|
276
297
|
There are two ways to define an information model in Lutaml::Model:
|
|
277
298
|
|
|
278
299
|
* Inheriting from the `Lutaml::Model::Serializable` class
|
|
@@ -376,7 +397,7 @@ same class and all their attributes are equal.
|
|
|
376
397
|
> # true
|
|
377
398
|
----
|
|
378
399
|
|
|
379
|
-
|
|
400
|
+
[[value-definition]]
|
|
380
401
|
== Value types
|
|
381
402
|
|
|
382
403
|
=== General types
|
|
@@ -496,7 +517,7 @@ serialization format and returns the object to be assigned to the `Value` class'
|
|
|
496
517
|
`to_{format}`:: Serializes the object to a string of the serialization format.
|
|
497
518
|
|
|
498
519
|
The `{format}` part of the method name is the serialization format in lowercase
|
|
499
|
-
(e.g. `json`, `xml`, `yaml`, `toml`).
|
|
520
|
+
(e.g. `hash`, `json`, `xml`, `yaml`, `toml`).
|
|
500
521
|
|
|
501
522
|
.Using custom serialization methods to handle a high-precision date-time type
|
|
502
523
|
[example]
|
|
@@ -564,6 +585,7 @@ part lost due to the inability of JSON to handle high-precision date-time.
|
|
|
564
585
|
====
|
|
565
586
|
|
|
566
587
|
|
|
588
|
+
[[attribute-definition]]
|
|
567
589
|
== Attributes
|
|
568
590
|
|
|
569
591
|
|
|
@@ -615,6 +637,56 @@ puts s.established
|
|
|
615
637
|
----
|
|
616
638
|
====
|
|
617
639
|
|
|
640
|
+
==== Restricting the value of an attribute
|
|
641
|
+
|
|
642
|
+
The `restrict` class method is used to update or refine the validation rules for an attribute that has already been defined. This allows you to apply additional or stricter constraints to an existing attribute without redefining it.
|
|
643
|
+
|
|
644
|
+
.Using the `restrict` class method to update the options of an existing attribute
|
|
645
|
+
[example]
|
|
646
|
+
====
|
|
647
|
+
[source,ruby]
|
|
648
|
+
----
|
|
649
|
+
class Studio < Lutaml::Model::Serializable
|
|
650
|
+
attribute :name, :string
|
|
651
|
+
restrict :name, collection: 1..3, pattern: /[A-Za-z]+/
|
|
652
|
+
end
|
|
653
|
+
----
|
|
654
|
+
====
|
|
655
|
+
|
|
656
|
+
.Apply different restrictions to the existing attribute in multiple subclasses
|
|
657
|
+
[example]
|
|
658
|
+
====
|
|
659
|
+
[source,ruby]
|
|
660
|
+
----
|
|
661
|
+
class Document < Lutaml::Model::Serializable
|
|
662
|
+
attribute :status, :string
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
class DraftDocument < Document
|
|
666
|
+
# Only allow "draft" or "in_review" as valid statuses for drafts
|
|
667
|
+
restrict :status, values: %w[draft in_review]
|
|
668
|
+
end
|
|
669
|
+
|
|
670
|
+
class PublishedDocument < Document
|
|
671
|
+
# Only allow "published" or "archived" as valid statuses for published documents
|
|
672
|
+
restrict :status, values: %w[published archived]
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# Usage
|
|
676
|
+
# Call .validate! to trigger validation and raise an error if the value is not allowed
|
|
677
|
+
Document.new(status: "draft").validate! # valid, there are no validation rules for `Document`
|
|
678
|
+
Document.new(status: "published").validate! # valid, there are no validation rules for `Document`
|
|
679
|
+
DraftDocument.new(status: "draft").validate! # valid
|
|
680
|
+
DraftDocument.new(status: "in_review").validate! # valid
|
|
681
|
+
DraftDocument.new(status: "published").validate! # raises error (not allowed)
|
|
682
|
+
PublishedDocument.new(status: "published").validate! # valid
|
|
683
|
+
PublishedDocument.new(status: "archived").validate! # valid
|
|
684
|
+
PublishedDocument.new(status: "draft").validate! # raises error (not allowed)
|
|
685
|
+
----
|
|
686
|
+
====
|
|
687
|
+
|
|
688
|
+
All options that are supported by the `attribute` class method are also supported by the `restrict` method. Any unsupported option passed to `restrict` will result in a `Lutaml::Model::InvalidAttributeOptionsError` being raised.
|
|
689
|
+
|
|
618
690
|
=== Polymorphic attributes
|
|
619
691
|
|
|
620
692
|
==== General
|
|
@@ -820,7 +892,7 @@ end
|
|
|
820
892
|
<1> The name of the polymorphic attribute.
|
|
821
893
|
<2> The polymorphic superclass class.
|
|
822
894
|
<3> Any options for the attribute.
|
|
823
|
-
<4> The `polymorphic` option that determines the acceptable polymorphic subclasses
|
|
895
|
+
<4> The `polymorphic` option that determines the acceptable polymorphic subclasses, or just `true`.
|
|
824
896
|
<5> The polymorphic subclasses.
|
|
825
897
|
|
|
826
898
|
The `polymorphic` option is an array of polymorphic subclasses that the
|
|
@@ -863,6 +935,25 @@ end
|
|
|
863
935
|
----
|
|
864
936
|
====
|
|
865
937
|
|
|
938
|
+
* If the attribute (collection or not) is meant to contain instances belonging
|
|
939
|
+
to any polymorphic subclass of a defined base class, then set the `polymorphic:
|
|
940
|
+
true` option.
|
|
941
|
+
+
|
|
942
|
+
[example]
|
|
943
|
+
====
|
|
944
|
+
In the following code, `ReferenceSet` is a class that has a polymorphic
|
|
945
|
+
attribute `references`. The `references` attribute can accept instances of
|
|
946
|
+
any polymorphic subclass of the `Reference` base class, so `polymorphic: true`
|
|
947
|
+
is set.
|
|
948
|
+
|
|
949
|
+
[source,ruby]
|
|
950
|
+
----
|
|
951
|
+
class ReferenceSet < Lutaml::Model::Serializable
|
|
952
|
+
attribute :references, Reference, collection: true, polymorphic: true
|
|
953
|
+
end
|
|
954
|
+
----
|
|
955
|
+
====
|
|
956
|
+
|
|
866
957
|
* If the attribute (collection or not) is meant to contain instances belonging
|
|
867
958
|
to more than one polymorphic subclass, then those acceptable polymorphic
|
|
868
959
|
subclasses should be explicitly specified in the `polymorphic: [...]` option.
|
|
@@ -1051,16 +1142,16 @@ class Reference < Lutaml::Model::Serializable
|
|
|
1051
1142
|
|
|
1052
1143
|
xml do
|
|
1053
1144
|
map_attribute "reference-type", to: :_class, polymorphic_map: {
|
|
1054
|
-
"document-ref" => "
|
|
1055
|
-
"anchor-ref" => "
|
|
1145
|
+
"document-ref" => "DocumentReference",
|
|
1146
|
+
"anchor-ref" => "AnchorReference",
|
|
1056
1147
|
}
|
|
1057
1148
|
map_element "name", to: :name
|
|
1058
1149
|
end
|
|
1059
1150
|
|
|
1060
1151
|
key_value do
|
|
1061
1152
|
map "_class", to: :_class, polymorphic_map: {
|
|
1062
|
-
"Document" => "
|
|
1063
|
-
"Anchor" => "
|
|
1153
|
+
"Document" => "DocumentReference",
|
|
1154
|
+
"Anchor" => "AnchorReference",
|
|
1064
1155
|
}
|
|
1065
1156
|
map "name", to: :name
|
|
1066
1157
|
end
|
|
@@ -1484,9 +1575,13 @@ puts SomeModel.new.to_yaml
|
|
|
1484
1575
|
----
|
|
1485
1576
|
====
|
|
1486
1577
|
|
|
1578
|
+
|
|
1487
1579
|
=== Derived attributes
|
|
1488
1580
|
|
|
1489
|
-
A derived attribute
|
|
1581
|
+
A derived attribute has a value computed dynamically on evaluation of an
|
|
1582
|
+
instance method.
|
|
1583
|
+
|
|
1584
|
+
It is defined using the `method:` option.
|
|
1490
1585
|
|
|
1491
1586
|
Syntax:
|
|
1492
1587
|
|
|
@@ -1572,6 +1667,7 @@ attributes.
|
|
|
1572
1667
|
* The last attribute `completeName` is optional.
|
|
1573
1668
|
====
|
|
1574
1669
|
|
|
1670
|
+
NOTE: The `choice` directive can be used with `import_model_attributes`. For more details, see <<import-model-attributes-inside-choice, Using import_model_attributes inside a choice block>>.
|
|
1575
1671
|
|
|
1576
1672
|
|
|
1577
1673
|
=== Importable models for reuse
|
|
@@ -1628,6 +1724,7 @@ class ComplexType < Lutaml::Model::Serializable
|
|
|
1628
1724
|
|
|
1629
1725
|
xml do
|
|
1630
1726
|
root "GroupOfItems"
|
|
1727
|
+
|
|
1631
1728
|
map_attribute "tag", to: :tag
|
|
1632
1729
|
map_content to: :content
|
|
1633
1730
|
map_element :group, to: :group
|
|
@@ -1659,6 +1756,161 @@ end
|
|
|
1659
1756
|
----
|
|
1660
1757
|
====
|
|
1661
1758
|
|
|
1759
|
+
[[import-model-mappings-inside-sequence]]
|
|
1760
|
+
==== Using `import_model_mappings` inside a `sequence`
|
|
1761
|
+
|
|
1762
|
+
You can use `import_model_mappings` within a `sequence` block to include the element mappings from another model. This is useful for composing complex XML structures from reusable model components.
|
|
1763
|
+
|
|
1764
|
+
The element mappings will be imported inside this specific `sequence` block that calls the import method, rest of the mappings like `content`, `attributes`, etc. will be inserted at the class level.
|
|
1765
|
+
|
|
1766
|
+
NOTE: `import_model` and `import_model_attributes` are not supported inside a `sequence` block.
|
|
1767
|
+
|
|
1768
|
+
[example]
|
|
1769
|
+
====
|
|
1770
|
+
[source,ruby]
|
|
1771
|
+
----
|
|
1772
|
+
class Address < Lutaml::Model::Serializable
|
|
1773
|
+
attribute :street, :string
|
|
1774
|
+
attribute :city, :string
|
|
1775
|
+
attribute :zip, :string
|
|
1776
|
+
|
|
1777
|
+
xml do
|
|
1778
|
+
no_root
|
|
1779
|
+
|
|
1780
|
+
map_element :street, to: :street
|
|
1781
|
+
map_element :city, to: :city
|
|
1782
|
+
map_element :zip, to: :zip
|
|
1783
|
+
end
|
|
1784
|
+
end
|
|
1785
|
+
|
|
1786
|
+
class Person < Lutaml::Model::Serializable
|
|
1787
|
+
attribute :name, :string
|
|
1788
|
+
import_model_attributes Address
|
|
1789
|
+
|
|
1790
|
+
xml do
|
|
1791
|
+
root "Person"
|
|
1792
|
+
|
|
1793
|
+
map_element :name, to: :name
|
|
1794
|
+
sequence do
|
|
1795
|
+
import_model_mappings Address
|
|
1796
|
+
end
|
|
1797
|
+
end
|
|
1798
|
+
end
|
|
1799
|
+
|
|
1800
|
+
# Example XML output:
|
|
1801
|
+
valid_xml = <<~XML
|
|
1802
|
+
<Person>
|
|
1803
|
+
<name>John Doe</name>
|
|
1804
|
+
<street>123 Main St</street>
|
|
1805
|
+
<city>Metropolis</city>
|
|
1806
|
+
<zip>12345</zip>
|
|
1807
|
+
</Person>
|
|
1808
|
+
XML
|
|
1809
|
+
invalid_xml = <<~XML
|
|
1810
|
+
<Person>
|
|
1811
|
+
<name>John Doe</name>
|
|
1812
|
+
<street>123 Main St</street>
|
|
1813
|
+
<zip>12345</zip>
|
|
1814
|
+
</Person>
|
|
1815
|
+
XML
|
|
1816
|
+
Person.from_xml(valid_xml) # #<Person:0x00000002d56b3988 @city="Metropolis", @name="John Doe", @street="123 Main St", @zip="12345">
|
|
1817
|
+
Person.from_xml(invalid_xml) # raises `Element `zip` does not match the expected sequence order element `city` (Lutaml::Model::IncorrectSequenceError)`
|
|
1818
|
+
----
|
|
1819
|
+
====
|
|
1820
|
+
|
|
1821
|
+
[[import-model-attributes-inside-choice]]
|
|
1822
|
+
==== Using import_model_attributes inside a choice block
|
|
1823
|
+
|
|
1824
|
+
You can use `import_model_attributes` within a `choice` block to allow a model to accept one or more sets of attributes from other models, with flexible cardinality. This is especially useful when you want to allow a user to provide one or more alternative forms of information (e.g., contact methods) in your model.
|
|
1825
|
+
|
|
1826
|
+
For example, suppose you want a `Person` model that can have either an `email`, a `phone`, or both as contact information. You can define `ContactEmail` and `ContactPhone` as importable models, and then use `import_model_attributes` for both, inside a `choice` block in the `Person` model.
|
|
1827
|
+
|
|
1828
|
+
NOTE: The `import_model_attributes` method is used to import the attributes from the other model into the current model. The imported attributes will be associated to the `choice` block that calls the import method.
|
|
1829
|
+
|
|
1830
|
+
[example]
|
|
1831
|
+
====
|
|
1832
|
+
[source,ruby]
|
|
1833
|
+
----
|
|
1834
|
+
class ContactEmail < Lutaml::Model::Serializable
|
|
1835
|
+
attribute :email, :string
|
|
1836
|
+
|
|
1837
|
+
xml do
|
|
1838
|
+
no_root
|
|
1839
|
+
|
|
1840
|
+
map_element :email, to: :email
|
|
1841
|
+
end
|
|
1842
|
+
end
|
|
1843
|
+
|
|
1844
|
+
class ContactPhone < Lutaml::Model::Serializable
|
|
1845
|
+
attribute :phone, :string
|
|
1846
|
+
|
|
1847
|
+
xml do
|
|
1848
|
+
no_root
|
|
1849
|
+
|
|
1850
|
+
map_element :phone, to: :phone
|
|
1851
|
+
end
|
|
1852
|
+
end
|
|
1853
|
+
|
|
1854
|
+
class Person < Lutaml::Model::Serializable
|
|
1855
|
+
# Allow either or both contact methods, but at least one must be present
|
|
1856
|
+
choice(min: 1, max: 2) do
|
|
1857
|
+
import_model_attributes ContactEmail
|
|
1858
|
+
import_model_attributes ContactPhone
|
|
1859
|
+
end
|
|
1860
|
+
|
|
1861
|
+
xml do
|
|
1862
|
+
root "Person"
|
|
1863
|
+
|
|
1864
|
+
map_element :email, to: :email
|
|
1865
|
+
map_element :phone, to: :phone
|
|
1866
|
+
end
|
|
1867
|
+
end
|
|
1868
|
+
|
|
1869
|
+
valid_xml = <<~XML
|
|
1870
|
+
<Person>
|
|
1871
|
+
<email>john.doe@example.com</email>
|
|
1872
|
+
<phone>1234567890</phone>
|
|
1873
|
+
</Person>
|
|
1874
|
+
XML
|
|
1875
|
+
|
|
1876
|
+
Person.from_xml(valid_xml).validate! # #<Person:0x00000002d0e27fe8 @email="john.doe@example.com", @phone="1234567890">
|
|
1877
|
+
|
|
1878
|
+
invalid_xml = <<~XML
|
|
1879
|
+
<Person></Person>
|
|
1880
|
+
XML
|
|
1881
|
+
|
|
1882
|
+
Person.from_xml(invalid_xml).validate! # raises `Lutaml::Model::ValidationError` error
|
|
1883
|
+
----
|
|
1884
|
+
====
|
|
1885
|
+
|
|
1886
|
+
==== Using register functionality
|
|
1887
|
+
|
|
1888
|
+
The register functionality is useful when you want to reference or reuse a model by a symbolic name (e.g., across files or in dynamic scenarios), rather than by direct class reference.
|
|
1889
|
+
|
|
1890
|
+
.Importing a model using a `Register`
|
|
1891
|
+
[example]
|
|
1892
|
+
====
|
|
1893
|
+
[source,ruby]
|
|
1894
|
+
----
|
|
1895
|
+
register = Lutaml::Model::Register.new(:importable_model)
|
|
1896
|
+
register.register_model(GroupOfItems, id: :group_of_items)
|
|
1897
|
+
----
|
|
1898
|
+
|
|
1899
|
+
The `id: :group_of_items` assigns a symbolic name to the registered model, which can then be used in `import_model :group_of_items`.
|
|
1900
|
+
|
|
1901
|
+
[source,ruby]
|
|
1902
|
+
----
|
|
1903
|
+
class GroupOfSubItems < Lutaml::Model::Serializable
|
|
1904
|
+
import_model :group_of_items
|
|
1905
|
+
end
|
|
1906
|
+
----
|
|
1907
|
+
====
|
|
1908
|
+
|
|
1909
|
+
The `import_model :group_of_items` will behave the same as `import_model GroupOfItems` except the class is resolved from the provided `register`.
|
|
1910
|
+
|
|
1911
|
+
NOTE: All the `import_*` methods support the use of `register` functionality.
|
|
1912
|
+
|
|
1913
|
+
NOTE: For more details on registers, see <<custom_registers, Custom Registers>>.
|
|
1662
1914
|
|
|
1663
1915
|
[[attribute-value-transform]]
|
|
1664
1916
|
=== Attribute value transform
|
|
@@ -1974,156 +2226,1474 @@ For the following XML snippet:
|
|
|
1974
2226
|
----
|
|
1975
2227
|
====
|
|
1976
2228
|
|
|
1977
|
-
|
|
2229
|
+
|
|
2230
|
+
== Collections
|
|
1978
2231
|
|
|
1979
2232
|
=== General
|
|
1980
2233
|
|
|
1981
|
-
|
|
1982
|
-
various serialization formats including XML, JSON, YAML, and TOML.
|
|
2234
|
+
Collections are used to represent a contained group of multiple instances of models.
|
|
1983
2235
|
|
|
1984
|
-
|
|
1985
|
-
|
|
2236
|
+
Typically, a collection represents an "Array" or a "Set" in information modeling
|
|
2237
|
+
and programming languages. In LutaML, a collection represents an array of model
|
|
2238
|
+
instances.
|
|
1986
2239
|
|
|
1987
|
-
|
|
1988
|
-
`toml` blocks.
|
|
2240
|
+
Models in a collection may be:
|
|
1989
2241
|
|
|
1990
|
-
|
|
1991
|
-
[source,ruby]
|
|
1992
|
-
----
|
|
1993
|
-
class Example < Lutaml::Model::Serializable
|
|
1994
|
-
xml do
|
|
1995
|
-
# ...
|
|
1996
|
-
end
|
|
2242
|
+
* constrained to be of a single kind;
|
|
1997
2243
|
|
|
1998
|
-
|
|
1999
|
-
# ...
|
|
2000
|
-
end
|
|
2244
|
+
* constrained to be of multiple kinds sharing common characteristics;
|
|
2001
2245
|
|
|
2002
|
-
|
|
2003
|
-
# ...
|
|
2004
|
-
end
|
|
2246
|
+
* unbounded of any kind.
|
|
2005
2247
|
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
end
|
|
2248
|
+
LutaML Model provides the `Lutaml::Model::Collection` class for defining
|
|
2249
|
+
collections of model instances.
|
|
2009
2250
|
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2251
|
+
=== Configuration
|
|
2252
|
+
|
|
2253
|
+
==== All formats
|
|
2254
|
+
|
|
2255
|
+
The `instances` directive defined at the `Collection` class level is used to
|
|
2256
|
+
define the collection attribute and the model type of the collection elements.
|
|
2257
|
+
|
|
2258
|
+
Syntax:
|
|
2259
|
+
|
|
2260
|
+
[source,ruby]
|
|
2261
|
+
----
|
|
2262
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2263
|
+
instances {attribute}, {ModelType}
|
|
2013
2264
|
end
|
|
2014
2265
|
----
|
|
2015
2266
|
|
|
2267
|
+
Where,
|
|
2016
2268
|
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
==== Setting root element name
|
|
2269
|
+
`attribute`:: The name of the attribute that contains the collection.
|
|
2270
|
+
`ModelType`:: The model type of the collection elements.
|
|
2020
2271
|
|
|
2021
|
-
The `root` method sets the root element tag name of the XML document.
|
|
2022
2272
|
|
|
2023
|
-
|
|
2024
|
-
root.
|
|
2273
|
+
==== Mapping instances: key-value formats only
|
|
2025
2274
|
|
|
2026
|
-
|
|
2027
|
-
Sets the tag name for `<example>` in XML `<example>...</example>`.
|
|
2275
|
+
The `map_instances` directive is only used in the `key_value` block.
|
|
2028
2276
|
|
|
2029
2277
|
Syntax:
|
|
2030
2278
|
|
|
2031
2279
|
[source,ruby]
|
|
2032
2280
|
----
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
end
|
|
2036
|
-
----
|
|
2281
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2282
|
+
instances {attribute}, ModelType
|
|
2037
2283
|
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
====
|
|
2041
|
-
[source,ruby]
|
|
2042
|
-
----
|
|
2043
|
-
class Example < Lutaml::Model::Serializable
|
|
2044
|
-
xml do
|
|
2045
|
-
root 'example'
|
|
2284
|
+
key_value do
|
|
2285
|
+
map_instances to: {attribute}
|
|
2046
2286
|
end
|
|
2047
2287
|
end
|
|
2048
2288
|
----
|
|
2049
2289
|
|
|
2050
|
-
|
|
2051
|
-
----
|
|
2052
|
-
> Example.new.to_xml
|
|
2053
|
-
> #<example></example>
|
|
2054
|
-
----
|
|
2055
|
-
====
|
|
2290
|
+
Where,
|
|
2056
2291
|
|
|
2292
|
+
`attribute`:: The name of the attribute that contains model instances.
|
|
2057
2293
|
|
|
2058
|
-
|
|
2294
|
+
This directive maps individual array elements to the defined `instances`
|
|
2295
|
+
attribute. These are the items considered part of the Collection and reflected
|
|
2296
|
+
as Enumerable elements.
|
|
2059
2297
|
|
|
2060
|
-
The root element can be omitted by using the `no_root` method.
|
|
2061
2298
|
|
|
2062
|
-
|
|
2063
|
-
element there cannot be attributes.
|
|
2299
|
+
==== Mapping instances: XML only
|
|
2064
2300
|
|
|
2065
|
-
|
|
2301
|
+
In the `xml` block, the `map_element`, `map_attribute` directives are used instead.
|
|
2066
2302
|
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
----
|
|
2303
|
+
These directives map individual array elements to the defined `instances`
|
|
2304
|
+
attribute. These are the items considered part of the Collection and reflected
|
|
2305
|
+
as Enumerable elements.
|
|
2306
|
+
|
|
2307
|
+
Syntax for an element collection:
|
|
2073
2308
|
|
|
2074
|
-
[example]
|
|
2075
|
-
====
|
|
2076
2309
|
[source,ruby]
|
|
2077
2310
|
----
|
|
2078
|
-
class
|
|
2079
|
-
attribute
|
|
2080
|
-
attribute :code, :string
|
|
2311
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2312
|
+
instances {attribute}, ModelType
|
|
2081
2313
|
|
|
2082
2314
|
xml do
|
|
2083
|
-
|
|
2084
|
-
map_element "code", to: :code
|
|
2085
|
-
map_element "name", to: :name
|
|
2315
|
+
map_element "element-name", to: {attribute}
|
|
2086
2316
|
end
|
|
2087
2317
|
end
|
|
2088
2318
|
----
|
|
2089
2319
|
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2320
|
+
Where,
|
|
2321
|
+
|
|
2322
|
+
`element-name`:: The name of the XML element of each model instance.
|
|
2323
|
+
|
|
2324
|
+
|
|
2325
|
+
Syntax for an attribute collection:
|
|
2095
2326
|
|
|
2096
2327
|
[source,ruby]
|
|
2097
2328
|
----
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2329
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2330
|
+
instances {attribute}, ModelType
|
|
2331
|
+
xml do
|
|
2332
|
+
map_attribute "attribute-name", to: {attribute}
|
|
2333
|
+
end
|
|
2334
|
+
end
|
|
2102
2335
|
----
|
|
2103
|
-
====
|
|
2104
2336
|
|
|
2337
|
+
Where,
|
|
2105
2338
|
|
|
2339
|
+
`attribute-name`:: The name of the XML attribute that contains all model instances.
|
|
2106
2340
|
|
|
2107
|
-
[[xml-map-all]]
|
|
2108
|
-
==== Mapping all XML content
|
|
2109
2341
|
|
|
2110
|
-
|
|
2111
|
-
element into a single attribute in the target Ruby object.
|
|
2342
|
+
=== Collection types
|
|
2112
2343
|
|
|
2113
|
-
|
|
2114
|
-
the XML element at all, and instead handle it as an XML string.
|
|
2344
|
+
A LutaML collections is used for a number of scenarios:
|
|
2115
2345
|
|
|
2116
|
-
|
|
2346
|
+
* Root collections (for key-value formats)
|
|
2347
|
+
* Named collections
|
|
2348
|
+
* Keyed collections (for key-value formats)
|
|
2349
|
+
* Attribute collections
|
|
2350
|
+
* Nested collections
|
|
2351
|
+
// * Polymorphic collections
|
|
2352
|
+
// * Polymorphic attribute collections
|
|
2117
2353
|
|
|
2118
|
-
WARNING: Notice that usage of mapping all will lead to incompatibility between
|
|
2119
|
-
serialization formats, i.e. the raw string content will not be portable as
|
|
2120
|
-
objects are across different formats.
|
|
2121
2354
|
|
|
2122
|
-
|
|
2123
|
-
handled by a Lutaml::Model::Serializable object.
|
|
2355
|
+
=== Root collections (key-value formats only)
|
|
2124
2356
|
|
|
2125
|
-
|
|
2126
|
-
|
|
2357
|
+
==== General
|
|
2358
|
+
|
|
2359
|
+
TODO: This case needs to be fixed for JSON.
|
|
2360
|
+
|
|
2361
|
+
A root collection is a collection that is not contained within a parent
|
|
2362
|
+
collection.
|
|
2363
|
+
|
|
2364
|
+
Root collections only apply to key-value serialization formats.
|
|
2365
|
+
The XML format does not support root collections.
|
|
2366
|
+
|
|
2367
|
+
NOTE: The https://www.w3.org/TR/xml11/[XML standard] mandates the existence
|
|
2368
|
+
of a non-repeated "root element" in an XML document. This means that a valid XML
|
|
2369
|
+
document must have a root element, and all elements in an XML document must
|
|
2370
|
+
exist within the root. This is why an XML document cannot be a "root collection".
|
|
2371
|
+
|
|
2372
|
+
NOTE: A root collection cannot be represented using a non-collection model.
|
|
2373
|
+
|
|
2374
|
+
Root collections store multiple instances of the same model type at the root
|
|
2375
|
+
level. In other words, these are model instances that do not have a defined
|
|
2376
|
+
container at the level of the LutaML Model.
|
|
2377
|
+
|
|
2378
|
+
There are two kinds of root collections depending on the type of the instance
|
|
2379
|
+
value:
|
|
2380
|
+
|
|
2381
|
+
"Root value collection":: the value is a "primitive type"
|
|
2382
|
+
|
|
2383
|
+
"Root object collection":: the value is a "model instance"
|
|
2384
|
+
|
|
2385
|
+
Regardless of the type of root collection, the instance in a collection is
|
|
2386
|
+
always a LutaML model instance.
|
|
2387
|
+
|
|
2388
|
+
|
|
2389
|
+
==== Root value collections
|
|
2390
|
+
|
|
2391
|
+
A root value collection is a collection that directly contains values of a
|
|
2392
|
+
primitive type.
|
|
2393
|
+
|
|
2394
|
+
.Simple root collection with each instance being a value
|
|
2395
|
+
[example]
|
|
2396
|
+
====
|
|
2397
|
+
[source,yaml]
|
|
2398
|
+
----
|
|
2399
|
+
---
|
|
2400
|
+
- Item One
|
|
2401
|
+
- Item Two
|
|
2402
|
+
- Item Three
|
|
2403
|
+
----
|
|
2404
|
+
|
|
2405
|
+
[source,json]
|
|
2406
|
+
----
|
|
2407
|
+
[
|
|
2408
|
+
"Item One",
|
|
2409
|
+
"Item Two",
|
|
2410
|
+
"Item Three"
|
|
2411
|
+
]
|
|
2412
|
+
----
|
|
2413
|
+
====
|
|
2414
|
+
|
|
2415
|
+
|
|
2416
|
+
Syntax:
|
|
2417
|
+
|
|
2418
|
+
[source,ruby]
|
|
2419
|
+
----
|
|
2420
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2421
|
+
instances :items, ModelType
|
|
2422
|
+
end
|
|
2423
|
+
|
|
2424
|
+
class ModelType < Lutaml::Model::Serializable
|
|
2425
|
+
attribute :name, :string
|
|
2426
|
+
end
|
|
2427
|
+
----
|
|
2428
|
+
|
|
2429
|
+
.Handling a root collection where each instance is a value
|
|
2430
|
+
[example]
|
|
2431
|
+
====
|
|
2432
|
+
Code:
|
|
2433
|
+
|
|
2434
|
+
[source,ruby]
|
|
2435
|
+
----
|
|
2436
|
+
class Title < Lutaml::Model::Serializable
|
|
2437
|
+
attribute :content, :string
|
|
2438
|
+
end
|
|
2439
|
+
|
|
2440
|
+
class TitleCollection < Lutaml::Model::Collection
|
|
2441
|
+
instances :titles, Title
|
|
2442
|
+
|
|
2443
|
+
key_value do
|
|
2444
|
+
no_root # default
|
|
2445
|
+
map_instances to: :titles
|
|
2446
|
+
end
|
|
2447
|
+
end
|
|
2448
|
+
----
|
|
2449
|
+
|
|
2450
|
+
Data:
|
|
2451
|
+
|
|
2452
|
+
[source,yaml]
|
|
2453
|
+
----
|
|
2454
|
+
---
|
|
2455
|
+
- Title One
|
|
2456
|
+
- Title Two
|
|
2457
|
+
- Title Three
|
|
2458
|
+
----
|
|
2459
|
+
|
|
2460
|
+
[source,json]
|
|
2461
|
+
----
|
|
2462
|
+
[
|
|
2463
|
+
"Title One",
|
|
2464
|
+
"Title Two",
|
|
2465
|
+
"Title Three"
|
|
2466
|
+
]
|
|
2467
|
+
----
|
|
2468
|
+
|
|
2469
|
+
Usage:
|
|
2470
|
+
|
|
2471
|
+
[source,ruby]
|
|
2472
|
+
----
|
|
2473
|
+
titles = TitleCollection.from_yaml(yaml_data)
|
|
2474
|
+
titles.count
|
|
2475
|
+
# => 3
|
|
2476
|
+
titles.first.content
|
|
2477
|
+
# => "Title One"
|
|
2478
|
+
----
|
|
2479
|
+
====
|
|
2480
|
+
|
|
2481
|
+
|
|
2482
|
+
==== Root object collections
|
|
2483
|
+
|
|
2484
|
+
A root object collection is a collection that directly contains model instances,
|
|
2485
|
+
each containing at least one serialized attribute.
|
|
2486
|
+
|
|
2487
|
+
.Simple root collection in YAML with each instance being a models with an attribute `name`
|
|
2488
|
+
[example]
|
|
2489
|
+
====
|
|
2490
|
+
[source,yaml]
|
|
2491
|
+
----
|
|
2492
|
+
---
|
|
2493
|
+
- name: Item One
|
|
2494
|
+
- name: Item Two
|
|
2495
|
+
- name: Item Three
|
|
2496
|
+
----
|
|
2497
|
+
|
|
2498
|
+
[source,json]
|
|
2499
|
+
----
|
|
2500
|
+
[
|
|
2501
|
+
{"name": "Item One"},
|
|
2502
|
+
{"name": "Item Two"},
|
|
2503
|
+
{"name": "Item Three"}
|
|
2504
|
+
]
|
|
2505
|
+
----
|
|
2506
|
+
====
|
|
2507
|
+
|
|
2508
|
+
|
|
2509
|
+
.Handling a root collection where each instance is defined by a model with attributes
|
|
2510
|
+
[example]
|
|
2511
|
+
====
|
|
2512
|
+
Code:
|
|
2513
|
+
|
|
2514
|
+
[source,ruby]
|
|
2515
|
+
----
|
|
2516
|
+
class Title < Lutaml::Model::Serializable
|
|
2517
|
+
attribute :content, :string
|
|
2518
|
+
end
|
|
2519
|
+
|
|
2520
|
+
class TitleCollection < Lutaml::Model::Collection
|
|
2521
|
+
instances :titles, Title
|
|
2522
|
+
|
|
2523
|
+
key_value do
|
|
2524
|
+
no_root # default
|
|
2525
|
+
map_instances to: :titles
|
|
2526
|
+
end
|
|
2527
|
+
end
|
|
2528
|
+
----
|
|
2529
|
+
|
|
2530
|
+
Data:
|
|
2531
|
+
|
|
2532
|
+
[source,yaml]
|
|
2533
|
+
----
|
|
2534
|
+
---
|
|
2535
|
+
- content: Title One
|
|
2536
|
+
- content: Title Two
|
|
2537
|
+
- content: Title Three
|
|
2538
|
+
----
|
|
2539
|
+
|
|
2540
|
+
[source,json]
|
|
2541
|
+
----
|
|
2542
|
+
[
|
|
2543
|
+
{"content": "Title One"},
|
|
2544
|
+
{"content": "Title Two"},
|
|
2545
|
+
{"content": "Title Three"}
|
|
2546
|
+
]
|
|
2547
|
+
----
|
|
2548
|
+
|
|
2549
|
+
Usage:
|
|
2550
|
+
|
|
2551
|
+
[source,ruby]
|
|
2552
|
+
----
|
|
2553
|
+
titles = TitleCollection.from_yaml(yaml_data)
|
|
2554
|
+
titles.count
|
|
2555
|
+
# => 3
|
|
2556
|
+
titles.first.content
|
|
2557
|
+
# => "Title One"
|
|
2558
|
+
----
|
|
2559
|
+
====
|
|
2560
|
+
|
|
2561
|
+
|
|
2562
|
+
=== Named collections
|
|
2563
|
+
|
|
2564
|
+
==== General
|
|
2565
|
+
|
|
2566
|
+
Named collections are collections wrapped inside a name or a key. The "name" of
|
|
2567
|
+
the collection serves as the container root of its contained model instances.
|
|
2568
|
+
|
|
2569
|
+
The named collection setup applies to XML and key-value serialization formats.
|
|
2570
|
+
|
|
2571
|
+
In a named collection setup, the collection is defined as a
|
|
2572
|
+
Lutaml::Model::Collection class, and each instance is defined as a
|
|
2573
|
+
Lutaml::Model::Serializable class.
|
|
2574
|
+
|
|
2575
|
+
There are two kinds of named collections depending on the type of the instance
|
|
2576
|
+
value:
|
|
2577
|
+
|
|
2578
|
+
"Named value collection":: the value is a "primitive type"
|
|
2579
|
+
|
|
2580
|
+
"Named object collection":: the value is a "model instance"
|
|
2581
|
+
|
|
2582
|
+
Regardless of the name of root collection, the instance in a collection is
|
|
2583
|
+
always a LutaML model instance.
|
|
2584
|
+
|
|
2585
|
+
|
|
2586
|
+
==== Named value collections
|
|
2587
|
+
|
|
2588
|
+
A named value collection is a collection that contains values of a
|
|
2589
|
+
primitive type.
|
|
2590
|
+
|
|
2591
|
+
.Named value collection in XML with models each containing an element with content
|
|
2592
|
+
[source,xml]
|
|
2593
|
+
----
|
|
2594
|
+
<names>
|
|
2595
|
+
<name>Item One</name>
|
|
2596
|
+
<name>Item Two</name>
|
|
2597
|
+
<name>Item Three</name>
|
|
2598
|
+
</names>
|
|
2599
|
+
----
|
|
2600
|
+
|
|
2601
|
+
.Named value collection in YAML with models each containing a value
|
|
2602
|
+
[source,yaml]
|
|
2603
|
+
----
|
|
2604
|
+
---
|
|
2605
|
+
names:
|
|
2606
|
+
- Item One
|
|
2607
|
+
- Item Two
|
|
2608
|
+
- Item Three
|
|
2609
|
+
----
|
|
2610
|
+
|
|
2611
|
+
Syntax:
|
|
2612
|
+
|
|
2613
|
+
[source,ruby]
|
|
2614
|
+
----
|
|
2615
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2616
|
+
instances :items, ModelType
|
|
2617
|
+
|
|
2618
|
+
xml do
|
|
2619
|
+
root "name-of-xml-container-element"
|
|
2620
|
+
end
|
|
2621
|
+
|
|
2622
|
+
key_value do
|
|
2623
|
+
root "name-of-key-value-container-element"
|
|
2624
|
+
end
|
|
2625
|
+
end
|
|
2626
|
+
|
|
2627
|
+
class ModelType < Lutaml::Model::Serializable
|
|
2628
|
+
attribute :name, :string
|
|
2629
|
+
end
|
|
2630
|
+
----
|
|
2631
|
+
|
|
2632
|
+
A named collection can alternatively be implemented as a non-collection model
|
|
2633
|
+
("Model class with an attribute") that contains the collection of instances. In
|
|
2634
|
+
this case, the attribute will be an Array object, which does not contain
|
|
2635
|
+
additional attributes and methods.
|
|
2636
|
+
|
|
2637
|
+
.Handling a named collection with instance elements directly containing values
|
|
2638
|
+
[example]
|
|
2639
|
+
====
|
|
2640
|
+
[source,ruby]
|
|
2641
|
+
----
|
|
2642
|
+
class Title < Lutaml::Model::Serializable
|
|
2643
|
+
attribute :title, :string
|
|
2644
|
+
|
|
2645
|
+
xml do
|
|
2646
|
+
root "title"
|
|
2647
|
+
map_content to: :title
|
|
2648
|
+
end
|
|
2649
|
+
end
|
|
2650
|
+
|
|
2651
|
+
class DirectTitleCollection < Lutaml::Model::Collection
|
|
2652
|
+
instances :items, Title
|
|
2653
|
+
|
|
2654
|
+
xml do
|
|
2655
|
+
root "titles"
|
|
2656
|
+
map_element "title", to: :items
|
|
2657
|
+
end
|
|
2658
|
+
|
|
2659
|
+
key_value do
|
|
2660
|
+
map_instances to: :items
|
|
2661
|
+
end
|
|
2662
|
+
end
|
|
2663
|
+
----
|
|
2664
|
+
|
|
2665
|
+
[source,xml]
|
|
2666
|
+
----
|
|
2667
|
+
<titles>
|
|
2668
|
+
<title>Title One</title>
|
|
2669
|
+
<title>Title Two</title>
|
|
2670
|
+
<title>Title Three</title>
|
|
2671
|
+
</titles>
|
|
2672
|
+
----
|
|
2673
|
+
|
|
2674
|
+
[source,yaml]
|
|
2675
|
+
----
|
|
2676
|
+
---
|
|
2677
|
+
titles:
|
|
2678
|
+
- Title One
|
|
2679
|
+
- Title Two
|
|
2680
|
+
- Title Three
|
|
2681
|
+
----
|
|
2682
|
+
|
|
2683
|
+
[source,json]
|
|
2684
|
+
----
|
|
2685
|
+
{
|
|
2686
|
+
"titles": [
|
|
2687
|
+
"Title One",
|
|
2688
|
+
"Title Two",
|
|
2689
|
+
"Title Three"
|
|
2690
|
+
]
|
|
2691
|
+
}
|
|
2692
|
+
----
|
|
2693
|
+
|
|
2694
|
+
[source,ruby]
|
|
2695
|
+
----
|
|
2696
|
+
titles = DirectTitleCollection.from_yaml(yaml_data)
|
|
2697
|
+
titles.count
|
|
2698
|
+
# => 3
|
|
2699
|
+
titles.first.title
|
|
2700
|
+
# => "Title One"
|
|
2701
|
+
titles.last.title
|
|
2702
|
+
# => "Title Three"
|
|
2703
|
+
----
|
|
2704
|
+
====
|
|
2705
|
+
|
|
2706
|
+
|
|
2707
|
+
==== Named object collections
|
|
2708
|
+
|
|
2709
|
+
A named object collection is a collection that contains model instances,
|
|
2710
|
+
each containing at least one serialized attribute.
|
|
2711
|
+
|
|
2712
|
+
NOTE: A named object collection can alternatively be implemented as a
|
|
2713
|
+
non-collection model ("Model class with an attribute") that contains the
|
|
2714
|
+
collection of instances. In this case, the attribute will be an Array object,
|
|
2715
|
+
which does not contain additional attributes and methods.
|
|
2716
|
+
|
|
2717
|
+
|
|
2718
|
+
.Named object collection in XML with instances each containing an element with a model attribute
|
|
2719
|
+
[source,xml]
|
|
2720
|
+
----
|
|
2721
|
+
<names>
|
|
2722
|
+
<name><content>Item One</content></name>
|
|
2723
|
+
<name><content>Item Two</content></name>
|
|
2724
|
+
<name><content>Item Three</content></name>
|
|
2725
|
+
</names>
|
|
2726
|
+
----
|
|
2727
|
+
|
|
2728
|
+
.Named object collection in YAML with instances each containing a model attribute
|
|
2729
|
+
[source,yaml]
|
|
2730
|
+
----
|
|
2731
|
+
---
|
|
2732
|
+
names:
|
|
2733
|
+
- name: Item One
|
|
2734
|
+
- name: Item Two
|
|
2735
|
+
- name: Item Three
|
|
2736
|
+
----
|
|
2737
|
+
|
|
2738
|
+
|
|
2739
|
+
.Named object collection with each instance containing at least one model attribute
|
|
2740
|
+
[example]
|
|
2741
|
+
====
|
|
2742
|
+
Data:
|
|
2743
|
+
|
|
2744
|
+
[source,xml]
|
|
2745
|
+
----
|
|
2746
|
+
<titles>
|
|
2747
|
+
<title><content>Title One</content></title>
|
|
2748
|
+
<title><content>Title Two</content></title>
|
|
2749
|
+
<title><content>Title Three</content></title>
|
|
2750
|
+
</titles>
|
|
2751
|
+
----
|
|
2752
|
+
|
|
2753
|
+
[source,yaml]
|
|
2754
|
+
----
|
|
2755
|
+
---
|
|
2756
|
+
titles:
|
|
2757
|
+
- title: Title One
|
|
2758
|
+
- title: Title Two
|
|
2759
|
+
- title: Title Three
|
|
2760
|
+
----
|
|
2761
|
+
|
|
2762
|
+
[source,json]
|
|
2763
|
+
----
|
|
2764
|
+
{
|
|
2765
|
+
"titles": [
|
|
2766
|
+
{"title": "Title One"},
|
|
2767
|
+
{"title": "Title Two"},
|
|
2768
|
+
{"title": "Title Three"}
|
|
2769
|
+
]
|
|
2770
|
+
}
|
|
2771
|
+
----
|
|
2772
|
+
|
|
2773
|
+
Code:
|
|
2774
|
+
|
|
2775
|
+
[source,ruby]
|
|
2776
|
+
----
|
|
2777
|
+
class Title < Lutaml::Model::Serializable
|
|
2778
|
+
attribute :title, :string
|
|
2779
|
+
|
|
2780
|
+
xml do
|
|
2781
|
+
root "title"
|
|
2782
|
+
map_element "content", to: :title
|
|
2783
|
+
end
|
|
2784
|
+
|
|
2785
|
+
key_value do
|
|
2786
|
+
map "title", to: :title
|
|
2787
|
+
end
|
|
2788
|
+
end
|
|
2789
|
+
|
|
2790
|
+
class TitleCollection < Lutaml::Model::Collection
|
|
2791
|
+
instances :items, Title
|
|
2792
|
+
|
|
2793
|
+
xml do
|
|
2794
|
+
root "titles"
|
|
2795
|
+
map_element 'title', to: :items
|
|
2796
|
+
end
|
|
2797
|
+
|
|
2798
|
+
key_value do
|
|
2799
|
+
root "titles"
|
|
2800
|
+
map_instances to: :items
|
|
2801
|
+
end
|
|
2802
|
+
end
|
|
2803
|
+
----
|
|
2804
|
+
|
|
2805
|
+
Usage:
|
|
2806
|
+
|
|
2807
|
+
[source,ruby]
|
|
2808
|
+
----
|
|
2809
|
+
titles = TitleCollection.from_yaml(yaml_data)
|
|
2810
|
+
titles.count
|
|
2811
|
+
# => 3
|
|
2812
|
+
titles.first.title
|
|
2813
|
+
# => "Title One"
|
|
2814
|
+
titles.last.title
|
|
2815
|
+
# => "Title Three"
|
|
2816
|
+
----
|
|
2817
|
+
====
|
|
2818
|
+
|
|
2819
|
+
|
|
2820
|
+
=== Attribute collection class
|
|
2821
|
+
|
|
2822
|
+
A model attribute that is a collection can be contained within a custom
|
|
2823
|
+
collection class.
|
|
2824
|
+
|
|
2825
|
+
A custom collection class can be defined to provide custom behavior for the
|
|
2826
|
+
collection inside a non-collection model, with attributes using
|
|
2827
|
+
`collection: true`.
|
|
2828
|
+
|
|
2829
|
+
Syntax:
|
|
2830
|
+
|
|
2831
|
+
[source,ruby]
|
|
2832
|
+
----
|
|
2833
|
+
class MyModel < Lutaml::Model::Serializable
|
|
2834
|
+
attribute {model-attribute}, ModelType, collection: MyCollection
|
|
2835
|
+
end
|
|
2836
|
+
|
|
2837
|
+
class MyCollection < Lutaml::Model::Collection
|
|
2838
|
+
instances {instance-name}, ModelType
|
|
2839
|
+
|
|
2840
|
+
# Custom behavior for the collection
|
|
2841
|
+
def custom_method
|
|
2842
|
+
# Custom logic here
|
|
2843
|
+
end
|
|
2844
|
+
end
|
|
2845
|
+
----
|
|
2846
|
+
|
|
2847
|
+
.Using a custom collection class for custom collection behavior
|
|
2848
|
+
[example]
|
|
2849
|
+
====
|
|
2850
|
+
Data:
|
|
2851
|
+
|
|
2852
|
+
[source,xml]
|
|
2853
|
+
----
|
|
2854
|
+
<titles>
|
|
2855
|
+
<title>Title One</title>
|
|
2856
|
+
<title>Title Two</title>
|
|
2857
|
+
<title>Title Three</title>
|
|
2858
|
+
</titles>
|
|
2859
|
+
----
|
|
2860
|
+
|
|
2861
|
+
[source,yaml]
|
|
2862
|
+
----
|
|
2863
|
+
titles:
|
|
2864
|
+
- title: Title One
|
|
2865
|
+
- title: Title Two
|
|
2866
|
+
- title: Title Three
|
|
2867
|
+
----
|
|
2868
|
+
|
|
2869
|
+
|
|
2870
|
+
[source,ruby]
|
|
2871
|
+
----
|
|
2872
|
+
class StringParts < Lutaml::Model::Collection
|
|
2873
|
+
instances :parts, :string
|
|
2874
|
+
|
|
2875
|
+
def to_s
|
|
2876
|
+
parts.join(' -- ')
|
|
2877
|
+
end
|
|
2878
|
+
end
|
|
2879
|
+
|
|
2880
|
+
class BibliographicItem < Lutaml::Model::Serializable
|
|
2881
|
+
attribute :title_parts, :string, collection: StringParts
|
|
2882
|
+
|
|
2883
|
+
xml do
|
|
2884
|
+
root "titles"
|
|
2885
|
+
map_element "title", to: :title_parts
|
|
2886
|
+
end
|
|
2887
|
+
|
|
2888
|
+
key_value do
|
|
2889
|
+
root "titles"
|
|
2890
|
+
map_instances to: :title_parts
|
|
2891
|
+
end
|
|
2892
|
+
|
|
2893
|
+
def render_title
|
|
2894
|
+
title_parts.to_s
|
|
2895
|
+
end
|
|
2896
|
+
end
|
|
2897
|
+
----
|
|
2898
|
+
|
|
2899
|
+
[source,ruby]
|
|
2900
|
+
----
|
|
2901
|
+
> bib_item = BibliographicItem.from_xml(xml_data)
|
|
2902
|
+
> bib_item.title_parts
|
|
2903
|
+
> # StringParts:0x0000000104ac7240 @parts=["Title One", "Title Two", "Title Three"]
|
|
2904
|
+
> bib_item.render_title
|
|
2905
|
+
> # "Title One -- Title Two -- Title Three"
|
|
2906
|
+
----
|
|
2907
|
+
====
|
|
2908
|
+
|
|
2909
|
+
|
|
2910
|
+
|
|
2911
|
+
=== Nested collections
|
|
2912
|
+
|
|
2913
|
+
TODO: This case needs to be fixed.
|
|
2914
|
+
|
|
2915
|
+
Collections can be nested within other models and define their own serialization
|
|
2916
|
+
rules.
|
|
2917
|
+
|
|
2918
|
+
Nested collections can be defined in the same way as root collections, but
|
|
2919
|
+
they are defined within the context of a parent model.
|
|
2920
|
+
|
|
2921
|
+
[example]
|
|
2922
|
+
====
|
|
2923
|
+
Data:
|
|
2924
|
+
|
|
2925
|
+
[source,xml]
|
|
2926
|
+
----
|
|
2927
|
+
<titles>
|
|
2928
|
+
<title-group>
|
|
2929
|
+
<artifact>
|
|
2930
|
+
<content>Title One</content>
|
|
2931
|
+
</artifact>
|
|
2932
|
+
<artifact>
|
|
2933
|
+
<content>Title Two</content>
|
|
2934
|
+
</artifact>
|
|
2935
|
+
<artifact>
|
|
2936
|
+
<content>Title Three</content>
|
|
2937
|
+
</artifact>
|
|
2938
|
+
</title-group>
|
|
2939
|
+
</titles>
|
|
2940
|
+
----
|
|
2941
|
+
|
|
2942
|
+
[source,yaml]
|
|
2943
|
+
----
|
|
2944
|
+
---
|
|
2945
|
+
titles:
|
|
2946
|
+
title-group:
|
|
2947
|
+
- artifact:
|
|
2948
|
+
content: Title One
|
|
2949
|
+
- artifact:
|
|
2950
|
+
content: Title Two
|
|
2951
|
+
- artifact:
|
|
2952
|
+
content: Title Three
|
|
2953
|
+
----
|
|
2954
|
+
|
|
2955
|
+
[source,ruby]
|
|
2956
|
+
----
|
|
2957
|
+
class Title < Lutaml::Model::Serializable
|
|
2958
|
+
attribute :content, :string
|
|
2959
|
+
end
|
|
2960
|
+
|
|
2961
|
+
class TitleCollection < Lutaml::Model::Collection
|
|
2962
|
+
instances :items, Title
|
|
2963
|
+
|
|
2964
|
+
xml do
|
|
2965
|
+
root "title-group"
|
|
2966
|
+
map_element "artifact", to: :items
|
|
2967
|
+
end
|
|
2968
|
+
end
|
|
2969
|
+
|
|
2970
|
+
class BibItem < Lutaml::Model::Serializable
|
|
2971
|
+
attribute :titles, TitleCollection
|
|
2972
|
+
|
|
2973
|
+
xml do
|
|
2974
|
+
root "bibitem"
|
|
2975
|
+
# This overrides the collection's root "title-group"
|
|
2976
|
+
map_element "titles", to: :titles
|
|
2977
|
+
end
|
|
2978
|
+
end
|
|
2979
|
+
----
|
|
2980
|
+
====
|
|
2981
|
+
|
|
2982
|
+
|
|
2983
|
+
=== Keyed collections (key-value serialization formats only)
|
|
2984
|
+
|
|
2985
|
+
==== General
|
|
2986
|
+
|
|
2987
|
+
In key-value serialization formats, a key can be used to uniquely identify each
|
|
2988
|
+
instance. This usage allows for enforcing uniqueness in the collection.
|
|
2989
|
+
|
|
2990
|
+
A collection that contains keyed objects as its instances is commonly called a
|
|
2991
|
+
"keyed collection". A keyed object in a serialization format is an object
|
|
2992
|
+
identified with a unique key.
|
|
2993
|
+
|
|
2994
|
+
NOTE: The concept of keyed collections does not typically apply to XML
|
|
2995
|
+
collections.
|
|
2996
|
+
|
|
2997
|
+
There are two kinds of keyed collections depending on the type of the keyed
|
|
2998
|
+
value:
|
|
2999
|
+
|
|
3000
|
+
"keyed value collection":: the value is a "primitive type"
|
|
3001
|
+
|
|
3002
|
+
"keyed object collection":: the value is a "model instance"
|
|
3003
|
+
|
|
3004
|
+
Regardless of the type of keyed collections, the instance in a collection is
|
|
3005
|
+
always a LutaML model instance.
|
|
3006
|
+
|
|
3007
|
+
|
|
3008
|
+
==== `map_key` method
|
|
3009
|
+
|
|
3010
|
+
The `map_key` method specifies that the unique key is to be moved into an
|
|
3011
|
+
attribute belonging to the instance model.
|
|
3012
|
+
|
|
3013
|
+
Syntax:
|
|
3014
|
+
|
|
3015
|
+
[source,ruby]
|
|
3016
|
+
----
|
|
3017
|
+
key_value do
|
|
3018
|
+
map_key to_instance: {instance-attribute-name}
|
|
3019
|
+
end
|
|
3020
|
+
----
|
|
3021
|
+
|
|
3022
|
+
Where,
|
|
3023
|
+
|
|
3024
|
+
`to_instance`:: Refers to the attribute name in the instance that contains the key.
|
|
3025
|
+
`{key_attribute}`:: The attribute name in the instance that contains the key.
|
|
3026
|
+
|
|
3027
|
+
|
|
3028
|
+
==== `map_value` method
|
|
3029
|
+
|
|
3030
|
+
The `map_value` method specifies that the value (the object referenced by the
|
|
3031
|
+
unique key) is to be moved into an attribute belonging to the instance model.
|
|
3032
|
+
|
|
3033
|
+
Syntax:
|
|
3034
|
+
|
|
3035
|
+
[source,ruby]
|
|
3036
|
+
----
|
|
3037
|
+
key_value do
|
|
3038
|
+
# basic pattern
|
|
3039
|
+
map_value {operation}: [*argument]
|
|
3040
|
+
|
|
3041
|
+
# Mapping the value object to a full instance through `to_instance`
|
|
3042
|
+
map_value to_instance: {instance-attribute-name}
|
|
3043
|
+
|
|
3044
|
+
# Mapping the value object to an attribute as_instance
|
|
3045
|
+
map_value as_attribute: {instance-attribute-name}
|
|
3046
|
+
end
|
|
3047
|
+
----
|
|
3048
|
+
|
|
3049
|
+
==== Keyed value collections
|
|
3050
|
+
|
|
3051
|
+
A keyed value collection is a collection where the keyed item in the serialization
|
|
3052
|
+
format is a primitive type (e.g. string, integer, etc.).
|
|
3053
|
+
|
|
3054
|
+
The instance item inside the collection is a model instance that contains both
|
|
3055
|
+
the serialized key and serialized value both as attributes inside the model.
|
|
3056
|
+
|
|
3057
|
+
All three `map_key`, `map_value`, and `map_instances` methods need to be used to
|
|
3058
|
+
define how instances are mapped in a keyed value collection.
|
|
3059
|
+
|
|
3060
|
+
.Creating a keyed value collection
|
|
3061
|
+
[example]
|
|
3062
|
+
====
|
|
3063
|
+
[source,ruby]
|
|
3064
|
+
----
|
|
3065
|
+
class AuthorAvailability < Lutaml::Model::Serializable
|
|
3066
|
+
attribute :id, :string
|
|
3067
|
+
attribute :available, :boolean
|
|
3068
|
+
end
|
|
3069
|
+
|
|
3070
|
+
class AuthorCollection < Lutaml::Model::Collection
|
|
3071
|
+
instances :authors, AuthorAvailability
|
|
3072
|
+
|
|
3073
|
+
key_value do
|
|
3074
|
+
map_key to_instance: :id # This refers to 'authors[].id'
|
|
3075
|
+
map_value as_attribute: :available # This refers to 'authors[].available'
|
|
3076
|
+
map_instances to: :authors
|
|
3077
|
+
end
|
|
3078
|
+
end
|
|
3079
|
+
----
|
|
3080
|
+
|
|
3081
|
+
[source,yaml]
|
|
3082
|
+
----
|
|
3083
|
+
---
|
|
3084
|
+
author_01: true
|
|
3085
|
+
author_02: false
|
|
3086
|
+
author_03: true
|
|
3087
|
+
----
|
|
3088
|
+
|
|
3089
|
+
[source,ruby]
|
|
3090
|
+
----
|
|
3091
|
+
authors = AuthorCollection.from_yaml(yaml_data)
|
|
3092
|
+
authors.first.id
|
|
3093
|
+
# => "author_01"
|
|
3094
|
+
authors.first.available
|
|
3095
|
+
# => true
|
|
3096
|
+
----
|
|
3097
|
+
====
|
|
3098
|
+
|
|
3099
|
+
|
|
3100
|
+
==== Keyed object collections
|
|
3101
|
+
|
|
3102
|
+
A keyed object collection is a collection where the keyed item in the
|
|
3103
|
+
serialization format contains multiple attributes.
|
|
3104
|
+
|
|
3105
|
+
The instance item inside the collection is a model instance that contains the
|
|
3106
|
+
serialized key as one attribute, and the serialized value attributes are all
|
|
3107
|
+
attributes inside the model.
|
|
3108
|
+
|
|
3109
|
+
Both the `map_key` and `map_instances` are used to define how instances are
|
|
3110
|
+
mapped in a keyed object collection.
|
|
3111
|
+
|
|
3112
|
+
.Creating a keyed object collection
|
|
3113
|
+
[example]
|
|
3114
|
+
====
|
|
3115
|
+
[source,ruby]
|
|
3116
|
+
----
|
|
3117
|
+
class Author < Lutaml::Model::Serializable
|
|
3118
|
+
attribute :id, :string
|
|
3119
|
+
attribute :name, :string
|
|
3120
|
+
end
|
|
3121
|
+
|
|
3122
|
+
class AuthorCollection < Lutaml::Model::Collection
|
|
3123
|
+
instances :authors, Author
|
|
3124
|
+
|
|
3125
|
+
key_value do
|
|
3126
|
+
map_key to_instance: :id # This refers to 'authors[].id'
|
|
3127
|
+
map_instances to: :authors
|
|
3128
|
+
end
|
|
3129
|
+
end
|
|
3130
|
+
----
|
|
3131
|
+
|
|
3132
|
+
[source,yaml]
|
|
3133
|
+
----
|
|
3134
|
+
---
|
|
3135
|
+
author_01:
|
|
3136
|
+
name: Author One
|
|
3137
|
+
author_02:
|
|
3138
|
+
name: Author Two
|
|
3139
|
+
author_03:
|
|
3140
|
+
name: Author Three
|
|
3141
|
+
----
|
|
3142
|
+
|
|
3143
|
+
[source,ruby]
|
|
3144
|
+
----
|
|
3145
|
+
authors = AuthorCollection.from_yaml(yaml_data)
|
|
3146
|
+
authors.first.id
|
|
3147
|
+
# => "author_01"
|
|
3148
|
+
authors.first.name
|
|
3149
|
+
# => "Author One"
|
|
3150
|
+
----
|
|
3151
|
+
====
|
|
3152
|
+
|
|
3153
|
+
|
|
3154
|
+
==== Nested keyed object collection
|
|
3155
|
+
|
|
3156
|
+
A nested keyed object collection is a keyed collection that contain other keyed
|
|
3157
|
+
collections. This case is simply a more complex arrangement of the principles
|
|
3158
|
+
applied to keyed object collections.
|
|
3159
|
+
|
|
3160
|
+
This pattern can extend to multiple levels of nesting, where each level contains
|
|
3161
|
+
a keyed object collection that can have its own key and value mappings.
|
|
3162
|
+
|
|
3163
|
+
Depends on whether a custom collection class is needed, the following
|
|
3164
|
+
mechanisms are available:
|
|
3165
|
+
|
|
3166
|
+
* When using a Lutaml::Model::Serializable class for a keyed collection,
|
|
3167
|
+
use the `child_mappings` option to map attributes.
|
|
3168
|
+
|
|
3169
|
+
* When using a Lutaml::Model::Collection class for a keyed collection,
|
|
3170
|
+
there are two options:
|
|
3171
|
+
|
|
3172
|
+
* use the `map_key`, `map_value`, and `map_instances` methods to map attributes;
|
|
3173
|
+
or
|
|
3174
|
+
|
|
3175
|
+
* use the `root_mappings` option to map attributes.
|
|
3176
|
+
|
|
3177
|
+
|
|
3178
|
+
.Nested 2-layer keyed object collection
|
|
3179
|
+
[example]
|
|
3180
|
+
====
|
|
3181
|
+
This example provides a two-layer nested structure where:
|
|
3182
|
+
|
|
3183
|
+
* The first layer keys pieces by type (`bowls`, `vases`).
|
|
3184
|
+
* The second layer keys glazes by finish name within each piece type.
|
|
3185
|
+
* Each glaze finish contains detailed attributes like temperature.
|
|
3186
|
+
|
|
3187
|
+
[source,ruby]
|
|
3188
|
+
----
|
|
3189
|
+
# Third layer represents glaze finishes.
|
|
3190
|
+
class GlazeFinish < Lutaml::Model::Serializable
|
|
3191
|
+
attribute :name, :string
|
|
3192
|
+
attribute :temperature, :integer
|
|
3193
|
+
|
|
3194
|
+
key_value do
|
|
3195
|
+
map "name", to: :name
|
|
3196
|
+
map "temperature", to: :temperature
|
|
3197
|
+
end
|
|
3198
|
+
end
|
|
3199
|
+
|
|
3200
|
+
# Second layer represents ceramic pieces each with multiple finishes.
|
|
3201
|
+
class CeramicPiece < Lutaml::Model::Serializable
|
|
3202
|
+
attribute :piece_type, :string
|
|
3203
|
+
attribute :glazes, GlazeFinish, collection: true
|
|
3204
|
+
|
|
3205
|
+
key_value do
|
|
3206
|
+
map "piece_type", to: :piece_type
|
|
3207
|
+
map "glazes", to: :glazes, child_mappings: {
|
|
3208
|
+
name: :key,
|
|
3209
|
+
temperature: :temperature
|
|
3210
|
+
}
|
|
3211
|
+
end
|
|
3212
|
+
end
|
|
3213
|
+
|
|
3214
|
+
# Uppermost layer represents the collection of ceramic pieces.
|
|
3215
|
+
class StudioInventory < Lutaml::Model::Collection
|
|
3216
|
+
instances :pieces, CeramicPiece
|
|
3217
|
+
|
|
3218
|
+
key_value do
|
|
3219
|
+
map to: :pieces, root_mappings: {
|
|
3220
|
+
piece_type: :key,
|
|
3221
|
+
glazes: :value,
|
|
3222
|
+
}
|
|
3223
|
+
end
|
|
3224
|
+
end
|
|
3225
|
+
----
|
|
3226
|
+
|
|
3227
|
+
[source,yaml]
|
|
3228
|
+
----
|
|
3229
|
+
---
|
|
3230
|
+
bowls:
|
|
3231
|
+
matte_finish:
|
|
3232
|
+
name: Earth Matte
|
|
3233
|
+
temperature: 1240
|
|
3234
|
+
glossy_finish:
|
|
3235
|
+
name: Ocean Blue
|
|
3236
|
+
temperature: 1260
|
|
3237
|
+
crackle_finish:
|
|
3238
|
+
name: Antique Crackle
|
|
3239
|
+
temperature: 1220
|
|
3240
|
+
vases:
|
|
3241
|
+
metallic_finish:
|
|
3242
|
+
name: Bronze Metallic
|
|
3243
|
+
temperature: 1280
|
|
3244
|
+
crystalline_finish:
|
|
3245
|
+
name: Ice Crystal
|
|
3246
|
+
temperature: 1300
|
|
3247
|
+
----
|
|
3248
|
+
|
|
3249
|
+
[source,ruby]
|
|
3250
|
+
----
|
|
3251
|
+
inventory = StudioInventory.from_yaml(yaml_data)
|
|
3252
|
+
|
|
3253
|
+
# Access nested data through the hierarchy
|
|
3254
|
+
puts inventory.pieces.bowls.matte_finish.name
|
|
3255
|
+
# => "Earth Matte"
|
|
3256
|
+
|
|
3257
|
+
puts inventory.pieces.bowls.matte_finish.temperature
|
|
3258
|
+
# => 1240
|
|
3259
|
+
|
|
3260
|
+
# Iterate through all pieces and their glazes
|
|
3261
|
+
inventory.pieces.each do |piece_type, piece|
|
|
3262
|
+
puts "#{piece_type.capitalize}:"
|
|
3263
|
+
piece.glazes.each do |glaze_name, glaze|
|
|
3264
|
+
puts " #{glaze_name}: #{glaze.name} (#{glaze.temperature}°C)"
|
|
3265
|
+
end
|
|
3266
|
+
end
|
|
3267
|
+
----
|
|
3268
|
+
====
|
|
3269
|
+
|
|
3270
|
+
|
|
3271
|
+
|
|
3272
|
+
|
|
3273
|
+
=== Behavior
|
|
3274
|
+
|
|
3275
|
+
==== Enumerable interface
|
|
3276
|
+
|
|
3277
|
+
Collections implement the Ruby `Enumerable` interface, providing standard
|
|
3278
|
+
collection operations.
|
|
3279
|
+
|
|
3280
|
+
Collections allow the following sample `Enumerable` methods:
|
|
3281
|
+
|
|
3282
|
+
* `each` - Iterate over collection items
|
|
3283
|
+
* `map` - Transform collection items
|
|
3284
|
+
* `select` - Filter collection items
|
|
3285
|
+
* `find` - Find items matching criteria
|
|
3286
|
+
* `reduce` - Aggregate collection items
|
|
3287
|
+
|
|
3288
|
+
.Usage of the collection Enumerable interface
|
|
3289
|
+
[example]
|
|
3290
|
+
====
|
|
3291
|
+
[source,ruby]
|
|
3292
|
+
----
|
|
3293
|
+
# Filter items
|
|
3294
|
+
filtered = collection.filter { |item| item.id == "1" }
|
|
3295
|
+
|
|
3296
|
+
# Reject items
|
|
3297
|
+
rejected = collection.reject { |item| item.id == "1" }
|
|
3298
|
+
|
|
3299
|
+
# Select items
|
|
3300
|
+
selected = collection.select { |item| item.id == "1" }
|
|
3301
|
+
|
|
3302
|
+
# Map items
|
|
3303
|
+
mapped = collection.map { |item| item.name }
|
|
3304
|
+
|
|
3305
|
+
# Count items
|
|
3306
|
+
count = collection.count
|
|
3307
|
+
----
|
|
3308
|
+
====
|
|
3309
|
+
|
|
3310
|
+
|
|
3311
|
+
// ==== Collection validation
|
|
3312
|
+
|
|
3313
|
+
// Collections can define validation rules for their elements.
|
|
3314
|
+
|
|
3315
|
+
// [example]
|
|
3316
|
+
// ====
|
|
3317
|
+
// [source,ruby]
|
|
3318
|
+
// ----
|
|
3319
|
+
// class PublicationCollection < Lutaml::Model::Collection
|
|
3320
|
+
// instances(:publications, Publication) do
|
|
3321
|
+
// validates :year, numericality: { greater_than: 1900 }
|
|
3322
|
+
|
|
3323
|
+
// validate :must_have_author
|
|
3324
|
+
|
|
3325
|
+
// def must_have_author(publications)
|
|
3326
|
+
// publications.each do |publication|
|
|
3327
|
+
// next unless publication.author.nil?
|
|
3328
|
+
// errors.add(:author, "`#{publication.title}` must have an author")
|
|
3329
|
+
// end
|
|
3330
|
+
// end
|
|
3331
|
+
// end
|
|
3332
|
+
// end
|
|
3333
|
+
// ----
|
|
3334
|
+
// ====
|
|
3335
|
+
|
|
3336
|
+
==== Initialization
|
|
3337
|
+
|
|
3338
|
+
Collections can be initialized with an array of items or through individual item
|
|
3339
|
+
addition.
|
|
3340
|
+
|
|
3341
|
+
[example]
|
|
3342
|
+
====
|
|
3343
|
+
[source,ruby]
|
|
3344
|
+
----
|
|
3345
|
+
# Empty collection
|
|
3346
|
+
collection = ItemCollection.new
|
|
3347
|
+
|
|
3348
|
+
# From an array of items
|
|
3349
|
+
collection = ItemCollection.new([item1, item2, item3])
|
|
3350
|
+
|
|
3351
|
+
# From an array of hashes
|
|
3352
|
+
collection = ItemCollection.new([
|
|
3353
|
+
{ id: "1", name: "Item 1" },
|
|
3354
|
+
{ id: "2", name: "Item 2" }
|
|
3355
|
+
])
|
|
3356
|
+
|
|
3357
|
+
# Adding items later
|
|
3358
|
+
collection << Item.new(id: "3", name: "Item 3")
|
|
3359
|
+
----
|
|
3360
|
+
====
|
|
3361
|
+
|
|
3362
|
+
==== Ordering
|
|
3363
|
+
|
|
3364
|
+
TODO: This case needs to be fixed.
|
|
3365
|
+
|
|
3366
|
+
Collections that maintain a specific ordering of elements.
|
|
3367
|
+
|
|
3368
|
+
Syntax:
|
|
3369
|
+
|
|
3370
|
+
[source,ruby]
|
|
3371
|
+
----
|
|
3372
|
+
class MyCollection < Lutaml::Model::Collection
|
|
3373
|
+
instances {instances-name}, ModelType
|
|
3374
|
+
ordered by: {attribute-of-instance}, order: {:asc | :desc}
|
|
3375
|
+
end
|
|
3376
|
+
----
|
|
3377
|
+
|
|
3378
|
+
Where,
|
|
3379
|
+
|
|
3380
|
+
`{instances-name}`:: name of the instances accessor within the collection
|
|
3381
|
+
`ModelType`:: The model type of the collection elements.
|
|
3382
|
+
|
|
3383
|
+
`{attribute-of-instance-or-proc}`:: How model instances are to be ordered by. Values supported are:
|
|
3384
|
+
`{attribute-of-instance}`::: Attribute name of an instance to be ordered by.
|
|
3385
|
+
`{proc}`::: Proc that returns a value to order by (same as `sort_by`), given the instance as input.
|
|
3386
|
+
|
|
3387
|
+
`order`::: Order direction of the value:
|
|
3388
|
+
`:asc`:::: Ascending order (default).
|
|
3389
|
+
`:desc`:::: Descending order.
|
|
3390
|
+
|
|
3391
|
+
.Ordered collection applied to a root collection
|
|
3392
|
+
[example]
|
|
3393
|
+
====
|
|
3394
|
+
Data:
|
|
3395
|
+
|
|
3396
|
+
[source,xml]
|
|
3397
|
+
----
|
|
3398
|
+
<items>
|
|
3399
|
+
<item id="3" name="Item Three"/>
|
|
3400
|
+
<item id="1" name="Item One"/>
|
|
3401
|
+
<item id="2" name="Item Two"/>
|
|
3402
|
+
</items>
|
|
3403
|
+
----
|
|
3404
|
+
|
|
3405
|
+
[source,yaml]
|
|
3406
|
+
----
|
|
3407
|
+
---
|
|
3408
|
+
- id: 3
|
|
3409
|
+
name: Item Three
|
|
3410
|
+
- id: 1
|
|
3411
|
+
name: Item One
|
|
3412
|
+
- id: 2
|
|
3413
|
+
name: Item Two
|
|
3414
|
+
----
|
|
3415
|
+
|
|
3416
|
+
[source,ruby]
|
|
3417
|
+
----
|
|
3418
|
+
class Item < Lutaml::Model::Serializable
|
|
3419
|
+
attribute :id, :string
|
|
3420
|
+
attribute :name, :string
|
|
3421
|
+
|
|
3422
|
+
xml do
|
|
3423
|
+
map_attribute "id", to: :id
|
|
3424
|
+
map_attribute "name", to: :name
|
|
3425
|
+
end
|
|
3426
|
+
end
|
|
3427
|
+
|
|
3428
|
+
class OrderedItemCollection < Lutaml::Model::Collection
|
|
3429
|
+
instances :items, Item
|
|
3430
|
+
ordered by: :id, order: :desc
|
|
3431
|
+
|
|
3432
|
+
xml do
|
|
3433
|
+
root "items"
|
|
3434
|
+
map_element "item", to: :items
|
|
3435
|
+
end
|
|
3436
|
+
|
|
3437
|
+
key_value do
|
|
3438
|
+
no_root
|
|
3439
|
+
map_instances to: :items
|
|
3440
|
+
end
|
|
3441
|
+
end
|
|
3442
|
+
----
|
|
3443
|
+
|
|
3444
|
+
[source,ruby]
|
|
3445
|
+
----
|
|
3446
|
+
> collection = OrderedItemCollection.from_xml(xml_data)
|
|
3447
|
+
> collection.map(&:id)
|
|
3448
|
+
> # ["3", "2", "1"]
|
|
3449
|
+
|
|
3450
|
+
> collection = OrderedItemCollection.from_yaml(yaml_data)
|
|
3451
|
+
> collection.map(&:id)
|
|
3452
|
+
> # ["3", "2", "1"]
|
|
3453
|
+
----
|
|
3454
|
+
====
|
|
3455
|
+
|
|
3456
|
+
|
|
3457
|
+
|
|
3458
|
+
// ==== Polymorphic collections
|
|
3459
|
+
|
|
3460
|
+
// Collections can contain instances of different model classes that share a common
|
|
3461
|
+
// base class.
|
|
3462
|
+
|
|
3463
|
+
// The polymorphic options for attributes are also applied here.
|
|
3464
|
+
|
|
3465
|
+
// [example]
|
|
3466
|
+
// ====
|
|
3467
|
+
// [source,ruby]
|
|
3468
|
+
// ----
|
|
3469
|
+
// class PolymorphicItemCollection < Lutaml::Model::Collection
|
|
3470
|
+
// instances :items, Item, polymorphic: true
|
|
3471
|
+
|
|
3472
|
+
// xml do
|
|
3473
|
+
// root "items"
|
|
3474
|
+
// map_element "item", to: :items
|
|
3475
|
+
// end
|
|
3476
|
+
|
|
3477
|
+
// key_value do
|
|
3478
|
+
// root "items"
|
|
3479
|
+
// map_instances to: :items
|
|
3480
|
+
// end
|
|
3481
|
+
// end
|
|
3482
|
+
// ----
|
|
3483
|
+
// ====
|
|
3484
|
+
|
|
3485
|
+
|
|
3486
|
+
== Serialization model mappings
|
|
3487
|
+
|
|
3488
|
+
=== General
|
|
3489
|
+
|
|
3490
|
+
Lutaml::Model allows you to translate a data model into serialization models of
|
|
3491
|
+
various serialization formats.
|
|
3492
|
+
|
|
3493
|
+
Depending on the serialization format, different methods are supported for
|
|
3494
|
+
defining serialization and deserialization mappings.
|
|
3495
|
+
|
|
3496
|
+
A serialization model mapping is defined using a format-specific DSL block
|
|
3497
|
+
in this syntax:
|
|
3498
|
+
|
|
3499
|
+
[source,ruby]
|
|
3500
|
+
----
|
|
3501
|
+
class Example < Lutaml::Model::Serializable
|
|
3502
|
+
{format-short-name} do <1>
|
|
3503
|
+
# ...
|
|
3504
|
+
end
|
|
3505
|
+
end
|
|
3506
|
+
----
|
|
3507
|
+
<1> `{format-short-name}` is the serialization format short name.
|
|
3508
|
+
|
|
3509
|
+
There are two kinds of serialization models:
|
|
3510
|
+
|
|
3511
|
+
* Represents a singular model (maps to a Lutaml::Model::Serializable)
|
|
3512
|
+
* Represents a group/collection of models (maps to Lutaml::Model::Collection)
|
|
3513
|
+
|
|
3514
|
+
A collection contains instances of singular models, and therefore is always
|
|
3515
|
+
inextricably linked to an underlying serialization format for singular models.
|
|
3516
|
+
For instance, JSONL represents a collection (itself being invalid JSON) that
|
|
3517
|
+
uses JSON for singular models.
|
|
3518
|
+
|
|
3519
|
+
The supported serialization formats and their short names are defined as follows:
|
|
3520
|
+
|
|
3521
|
+
Model serialization formats::
|
|
3522
|
+
|
|
3523
|
+
`xml`::: XML
|
|
3524
|
+
`hsh`::: Hash
|
|
3525
|
+
+
|
|
3526
|
+
NOTE: Yes a 3-letter abbreviation for Hash!
|
|
3527
|
+
|
|
3528
|
+
`json`::: JSON
|
|
3529
|
+
`yaml`::: YAML
|
|
3530
|
+
`toml`::: TOML
|
|
3531
|
+
`key_value`::: Key-value format, a shorthand for all key-value formats (including
|
|
3532
|
+
JSON, YAML and TOML).
|
|
3533
|
+
|
|
3534
|
+
Collection serialization formats::
|
|
3535
|
+
|
|
3536
|
+
`jsonl`::: JSONL (JSON Lines)
|
|
3537
|
+
`yamls`::: YAML Stream (multi-document format)
|
|
3538
|
+
|
|
3539
|
+
|
|
3540
|
+
.Using the `xml`, `hsh`, `json`, `yaml`, `toml` and `key_value` blocks to define serialization mappings
|
|
3541
|
+
[example]
|
|
3542
|
+
====
|
|
3543
|
+
[source,ruby]
|
|
3544
|
+
----
|
|
3545
|
+
class Example < Lutaml::Model::Serializable
|
|
3546
|
+
xml do
|
|
3547
|
+
# ...
|
|
3548
|
+
end
|
|
3549
|
+
|
|
3550
|
+
hsh do
|
|
3551
|
+
# ...
|
|
3552
|
+
end
|
|
3553
|
+
|
|
3554
|
+
json do
|
|
3555
|
+
# ...
|
|
3556
|
+
end
|
|
3557
|
+
|
|
3558
|
+
yaml do
|
|
3559
|
+
# ...
|
|
3560
|
+
end
|
|
3561
|
+
|
|
3562
|
+
toml do
|
|
3563
|
+
# ...
|
|
3564
|
+
end
|
|
3565
|
+
|
|
3566
|
+
key_value do
|
|
3567
|
+
# ...
|
|
3568
|
+
end
|
|
3569
|
+
end
|
|
3570
|
+
----
|
|
3571
|
+
====
|
|
3572
|
+
|
|
3573
|
+
.Using the `jsonl` block to define serialization mappings to a collection
|
|
3574
|
+
[example]
|
|
3575
|
+
====
|
|
3576
|
+
[source,ruby]
|
|
3577
|
+
----
|
|
3578
|
+
class Example < Lutaml::Model::Collection
|
|
3579
|
+
jsonl do
|
|
3580
|
+
# ...
|
|
3581
|
+
end
|
|
3582
|
+
end
|
|
3583
|
+
----
|
|
3584
|
+
====
|
|
3585
|
+
|
|
3586
|
+
|
|
3587
|
+
=== XML
|
|
3588
|
+
|
|
3589
|
+
==== Setting root element name
|
|
3590
|
+
|
|
3591
|
+
The `root` method sets the root element tag name of the XML document.
|
|
3592
|
+
|
|
3593
|
+
If `root` is not given, then the snake-cased class name will be used as the
|
|
3594
|
+
root.
|
|
3595
|
+
|
|
3596
|
+
[example]
|
|
3597
|
+
Sets the tag name for `<example>` in XML `<example>...</example>`.
|
|
3598
|
+
|
|
3599
|
+
Syntax:
|
|
3600
|
+
|
|
3601
|
+
[source,ruby]
|
|
3602
|
+
----
|
|
3603
|
+
xml do
|
|
3604
|
+
root 'xml_element_name'
|
|
3605
|
+
end
|
|
3606
|
+
----
|
|
3607
|
+
|
|
3608
|
+
.Setting the root element name to `example`
|
|
3609
|
+
[example]
|
|
3610
|
+
====
|
|
3611
|
+
[source,ruby]
|
|
3612
|
+
----
|
|
3613
|
+
class Example < Lutaml::Model::Serializable
|
|
3614
|
+
xml do
|
|
3615
|
+
root 'example'
|
|
3616
|
+
end
|
|
3617
|
+
end
|
|
3618
|
+
----
|
|
3619
|
+
|
|
3620
|
+
[source,ruby]
|
|
3621
|
+
----
|
|
3622
|
+
> Example.new.to_xml
|
|
3623
|
+
> #<example></example>
|
|
3624
|
+
----
|
|
3625
|
+
====
|
|
3626
|
+
|
|
3627
|
+
|
|
3628
|
+
==== Ommiting root element
|
|
3629
|
+
|
|
3630
|
+
The root element can be omitted by using the `no_root` method.
|
|
3631
|
+
|
|
3632
|
+
When `no_root` is used, only `map_element` can be used because without a root
|
|
3633
|
+
element there cannot be attributes.
|
|
3634
|
+
|
|
3635
|
+
Syntax:
|
|
3636
|
+
|
|
3637
|
+
[source,ruby]
|
|
3638
|
+
----
|
|
3639
|
+
xml do
|
|
3640
|
+
no_root
|
|
3641
|
+
end
|
|
3642
|
+
----
|
|
3643
|
+
|
|
3644
|
+
[example]
|
|
3645
|
+
====
|
|
3646
|
+
[source,ruby]
|
|
3647
|
+
----
|
|
3648
|
+
class NameAndCode < Lutaml::Model::Serializable
|
|
3649
|
+
attribute :name, :string
|
|
3650
|
+
attribute :code, :string
|
|
3651
|
+
|
|
3652
|
+
xml do
|
|
3653
|
+
no_root
|
|
3654
|
+
map_element "code", to: :code
|
|
3655
|
+
map_element "name", to: :name
|
|
3656
|
+
end
|
|
3657
|
+
end
|
|
3658
|
+
----
|
|
3659
|
+
|
|
3660
|
+
[source,xml]
|
|
3661
|
+
----
|
|
3662
|
+
<name>Name</name>
|
|
3663
|
+
<code>ID-001</code>
|
|
3664
|
+
----
|
|
3665
|
+
|
|
3666
|
+
[source,ruby]
|
|
3667
|
+
----
|
|
3668
|
+
> parsed = NameAndCode.from_xml(xml)
|
|
3669
|
+
> # <NameAndCode:0x0000000107a3ca70 @code="ID-001", @name="Name">
|
|
3670
|
+
> parsed.to_xml
|
|
3671
|
+
> # <code>ID-001</code><name>Name</name>
|
|
3672
|
+
----
|
|
3673
|
+
====
|
|
3674
|
+
|
|
3675
|
+
|
|
3676
|
+
|
|
3677
|
+
[[xml-map-all]]
|
|
3678
|
+
==== Mapping all XML content
|
|
3679
|
+
|
|
3680
|
+
The `map_all` tag in XML mapping captures and maps all content within an XML
|
|
3681
|
+
element into a single attribute in the target Ruby object.
|
|
3682
|
+
|
|
3683
|
+
The use case for `map_all` is to tell Lutaml::Model to not parse the content of
|
|
3684
|
+
the XML element at all, and instead handle it as an XML string.
|
|
3685
|
+
|
|
3686
|
+
NOTE: The corresponding method for key-value formats is at <<key-value-map-all>>.
|
|
3687
|
+
|
|
3688
|
+
WARNING: Notice that usage of mapping all will lead to incompatibility between
|
|
3689
|
+
serialization formats, i.e. the raw string content will not be portable as
|
|
3690
|
+
objects are across different formats.
|
|
3691
|
+
|
|
3692
|
+
This is useful in the case where the content of an XML element is not to be
|
|
3693
|
+
handled by a Lutaml::Model::Serializable object.
|
|
3694
|
+
|
|
3695
|
+
This feature is commonly used with custom methods or a custom model object to
|
|
3696
|
+
handle the content.
|
|
2127
3697
|
|
|
2128
3698
|
This includes:
|
|
2129
3699
|
|
|
@@ -2983,6 +4553,7 @@ HERE
|
|
|
2983
4553
|
----
|
|
2984
4554
|
====
|
|
2985
4555
|
|
|
4556
|
+
NOTE: For importing model mappings inside a `sequence` block, refer to <<import-model-mappings-inside-sequence, Importing model mappings inside a `sequence`>>.
|
|
2986
4557
|
|
|
2987
4558
|
[[xml-schema-location]]
|
|
2988
4559
|
==== Automatic support of `xsi:schemaLocation`
|
|
@@ -3448,11 +5019,20 @@ This XML snippet is in UTF-8.
|
|
|
3448
5019
|
|
|
3449
5020
|
==== General
|
|
3450
5021
|
|
|
3451
|
-
Key-value data models
|
|
3452
|
-
|
|
5022
|
+
Key-value data models share a similar structure where data is stored as
|
|
5023
|
+
key-value pairs.
|
|
3453
5024
|
|
|
3454
5025
|
`Lutaml::Model` works with these formats in a similar way.
|
|
3455
5026
|
|
|
5027
|
+
Key-value data models supported are identified by their short name:
|
|
5028
|
+
|
|
5029
|
+
`hsh`:: Hash (Ruby `Hash` class)
|
|
5030
|
+
`json`:: JSON
|
|
5031
|
+
`yaml`:: YAML
|
|
5032
|
+
`toml`:: TOML
|
|
5033
|
+
`key_value`:: A way to configure key-value mappings for all supported key-value data models.
|
|
5034
|
+
|
|
5035
|
+
|
|
3456
5036
|
==== Mapping
|
|
3457
5037
|
|
|
3458
5038
|
The `map` method is used to define key-value mappings.
|
|
@@ -3461,16 +5041,40 @@ Syntax:
|
|
|
3461
5041
|
|
|
3462
5042
|
[source,ruby]
|
|
3463
5043
|
----
|
|
3464
|
-
|
|
5044
|
+
{key_value_type_short} do <1>
|
|
3465
5045
|
map 'key_value_model_attribute_name', to: :name_of_attribute
|
|
3466
5046
|
end
|
|
3467
5047
|
----
|
|
5048
|
+
<1> `key_value_type_short` is the key-value data model's short name.
|
|
5049
|
+
|
|
5050
|
+
.Creating a key-value data model mapping for only the JSON format
|
|
5051
|
+
[example]
|
|
5052
|
+
====
|
|
5053
|
+
[source,ruby]
|
|
5054
|
+
----
|
|
5055
|
+
json do
|
|
5056
|
+
map :color, to: :color
|
|
5057
|
+
map :desc, to: :description
|
|
5058
|
+
end
|
|
5059
|
+
----
|
|
5060
|
+
====
|
|
3468
5061
|
|
|
5062
|
+
.Creating a key-value data model mapping for all key-value formats
|
|
5063
|
+
[example]
|
|
5064
|
+
====
|
|
5065
|
+
[source,ruby]
|
|
5066
|
+
----
|
|
5067
|
+
key_value do
|
|
5068
|
+
map :color, to: :color
|
|
5069
|
+
map :desc, to: :description
|
|
5070
|
+
end
|
|
5071
|
+
----
|
|
5072
|
+
====
|
|
3469
5073
|
|
|
3470
5074
|
==== Unified mapping
|
|
3471
5075
|
|
|
3472
5076
|
The `key_value` method is a streamlined way to map all attributes for
|
|
3473
|
-
serialization into key-value formats including JSON, YAML, and TOML.
|
|
5077
|
+
serialization into key-value formats including Hash, JSON, YAML, and TOML.
|
|
3474
5078
|
|
|
3475
5079
|
If there is no definite differentiation between the key value formats, the
|
|
3476
5080
|
`key_value` method simplifies defining mappings and improves code readability.
|
|
@@ -3489,19 +5093,11 @@ class CeramicModel < Lutaml::Model::Serializable
|
|
|
3489
5093
|
attribute :glaze, :string
|
|
3490
5094
|
attribute :description, :string
|
|
3491
5095
|
|
|
3492
|
-
key_value do
|
|
3493
|
-
map :color, to: color
|
|
5096
|
+
key_value do # or any other key-value data model
|
|
5097
|
+
map :color, to: :color
|
|
3494
5098
|
map :glz, to: :glaze
|
|
3495
5099
|
map :desc, to: :description
|
|
3496
5100
|
end
|
|
3497
|
-
|
|
3498
|
-
# Equivalent to the JSON, YAML, and TOML mappings.
|
|
3499
|
-
#
|
|
3500
|
-
# json and yaml and toml do
|
|
3501
|
-
# map :id, to: color
|
|
3502
|
-
# map :name, to: :full_name
|
|
3503
|
-
# map :status, to: :current_status
|
|
3504
|
-
# end
|
|
3505
5101
|
end
|
|
3506
5102
|
----
|
|
3507
5103
|
|
|
@@ -3532,12 +5128,7 @@ desc: A ceramic with a navy blue color and clear glaze.
|
|
|
3532
5128
|
|
|
3533
5129
|
==== Specific format mappings
|
|
3534
5130
|
|
|
3535
|
-
Specific key value formats can be mapping independently of other formats
|
|
3536
|
-
|
|
3537
|
-
* `json` for the JSON format
|
|
3538
|
-
* `yaml` for the YAML format
|
|
3539
|
-
* `toml` for the TOML format
|
|
3540
|
-
|
|
5131
|
+
Specific key value formats can be mapping independently of other formats.
|
|
3541
5132
|
|
|
3542
5133
|
.Using the `map` method to define key-value mappings per format
|
|
3543
5134
|
[example]
|
|
@@ -3548,6 +5139,11 @@ class Example < Lutaml::Model::Serializable
|
|
|
3548
5139
|
attribute :name, :string
|
|
3549
5140
|
attribute :value, :integer
|
|
3550
5141
|
|
|
5142
|
+
hsh do
|
|
5143
|
+
map 'name', to: :name
|
|
5144
|
+
map 'value', to: :value
|
|
5145
|
+
end
|
|
5146
|
+
|
|
3551
5147
|
json do
|
|
3552
5148
|
map 'name', to: :name
|
|
3553
5149
|
map 'value', to: :value
|
|
@@ -3610,7 +5206,7 @@ Syntax:
|
|
|
3610
5206
|
|
|
3611
5207
|
[source,ruby]
|
|
3612
5208
|
----
|
|
3613
|
-
json | yaml | toml | key_value do
|
|
5209
|
+
hsh | json | yaml | toml | key_value do
|
|
3614
5210
|
map_all to: :name_of_attribute
|
|
3615
5211
|
end
|
|
3616
5212
|
----
|
|
@@ -3623,6 +5219,10 @@ end
|
|
|
3623
5219
|
class Document < Lutaml::Model::Serializable
|
|
3624
5220
|
attribute :content, :string
|
|
3625
5221
|
|
|
5222
|
+
hsh do
|
|
5223
|
+
map_all to: :content
|
|
5224
|
+
end
|
|
5225
|
+
|
|
3626
5226
|
json do
|
|
3627
5227
|
map_all to: :content
|
|
3628
5228
|
end
|
|
@@ -3796,7 +5396,7 @@ Syntax:
|
|
|
3796
5396
|
class SomeKeyedCollection < Lutaml::Model::Serializable
|
|
3797
5397
|
attribute :name_of_attribute, AttributeValueType, collection: true
|
|
3798
5398
|
|
|
3799
|
-
json | yaml | toml | key_value do
|
|
5399
|
+
hsh | json | yaml | toml | key_value do
|
|
3800
5400
|
map to: :name_of_attribute, <1>
|
|
3801
5401
|
root_mappings: { <2>
|
|
3802
5402
|
# `:key` is a reserved keyword
|
|
@@ -3870,7 +5470,7 @@ Syntax:
|
|
|
3870
5470
|
class SomeKeyedCollection < Lutaml::Model::Serializable
|
|
3871
5471
|
attribute :name_of_attribute, AttributeValueType, collection: true
|
|
3872
5472
|
|
|
3873
|
-
json | yaml | toml | key_value do
|
|
5473
|
+
hsh | json | yaml | toml | key_value do
|
|
3874
5474
|
map to: :name_of_attribute,
|
|
3875
5475
|
root_mappings: {
|
|
3876
5476
|
value_type_attribute_name_for_key: :key, <1>
|
|
@@ -4008,7 +5608,7 @@ Syntax:
|
|
|
4008
5608
|
class SomeKeyedCollection < Lutaml::Model::Serializable
|
|
4009
5609
|
attribute :name_of_attribute, AttributeValueType, collection: true
|
|
4010
5610
|
|
|
4011
|
-
json | yaml | toml | key_value do
|
|
5611
|
+
hsh | json | yaml | toml | key_value do
|
|
4012
5612
|
map to: :name_of_attribute,
|
|
4013
5613
|
root_mappings: {
|
|
4014
5614
|
value_type_attribute_name_for_key: :key, <1>
|
|
@@ -4214,7 +5814,7 @@ Syntax:
|
|
|
4214
5814
|
class SomeKeyedCollection < Lutaml::Model::Serializable
|
|
4215
5815
|
attribute :name_of_attribute, AttributeValueType, collection: true
|
|
4216
5816
|
|
|
4217
|
-
json | yaml | toml | key_value do
|
|
5817
|
+
hsh | json | yaml | toml | key_value do
|
|
4218
5818
|
map to: :name_of_attribute,
|
|
4219
5819
|
root_mappings: {
|
|
4220
5820
|
value_type_attribute_name_for_key: :key, <1>
|
|
@@ -4378,7 +5978,7 @@ end
|
|
|
4378
5978
|
NOTE: This feature is for key-value data model serialization only.
|
|
4379
5979
|
|
|
4380
5980
|
The `child_mappings` option is used to extract results from a key-value
|
|
4381
|
-
serialization data model (JSON, YAML, TOML) into a `Lutaml::Model::Serializable`
|
|
5981
|
+
serialization data model (Hash, JSON, YAML, TOML) into a `Lutaml::Model::Serializable`
|
|
4382
5982
|
object (collection or not).
|
|
4383
5983
|
|
|
4384
5984
|
The values are extracted from the key-value data model using the list of keys
|
|
@@ -4391,7 +5991,7 @@ Syntax:
|
|
|
4391
5991
|
class SomeObject < Lutaml::Model::Serializable
|
|
4392
5992
|
attribute :name_of_attribute, AttributeValueType, collection: true
|
|
4393
5993
|
|
|
4394
|
-
json | yaml | toml | key_value do
|
|
5994
|
+
hsh | json | yaml | toml | key_value do
|
|
4395
5995
|
map 'key_value_model_attribute_name', to: :name_of_attribute,
|
|
4396
5996
|
child_mappings: {
|
|
4397
5997
|
value_type_attribute_name_1: <1>
|
|
@@ -4556,12 +6156,353 @@ The output becomes:
|
|
|
4556
6156
|
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}}
|
|
4557
6157
|
----
|
|
4558
6158
|
|
|
4559
|
-
In this example:
|
|
6159
|
+
In this example:
|
|
6160
|
+
|
|
6161
|
+
* The `key` of each schema (`foo` and `bar`) is mapped to the `id` attribute.
|
|
6162
|
+
|
|
6163
|
+
* The nested `path.link` and `path.name` keys are mapped to the `link` and
|
|
6164
|
+
`name` attributes, respectively.
|
|
6165
|
+
====
|
|
6166
|
+
|
|
6167
|
+
=== Collection data models
|
|
6168
|
+
|
|
6169
|
+
==== General
|
|
6170
|
+
|
|
6171
|
+
Collection data models represent a group of models, mapping to an instance of
|
|
6172
|
+
Lutaml::Model::Collection.
|
|
6173
|
+
|
|
6174
|
+
Collection data models supported are identified by their short name:
|
|
6175
|
+
|
|
6176
|
+
`jsonl`:: JSONL (JSON Lines)
|
|
6177
|
+
`yamls`:: YAML Stream (multi-document format)
|
|
6178
|
+
|
|
6179
|
+
|
|
6180
|
+
==== Mapping
|
|
6181
|
+
|
|
6182
|
+
As with collections in general, the `map` method is used to define collection
|
|
6183
|
+
mappings.
|
|
6184
|
+
|
|
6185
|
+
Syntax:
|
|
6186
|
+
|
|
6187
|
+
[source,ruby]
|
|
6188
|
+
----
|
|
6189
|
+
class MySerializedCollection < Lutaml::Model::Collection
|
|
6190
|
+
instances {attribute}, ModelType
|
|
6191
|
+
|
|
6192
|
+
{collection_type_short} do
|
|
6193
|
+
map_instances to: {attribute}
|
|
6194
|
+
end
|
|
6195
|
+
end
|
|
6196
|
+
----
|
|
6197
|
+
|
|
6198
|
+
Where,
|
|
6199
|
+
|
|
6200
|
+
`{collection_type_short}`:: The short name of the collection type (e.g. `jsonl`, `yamls`).
|
|
6201
|
+
|
|
6202
|
+
`{attribute}`:: The name of the attribute in the collection that will hold the
|
|
6203
|
+
collection data.
|
|
6204
|
+
|
|
6205
|
+
`ModelType`:: The type of the model that will be used in the collection.
|
|
6206
|
+
|
|
6207
|
+
A singular model may also utilize collection data models in the following manner.
|
|
6208
|
+
|
|
6209
|
+
Syntax:
|
|
6210
|
+
|
|
6211
|
+
[source,ruby]
|
|
6212
|
+
----
|
|
6213
|
+
class MySerializedCollection < Lutaml::Model::Serializeable
|
|
6214
|
+
attribute {attribute}, ModelType, collection: true
|
|
6215
|
+
|
|
6216
|
+
{collection_type_short} do
|
|
6217
|
+
# Notice that there is no key_name i.e map <key_name>, to: <attribute_name>,
|
|
6218
|
+
# This is because in a collection there are no keys. Each object needs to be
|
|
6219
|
+
# mapped to the attribute.
|
|
6220
|
+
map to: {attribute}
|
|
6221
|
+
end
|
|
6222
|
+
end
|
|
6223
|
+
----
|
|
6224
|
+
|
|
6225
|
+
Where,
|
|
6226
|
+
|
|
6227
|
+
`{collection_type_short}`:: The short name of the collection type (e.g. `jsonl`, `yamls`).
|
|
6228
|
+
|
|
6229
|
+
`{attribute}`:: The name of the attribute in the collection that will hold the
|
|
6230
|
+
collection data.
|
|
6231
|
+
|
|
6232
|
+
`ModelType`:: The type of the model that will be used in the collection.
|
|
6233
|
+
|
|
6234
|
+
|
|
6235
|
+
==== JSONL
|
|
6236
|
+
|
|
6237
|
+
JSONL (short for JSON Lines) is a serialization format where each line
|
|
6238
|
+
represents a valid JSON object. The format is meant to be efficient for large
|
|
6239
|
+
datasets such as for streaming or batch processing.
|
|
6240
|
+
|
|
6241
|
+
It represents a collection of JSON objects encoded one object per line.
|
|
6242
|
+
|
|
6243
|
+
NOTE: The contents of JSONL itself is not valid JSON, but each line is a valid
|
|
6244
|
+
JSON.
|
|
6245
|
+
|
|
6246
|
+
Since JSONL contains JSON elements, the model specified with `instances` or
|
|
6247
|
+
`attribute` must support JSON.
|
|
6248
|
+
|
|
6249
|
+
Every line in a JSONL file is also a valid JSON object. If JSONL-specific
|
|
6250
|
+
mappings (through `jsonl`) are not defined in the model, the existing `json`
|
|
6251
|
+
mappings are used instead as a fallback for serialization and deserialization.
|
|
6252
|
+
|
|
6253
|
+
|
|
6254
|
+
.Parsing a JSONL collection using a collection
|
|
6255
|
+
[example]
|
|
6256
|
+
====
|
|
6257
|
+
[source,ruby]
|
|
6258
|
+
----
|
|
6259
|
+
class Person
|
|
6260
|
+
attribute :name, :string
|
|
6261
|
+
attribute :age, :integer
|
|
6262
|
+
attribute :id, :string
|
|
6263
|
+
end
|
|
6264
|
+
|
|
6265
|
+
class Directory < Lutaml::Model::Collection
|
|
6266
|
+
instances :persons, Person
|
|
6267
|
+
|
|
6268
|
+
jsonl do
|
|
6269
|
+
map_instances to: :persons
|
|
6270
|
+
end
|
|
6271
|
+
end
|
|
6272
|
+
|
|
6273
|
+
jsonl = <<~JSONL
|
|
6274
|
+
{"name":"John","age":30,"id":"abc-123"}
|
|
6275
|
+
{"name":"Jane","age":25,"id":"def-456"}
|
|
6276
|
+
JSONL
|
|
6277
|
+
|
|
6278
|
+
jsonl = Directory.from_jsonl(jsonl)
|
|
6279
|
+
|
|
6280
|
+
# => <Directory:0x00007fae4b0c9b10
|
|
6281
|
+
# @persons=[
|
|
6282
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
|
6283
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
|
6284
|
+
# ]>
|
|
6285
|
+
----
|
|
6286
|
+
====
|
|
6287
|
+
|
|
6288
|
+
.Parsing a JSONL collection using a singlular model
|
|
6289
|
+
[example]
|
|
6290
|
+
====
|
|
6291
|
+
[source,ruby]
|
|
6292
|
+
----
|
|
6293
|
+
class Person
|
|
6294
|
+
attribute :name, :string
|
|
6295
|
+
attribute :age, :integer
|
|
6296
|
+
attribute :id, :string
|
|
6297
|
+
end
|
|
6298
|
+
|
|
6299
|
+
class Directory < Lutaml::Model::Serializeable
|
|
6300
|
+
attribute :persons, Person, collection: true
|
|
6301
|
+
|
|
6302
|
+
jsonl do
|
|
6303
|
+
map_instances to: :persons
|
|
6304
|
+
end
|
|
6305
|
+
end
|
|
6306
|
+
|
|
6307
|
+
jsonl = <<~JSONL
|
|
6308
|
+
{"name":"John","age":30,"id":"abc-123"}
|
|
6309
|
+
{"name":"Jane","age":25,"id":"def-456"}
|
|
6310
|
+
JSONL
|
|
6311
|
+
|
|
6312
|
+
jsonl = Directory.from_jsonl(jsonl)
|
|
6313
|
+
|
|
6314
|
+
# => <Directory:0x00007fae4b0c9b10
|
|
6315
|
+
# @persons=[
|
|
6316
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
|
6317
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
|
6318
|
+
# ]>
|
|
6319
|
+
----
|
|
6320
|
+
====
|
|
6321
|
+
|
|
6322
|
+
.Parsing a JSONL collection relying on JSON mappings on instance model
|
|
6323
|
+
[example]
|
|
6324
|
+
====
|
|
6325
|
+
[source,ruby]
|
|
6326
|
+
----
|
|
6327
|
+
class Person
|
|
6328
|
+
attribute :name, :string
|
|
6329
|
+
attribute :age, :integer
|
|
6330
|
+
attribute :id, :string
|
|
6331
|
+
|
|
6332
|
+
json do
|
|
6333
|
+
map "full_name", to: :name
|
|
6334
|
+
map "age", to: :age
|
|
6335
|
+
map "id", to: :id
|
|
6336
|
+
end
|
|
6337
|
+
end
|
|
6338
|
+
|
|
6339
|
+
class Directory < Lutaml::Model::Collection
|
|
6340
|
+
instances :persons, Person
|
|
6341
|
+
|
|
6342
|
+
jsonl do
|
|
6343
|
+
map_instances to: :persons
|
|
6344
|
+
end
|
|
6345
|
+
end
|
|
6346
|
+
|
|
6347
|
+
jsonl = <<~JSONL
|
|
6348
|
+
{"full_name":"John Doe","age":30,"id":"abc-123"}
|
|
6349
|
+
{"full_name":"Jane Smith","age":25,"id":"def-456"}
|
|
6350
|
+
JSONL
|
|
6351
|
+
|
|
6352
|
+
jsonl = Directory.from_jsonl(jsonl)
|
|
6353
|
+
# => <Directory:0x00007fae4b0c9b10
|
|
6354
|
+
# @persons=[
|
|
6355
|
+
# <Person:0x00007fae4b0c9970 @name="John Doe", @age=30, @id="abc-123">,
|
|
6356
|
+
# <Person:0x00007fae4b0c9838 @name="Jane Smith", @age=25, @id="def-456">
|
|
6357
|
+
# ]>
|
|
6358
|
+
----
|
|
6359
|
+
====
|
|
6360
|
+
|
|
6361
|
+
|
|
6362
|
+
==== YAML Stream
|
|
6363
|
+
|
|
6364
|
+
YAML Stream (short for YAML multi-document format) is a serialization format
|
|
6365
|
+
where each document is separated by a document separator (`---`). The format is
|
|
6366
|
+
meant to be efficient for large datasets such as for streaming or batch
|
|
6367
|
+
processing.
|
|
6368
|
+
|
|
6369
|
+
It represents a collection of YAML documents encoded one document per stream.
|
|
6370
|
+
|
|
6371
|
+
NOTE: The contents of YAML Stream is valid YAML, where each document is a valid
|
|
6372
|
+
YAML document separated by document separators.
|
|
6373
|
+
|
|
6374
|
+
Since YAML Stream contains YAML elements, the model specified with `instances`
|
|
6375
|
+
or `attribute` must support YAML.
|
|
6376
|
+
|
|
6377
|
+
Every document in a YAML Stream file is also a valid YAML document. If YAML
|
|
6378
|
+
Stream-specific mappings (through `yamls`) are not defined in the model, the
|
|
6379
|
+
existing `yaml` mappings are used instead as a fallback for serialization and
|
|
6380
|
+
deserialization.
|
|
6381
|
+
|
|
6382
|
+
|
|
6383
|
+
.Parsing a YAML Stream collection using a collection
|
|
6384
|
+
[example]
|
|
6385
|
+
====
|
|
6386
|
+
[source,ruby]
|
|
6387
|
+
----
|
|
6388
|
+
class Person
|
|
6389
|
+
attribute :name, :string
|
|
6390
|
+
attribute :age, :integer
|
|
6391
|
+
attribute :id, :string
|
|
6392
|
+
end
|
|
6393
|
+
|
|
6394
|
+
class Directory < Lutaml::Model::Collection
|
|
6395
|
+
instances :persons, Person
|
|
6396
|
+
|
|
6397
|
+
yamls do
|
|
6398
|
+
map_instances to: :persons
|
|
6399
|
+
end
|
|
6400
|
+
end
|
|
6401
|
+
|
|
6402
|
+
yamls = <<~YAMLS
|
|
6403
|
+
---
|
|
6404
|
+
name: John
|
|
6405
|
+
age: 30
|
|
6406
|
+
id: abc-123
|
|
6407
|
+
---
|
|
6408
|
+
name: Jane
|
|
6409
|
+
age: 25
|
|
6410
|
+
id: def-456
|
|
6411
|
+
YAMLS
|
|
6412
|
+
|
|
6413
|
+
yamls = Directory.from_yamls(yamls)
|
|
6414
|
+
|
|
6415
|
+
# => <Directory:0x00007fae4b0c9b10
|
|
6416
|
+
# @persons=[
|
|
6417
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
|
6418
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
|
6419
|
+
# ]>
|
|
6420
|
+
----
|
|
6421
|
+
====
|
|
6422
|
+
|
|
6423
|
+
.Parsing a YAML Stream collection using a singlular model
|
|
6424
|
+
[example]
|
|
6425
|
+
====
|
|
6426
|
+
[source,ruby]
|
|
6427
|
+
----
|
|
6428
|
+
class Person
|
|
6429
|
+
attribute :name, :string
|
|
6430
|
+
attribute :age, :integer
|
|
6431
|
+
attribute :id, :string
|
|
6432
|
+
end
|
|
6433
|
+
|
|
6434
|
+
class Directory < Lutaml::Model::Serializeable
|
|
6435
|
+
attribute :persons, Person, collection: true
|
|
6436
|
+
|
|
6437
|
+
yamls do
|
|
6438
|
+
map_instances to: :persons
|
|
6439
|
+
end
|
|
6440
|
+
end
|
|
6441
|
+
|
|
6442
|
+
yamls = <<~YAMLS
|
|
6443
|
+
---
|
|
6444
|
+
name: John
|
|
6445
|
+
age: 30
|
|
6446
|
+
id: abc-123
|
|
6447
|
+
---
|
|
6448
|
+
name: Jane
|
|
6449
|
+
age: 25
|
|
6450
|
+
id: def-456
|
|
6451
|
+
YAMLS
|
|
6452
|
+
|
|
6453
|
+
yamls = Directory.from_yamls(yamls)
|
|
6454
|
+
|
|
6455
|
+
# => <Directory:0x00007fae4b0c9b10
|
|
6456
|
+
# @persons=[
|
|
6457
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
|
6458
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
|
6459
|
+
# ]>
|
|
6460
|
+
----
|
|
6461
|
+
====
|
|
6462
|
+
|
|
6463
|
+
.Parsing a YAML Stream collection relying on YAML mappings on instance model
|
|
6464
|
+
[example]
|
|
6465
|
+
====
|
|
6466
|
+
[source,ruby]
|
|
6467
|
+
----
|
|
6468
|
+
class Person
|
|
6469
|
+
attribute :name, :string
|
|
6470
|
+
attribute :age, :integer
|
|
6471
|
+
attribute :id, :string
|
|
6472
|
+
|
|
6473
|
+
yaml do
|
|
6474
|
+
map "full_name", to: :name
|
|
6475
|
+
map "age", to: :age
|
|
6476
|
+
map "id", to: :id
|
|
6477
|
+
end
|
|
6478
|
+
end
|
|
4560
6479
|
|
|
4561
|
-
|
|
6480
|
+
class Directory < Lutaml::Model::Collection
|
|
6481
|
+
instances :persons, Person
|
|
4562
6482
|
|
|
4563
|
-
|
|
4564
|
-
|
|
6483
|
+
yamls do
|
|
6484
|
+
map_instances to: :persons
|
|
6485
|
+
end
|
|
6486
|
+
end
|
|
6487
|
+
|
|
6488
|
+
yamls = <<~YAMLS
|
|
6489
|
+
---
|
|
6490
|
+
full_name: John Doe
|
|
6491
|
+
age: 30
|
|
6492
|
+
id: abc-123
|
|
6493
|
+
---
|
|
6494
|
+
full_name: Jane Smith
|
|
6495
|
+
age: 25
|
|
6496
|
+
id: def-456
|
|
6497
|
+
YAMLS
|
|
6498
|
+
|
|
6499
|
+
yamls = Directory.from_yamls(yamls)
|
|
6500
|
+
# => <Directory:0x00007fae4b0c9b10
|
|
6501
|
+
# @persons=[
|
|
6502
|
+
# <Person:0x00007fae4b0c9970 @name="John Doe", @age=30, @id="abc-123">,
|
|
6503
|
+
# <Person:0x00007fae4b0c9838 @name="Jane Smith", @age=25, @id="def-456">
|
|
6504
|
+
# ]>
|
|
6505
|
+
----
|
|
4565
6506
|
====
|
|
4566
6507
|
|
|
4567
6508
|
=== Format-independent mechanisms
|
|
@@ -4639,7 +6580,7 @@ being retrieved from the model.
|
|
|
4639
6580
|
Attribute-level `import`:: The transformation `Proc` for the value when it is
|
|
4640
6581
|
being assigned to the model.
|
|
4641
6582
|
|
|
4642
|
-
`{key_value_formats}`:: The serialization format (e.g. `json`, `yaml`, `toml`,
|
|
6583
|
+
`{key_value_formats}`:: The serialization format (e.g. `hsh`, `json`, `yaml`, `toml`,
|
|
4643
6584
|
`key_value`) for which the mapping is defined.
|
|
4644
6585
|
|
|
4645
6586
|
Mapping-level `transform`:: The option to define a transformation for the
|
|
@@ -4738,18 +6679,18 @@ format-specific custom methods.
|
|
|
4738
6679
|
╔════════════════════════════╗ ╔════════════════════════════╗
|
|
4739
6680
|
║ Serialization Format Value ║ ║ Serialization Format Value ║
|
|
4740
6681
|
╚════════════════════════════╝ ╚════════════════════════════╝
|
|
4741
|
-
|
|
|
4742
|
-
▼
|
|
6682
|
+
| ▲
|
|
6683
|
+
▼ |
|
|
4743
6684
|
╔════════════════════════════╗ ╔════════════════════════════╗
|
|
4744
6685
|
║ Mapping Transform ║ ║ Mapping Transform ║
|
|
4745
6686
|
╚════════════════════════════╝ ╚════════════════════════════╝
|
|
4746
|
-
|
|
|
4747
|
-
▼
|
|
6687
|
+
| ▲
|
|
6688
|
+
▼ |
|
|
4748
6689
|
╔════════════════════════════╗ ╔════════════════════════════╗
|
|
4749
6690
|
║ Attribute Transform ║ ║ Attribute Transform ║
|
|
4750
6691
|
╚════════════════════════════╝ ╚════════════════════════════╝
|
|
4751
|
-
|
|
|
4752
|
-
▼
|
|
6692
|
+
| ▲
|
|
6693
|
+
▼ |
|
|
4753
6694
|
╔════════════════════════════╗ ╔════════════════════════════╗
|
|
4754
6695
|
║ Model Attribute Value ║ ║ Model Attribute Value ║
|
|
4755
6696
|
╚════════════════════════════╝ ╚════════════════════════════╝
|
|
@@ -4994,7 +6935,7 @@ xml do
|
|
|
4994
6935
|
map_attribute 'name_of_attribute', to: :name_of_attribute, render_default: true
|
|
4995
6936
|
end
|
|
4996
6937
|
|
|
4997
|
-
json | yaml | toml | key_value do
|
|
6938
|
+
hsh | json | yaml | toml | key_value do
|
|
4998
6939
|
map 'name_of_attribute', to: :name_of_attribute, render_default: true
|
|
4999
6940
|
end
|
|
5000
6941
|
----
|
|
@@ -5071,7 +7012,7 @@ Syntax:
|
|
|
5071
7012
|
|
|
5072
7013
|
[source,ruby]
|
|
5073
7014
|
----
|
|
5074
|
-
json | yaml | toml | key_value do
|
|
7015
|
+
hsh | json | yaml | toml | key_value do
|
|
5075
7016
|
map ["name1", "name2"], to: :attribute_name
|
|
5076
7017
|
end
|
|
5077
7018
|
|
|
@@ -5185,7 +7126,7 @@ Syntax:
|
|
|
5185
7126
|
|
|
5186
7127
|
[source,ruby]
|
|
5187
7128
|
----
|
|
5188
|
-
xml | json | yaml | toml do
|
|
7129
|
+
xml | hsh | json | yaml | toml do
|
|
5189
7130
|
map 'key_value_model_attribute_name', to: :name_of_attribute, delegate: :model_to_delegate_to
|
|
5190
7131
|
end
|
|
5191
7132
|
----
|
|
@@ -5466,7 +7407,7 @@ NOTE: For `NokogiriAdapter`, we can also call `to_xml` on `value.node.adapter_no
|
|
|
5466
7407
|
.Key-value data model serialization with custom methods
|
|
5467
7408
|
[source,ruby]
|
|
5468
7409
|
----
|
|
5469
|
-
json | yaml | toml do
|
|
7410
|
+
hsh | json | yaml | toml do
|
|
5470
7411
|
map 'attribute_name', to: :name_of_attribute, with: {
|
|
5471
7412
|
to: :method_name_to_serialize,
|
|
5472
7413
|
from: :method_name_to_deserialize
|
|
@@ -5533,12 +7474,122 @@ the undefined value:: the value is not defined
|
|
|
5533
7474
|
There are also different ways to represent these missing values
|
|
5534
7475
|
when the attribute accepts a single value or a collection of values.
|
|
5535
7476
|
|
|
7477
|
+
.Support of missing value types in different technologies
|
|
7478
|
+
|===
|
|
7479
|
+
| Technology | Missing value type | Realized as
|
|
7480
|
+
|
|
7481
|
+
.3+| Lutaml::Model
|
|
7482
|
+
| empty value | Ruby empty string (`""`)
|
|
7483
|
+
| non-existent value | Ruby `NilClass` (`nil`)
|
|
7484
|
+
| undefined value | `class Uninitialized`
|
|
7485
|
+
|
|
7486
|
+
.3+| XML element
|
|
7487
|
+
| empty value | XML blank element: `<status></status>` or `<status/>`
|
|
7488
|
+
| non-existent value | XML blank element with attribute `xsi:nil`: `<status xsi:nil="true"/>`
|
|
7489
|
+
| undefined value | the XML element is omitted
|
|
7490
|
+
|
|
7491
|
+
.3+| XML attribute
|
|
7492
|
+
| empty value | XML blank attribute: `status=""`
|
|
7493
|
+
| non-existent value | the XML attribute is omitted
|
|
7494
|
+
| undefined value | the XML attribute is omitted
|
|
7495
|
+
|
|
7496
|
+
.3+| JSON
|
|
7497
|
+
| empty value | JSON empty string (`""`)
|
|
7498
|
+
| non-existent value | JSON `null` value
|
|
7499
|
+
| undefined value | the JSON key is omitted
|
|
7500
|
+
|
|
7501
|
+
.3+| TOML
|
|
7502
|
+
| empty value | TOML empty string
|
|
7503
|
+
| non-existent value | the TOML key is omitted since TOML does not support the concept of null.
|
|
7504
|
+
| undefined value | the TOML key is omitted
|
|
7505
|
+
|
|
7506
|
+
|===
|
|
7507
|
+
|
|
7508
|
+
NOTE: The `Uninitialized` class is a special Lutaml::Model construct, it is not
|
|
7509
|
+
supported by normal Ruby objects.
|
|
7510
|
+
|
|
7511
|
+
The challenge for the developer is how to represent fully compatible semantics
|
|
7512
|
+
using interoperable data models across different technologies.
|
|
7513
|
+
|
|
7514
|
+
Lutaml::Model provides you with several mechanisms to retain the missing values
|
|
7515
|
+
semantics. An example mapping is shown in the following diagram.
|
|
7516
|
+
|
|
7517
|
+
.Mapping of missing value types between Lutaml::Model and YAML
|
|
7518
|
+
[source]
|
|
7519
|
+
----
|
|
7520
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
7521
|
+
│ ╔════════════════════════╗ ╔════════════════════════╗ │
|
|
7522
|
+
│ ║ Lutaml::Model Values ║ ║ YAML Values ║ │
|
|
7523
|
+
│ ╠════════════════════════╣ ╠════════════════════════╣ │
|
|
7524
|
+
│ ║ ║ mapping ║ ║ │
|
|
7525
|
+
│ ║ "empty" ║◀──────────────────▶║ "empty" ║ │
|
|
7526
|
+
│ ║ (empty string, []) ║ to empty ║ (empty string, []) ║ │
|
|
7527
|
+
│ ║ ║ ║ ║ │
|
|
7528
|
+
│ ╟────────────────────────╢ ╟────────────────────────╢ │
|
|
7529
|
+
│ ║ ║ mapping ║ ║ │
|
|
7530
|
+
│ ║ "non-existent" ║◀──────────────────▶║ "non-existent" ║ │
|
|
7531
|
+
│ ║ (nil) ║ to non-existent ║ (null) ║ │
|
|
7532
|
+
│ ║ ║ ║ ║ │
|
|
7533
|
+
│ ╟────────────────────────╢ ╟────────────────────────╢ │
|
|
7534
|
+
│ ║ ║ mapping ║ ║ │
|
|
7535
|
+
│ ║ "undefined" ║◀──────────────────▶║ "undefined" ║ │
|
|
7536
|
+
│ ║ (uninitialized) ║ to undefined ║ (key omitted) ║ │
|
|
7537
|
+
│ ║ ║ ║ ║ │
|
|
7538
|
+
│ ╚════════════════════════╝ ╚════════════════════════╝ │
|
|
7539
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
7540
|
+
----
|
|
7541
|
+
|
|
7542
|
+
In the case where the interoperating technologies do not support the full spectrum
|
|
7543
|
+
of missing value types, it is necessary for the developer to understand any
|
|
7544
|
+
such behavior and relevant handling.
|
|
7545
|
+
|
|
7546
|
+
.Mapping of missing value types between Lutaml::Model and TOML
|
|
7547
|
+
[source]
|
|
7548
|
+
----
|
|
7549
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
7550
|
+
│ ╔════════════════════════╗ ╔════════════════════════╗ │
|
|
7551
|
+
│ ║ Lutaml::Model Values ║ ║ TOML Values ║ │
|
|
7552
|
+
│ ╠════════════════════════╣ ╠════════════════════════╣ │
|
|
7553
|
+
│ ║ ║ mapping ║ ║ │
|
|
7554
|
+
│ ║ "empty" ║◀──────────────────▶║ "empty" ║ │
|
|
7555
|
+
│ ║ (empty string, []) ║ to empty ║ (empty string, []) ║ │
|
|
7556
|
+
│ ║ ║ ║ ║ │
|
|
7557
|
+
│ ╟────────────────────────╢ ╟────────────────────────╢ │
|
|
7558
|
+
│ ║ ║ mapping ║ ║ │
|
|
7559
|
+
│ ║ "non-existent" ║◀──────────────────▶║ ║ │
|
|
7560
|
+
│ ║ (nil) ║ to undefined ║ "undefined" ║ │
|
|
7561
|
+
│ ║ ║ ║ (key omitted) ║ │
|
|
7562
|
+
│ ╟────────────────────────╢ ║ ║ │
|
|
7563
|
+
│ ║ ║ mapping ║ ║ │
|
|
7564
|
+
│ ║ "undefined" ║─────(one-way)─────▶║ TOML does not ║ │
|
|
7565
|
+
│ ║ (uninitialized) ║ to undefined ║ support NULL ║ │
|
|
7566
|
+
│ ║ ║ ║ ║ │
|
|
7567
|
+
│ ╚════════════════════════╝ ╚════════════════════════╝ │
|
|
7568
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
7569
|
+
----
|
|
7570
|
+
|
|
7571
|
+
There are the following additional challenges that a developer must take into account of:
|
|
7572
|
+
|
|
7573
|
+
* Single attribute value vs collection attribute value. Different technologies
|
|
7574
|
+
treat single/collection values differently.
|
|
7575
|
+
|
|
7576
|
+
* External schemas and systems that interoperate with serializations from
|
|
7577
|
+
Lutaml::Model. Many schemas and systems adopt "different" conventions for
|
|
7578
|
+
representing missing value semantics (sometimes very awkward ones).
|
|
7579
|
+
|
|
7580
|
+
The solution for the first challenge is to understand the behavior of the
|
|
7581
|
+
different technologies used. The default mappings are described in <<value_representation_in_lutaml-model>>
|
|
7582
|
+
and <<value_representation_in_serialization_formats>>.
|
|
7583
|
+
|
|
7584
|
+
|
|
7585
|
+
[[value_representation_in_lutaml-model]]
|
|
5536
7586
|
==== Value representation in Lutaml::Model
|
|
5537
7587
|
|
|
5538
7588
|
The following table summarizes the behavior of the Lutaml::Model
|
|
5539
7589
|
in regards of the "missing values" family.
|
|
5540
7590
|
|
|
5541
|
-
|
|
7591
|
+
.Handling of missing value types in Lutaml::Model data types
|
|
7592
|
+
[cols="1,1,2,2"]
|
|
5542
7593
|
|===
|
|
5543
7594
|
| LutaML value type | Cardinality (1 or n) | Missing value type | Ruby value
|
|
5544
7595
|
|
|
@@ -5556,55 +7607,49 @@ in regards of the "missing values" family.
|
|
|
5556
7607
|
|
|
5557
7608
|
.3+| `:integer`
|
|
5558
7609
|
.3+| single
|
|
5559
|
-
| empty value |
|
|
7610
|
+
| empty value | N/A
|
|
5560
7611
|
| non-existent value | `nil` (NilClass)
|
|
5561
7612
|
| undefined value | No assigned value
|
|
5562
7613
|
|
|
5563
7614
|
.3+| `:float`
|
|
5564
7615
|
.3+| single
|
|
5565
|
-
| empty value |
|
|
7616
|
+
| empty value | N/A
|
|
5566
7617
|
| non-existent value | `nil` (NilClass)
|
|
5567
7618
|
| undefined value | No assigned value
|
|
5568
7619
|
|
|
5569
7620
|
.3+| `:boolean`
|
|
5570
7621
|
.3+| single
|
|
5571
|
-
| empty value |
|
|
5572
|
-
| non-existent value | `nil` (NilClass)
|
|
5573
|
-
| undefined value | No assigned value
|
|
5574
|
-
|
|
5575
|
-
.3+| `:date`
|
|
5576
|
-
.3+| single
|
|
5577
|
-
| empty value | `Date.new(0)` (Date)
|
|
7622
|
+
| empty value | N/A
|
|
5578
7623
|
| non-existent value | `nil` (NilClass)
|
|
5579
7624
|
| undefined value | No assigned value
|
|
5580
7625
|
|
|
5581
7626
|
.3+| `:date`
|
|
5582
7627
|
.3+| single
|
|
5583
|
-
| empty value |
|
|
7628
|
+
| empty value | N/A
|
|
5584
7629
|
| non-existent value | `nil` (NilClass)
|
|
5585
7630
|
| undefined value | No assigned value
|
|
5586
7631
|
|
|
5587
7632
|
.3+| `:time_without_date`
|
|
5588
7633
|
.3+| single
|
|
5589
|
-
| empty value |
|
|
7634
|
+
| empty value | N/A
|
|
5590
7635
|
| non-existent value | `nil` (NilClass)
|
|
5591
7636
|
| undefined value | No assigned value
|
|
5592
7637
|
|
|
5593
7638
|
.3+| `:date_time`
|
|
5594
7639
|
.3+| single
|
|
5595
|
-
| empty value |
|
|
7640
|
+
| empty value | N/A
|
|
5596
7641
|
| non-existent value | `nil` (NilClass)
|
|
5597
7642
|
| undefined value | No assigned value
|
|
5598
7643
|
|
|
5599
7644
|
.3+| `:time`
|
|
5600
7645
|
.3+| single
|
|
5601
|
-
| empty value |
|
|
7646
|
+
| empty value | N/A
|
|
5602
7647
|
| non-existent value | `nil` (NilClass)
|
|
5603
7648
|
| undefined value | No assigned value
|
|
5604
7649
|
|
|
5605
7650
|
.3+| `:decimal`
|
|
5606
7651
|
.3+| single
|
|
5607
|
-
| empty value |
|
|
7652
|
+
| empty value | N/A
|
|
5608
7653
|
| non-existent value | `nil` (NilClass)
|
|
5609
7654
|
| undefined value | No assigned value
|
|
5610
7655
|
|
|
@@ -5617,6 +7662,7 @@ in regards of the "missing values" family.
|
|
|
5617
7662
|
|===
|
|
5618
7663
|
|
|
5619
7664
|
|
|
7665
|
+
[[value_representation_in_serialization_formats]]
|
|
5620
7666
|
==== Value representation in serialization formats
|
|
5621
7667
|
|
|
5622
7668
|
Every serialization format uses a different information model to represent
|
|
@@ -5674,6 +7720,8 @@ while others only support a subset of them.
|
|
|
5674
7720
|
|
|
5675
7721
|
==== Missing value mapping
|
|
5676
7722
|
|
|
7723
|
+
===== General
|
|
7724
|
+
|
|
5677
7725
|
Lutaml::Model provides a comprehensive way to handle the missing values family
|
|
5678
7726
|
across different serialization formats.
|
|
5679
7727
|
|
|
@@ -5689,7 +7737,8 @@ A hash of key-value pairs that determines the mapping of a missing value at the
|
|
|
5689
7737
|
The key is the missing value type in the serialization format, and the value is
|
|
5690
7738
|
the missing value type in the LutaML Model.
|
|
5691
7739
|
+
|
|
5692
|
-
NOTE: In other words, used when converting the serialized format into a
|
|
7740
|
+
NOTE: In other words, used when converting the serialized format into a
|
|
7741
|
+
Lutaml::Model Ruby object.
|
|
5693
7742
|
|
|
5694
7743
|
`to` pairs::
|
|
5695
7744
|
A hash of key-value pairs that determines the mapping of a LutaML Model ("to") missing
|
|
@@ -5697,7 +7746,8 @@ to a missing value choice at the serialization format where this mapping applies
|
|
|
5697
7746
|
The key is the missing value type in the LutaML Model, and the value is the
|
|
5698
7747
|
missing value type in the serialization format.
|
|
5699
7748
|
+
|
|
5700
|
-
NOTE: In other words, used when converting Ruby
|
|
7749
|
+
NOTE: In other words, used when converting a Lutaml::Model Ruby object into the
|
|
7750
|
+
serialized format.
|
|
5701
7751
|
|
|
5702
7752
|
Syntax:
|
|
5703
7753
|
|
|
@@ -5705,101 +7755,616 @@ Syntax:
|
|
|
5705
7755
|
----
|
|
5706
7756
|
{map_command} 'format-key', to: :attribute_name, value_map: { <1>
|
|
5707
7757
|
from: {
|
|
5708
|
-
{format-missing-value-
|
|
5709
|
-
{format-missing-value-
|
|
5710
|
-
{format-missing-value-
|
|
7758
|
+
{format-missing-value-n}: {model-missing-value-n}, <2>
|
|
7759
|
+
{format-missing-value-m}: {model-missing-value-m},
|
|
7760
|
+
{format-missing-value-o}: {model-missing-value-o}
|
|
5711
7761
|
},
|
|
5712
7762
|
to: {
|
|
5713
|
-
{model-missing-value-
|
|
5714
|
-
{model-missing-value-
|
|
5715
|
-
{model-missing-value-
|
|
7763
|
+
{model-missing-value-n}: {format-missing-value-n}, <3>
|
|
7764
|
+
{model-missing-value-m}: {format-missing-value-m},
|
|
7765
|
+
{model-missing-value-o}: {format-missing-value-o}
|
|
7766
|
+
}
|
|
7767
|
+
}
|
|
7768
|
+
----
|
|
7769
|
+
<1> The `{map_command}` is a mapping rule with the actual command depending on
|
|
7770
|
+
the serialization format.
|
|
7771
|
+
<2> In the `from` mapping, the keys are the missing value types in the
|
|
7772
|
+
serialization format.
|
|
7773
|
+
<3> In the `to` mapping, the keys are the missing value types in the LutaML
|
|
7774
|
+
Model.
|
|
7775
|
+
|
|
7776
|
+
The missing value type mapping differs per serialization format,
|
|
7777
|
+
as serialization formats may not fully support all missing value types.
|
|
7778
|
+
|
|
7779
|
+
The availability of `from` and `to` keys and values depend on the types of
|
|
7780
|
+
missing values supported by that particular serialization format.
|
|
7781
|
+
|
|
7782
|
+
The available values for `from` and `to` for serialization formats
|
|
7783
|
+
are presented below, where the allowed values are to be used in the direction
|
|
7784
|
+
of the format. That means if the format supports `:empty`, it can be used
|
|
7785
|
+
as a key in `from:` direction, and the value in the `to:` direction (see `{format-missing-value-n}`) in the syntax.
|
|
7786
|
+
|
|
7787
|
+
.Available missing value types in different mapping commands
|
|
7788
|
+
[cols="a,2a"]
|
|
7789
|
+
|===
|
|
7790
|
+
| Map command | Missing value types available (key in `from:` direction, value in the `to:` direction)
|
|
7791
|
+
|
|
7792
|
+
| XML element `map_element` | `:empty`, `:omitted`, `:nil`
|
|
7793
|
+
| XML attribute `map_attribute` | `:empty`, `:omitted`
|
|
7794
|
+
| Hash `map`, `map_content` | `:empty`, `:omitted`, `:nil`
|
|
7795
|
+
| JSON `map`, `map_content` | `:empty`, `:omitted`, `:nil`
|
|
7796
|
+
| YAML `map`, `map_content` | `:empty`, `:omitted`, `:nil`
|
|
7797
|
+
| TOML `map`, `map_content` | `:empty`, `:omitted`
|
|
7798
|
+
|
|
7799
|
+
|===
|
|
7800
|
+
|
|
7801
|
+
|
|
7802
|
+
[example]
|
|
7803
|
+
For instance, TOML does not support the notion of "null" and therefore the
|
|
7804
|
+
missing value type of `nil` cannot be used; therefore in a `from:`
|
|
7805
|
+
value map, it is not possible to indicate `nil: {model-missing-value}`.
|
|
7806
|
+
|
|
7807
|
+
[example]
|
|
7808
|
+
====
|
|
7809
|
+
In an XML mapping block, it is possible to do the following.
|
|
7810
|
+
|
|
7811
|
+
[source,ruby]
|
|
7812
|
+
----
|
|
7813
|
+
xml do
|
|
7814
|
+
map_element 'status', to: :status, value_map: {
|
|
7815
|
+
from: { empty: :nil, omitted: :omitted, nil: :nil },
|
|
7816
|
+
to: { empty: :nil, omitted: :omitted, nil: :nil }
|
|
7817
|
+
}
|
|
7818
|
+
end
|
|
7819
|
+
----
|
|
7820
|
+
====
|
|
7821
|
+
|
|
7822
|
+
Each serialization format has specific behavior when handling values
|
|
7823
|
+
such as `empty`, `omitted`, and `nil`.
|
|
7824
|
+
|
|
7825
|
+
Users can specify the mapping for both `from` and `to` values using the
|
|
7826
|
+
`value_map` option in the attribute definition.
|
|
7827
|
+
|
|
7828
|
+
* The keys that can be used in the `from` and `to` mappings are `empty`,
|
|
7829
|
+
`omitted`, and `nil`.
|
|
7830
|
+
|
|
7831
|
+
* The values in the mappings can also be `empty`, `omitted`, and `nil`.
|
|
7832
|
+
|
|
7833
|
+
NOTE: Since `nil` is not supported in TOML, so mappings like `nil:
|
|
7834
|
+
{any_option}` or `{any_option}: :nil` will not work in TOML.
|
|
7835
|
+
|
|
7836
|
+
NOTE: In a collection attribute, the values of `value_map` also depend on the
|
|
7837
|
+
`initialize_empty` setting, where an omitted value in the serialization format can still lead to a `nil`
|
|
7838
|
+
or an empty array `[]` at the attribute-level (instead of the mapping-level).
|
|
7839
|
+
|
|
7840
|
+
|
|
7841
|
+
|
|
7842
|
+
===== Default value maps for serialization formats
|
|
7843
|
+
|
|
7844
|
+
The table below describes the default `value_map` configurations for supported
|
|
7845
|
+
serialization formats.
|
|
7846
|
+
|
|
7847
|
+
// TODO: Find a place to write this
|
|
7848
|
+
// XML Handling::
|
|
7849
|
+
// - `<status>new</status>` will be treated as `["new"]`.
|
|
7850
|
+
// - `<status>new</status><status>assigned</status>` will be treated as `["new", "assigned"]`.
|
|
7851
|
+
|
|
7852
|
+
====== Default value map for XML element (single attribute)
|
|
7853
|
+
|
|
7854
|
+
[source,ruby]
|
|
7855
|
+
----
|
|
7856
|
+
attribute :attr, :string
|
|
7857
|
+
|
|
7858
|
+
xml do
|
|
7859
|
+
map_element 'key', to: :attr, value_map: {
|
|
7860
|
+
from: { empty: :nil, omitted: :omitted, nil: :nil },
|
|
7861
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
7862
|
+
}
|
|
7863
|
+
end
|
|
7864
|
+
----
|
|
7865
|
+
|
|
7866
|
+
.Default missing value mapping configuration for single attributes in XML elements
|
|
7867
|
+
[cols="a,a,a,a"]
|
|
7868
|
+
|===
|
|
7869
|
+
|
|
7870
|
+
h| Direction h| Map rule h| XML source h| Model target
|
|
7871
|
+
|
|
7872
|
+
.3+|`from:`
|
|
7873
|
+
| `empty: :nil`
|
|
7874
|
+
| blank XML element (`<status/>`)
|
|
7875
|
+
| `nil`
|
|
7876
|
+
|
|
7877
|
+
| `omitted: :omitted`
|
|
7878
|
+
| absent XML element
|
|
7879
|
+
| omitted from the model
|
|
7880
|
+
|
|
7881
|
+
| `nil: :nil`
|
|
7882
|
+
| blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
|
|
7883
|
+
| `nil` value in the model
|
|
7884
|
+
|
|
7885
|
+
|
|
7886
|
+
h| Direction h| Map rule h| Model source h| XML target
|
|
7887
|
+
|
|
7888
|
+
.3+|`to:`
|
|
7889
|
+
| `empty: :empty`
|
|
7890
|
+
| empty string (`""`)
|
|
7891
|
+
| blank XML element
|
|
7892
|
+
|
|
7893
|
+
| `omitted: :omitted`
|
|
7894
|
+
| omitted in the model
|
|
7895
|
+
| XML element not rendered
|
|
7896
|
+
|
|
7897
|
+
| `nil: :nil`
|
|
7898
|
+
| `nil` value in the model
|
|
7899
|
+
| blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
|
|
7900
|
+
|
|
7901
|
+
|===
|
|
7902
|
+
|
|
7903
|
+
====== Default value map for XML element (collection attribute)
|
|
7904
|
+
|
|
7905
|
+
[source,ruby]
|
|
7906
|
+
----
|
|
7907
|
+
attribute :attr, :string, collection: true
|
|
7908
|
+
|
|
7909
|
+
xml do
|
|
7910
|
+
map_element 'key', to: :attr, value_map: {
|
|
7911
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
|
7912
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
7913
|
+
}
|
|
7914
|
+
end
|
|
7915
|
+
----
|
|
7916
|
+
|
|
7917
|
+
.Default missing value mapping configuration for collection attributes in XML elements
|
|
7918
|
+
[cols="a,a,a,a"]
|
|
7919
|
+
|===
|
|
7920
|
+
|
|
7921
|
+
h| Direction h| Map rule h| XML source h| Model target
|
|
7922
|
+
|
|
7923
|
+
.3+|`from:`
|
|
7924
|
+
| `empty: :nil`
|
|
7925
|
+
| blank XML element (`<status/>`)
|
|
7926
|
+
| empty array (`[]`)
|
|
7927
|
+
|
|
7928
|
+
| `omitted: :omitted`
|
|
7929
|
+
| absent XML element
|
|
7930
|
+
| omitted from the model
|
|
7931
|
+
|
|
7932
|
+
| `nil: :nil`
|
|
7933
|
+
| blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
|
|
7934
|
+
| `nil` value in the model
|
|
7935
|
+
|
|
7936
|
+
|
|
7937
|
+
h| Direction h| Map rule h| Model source h| XML target
|
|
7938
|
+
|
|
7939
|
+
.3+|`to:`
|
|
7940
|
+
| `empty: :empty`
|
|
7941
|
+
| empty array (`[]`)
|
|
7942
|
+
| blank XML element
|
|
7943
|
+
|
|
7944
|
+
| `omitted: :omitted`
|
|
7945
|
+
| omitted in the model
|
|
7946
|
+
| XML element not rendered
|
|
7947
|
+
|
|
7948
|
+
| `nil: :nil`
|
|
7949
|
+
| `nil` value in the model
|
|
7950
|
+
| blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
|
|
7951
|
+
|
|
7952
|
+
|===
|
|
7953
|
+
|
|
7954
|
+
====== Default value map for XML attribute (single attribute)
|
|
7955
|
+
|
|
7956
|
+
[source,ruby]
|
|
7957
|
+
----
|
|
7958
|
+
attribute :attr, :string
|
|
7959
|
+
|
|
7960
|
+
xml do
|
|
7961
|
+
map_attribute 'attr_name', to: :attr, value_map: {
|
|
7962
|
+
from: { empty: :nil, omitted: :omitted },
|
|
7963
|
+
to: { empty: :empty, nil: :empty, omitted: :omitted }
|
|
7964
|
+
}
|
|
7965
|
+
end
|
|
7966
|
+
----
|
|
7967
|
+
|
|
7968
|
+
.Default missing value mapping configuration for single attributes in XML attributes
|
|
7969
|
+
[cols="a,a,a,a"]
|
|
7970
|
+
|===
|
|
7971
|
+
|
|
7972
|
+
h| Direction h| Map rule h| XML source h| Model target
|
|
7973
|
+
|
|
7974
|
+
.2+|`from:` (source only supports `empty` and `omitted`)
|
|
7975
|
+
| `empty: :nil`
|
|
7976
|
+
| blank XML attribute (`status=""`)
|
|
7977
|
+
| `nil`
|
|
7978
|
+
|
|
7979
|
+
| `omitted: :omitted`
|
|
7980
|
+
| absent XML attribute
|
|
7981
|
+
| omitted from the model
|
|
7982
|
+
|
|
7983
|
+
h| Direction h| Map rule h| Model source h| XML target
|
|
7984
|
+
|
|
7985
|
+
.3+|`to:` (target only accepts `empty` and `omitted`)
|
|
7986
|
+
| `empty: :empty`
|
|
7987
|
+
| empty string (`""`)
|
|
7988
|
+
| blank XML attribute (`status=""`)
|
|
7989
|
+
|
|
7990
|
+
| `omitted: :omitted`
|
|
7991
|
+
| omitted in the model
|
|
7992
|
+
| XML attribute not rendered
|
|
7993
|
+
|
|
7994
|
+
| `nil: :empty`
|
|
7995
|
+
| `nil`
|
|
7996
|
+
| blank XML attribute (`status=""`)
|
|
7997
|
+
|
|
7998
|
+
|===
|
|
7999
|
+
|
|
8000
|
+
====== Default value map for XML attribute (collection attribute)
|
|
8001
|
+
|
|
8002
|
+
[source,ruby]
|
|
8003
|
+
----
|
|
8004
|
+
attribute :attr, :string, collection: true
|
|
8005
|
+
|
|
8006
|
+
xml do
|
|
8007
|
+
map_attribute 'attr_name', to: :attr, value_map: {
|
|
8008
|
+
from: { empty: :empty, omitted: :omitted },
|
|
8009
|
+
to: { empty: :empty, nil: :omitted, omitted: :omitted }
|
|
8010
|
+
}
|
|
8011
|
+
end
|
|
8012
|
+
----
|
|
8013
|
+
|
|
8014
|
+
.Default missing value mapping configuration for collection attributes in XML attributes
|
|
8015
|
+
[cols="a,a,a,a"]
|
|
8016
|
+
|===
|
|
8017
|
+
|
|
8018
|
+
h| Direction h| Map rule h| XML source h| Model target
|
|
8019
|
+
|
|
8020
|
+
.2+|`from:` (source only supports `empty` and `omitted`)
|
|
8021
|
+
| `empty: :empty`
|
|
8022
|
+
| blank XML attribute (`status=""`)
|
|
8023
|
+
| empty array (`[]`)
|
|
8024
|
+
|
|
8025
|
+
| `omitted: :omitted`
|
|
8026
|
+
| absent XML attribute
|
|
8027
|
+
| omitted from the model
|
|
8028
|
+
|
|
8029
|
+
h| Direction h| Map rule h| Model source h| XML target
|
|
8030
|
+
|
|
8031
|
+
.3+|`to:` (target only accepts `empty` and `omitted`)
|
|
8032
|
+
| `empty: :empty`
|
|
8033
|
+
| empty array (`[]`)
|
|
8034
|
+
| blank XML attribute (`status=""`)
|
|
8035
|
+
|
|
8036
|
+
| `omitted: :omitted`
|
|
8037
|
+
| omitted in the model
|
|
8038
|
+
| XML attribute not rendered
|
|
8039
|
+
|
|
8040
|
+
| `nil: :omitted`
|
|
8041
|
+
| `nil`
|
|
8042
|
+
| XML attribute not rendered
|
|
8043
|
+
|
|
8044
|
+
|===
|
|
8045
|
+
|
|
8046
|
+
|
|
8047
|
+
|
|
8048
|
+
====== Default value map for YAML (single attribute)
|
|
8049
|
+
|
|
8050
|
+
[source,ruby]
|
|
8051
|
+
----
|
|
8052
|
+
attribute :attr, :string
|
|
8053
|
+
|
|
8054
|
+
yaml do
|
|
8055
|
+
map 'key', to: :attr, value_map: {
|
|
8056
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
|
8057
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
8058
|
+
}
|
|
8059
|
+
end
|
|
8060
|
+
----
|
|
8061
|
+
|
|
8062
|
+
.Default missing value mapping configuration for single attributes in YAML
|
|
8063
|
+
[cols="a,a,a,a"]
|
|
8064
|
+
|===
|
|
8065
|
+
|
|
8066
|
+
h| Direction h| Map rule h| XML source h| Model target
|
|
8067
|
+
|
|
8068
|
+
.3+|`from:`
|
|
8069
|
+
| `empty: :empty`
|
|
8070
|
+
| empty string in YAML (`status:` or `status: ""`)
|
|
8071
|
+
| empty string (`""`)
|
|
8072
|
+
|
|
8073
|
+
| `omitted: :omitted`
|
|
8074
|
+
| absent YAML key
|
|
8075
|
+
| omitted from the model
|
|
8076
|
+
|
|
8077
|
+
| `nil: :nil`
|
|
8078
|
+
| YAML `null`
|
|
8079
|
+
| `nil`
|
|
8080
|
+
|
|
8081
|
+
h| Direction h| Map rule h| Model source h| XML target
|
|
8082
|
+
|
|
8083
|
+
.3+|`to:`
|
|
8084
|
+
| `empty: :empty`
|
|
8085
|
+
| empty string (`""`)
|
|
8086
|
+
| empty string in YAML (`status:`)
|
|
8087
|
+
|
|
8088
|
+
| `omitted: :omitted`
|
|
8089
|
+
| omitted in the model
|
|
8090
|
+
| YAML key omitted
|
|
8091
|
+
|
|
8092
|
+
| `nil: :nil`
|
|
8093
|
+
| `nil`
|
|
8094
|
+
| YAML `null`
|
|
8095
|
+
|
|
8096
|
+
|===
|
|
8097
|
+
|
|
8098
|
+
NOTE: In order to treat a YAML value like `status: ''` to `nil`, the mapping of
|
|
8099
|
+
`value_map: { from: { empty: :nil } }` can be applied.
|
|
8100
|
+
|
|
8101
|
+
|
|
8102
|
+
====== Default value map for YAML (collection attribute)
|
|
8103
|
+
|
|
8104
|
+
[source,ruby]
|
|
8105
|
+
----
|
|
8106
|
+
attribute :attr, :string, collection: true
|
|
8107
|
+
|
|
8108
|
+
yaml do
|
|
8109
|
+
map 'key', to: :attr, value_map: {
|
|
8110
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
|
8111
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
8112
|
+
}
|
|
8113
|
+
end
|
|
8114
|
+
----
|
|
8115
|
+
|
|
8116
|
+
.Default missing value mapping configuration for collection attributes in YAML
|
|
8117
|
+
[cols="a,a,a,a"]
|
|
8118
|
+
|===
|
|
8119
|
+
|
|
8120
|
+
h| Direction h| Map rule h| YAML source h| Model target
|
|
8121
|
+
|
|
8122
|
+
.3+|`from:`
|
|
8123
|
+
| `empty: :empty`
|
|
8124
|
+
| empty YAML array (`status:` or `status: []`)
|
|
8125
|
+
| empty array (`[]`)
|
|
8126
|
+
|
|
8127
|
+
| `omitted: :omitted`
|
|
8128
|
+
| absent YAML key
|
|
8129
|
+
| omitted from the model
|
|
8130
|
+
|
|
8131
|
+
| `nil: :nil`
|
|
8132
|
+
| YAML `null`
|
|
8133
|
+
| `nil`
|
|
8134
|
+
|
|
8135
|
+
h| Direction h| Map rule h| Model source h| YAML target
|
|
8136
|
+
|
|
8137
|
+
.3+|`to:`
|
|
8138
|
+
| `empty: :empty`
|
|
8139
|
+
| empty array (`[]`)
|
|
8140
|
+
| empty YAML array (`status: []`)
|
|
8141
|
+
|
|
8142
|
+
| `omitted: :omitted`
|
|
8143
|
+
| omitted in the model
|
|
8144
|
+
| YAML key omitted
|
|
8145
|
+
|
|
8146
|
+
| `nil: :nil`
|
|
8147
|
+
| `nil`
|
|
8148
|
+
| YAML `null`
|
|
8149
|
+
|
|
8150
|
+
|===
|
|
8151
|
+
|
|
8152
|
+
NOTE: If the YAML key for the collection attribute is omitted, it will be treated
|
|
8153
|
+
as `nil` or an empty array depending on the `initialize_empty` setting.
|
|
8154
|
+
|
|
8155
|
+
|
|
8156
|
+
====== Default value map for JSON (single attribute)
|
|
8157
|
+
|
|
8158
|
+
[source,ruby]
|
|
8159
|
+
----
|
|
8160
|
+
attribute :attr, :string
|
|
8161
|
+
|
|
8162
|
+
json do
|
|
8163
|
+
map 'key', to: :attr, value_map: {
|
|
8164
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
|
8165
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
8166
|
+
}
|
|
8167
|
+
end
|
|
8168
|
+
----
|
|
8169
|
+
|
|
8170
|
+
.Default missing value mapping configuration for single attributes in JSON
|
|
8171
|
+
[cols="a,a,a,a"]
|
|
8172
|
+
|===
|
|
8173
|
+
|
|
8174
|
+
h| Direction h| Map rule h| JSON source h| Model target
|
|
8175
|
+
|
|
8176
|
+
.3+|`from:`
|
|
8177
|
+
| `empty: :empty`
|
|
8178
|
+
| empty string in JSON (`"status" : ""`)
|
|
8179
|
+
| empty string (`""`)
|
|
8180
|
+
|
|
8181
|
+
| `omitted: :omitted`
|
|
8182
|
+
| absent JSON key
|
|
8183
|
+
| omitted from the model
|
|
8184
|
+
|
|
8185
|
+
| `nil: :nil`
|
|
8186
|
+
| JSON `null`
|
|
8187
|
+
| `nil`
|
|
8188
|
+
|
|
8189
|
+
h| Direction h| Map rule h| Model source h| JSON target
|
|
8190
|
+
|
|
8191
|
+
.3+|`to:`
|
|
8192
|
+
| `empty: :empty`
|
|
8193
|
+
| empty string (`""`)
|
|
8194
|
+
| empty string in JSON (`"status" : ""`)
|
|
8195
|
+
|
|
8196
|
+
| `omitted: :omitted`
|
|
8197
|
+
| omitted in the model
|
|
8198
|
+
| JSON key omitted
|
|
8199
|
+
|
|
8200
|
+
| `nil: :nil`
|
|
8201
|
+
| `nil`
|
|
8202
|
+
| JSON `null`
|
|
8203
|
+
|
|
8204
|
+
|===
|
|
8205
|
+
|
|
8206
|
+
|
|
8207
|
+
|
|
8208
|
+
====== Default value map for JSON (collection attribute)
|
|
8209
|
+
|
|
8210
|
+
[source,ruby]
|
|
8211
|
+
----
|
|
8212
|
+
attribute :attr, :string, collection: true
|
|
8213
|
+
|
|
8214
|
+
json do
|
|
8215
|
+
map 'key', to: :attr, value_map: {
|
|
8216
|
+
from: { empty: :empty, omitted: :omitted, nil: :nil },
|
|
8217
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
5716
8218
|
}
|
|
5717
|
-
|
|
8219
|
+
end
|
|
5718
8220
|
----
|
|
5719
|
-
<1> The `{map_command}` is a mapping rule with the actual command depending on
|
|
5720
|
-
the serialization format.
|
|
5721
|
-
<2> In the `from` mapping, the keys are the missing value types in the
|
|
5722
|
-
serialization format.
|
|
5723
|
-
<3> In the `to` mapping, the keys are the missing value types in the LutaML
|
|
5724
|
-
Model.
|
|
5725
8221
|
|
|
5726
|
-
|
|
5727
|
-
|
|
8222
|
+
.Default missing value mapping configuration for collection attributes in JSON
|
|
8223
|
+
[cols="a,a,a,a"]
|
|
8224
|
+
|===
|
|
5728
8225
|
|
|
5729
|
-
|
|
5730
|
-
For instance, TOML does not support the notion of "null" and therefore the
|
|
5731
|
-
missing value type of `nil` cannot be used; therefore in a `from:`
|
|
5732
|
-
value map, it is not possible to indicate `nil: {model-missing-value}`.
|
|
8226
|
+
h| Direction h| Map rule h| JSON source h| Model target
|
|
5733
8227
|
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
8228
|
+
.3+|`from:`
|
|
8229
|
+
| `empty: :empty`
|
|
8230
|
+
| empty JSON array (`"status": []`)
|
|
8231
|
+
| empty array (`[]`)
|
|
8232
|
+
|
|
8233
|
+
| `omitted: :omitted`
|
|
8234
|
+
| absent JSON key
|
|
8235
|
+
| omitted from the model
|
|
8236
|
+
|
|
8237
|
+
| `nil: :nil`
|
|
8238
|
+
| JSON `null`
|
|
8239
|
+
| `nil`
|
|
8240
|
+
|
|
8241
|
+
h| Direction h| Map rule h| Model source h| JSON target
|
|
8242
|
+
|
|
8243
|
+
.3+|`to:`
|
|
8244
|
+
| `empty: :empty`
|
|
8245
|
+
| empty array (`[]`)
|
|
8246
|
+
| empty JSON array (`"status": []`)
|
|
8247
|
+
|
|
8248
|
+
| `omitted: :omitted`
|
|
8249
|
+
| omitted in the model
|
|
8250
|
+
| JSON key omitted
|
|
8251
|
+
|
|
8252
|
+
| `nil: :nil`
|
|
8253
|
+
| `nil`
|
|
8254
|
+
| JSON `null`
|
|
8255
|
+
|
|
8256
|
+
|===
|
|
8257
|
+
|
|
8258
|
+
====== Default value map for TOML (single attribute)
|
|
8259
|
+
|
|
8260
|
+
TOML does not support the concept of `nil` and therefore the mapping of `from:`
|
|
8261
|
+
direction with `nil` to will not work in TOML.
|
|
8262
|
+
|
|
8263
|
+
The `nil` mapping is only supported in the `to:` direction (model to TOML).
|
|
5737
8264
|
|
|
5738
8265
|
[source,ruby]
|
|
5739
8266
|
----
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
8267
|
+
attribute :attr, :string
|
|
8268
|
+
|
|
8269
|
+
toml do
|
|
8270
|
+
map 'key', to: :attr, value_map: {
|
|
8271
|
+
from: { empty: :empty, omitted: :omitted },
|
|
8272
|
+
to: { empty: :empty, omitted: :omitted, nil: :omitted }
|
|
5744
8273
|
}
|
|
5745
8274
|
end
|
|
5746
8275
|
----
|
|
5747
|
-
====
|
|
5748
8276
|
|
|
5749
|
-
|
|
5750
|
-
|
|
8277
|
+
.Default missing value mapping configuration for single attributes in TOML
|
|
8278
|
+
[cols="a,a,a,a"]
|
|
8279
|
+
|===
|
|
5751
8280
|
|
|
5752
|
-
|
|
5753
|
-
`value_map` option in the attribute definition.
|
|
8281
|
+
h| Direction h| Map rule h| TOML source h| Model target
|
|
5754
8282
|
|
|
5755
|
-
|
|
5756
|
-
`
|
|
8283
|
+
.2+|`from:` (source only supports `empty` and `omitted`)
|
|
8284
|
+
| `empty: :empty`
|
|
8285
|
+
| empty string in TOML (`[status]` with no value)
|
|
8286
|
+
| empty string (`""`)
|
|
5757
8287
|
|
|
5758
|
-
|
|
8288
|
+
| `omitted: :omitted`
|
|
8289
|
+
| absent TOML key
|
|
8290
|
+
| omitted from the model
|
|
5759
8291
|
|
|
5760
|
-
|
|
5761
|
-
{any_option}` or `{any_option}: :nil` will not work in TOML.
|
|
8292
|
+
h| Direction h| Map rule h| Model source h| TOML target
|
|
5762
8293
|
|
|
5763
|
-
|
|
5764
|
-
|
|
8294
|
+
.3+|`to:` (source only supports `empty` and `omitted`)
|
|
8295
|
+
| `empty: :empty`
|
|
8296
|
+
| empty string (`""`)
|
|
8297
|
+
| empty string in TOML (`[status]` with no value)
|
|
8298
|
+
|
|
8299
|
+
| `omitted: :omitted`
|
|
8300
|
+
| omitted in the model
|
|
8301
|
+
| TOML key omitted
|
|
8302
|
+
|
|
8303
|
+
| `nil: :omitted`
|
|
8304
|
+
| `nil`
|
|
8305
|
+
| TOML key omitted
|
|
8306
|
+
|
|
8307
|
+
|===
|
|
8308
|
+
|
|
8309
|
+
|
|
8310
|
+
|
|
8311
|
+
====== Default value map for TOML (collection attribute)
|
|
8312
|
+
|
|
8313
|
+
TOML does not support the concept of `nil` and therefore the mapping of `from:`
|
|
8314
|
+
direction with `nil` to will not work in TOML.
|
|
8315
|
+
|
|
8316
|
+
The `nil` mapping is only supported in the `to:` direction (model to TOML).
|
|
8317
|
+
|
|
8318
|
+
[source,ruby]
|
|
8319
|
+
----
|
|
8320
|
+
attribute :attr, :string, collection: true
|
|
8321
|
+
|
|
8322
|
+
toml do
|
|
8323
|
+
map 'key', to: :attr, value_map: {
|
|
8324
|
+
from: { empty: :empty, omitted: :omitted },
|
|
8325
|
+
to: { empty: :empty, omitted: :omitted, nil: :omitted }
|
|
8326
|
+
}
|
|
8327
|
+
end
|
|
8328
|
+
----
|
|
5765
8329
|
|
|
5766
|
-
.Default missing value mapping configuration for
|
|
5767
|
-
[cols="
|
|
8330
|
+
.Default missing value mapping configuration for collection attributes in TOML
|
|
8331
|
+
[cols="a,a,a,a"]
|
|
5768
8332
|
|===
|
|
5769
|
-
| Serialization format | `from` value | `to` value | Description
|
|
5770
8333
|
|
|
5771
|
-
|
|
5772
|
-
| `empty` | `nil` |
|
|
5773
|
-
* Element: An empty value in the model is treated as an element with attribute `xsi:nil` during serialization.
|
|
5774
|
-
* Attribute: An empty value in the model is treated as an empty string in the attribute.
|
|
8334
|
+
h| Direction h| Map rule h| TOML source h| Model target
|
|
5775
8335
|
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
8336
|
+
.2+|`from:` (source only supports `empty` and `omitted`)
|
|
8337
|
+
| `empty: :empty`
|
|
8338
|
+
| empty TOML array (`[status]` with no value)
|
|
8339
|
+
| empty array (`[]`)
|
|
5779
8340
|
|
|
5780
|
-
| `
|
|
5781
|
-
|
|
5782
|
-
|
|
8341
|
+
| `omitted: :omitted`
|
|
8342
|
+
| absent TOML key
|
|
8343
|
+
| omitted from the model
|
|
5783
8344
|
|
|
5784
|
-
|
|
5785
|
-
| `empty` | `empty` | An empty string (`""`) in the model is deserialized as `empty` (an empty YAML string).
|
|
5786
|
-
| `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the YAML output.
|
|
5787
|
-
| `nil` | `nil` | A `nil` value in the model is serialized as YAML `null`.
|
|
8345
|
+
h| Direction h| Map rule h| Model source h| TOML target
|
|
5788
8346
|
|
|
5789
|
-
.3
|
|
5790
|
-
| `empty
|
|
5791
|
-
|
|
|
5792
|
-
|
|
|
8347
|
+
.3+|`to:` (source only supports `empty` and `omitted`)
|
|
8348
|
+
| `empty: :empty`
|
|
8349
|
+
| empty array (`[]`)
|
|
8350
|
+
| empty TOML array (`[status]` with no value)
|
|
5793
8351
|
|
|
5794
|
-
|
|
5795
|
-
|
|
|
5796
|
-
|
|
|
5797
|
-
|
|
8352
|
+
| `omitted: :omitted`
|
|
8353
|
+
| omitted in the model
|
|
8354
|
+
| TOML key omitted
|
|
8355
|
+
|
|
8356
|
+
| `nil: :omitted`
|
|
8357
|
+
| `nil`
|
|
8358
|
+
| TOML key omitted
|
|
5798
8359
|
|
|
5799
8360
|
|===
|
|
5800
8361
|
|
|
5801
8362
|
|
|
5802
|
-
|
|
8363
|
+
|
|
8364
|
+
===== Replacing missing values type mapping with `value_map`
|
|
8365
|
+
|
|
8366
|
+
The `value_map` option can be defined to meticulously map for each serialization
|
|
8367
|
+
format as follows.
|
|
5803
8368
|
|
|
5804
8369
|
[example]
|
|
5805
8370
|
====
|
|
@@ -5816,7 +8381,7 @@ class ExampleClass < Lutaml::Model::Serializable
|
|
|
5816
8381
|
}
|
|
5817
8382
|
end
|
|
5818
8383
|
|
|
5819
|
-
json | yaml | toml | key_value do
|
|
8384
|
+
hsh | json | yaml | toml | key_value do
|
|
5820
8385
|
map 'status', to: :status, value_map: {
|
|
5821
8386
|
from: { empty: :nil, omitted: :omitted, nil: :nil },
|
|
5822
8387
|
to: { empty: :nil, omitted: :omitted, nil: :nil }
|
|
@@ -5857,57 +8422,6 @@ When defining an attribute with `collection: true`, the attribute will behave as
|
|
|
5857
8422
|
attribute :status, :string, collection: true
|
|
5858
8423
|
----
|
|
5859
8424
|
|
|
5860
|
-
- **XML Handling**:
|
|
5861
|
-
- `<status />` will be treated as `[]` (an empty array).
|
|
5862
|
-
- `<status>new</status>` will be treated as `["new"]`.
|
|
5863
|
-
- `<status>new</status><status>assigned</status>` will be treated as `["new", "assigned"]`.
|
|
5864
|
-
|
|
5865
|
-
- **YAML Handling**:
|
|
5866
|
-
- A value like `status: ''` in YAML will be deserialized as `nil` when `value_map: { from: { empty: :nil } }` is applied.
|
|
5867
|
-
- If the collection is omitted, it will be initialized as `nil` or an empty array depending on the `initialize_empty` setting.
|
|
5868
|
-
|
|
5869
|
-
- **JSON Handling**:
|
|
5870
|
-
- A `status: []` in the serialized output will correspond to an empty array when `value_map: { from: { empty: :empty } }` is applied.
|
|
5871
|
-
|
|
5872
|
-
- **TOML Handling**:
|
|
5873
|
-
- TOML treats empty collections similarly to other formats, but `nil` values are not supported so we can not read or write nil values in TOML.
|
|
5874
|
-
|
|
5875
|
-
.Default missing value mapping configuration for collection attributes to serialization formats
|
|
5876
|
-
[cols="1,1,1,1", options="header"]
|
|
5877
|
-
|===
|
|
5878
|
-
| Serialization format | `from` value | `to` value | Description
|
|
5879
|
-
|
|
5880
|
-
.3+| XML (supports all 3 types)
|
|
5881
|
-
| `empty` | `empty` |
|
|
5882
|
-
* Element: An empty array (`[]`) in the model is treated a blank element (`<x/>`)
|
|
5883
|
-
* Attribute: An empty array (`[]`) in the model is treated as an empty string in the attribute.
|
|
5884
|
-
|
|
5885
|
-
| `omitted` | `omitted` |
|
|
5886
|
-
* Element: If the value is omitted in the model, the element is omitted from the XML output.
|
|
5887
|
-
* Attribute: If the value is omitted in the model, the attribute is omitted from the XML output.
|
|
5888
|
-
|
|
5889
|
-
| `nil` | `nil` |
|
|
5890
|
-
* Element: A `nil` value in the model is serialized as an element with attribute `xsi:nil`.
|
|
5891
|
-
* Attribute: A `nil` value in the model is treated as an empty string in the attribute.
|
|
5892
|
-
|
|
5893
|
-
.3+| YAML (supports all 3 types)
|
|
5894
|
-
| `empty` | `empty` | An empty array (`[]`) in the model is deserialized as `empty` (an empty YAML array).
|
|
5895
|
-
| `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the YAML output.
|
|
5896
|
-
| `nil` | `nil` | A `nil` value in the model is serialized as YAML `null`.
|
|
5897
|
-
|
|
5898
|
-
.3+| JSON (supports all 3 types)
|
|
5899
|
-
| `empty` | `empty` | An empty array (`[]`) in the model is serialized as `empty` (an empty JSON array).
|
|
5900
|
-
| `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the JSON output.
|
|
5901
|
-
| `nil` | `nil` | A `nil` value in the model is serialized as JSON `null`.
|
|
5902
|
-
|
|
5903
|
-
.3+| TOML (does not support `nil`)
|
|
5904
|
-
| `empty` | `empty` | An empty array (`[]`) in the model is serialized as `empty` (an empty TOML array).
|
|
5905
|
-
| `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the TOML output.
|
|
5906
|
-
| `nil` | `omitted` | In TOML, `nil` is represented as `omitted` due to TOML limitations.
|
|
5907
|
-
|
|
5908
|
-
|===
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
8425
|
Here's an example of how you can use the `value_map` with a collection attribute.
|
|
5912
8426
|
|
|
5913
8427
|
.Using `value_map` with a collection attribute
|
|
@@ -5925,12 +8439,19 @@ class ExampleClass < Lutaml::Model::Serializable
|
|
|
5925
8439
|
}
|
|
5926
8440
|
end
|
|
5927
8441
|
|
|
5928
|
-
|
|
8442
|
+
hsh | json | yaml | key_value do
|
|
5929
8443
|
map 'status', to: :status, value_map: {
|
|
5930
8444
|
from: { empty: :nil, omitted: :omitted, nil: :nil },
|
|
5931
8445
|
to: { empty: :nil, omitted: :omitted, nil: :nil }
|
|
5932
8446
|
}
|
|
5933
8447
|
end
|
|
8448
|
+
|
|
8449
|
+
toml do
|
|
8450
|
+
map 'status', to: :status, value_map: {
|
|
8451
|
+
from: { empty: :nil, omitted: :omitted },
|
|
8452
|
+
to: { empty: :nil, omitted: :omitted, nil: :omitted }
|
|
8453
|
+
}
|
|
8454
|
+
end
|
|
5934
8455
|
end
|
|
5935
8456
|
|
|
5936
8457
|
yaml = <<~YAML
|
|
@@ -5943,9 +8464,9 @@ y = ExampleClass.from_yaml(yaml)
|
|
|
5943
8464
|
----
|
|
5944
8465
|
====
|
|
5945
8466
|
|
|
5946
|
-
TODO: Need to improve this example.
|
|
8467
|
+
// TODO: Need to improve this example.
|
|
5947
8468
|
|
|
5948
|
-
==== Specific overrides of value map
|
|
8469
|
+
==== Specific overrides of value map (`render_*` and `treat_*`)
|
|
5949
8470
|
|
|
5950
8471
|
===== General
|
|
5951
8472
|
|
|
@@ -5982,6 +8503,52 @@ missing value types into the model.
|
|
|
5982
8503
|
`{format-value}`::: specifies the missing value type in the serialization format.
|
|
5983
8504
|
`{model-value}`::: specifies the missing value type in the LutaML Model.
|
|
5984
8505
|
|
|
8506
|
+
In effect, the default `value_map` is overriden by the `:render_*` and `:treat_*`
|
|
8507
|
+
directives.
|
|
8508
|
+
|
|
8509
|
+
[example]
|
|
8510
|
+
====
|
|
8511
|
+
Given the default mapping for an XML element, the `:render_*` and `:treat_*`
|
|
8512
|
+
options can be used to selectively override behavior.
|
|
8513
|
+
|
|
8514
|
+
[source,ruby]
|
|
8515
|
+
----
|
|
8516
|
+
xml do
|
|
8517
|
+
map_element 'key', to: :attr, value_map: {
|
|
8518
|
+
from: { empty: :nil, omitted: :omitted, nil: :nil },
|
|
8519
|
+
to: { empty: :empty, omitted: :omitted, nil: :nil }
|
|
8520
|
+
}
|
|
8521
|
+
end
|
|
8522
|
+
----
|
|
8523
|
+
|
|
8524
|
+
By changing to this:
|
|
8525
|
+
|
|
8526
|
+
[source,ruby]
|
|
8527
|
+
----
|
|
8528
|
+
xml do
|
|
8529
|
+
map_element 'key', to: :attr,
|
|
8530
|
+
render_nil: :as_empty, <1>
|
|
8531
|
+
treat_omitted: :as_nil <2>
|
|
8532
|
+
end
|
|
8533
|
+
----
|
|
8534
|
+
<1> This overrides the `to:` direction `nil: :nil` mapping.
|
|
8535
|
+
<2> This overrides the `from:` direction `omitted: :omitted` mapping
|
|
8536
|
+
|
|
8537
|
+
The resulting value map would be:
|
|
8538
|
+
|
|
8539
|
+
[source,ruby]
|
|
8540
|
+
----
|
|
8541
|
+
xml do
|
|
8542
|
+
map_element 'status', to: :status, value_map: {
|
|
8543
|
+
from: { empty: :nil, omitted: :omitted, nil: :empty }, <1>
|
|
8544
|
+
to: { empty: :nil, omitted: :nil, nil: :nil } <2>
|
|
8545
|
+
}
|
|
8546
|
+
end
|
|
8547
|
+
----
|
|
8548
|
+
<1> See that `nil: :nil` is now `nil: :empty`.
|
|
8549
|
+
<2> See that `omitted: :omitted` is now `omitted: :nil`
|
|
8550
|
+
====
|
|
8551
|
+
|
|
5985
8552
|
|
|
5986
8553
|
==== `render_nil`
|
|
5987
8554
|
|
|
@@ -6014,7 +8581,7 @@ end
|
|
|
6014
8581
|
|
|
6015
8582
|
[source,ruby]
|
|
6016
8583
|
----
|
|
6017
|
-
json | yaml | toml do
|
|
8584
|
+
hsh | json | yaml | toml do
|
|
6018
8585
|
map 'key_value_model_attribute_name', to: :name_of_attribute, render_nil: {option}
|
|
6019
8586
|
end
|
|
6020
8587
|
----
|
|
@@ -6153,7 +8720,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
6153
8720
|
map_element 'collection', to: :coll, render_nil: :as_nil
|
|
6154
8721
|
end
|
|
6155
8722
|
|
|
6156
|
-
json | yaml do
|
|
8723
|
+
hsh | json | yaml do
|
|
6157
8724
|
map 'collection', to: :coll, render_nil: :as_nil
|
|
6158
8725
|
end
|
|
6159
8726
|
end
|
|
@@ -6238,7 +8805,7 @@ end
|
|
|
6238
8805
|
|
|
6239
8806
|
[source,ruby]
|
|
6240
8807
|
----
|
|
6241
|
-
json | yaml | toml do
|
|
8808
|
+
hsh | json | yaml | toml do
|
|
6242
8809
|
map 'key_value_model_attribute_name', to: :name_of_attribute, render_empty: {option}
|
|
6243
8810
|
end
|
|
6244
8811
|
----
|
|
@@ -6297,7 +8864,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
6297
8864
|
map_element 'collection', to: :coll, render_empty: :as_nil
|
|
6298
8865
|
end
|
|
6299
8866
|
|
|
6300
|
-
json | yaml do
|
|
8867
|
+
hsh | json | yaml do
|
|
6301
8868
|
map 'collection', to: :coll, render_empty: :as_nil
|
|
6302
8869
|
end
|
|
6303
8870
|
end
|
|
@@ -6356,9 +8923,91 @@ puts SomeModel.new.to_yaml
|
|
|
6356
8923
|
|
|
6357
8924
|
|
|
6358
8925
|
|
|
6359
|
-
==
|
|
8926
|
+
== Schema generation and import
|
|
6360
8927
|
|
|
6361
|
-
===
|
|
8928
|
+
=== Schema generation
|
|
8929
|
+
|
|
8930
|
+
Lutaml::Model provides functionality to generate schema definitions from LutaML models. This allows you to create schemas that can be used for validation or documentation purposes.
|
|
8931
|
+
|
|
8932
|
+
Currently, the following schema formats are supported:
|
|
8933
|
+
|
|
8934
|
+
* JSON Schema (https://json-schema.org/understanding-json-schema/[JSON Schema])
|
|
8935
|
+
* YAML Schema (https://yaml.org/spec/1.2/spec.html[YAML])
|
|
8936
|
+
|
|
8937
|
+
==== JSON Schema generation
|
|
8938
|
+
|
|
8939
|
+
The `Lutaml::Model::Schema.to_json` method generates a JSON Schema from a LutaML model class. The generated schema includes:
|
|
8940
|
+
|
|
8941
|
+
* Properties based on model attributes
|
|
8942
|
+
* Validation constraints (patterns, enumerations, etc.)
|
|
8943
|
+
* Support for polymorphic types
|
|
8944
|
+
* Support for inheritance
|
|
8945
|
+
* Support for choice attributes
|
|
8946
|
+
* Collection constraints
|
|
8947
|
+
|
|
8948
|
+
Example:
|
|
8949
|
+
|
|
8950
|
+
[source,ruby]
|
|
8951
|
+
----
|
|
8952
|
+
class Glaze < Lutaml::Model::Serializable
|
|
8953
|
+
attribute :color, :string
|
|
8954
|
+
attribute :finish, :string
|
|
8955
|
+
end
|
|
8956
|
+
|
|
8957
|
+
class Vase < Lutaml::Model::Serializable
|
|
8958
|
+
attribute :height, :float
|
|
8959
|
+
attribute :diameter, :float
|
|
8960
|
+
attribute :glaze, Glaze
|
|
8961
|
+
attribute :materials, :string, collection: true
|
|
8962
|
+
end
|
|
8963
|
+
|
|
8964
|
+
# Generate JSON schema
|
|
8965
|
+
schema = Lutaml::Model::Schema.to_json(
|
|
8966
|
+
Vase,
|
|
8967
|
+
id: "https://example.com/vase.schema.json",
|
|
8968
|
+
description: "A vase schema",
|
|
8969
|
+
pretty: true
|
|
8970
|
+
)
|
|
8971
|
+
|
|
8972
|
+
# Write to file
|
|
8973
|
+
File.write("vase.schema.json", schema)
|
|
8974
|
+
----
|
|
8975
|
+
|
|
8976
|
+
The generated schema will include definitions for all nested models and their attributes.
|
|
8977
|
+
|
|
8978
|
+
==== YAML Schema generation
|
|
8979
|
+
|
|
8980
|
+
The `Lutaml::Model::Schema.to_yaml` method generates a YAML Schema from a LutaML model class. The generated schema includes the same features as the JSON Schema generation.
|
|
8981
|
+
|
|
8982
|
+
Example:
|
|
8983
|
+
|
|
8984
|
+
[source,ruby]
|
|
8985
|
+
----
|
|
8986
|
+
class Glaze < Lutaml::Model::Serializable
|
|
8987
|
+
attribute :color, :string
|
|
8988
|
+
attribute :finish, :string
|
|
8989
|
+
end
|
|
8990
|
+
|
|
8991
|
+
class Vase < Lutaml::Model::Serializable
|
|
8992
|
+
attribute :height, :float
|
|
8993
|
+
attribute :diameter, :float
|
|
8994
|
+
attribute :glaze, Glaze
|
|
8995
|
+
attribute :materials, :string, collection: true
|
|
8996
|
+
end
|
|
8997
|
+
|
|
8998
|
+
# Generate YAML schema
|
|
8999
|
+
schema = Lutaml::Model::Schema.to_yaml(
|
|
9000
|
+
Vase,
|
|
9001
|
+
id: "http://example.com/schemas/vase",
|
|
9002
|
+
description: "A vase schema",
|
|
9003
|
+
pretty: true
|
|
9004
|
+
)
|
|
9005
|
+
|
|
9006
|
+
# Write to file
|
|
9007
|
+
File.write("vase.schema.yaml", schema)
|
|
9008
|
+
----
|
|
9009
|
+
|
|
9010
|
+
=== Importing data models
|
|
6362
9011
|
|
|
6363
9012
|
Lutaml::Model provides a way to import data models defined from various formats
|
|
6364
9013
|
into the LutaML data modeling system.
|
|
@@ -6883,57 +9532,129 @@ puts output
|
|
|
6883
9532
|
----
|
|
6884
9533
|
====
|
|
6885
9534
|
|
|
6886
|
-
|
|
9535
|
+
|
|
9536
|
+
== Serialization adapters
|
|
6887
9537
|
|
|
6888
9538
|
=== General
|
|
6889
9539
|
|
|
6890
|
-
|
|
6891
|
-
serialization format.
|
|
9540
|
+
The LutaML component that serializes a model into a serialization format is
|
|
9541
|
+
called an adapter. A serialization format may be supported by multiple adapters.
|
|
9542
|
+
|
|
9543
|
+
An adapter typically:
|
|
6892
9544
|
|
|
6893
|
-
|
|
9545
|
+
* supports a specific serialization format
|
|
9546
|
+
* provides a set of methods to serialize and deserialize models and collections of models
|
|
9547
|
+
|
|
9548
|
+
LutaML, out of the box, supports the following serialization formats:
|
|
6894
9549
|
|
|
6895
9550
|
* XML (https://www.w3.org/TR/xmlschema-1/[W3C XML Schema (Second Edition)], XML 1.0)
|
|
6896
9551
|
* YAML (https://yaml.org/[YAML version 1.2])
|
|
6897
9552
|
* JSON (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/[ECMA-404 The JSON Data Interchange Standard], unofficial link: https://www.json.org[JSON])
|
|
6898
9553
|
* TOML (https://toml.io/en[TOML version 1.0])
|
|
6899
9554
|
|
|
6900
|
-
|
|
6901
|
-
|
|
9555
|
+
The adapter interface is also used to support certain transformation of models
|
|
9556
|
+
into an "end format", which is not a serialization format. For example, the
|
|
9557
|
+
`Lutaml::Model::HashAdapter` is used to convert a model into a hash format
|
|
9558
|
+
that is not a serialization format.
|
|
9559
|
+
|
|
9560
|
+
Users can extend LutaML by creating custom adapters for other serialization
|
|
9561
|
+
formats or for other data formats. The
|
|
9562
|
+
link:docs/custom_adapters.adoc[Custom Adapters Guide] describes this process in
|
|
9563
|
+
detail.
|
|
6902
9564
|
|
|
6903
|
-
|
|
9565
|
+
For certain serialization formats, LutaML provides multiple adapters to support
|
|
9566
|
+
different serialization libraries. Please refer to their specific sections for
|
|
9567
|
+
more information.
|
|
9568
|
+
|
|
9569
|
+
|
|
9570
|
+
=== Configuration
|
|
9571
|
+
|
|
9572
|
+
==== General
|
|
9573
|
+
|
|
9574
|
+
It is necessary to configure the adapter to be used for serialization and
|
|
9575
|
+
deserialization for a set of formats that the LutaML models will be transformed
|
|
9576
|
+
into.
|
|
9577
|
+
|
|
9578
|
+
There are two cases where you need to define such configuration:
|
|
9579
|
+
|
|
9580
|
+
* End-user usage of the LutaML models. This is the case where you are
|
|
9581
|
+
using LutaML models in your application and want to serialize them into a
|
|
9582
|
+
specific format. If you are a gem developer that relies on lutaml-model,
|
|
9583
|
+
this case does not apply to you, because the end-user of your gem should
|
|
9584
|
+
determine the adapter configuration.
|
|
9585
|
+
|
|
9586
|
+
* Testing purposes, e.g. RSpec. In order to run tests that involve verifying
|
|
9587
|
+
correctness of serialization, it is necessary to define adapter configuration.
|
|
9588
|
+
|
|
9589
|
+
There are two ways to specify a configuration:
|
|
9590
|
+
|
|
9591
|
+
* by providing a predefined symbol (preferred)
|
|
9592
|
+
* by providing the actual adapter classes
|
|
9593
|
+
|
|
9594
|
+
There is a default configuration for adapters for commonly used formats:
|
|
9595
|
+
|
|
9596
|
+
* YAML: `yaml_adapter_type` is set to `:standard_yaml`
|
|
9597
|
+
* JSON: `json_adapter_type` is set to `:standard_json`
|
|
9598
|
+
* Hash: `hash_adapter_type` is set to `:standard_hash`
|
|
9599
|
+
* XML: not defined
|
|
9600
|
+
* TOML: not defined
|
|
9601
|
+
|
|
9602
|
+
|
|
9603
|
+
==== Configure adapters through symbol choices
|
|
9604
|
+
|
|
9605
|
+
The end-user or a gem developer can copy and paste the following configuration
|
|
9606
|
+
into an early loading file in their application or gem.
|
|
9607
|
+
|
|
9608
|
+
This configuration is preferred over the class choices because it is more
|
|
9609
|
+
concise and does not require any `require` code specific to the internals
|
|
9610
|
+
of the LutaML runtime implementation.
|
|
9611
|
+
|
|
9612
|
+
Syntax:
|
|
6904
9613
|
|
|
6905
9614
|
[source,ruby]
|
|
6906
9615
|
----
|
|
6907
9616
|
require 'lutaml/model'
|
|
6908
|
-
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
|
6909
|
-
require 'lutaml/model/json_adapter/standard_json_adapter'
|
|
6910
|
-
require 'lutaml/model/toml_adapter/toml_rb_adapter'
|
|
6911
|
-
require 'lutaml/model/yaml_adapter/standard_yaml_adapter'
|
|
6912
9617
|
|
|
6913
9618
|
Lutaml::Model::Config.configure do |config|
|
|
6914
|
-
config.
|
|
6915
|
-
config.
|
|
6916
|
-
config.
|
|
6917
|
-
config.
|
|
9619
|
+
config.xml_adapter_type = :nokogiri # can be one of [:nokogiri, :ox, :oga]
|
|
9620
|
+
config.hash_adapter_type = :standard_hash
|
|
9621
|
+
config.yaml_adapter_type = :standard_yaml
|
|
9622
|
+
config.json_adapter_type = :standard_json # can be one of [:standard_json, :multi_json]
|
|
9623
|
+
config.toml_adapter_type = :toml_rb # can be one of [:toml_rb, :tomlib]
|
|
6918
9624
|
end
|
|
6919
9625
|
----
|
|
6920
9626
|
|
|
6921
|
-
You can also provide the adapter type by using symbols like
|
|
6922
9627
|
|
|
9628
|
+
==== Configure adapters through class choices
|
|
9629
|
+
|
|
9630
|
+
The end-uesr or a gem developer can copy and paste the following configuration
|
|
9631
|
+
into an early loading file in their application or gem.
|
|
9632
|
+
|
|
9633
|
+
Only the serialization formats used will require a configuration.
|
|
9634
|
+
|
|
9635
|
+
Syntax:
|
|
9636
|
+
|
|
9637
|
+
[example]
|
|
9638
|
+
====
|
|
6923
9639
|
[source,ruby]
|
|
6924
9640
|
----
|
|
6925
9641
|
require 'lutaml/model'
|
|
9642
|
+
require 'lutaml/model/xml/nokogiri_adapter'
|
|
9643
|
+
require 'lutaml/model/hash_adapter/standard_adapter'
|
|
9644
|
+
require 'lutaml/model/json/standard_adapter'
|
|
9645
|
+
require 'lutaml/model/yaml/standard_adapter'
|
|
9646
|
+
require 'lutaml/model/toml/toml_rb_adapter'
|
|
6926
9647
|
|
|
6927
9648
|
Lutaml::Model::Config.configure do |config|
|
|
6928
|
-
config.
|
|
6929
|
-
config.
|
|
6930
|
-
config.
|
|
6931
|
-
config.
|
|
9649
|
+
config.xml_adapter = Lutaml::Model::Xml::NokogiriAdapter
|
|
9650
|
+
config.hash_adapter = Lutaml::Model::HashAdapter::StandardAdapter
|
|
9651
|
+
config.yaml_adapter = Lutaml::Model::Yaml::StandardAdapter
|
|
9652
|
+
config.json_adapter = Lutaml::Model::Json::StandardAdapter
|
|
9653
|
+
config.toml_adapter = Lutaml::Model::Toml::TomlRbAdapter
|
|
6932
9654
|
end
|
|
6933
9655
|
----
|
|
9656
|
+
====
|
|
6934
9657
|
|
|
6935
|
-
NOTE: By default `yaml_adapter_type` and `json_adapter_type` are set to
|
|
6936
|
-
`:standard_yaml` and `:standard_json` respectively.
|
|
6937
9658
|
|
|
6938
9659
|
|
|
6939
9660
|
=== XML
|
|
@@ -6960,36 +9681,17 @@ Requires native extensions (i.e. compiled C code).
|
|
|
6960
9681
|
Requires the `ox` gem.
|
|
6961
9682
|
|
|
6962
9683
|
|
|
6963
|
-
.Using
|
|
6964
|
-
[source,ruby]
|
|
6965
|
-
----
|
|
6966
|
-
require 'lutaml/model'
|
|
6967
|
-
|
|
6968
|
-
Lutaml::Model::Config.configure do |config|
|
|
6969
|
-
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
|
6970
|
-
config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
|
|
6971
|
-
end
|
|
6972
|
-
----
|
|
6973
|
-
|
|
6974
|
-
.Using the Oga XML adapter
|
|
6975
|
-
[source,ruby]
|
|
6976
|
-
----
|
|
6977
|
-
require 'lutaml/model'
|
|
6978
|
-
|
|
6979
|
-
Lutaml::Model::Config.configure do |config|
|
|
6980
|
-
require 'lutaml/model/xml_adapter/oga_adapter'
|
|
6981
|
-
config.xml_adapter = Lutaml::Model::XmlAdapter::OgaAdapter
|
|
6982
|
-
end
|
|
6983
|
-
----
|
|
6984
|
-
|
|
6985
|
-
.Using the Ox XML adapter
|
|
9684
|
+
.Using an XML adapter
|
|
6986
9685
|
[source,ruby]
|
|
6987
9686
|
----
|
|
6988
9687
|
require 'lutaml/model'
|
|
6989
9688
|
|
|
6990
9689
|
Lutaml::Model::Config.configure do |config|
|
|
6991
|
-
|
|
6992
|
-
|
|
9690
|
+
config.xml_adapter = :nokogiri
|
|
9691
|
+
# or
|
|
9692
|
+
config.xml_adapter = :oga
|
|
9693
|
+
# or
|
|
9694
|
+
config.xml_adapter = :ox
|
|
6993
9695
|
end
|
|
6994
9696
|
----
|
|
6995
9697
|
|
|
@@ -7003,19 +9705,17 @@ YAML::
|
|
|
7003
9705
|
The Psych YAML parser and emitter for Ruby.
|
|
7004
9706
|
Included in the Ruby standard library.
|
|
7005
9707
|
|
|
7006
|
-
.Using
|
|
9708
|
+
.Using a YAML adapter
|
|
7007
9709
|
[source,ruby]
|
|
7008
9710
|
----
|
|
7009
9711
|
require 'lutaml/model'
|
|
7010
9712
|
|
|
7011
9713
|
Lutaml::Model::Config.configure do |config|
|
|
7012
|
-
|
|
7013
|
-
config.yaml_adapter = Lutaml::Model::YamlAdapter::StandardYamlAdapter
|
|
9714
|
+
config.yaml_adapter = :standard_yaml
|
|
7014
9715
|
end
|
|
7015
9716
|
----
|
|
7016
9717
|
|
|
7017
9718
|
|
|
7018
|
-
|
|
7019
9719
|
=== JSON
|
|
7020
9720
|
|
|
7021
9721
|
Lutaml::Model supports the following JSON adapters:
|
|
@@ -7030,27 +9730,18 @@ MultiJson::
|
|
|
7030
9730
|
A gem that provides a common interface to multiple JSON libraries.
|
|
7031
9731
|
Requires the `multi_json` gem.
|
|
7032
9732
|
|
|
7033
|
-
.Using
|
|
9733
|
+
.Using a JSON adapter
|
|
7034
9734
|
[source,ruby]
|
|
7035
9735
|
----
|
|
7036
9736
|
require 'lutaml/model'
|
|
7037
9737
|
|
|
7038
9738
|
Lutaml::Model::Config.configure do |config|
|
|
7039
|
-
|
|
7040
|
-
|
|
9739
|
+
config.json_adapter = :standard_json
|
|
9740
|
+
# or
|
|
9741
|
+
config.json_adapter = :multi_json
|
|
7041
9742
|
end
|
|
7042
9743
|
----
|
|
7043
9744
|
|
|
7044
|
-
.Using the MultiJson adapter
|
|
7045
|
-
[source,ruby]
|
|
7046
|
-
----
|
|
7047
|
-
require 'lutaml/model'
|
|
7048
|
-
|
|
7049
|
-
Lutaml::Model::Config.configure do |config|
|
|
7050
|
-
require 'lutaml/model/json_adapter/multi_json_adapter'
|
|
7051
|
-
config.json_adapter = Lutaml::Model::JsonAdapter::MultiJsonAdapter
|
|
7052
|
-
end
|
|
7053
|
-
----
|
|
7054
9745
|
|
|
7055
9746
|
=== TOML
|
|
7056
9747
|
|
|
@@ -7069,28 +9760,82 @@ additional features.
|
|
|
7069
9760
|
Requires the `tomlib` gem.
|
|
7070
9761
|
|
|
7071
9762
|
|
|
7072
|
-
.Using
|
|
9763
|
+
.Using a TOML adapter
|
|
7073
9764
|
[source,ruby]
|
|
7074
9765
|
----
|
|
7075
9766
|
require 'lutaml/model'
|
|
7076
9767
|
|
|
7077
9768
|
Lutaml::Model::Config.configure do |config|
|
|
7078
|
-
|
|
7079
|
-
|
|
9769
|
+
config.toml_adapter = :toml_rb
|
|
9770
|
+
# or
|
|
9771
|
+
config.toml_adapter = :tomlib
|
|
7080
9772
|
end
|
|
7081
9773
|
----
|
|
7082
9774
|
|
|
7083
|
-
.Using the Tomlib adapter
|
|
7084
|
-
[source,ruby]
|
|
7085
|
-
----
|
|
7086
|
-
require 'lutaml/model'
|
|
7087
9775
|
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
9776
|
+
[[custom-adapters]]
|
|
9777
|
+
== Custom serialization adapters
|
|
9778
|
+
|
|
9779
|
+
Lutaml::Model provides a flexible system for creating custom adapters to handle
|
|
9780
|
+
different data formats.
|
|
9781
|
+
|
|
9782
|
+
Please refer to link:docs/custom_adapters.adoc[Custom adapters] for details
|
|
9783
|
+
and examples.
|
|
9784
|
+
|
|
9785
|
+
|
|
9786
|
+
[[schema-generation]]
|
|
9787
|
+
== Schema generation
|
|
9788
|
+
|
|
9789
|
+
Lutaml::Model provides functionality to generate schema definitions from LutaML models. This allows you to create schemas that can be used for validation or documentation purposes.
|
|
9790
|
+
|
|
9791
|
+
Currently, the following schema formats are supported:
|
|
9792
|
+
|
|
9793
|
+
* JSON Schema (https://json-schema.org/understanding-json-schema/[JSON Schema])
|
|
9794
|
+
* YAML Schema (https://yaml.org/spec/1.2/spec.html[YAML])
|
|
9795
|
+
* XSD (https://w3.org/TR/xmlschema-1/[XML Schema Definition Language])
|
|
9796
|
+
* RELAX NG (https://relaxng.org/[RELAX NG])
|
|
9797
|
+
|
|
9798
|
+
The schema generation supports advanced features such as:
|
|
7093
9799
|
|
|
9800
|
+
* Validation constraints (patterns, enumerations, ranges)
|
|
9801
|
+
* Choice attributes with min/max constraints
|
|
9802
|
+
* Polymorphic types with oneOf validation
|
|
9803
|
+
* Collection constraints with minItems/maxItems
|
|
9804
|
+
|
|
9805
|
+
Please refer to link:docs/schema_generation.adoc[Schema Generation] for details
|
|
9806
|
+
and examples.
|
|
9807
|
+
|
|
9808
|
+
|
|
9809
|
+
[[schema-import]]
|
|
9810
|
+
== Schema import
|
|
9811
|
+
|
|
9812
|
+
Lutaml::Model provides functionality to import schema definitions into LutaML
|
|
9813
|
+
models. This allows you to create models from existing schema definitions.
|
|
9814
|
+
|
|
9815
|
+
Currently, the following schema formats are supported:
|
|
9816
|
+
|
|
9817
|
+
* XSD (https://w3.org/TR/xmlschema-1/[XML Schema Definition Language])
|
|
9818
|
+
* JSON/YAML Schema
|
|
9819
|
+
|
|
9820
|
+
Please refer to link:docs/schema_import.adoc[Schema Import] for details
|
|
9821
|
+
and examples.
|
|
9822
|
+
|
|
9823
|
+
|
|
9824
|
+
[[custom-registers]]
|
|
9825
|
+
=== Custom Registers
|
|
9826
|
+
|
|
9827
|
+
A *LutaML::Model* *Register* allows for dynamic modification and reconfiguration of model hierarchies without altering the original model definitions.
|
|
9828
|
+
For more information, refer to the link:docs/custom_registers.adoc[Custom Registers Guide].
|
|
9829
|
+
|
|
9830
|
+
NOTE: Before using the `Lutaml::Model::Register` instance, make sure to register it in `Lutaml::Model::GlobalRegister`.
|
|
9831
|
+
|
|
9832
|
+
NOTE: By default, a `default_register` with the id `:default` is created and registered in the GlobalRegister. This default register is also set in `Lutaml::Model::Config.default_register` as the default value.
|
|
9833
|
+
|
|
9834
|
+
The default register can be set at the configuration level using the following syntax:
|
|
9835
|
+
|
|
9836
|
+
```ruby
|
|
9837
|
+
Lutaml::Model::Config.default_register = :default # the register id goes here.
|
|
9838
|
+
```
|
|
7094
9839
|
|
|
7095
9840
|
== Comparison with Shale
|
|
7096
9841
|
|
|
@@ -7205,6 +9950,11 @@ data models.
|
|
|
7205
9950
|
| No.
|
|
7206
9951
|
| Lutaml::Model supports attribute extraction from key-value data models.
|
|
7207
9952
|
|
|
9953
|
+
| Register
|
|
9954
|
+
| Yes. Supports three types of registers(<<custom-register, read more details>>) with different types of functionalities.
|
|
9955
|
+
| Supports `register` functionality for *Shale::Type* classes only.
|
|
9956
|
+
| `Lutaml::Model` registers both `Registrable` classes and `Lutaml::Model::Type` classes, offering a more comprehensive registration system.
|
|
9957
|
+
|
|
7208
9958
|
|===
|
|
7209
9959
|
|
|
7210
9960
|
|