lutaml-model 0.7.3 → 0.7.5
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/release.yml +3 -0
- data/.gitignore +6 -1
- data/.irbrc +1 -0
- data/.pryrc +1 -0
- data/.rubocop_todo.yml +25 -52
- data/README.adoc +2177 -192
- 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 +230 -94
- data/lib/lutaml/model/choice.rb +30 -0
- data/lib/lutaml/model/collection.rb +194 -0
- data/lib/lutaml/model/comparable_model.rb +3 -3
- data/lib/lutaml/model/config.rb +26 -3
- 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/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 +5 -2
- data/lib/lutaml/model/global_register.rb +41 -0
- data/lib/lutaml/model/{hash.rb → hash_adapter.rb} +5 -5
- 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 +3 -2
- data/lib/lutaml/model/mapping/key_value_mapping.rb +64 -4
- data/lib/lutaml/model/mapping/key_value_mapping_rule.rb +4 -0
- data/lib/lutaml/model/mapping/mapping_rule.rb +8 -3
- data/lib/lutaml/model/register.rb +105 -0
- data/lib/lutaml/model/registrable.rb +6 -0
- 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/serialize.rb +175 -53
- data/lib/lutaml/model/services/base.rb +11 -0
- data/lib/lutaml/model/services/logger.rb +2 -2
- data/lib/lutaml/model/services/rule_value_extractor.rb +92 -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 +3 -0
- data/lib/lutaml/model/transform/key_value_transform.rb +60 -50
- data/lib/lutaml/model/transform/xml_transform.rb +46 -57
- data/lib/lutaml/model/transform.rb +22 -8
- 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 +5 -1
- data/lib/lutaml/model/type.rb +5 -2
- data/lib/lutaml/model/utils.rb +30 -8
- data/lib/lutaml/model/validation.rb +6 -4
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml/document.rb +37 -19
- data/lib/lutaml/model/xml/mapping.rb +74 -13
- data/lib/lutaml/model/xml/mapping_rule.rb +10 -2
- data/lib/lutaml/model/xml/nokogiri_adapter.rb +5 -3
- data/lib/lutaml/model/xml/oga/element.rb +4 -1
- data/lib/lutaml/model/xml/oga_adapter.rb +4 -3
- data/lib/lutaml/model/xml/ox_adapter.rb +20 -6
- data/lib/lutaml/model/xml/xml_element.rb +3 -28
- data/lib/lutaml/model/xml_adapter/element.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +1 -1
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -1
- 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 +7 -31
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -5
- 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/choice_spec.rb +44 -0
- data/spec/lutaml/model/custom_collection_spec.rb +830 -0
- data/spec/lutaml/model/custom_model_spec.rb +15 -3
- data/spec/lutaml/model/defaults_spec.rb +5 -1
- data/spec/lutaml/model/global_register_spec.rb +108 -0
- data/spec/lutaml/model/group_spec.rb +9 -3
- data/spec/lutaml/model/jsonl/standard_adapter_spec.rb +91 -0
- data/spec/lutaml/model/jsonl_spec.rb +229 -0
- data/spec/lutaml/model/multiple_mapping_spec.rb +1 -1
- data/spec/lutaml/model/register/key_value_spec.rb +275 -0
- data/spec/lutaml/model/register/xml_spec.rb +187 -0
- data/spec/lutaml/model/register_spec.rb +147 -0
- 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 +31 -0
- data/spec/lutaml/model/type_spec.rb +8 -4
- 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/xml_element_spec.rb +7 -1
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
- data/spec/lutaml/model/xml_adapter_spec.rb +24 -0
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +11 -4
- data/spec/lutaml/model/xml_mapping_spec.rb +1 -1
- 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 +1 -0
- metadata +105 -9
- data/lib/lutaml/model/schema/templates/simple_type.rb +0 -247
- /data/lib/lutaml/model/{hash → hash_adapter}/document.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/mapping.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/mapping_rule.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/standard_adapter.rb +0 -0
- /data/lib/lutaml/model/{hash → hash_adapter}/transform.rb +0 -0
data/README.adoc
CHANGED
@@ -39,7 +39,11 @@ Lutaml::Model are provided in <<migrate-from-shale>>.
|
|
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>>)
|
42
44
|
* Create custom adapters for additional data formats (see <<custom-adapters>>)
|
45
|
+
* Dynamically modify model attribute types using registers (see <<custom-registers>>)
|
46
|
+
|
43
47
|
|
44
48
|
|
45
49
|
== Data modeling in a nutshell
|
@@ -270,10 +274,26 @@ Or install it yourself as:
|
|
270
274
|
gem install lutaml-model
|
271
275
|
----
|
272
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]]
|
273
290
|
== Model
|
274
291
|
|
275
292
|
=== General
|
276
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
|
+
|
277
297
|
There are two ways to define an information model in Lutaml::Model:
|
278
298
|
|
279
299
|
* Inheriting from the `Lutaml::Model::Serializable` class
|
@@ -377,7 +397,7 @@ same class and all their attributes are equal.
|
|
377
397
|
> # true
|
378
398
|
----
|
379
399
|
|
380
|
-
|
400
|
+
[[value-definition]]
|
381
401
|
== Value types
|
382
402
|
|
383
403
|
=== General types
|
@@ -565,6 +585,7 @@ part lost due to the inability of JSON to handle high-precision date-time.
|
|
565
585
|
====
|
566
586
|
|
567
587
|
|
588
|
+
[[attribute-definition]]
|
568
589
|
== Attributes
|
569
590
|
|
570
591
|
|
@@ -616,6 +637,56 @@ puts s.established
|
|
616
637
|
----
|
617
638
|
====
|
618
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
|
+
|
619
690
|
=== Polymorphic attributes
|
620
691
|
|
621
692
|
==== General
|
@@ -821,7 +892,7 @@ end
|
|
821
892
|
<1> The name of the polymorphic attribute.
|
822
893
|
<2> The polymorphic superclass class.
|
823
894
|
<3> Any options for the attribute.
|
824
|
-
<4> The `polymorphic` option that determines the acceptable polymorphic subclasses
|
895
|
+
<4> The `polymorphic` option that determines the acceptable polymorphic subclasses, or just `true`.
|
825
896
|
<5> The polymorphic subclasses.
|
826
897
|
|
827
898
|
The `polymorphic` option is an array of polymorphic subclasses that the
|
@@ -864,6 +935,25 @@ end
|
|
864
935
|
----
|
865
936
|
====
|
866
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
|
+
|
867
957
|
* If the attribute (collection or not) is meant to contain instances belonging
|
868
958
|
to more than one polymorphic subclass, then those acceptable polymorphic
|
869
959
|
subclasses should be explicitly specified in the `polymorphic: [...]` option.
|
@@ -1485,9 +1575,13 @@ puts SomeModel.new.to_yaml
|
|
1485
1575
|
----
|
1486
1576
|
====
|
1487
1577
|
|
1578
|
+
|
1488
1579
|
=== Derived attributes
|
1489
1580
|
|
1490
|
-
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.
|
1491
1585
|
|
1492
1586
|
Syntax:
|
1493
1587
|
|
@@ -1573,6 +1667,7 @@ attributes.
|
|
1573
1667
|
* The last attribute `completeName` is optional.
|
1574
1668
|
====
|
1575
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>>.
|
1576
1671
|
|
1577
1672
|
|
1578
1673
|
=== Importable models for reuse
|
@@ -1629,6 +1724,7 @@ class ComplexType < Lutaml::Model::Serializable
|
|
1629
1724
|
|
1630
1725
|
xml do
|
1631
1726
|
root "GroupOfItems"
|
1727
|
+
|
1632
1728
|
map_attribute "tag", to: :tag
|
1633
1729
|
map_content to: :content
|
1634
1730
|
map_element :group, to: :group
|
@@ -1660,6 +1756,161 @@ end
|
|
1660
1756
|
----
|
1661
1757
|
====
|
1662
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>>.
|
1663
1914
|
|
1664
1915
|
[[attribute-value-transform]]
|
1665
1916
|
=== Attribute value transform
|
@@ -1973,22 +2224,1205 @@ For the following XML snippet:
|
|
1973
2224
|
@name="John Doe",
|
1974
2225
|
@ordered=nil>
|
1975
2226
|
----
|
1976
|
-
====
|
2227
|
+
====
|
2228
|
+
|
2229
|
+
|
2230
|
+
== Collections
|
2231
|
+
|
2232
|
+
=== General
|
2233
|
+
|
2234
|
+
Collections are used to represent a contained group of multiple instances of models.
|
2235
|
+
|
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.
|
2239
|
+
|
2240
|
+
Models in a collection may be:
|
2241
|
+
|
2242
|
+
* constrained to be of a single kind;
|
2243
|
+
|
2244
|
+
* constrained to be of multiple kinds sharing common characteristics;
|
2245
|
+
|
2246
|
+
* unbounded of any kind.
|
2247
|
+
|
2248
|
+
LutaML Model provides the `Lutaml::Model::Collection` class for defining
|
2249
|
+
collections of model instances.
|
2250
|
+
|
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}
|
2264
|
+
end
|
2265
|
+
----
|
2266
|
+
|
2267
|
+
Where,
|
2268
|
+
|
2269
|
+
`attribute`:: The name of the attribute that contains the collection.
|
2270
|
+
`ModelType`:: The model type of the collection elements.
|
2271
|
+
|
2272
|
+
|
2273
|
+
==== Mapping instances: key-value formats only
|
2274
|
+
|
2275
|
+
The `map_instances` directive is only used in the `key_value` block.
|
2276
|
+
|
2277
|
+
Syntax:
|
2278
|
+
|
2279
|
+
[source,ruby]
|
2280
|
+
----
|
2281
|
+
class MyCollection < Lutaml::Model::Collection
|
2282
|
+
instances {attribute}, ModelType
|
2283
|
+
|
2284
|
+
key_value do
|
2285
|
+
map_instances to: {attribute}
|
2286
|
+
end
|
2287
|
+
end
|
2288
|
+
----
|
2289
|
+
|
2290
|
+
Where,
|
2291
|
+
|
2292
|
+
`attribute`:: The name of the attribute that contains model instances.
|
2293
|
+
|
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.
|
2297
|
+
|
2298
|
+
|
2299
|
+
==== Mapping instances: XML only
|
2300
|
+
|
2301
|
+
In the `xml` block, the `map_element`, `map_attribute` directives are used instead.
|
2302
|
+
|
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:
|
2308
|
+
|
2309
|
+
[source,ruby]
|
2310
|
+
----
|
2311
|
+
class MyCollection < Lutaml::Model::Collection
|
2312
|
+
instances {attribute}, ModelType
|
2313
|
+
|
2314
|
+
xml do
|
2315
|
+
map_element "element-name", to: {attribute}
|
2316
|
+
end
|
2317
|
+
end
|
2318
|
+
----
|
2319
|
+
|
2320
|
+
Where,
|
2321
|
+
|
2322
|
+
`element-name`:: The name of the XML element of each model instance.
|
2323
|
+
|
2324
|
+
|
2325
|
+
Syntax for an attribute collection:
|
2326
|
+
|
2327
|
+
[source,ruby]
|
2328
|
+
----
|
2329
|
+
class MyCollection < Lutaml::Model::Collection
|
2330
|
+
instances {attribute}, ModelType
|
2331
|
+
xml do
|
2332
|
+
map_attribute "attribute-name", to: {attribute}
|
2333
|
+
end
|
2334
|
+
end
|
2335
|
+
----
|
2336
|
+
|
2337
|
+
Where,
|
2338
|
+
|
2339
|
+
`attribute-name`:: The name of the XML attribute that contains all model instances.
|
2340
|
+
|
2341
|
+
|
2342
|
+
=== Collection types
|
2343
|
+
|
2344
|
+
A LutaML collections is used for a number of scenarios:
|
2345
|
+
|
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
|
2353
|
+
|
2354
|
+
|
2355
|
+
=== Root collections (key-value formats only)
|
2356
|
+
|
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
|
+
|
3155
|
+
|
3156
|
+
=== Behavior
|
3157
|
+
|
3158
|
+
==== Enumerable interface
|
3159
|
+
|
3160
|
+
Collections implement the Ruby `Enumerable` interface, providing standard
|
3161
|
+
collection operations.
|
3162
|
+
|
3163
|
+
Collections allow the following sample `Enumerable` methods:
|
3164
|
+
|
3165
|
+
* `each` - Iterate over collection items
|
3166
|
+
* `map` - Transform collection items
|
3167
|
+
* `select` - Filter collection items
|
3168
|
+
* `find` - Find items matching criteria
|
3169
|
+
* `reduce` - Aggregate collection items
|
3170
|
+
|
3171
|
+
.Usage of the collection Enumerable interface
|
3172
|
+
[example]
|
3173
|
+
====
|
3174
|
+
[source,ruby]
|
3175
|
+
----
|
3176
|
+
# Filter items
|
3177
|
+
filtered = collection.filter { |item| item.id == "1" }
|
3178
|
+
|
3179
|
+
# Reject items
|
3180
|
+
rejected = collection.reject { |item| item.id == "1" }
|
3181
|
+
|
3182
|
+
# Select items
|
3183
|
+
selected = collection.select { |item| item.id == "1" }
|
3184
|
+
|
3185
|
+
# Map items
|
3186
|
+
mapped = collection.map { |item| item.name }
|
3187
|
+
|
3188
|
+
# Count items
|
3189
|
+
count = collection.count
|
3190
|
+
----
|
3191
|
+
====
|
3192
|
+
|
3193
|
+
|
3194
|
+
// ==== Collection validation
|
3195
|
+
|
3196
|
+
// Collections can define validation rules for their elements.
|
3197
|
+
|
3198
|
+
// [example]
|
3199
|
+
// ====
|
3200
|
+
// [source,ruby]
|
3201
|
+
// ----
|
3202
|
+
// class PublicationCollection < Lutaml::Model::Collection
|
3203
|
+
// instances(:publications, Publication) do
|
3204
|
+
// validates :year, numericality: { greater_than: 1900 }
|
3205
|
+
|
3206
|
+
// validate :must_have_author
|
3207
|
+
|
3208
|
+
// def must_have_author(publications)
|
3209
|
+
// publications.each do |publication|
|
3210
|
+
// next unless publication.author.nil?
|
3211
|
+
// errors.add(:author, "`#{publication.title}` must have an author")
|
3212
|
+
// end
|
3213
|
+
// end
|
3214
|
+
// end
|
3215
|
+
// end
|
3216
|
+
// ----
|
3217
|
+
// ====
|
3218
|
+
|
3219
|
+
==== Initialization
|
3220
|
+
|
3221
|
+
Collections can be initialized with an array of items or through individual item
|
3222
|
+
addition.
|
3223
|
+
|
3224
|
+
[example]
|
3225
|
+
====
|
3226
|
+
[source,ruby]
|
3227
|
+
----
|
3228
|
+
# Empty collection
|
3229
|
+
collection = ItemCollection.new
|
3230
|
+
|
3231
|
+
# From an array of items
|
3232
|
+
collection = ItemCollection.new([item1, item2, item3])
|
3233
|
+
|
3234
|
+
# From an array of hashes
|
3235
|
+
collection = ItemCollection.new([
|
3236
|
+
{ id: "1", name: "Item 1" },
|
3237
|
+
{ id: "2", name: "Item 2" }
|
3238
|
+
])
|
3239
|
+
|
3240
|
+
# Adding items later
|
3241
|
+
collection << Item.new(id: "3", name: "Item 3")
|
3242
|
+
----
|
3243
|
+
====
|
3244
|
+
|
3245
|
+
==== Ordering
|
3246
|
+
|
3247
|
+
TODO: This case needs to be fixed.
|
3248
|
+
|
3249
|
+
Collections that maintain a specific ordering of elements.
|
3250
|
+
|
3251
|
+
Syntax:
|
3252
|
+
|
3253
|
+
[source,ruby]
|
3254
|
+
----
|
3255
|
+
class MyCollection < Lutaml::Model::Collection
|
3256
|
+
instances {instances-name}, ModelType
|
3257
|
+
ordered by: {attribute-of-instance}, order: {:asc | :desc}
|
3258
|
+
end
|
3259
|
+
----
|
3260
|
+
|
3261
|
+
Where,
|
3262
|
+
|
3263
|
+
`{instances-name}`:: name of the instances accessor within the collection
|
3264
|
+
`ModelType`:: The model type of the collection elements.
|
3265
|
+
|
3266
|
+
`{attribute-of-instance-or-proc}`:: How model instances are to be ordered by. Values supported are:
|
3267
|
+
`{attribute-of-instance}`::: Attribute name of an instance to be ordered by.
|
3268
|
+
`{proc}`::: Proc that returns a value to order by (same as `sort_by`), given the instance as input.
|
3269
|
+
|
3270
|
+
`order`::: Order direction of the value:
|
3271
|
+
`:asc`:::: Ascending order (default).
|
3272
|
+
`:desc`:::: Descending order.
|
3273
|
+
|
3274
|
+
.Ordered collection applied to a root collection
|
3275
|
+
[example]
|
3276
|
+
====
|
3277
|
+
Data:
|
3278
|
+
|
3279
|
+
[source,xml]
|
3280
|
+
----
|
3281
|
+
<items>
|
3282
|
+
<item id="3" name="Item Three"/>
|
3283
|
+
<item id="1" name="Item One"/>
|
3284
|
+
<item id="2" name="Item Two"/>
|
3285
|
+
</items>
|
3286
|
+
----
|
3287
|
+
|
3288
|
+
[source,yaml]
|
3289
|
+
----
|
3290
|
+
---
|
3291
|
+
- id: 3
|
3292
|
+
name: Item Three
|
3293
|
+
- id: 1
|
3294
|
+
name: Item One
|
3295
|
+
- id: 2
|
3296
|
+
name: Item Two
|
3297
|
+
----
|
3298
|
+
|
3299
|
+
[source,ruby]
|
3300
|
+
----
|
3301
|
+
class Item < Lutaml::Model::Serializable
|
3302
|
+
attribute :id, :string
|
3303
|
+
attribute :name, :string
|
3304
|
+
|
3305
|
+
xml do
|
3306
|
+
map_attribute "id", to: :id
|
3307
|
+
map_attribute "name", to: :name
|
3308
|
+
end
|
3309
|
+
end
|
3310
|
+
|
3311
|
+
class OrderedItemCollection < Lutaml::Model::Collection
|
3312
|
+
instances :items, Item
|
3313
|
+
ordered by: :id, order: :desc
|
3314
|
+
|
3315
|
+
xml do
|
3316
|
+
root "items"
|
3317
|
+
map_element "item", to: :items
|
3318
|
+
end
|
3319
|
+
|
3320
|
+
key_value do
|
3321
|
+
no_root
|
3322
|
+
map_instances to: :items
|
3323
|
+
end
|
3324
|
+
end
|
3325
|
+
----
|
3326
|
+
|
3327
|
+
[source,ruby]
|
3328
|
+
----
|
3329
|
+
> collection = OrderedItemCollection.from_xml(xml_data)
|
3330
|
+
> collection.map(&:id)
|
3331
|
+
> # ["3", "2", "1"]
|
3332
|
+
|
3333
|
+
> collection = OrderedItemCollection.from_yaml(yaml_data)
|
3334
|
+
> collection.map(&:id)
|
3335
|
+
> # ["3", "2", "1"]
|
3336
|
+
----
|
3337
|
+
====
|
3338
|
+
|
3339
|
+
|
3340
|
+
|
3341
|
+
// ==== Polymorphic collections
|
3342
|
+
|
3343
|
+
// Collections can contain instances of different model classes that share a common
|
3344
|
+
// base class.
|
3345
|
+
|
3346
|
+
// The polymorphic options for attributes are also applied here.
|
3347
|
+
|
3348
|
+
// [example]
|
3349
|
+
// ====
|
3350
|
+
// [source,ruby]
|
3351
|
+
// ----
|
3352
|
+
// class PolymorphicItemCollection < Lutaml::Model::Collection
|
3353
|
+
// instances :items, Item, polymorphic: true
|
3354
|
+
|
3355
|
+
// xml do
|
3356
|
+
// root "items"
|
3357
|
+
// map_element "item", to: :items
|
3358
|
+
// end
|
3359
|
+
|
3360
|
+
// key_value do
|
3361
|
+
// root "items"
|
3362
|
+
// map_instances to: :items
|
3363
|
+
// end
|
3364
|
+
// end
|
3365
|
+
// ----
|
3366
|
+
// ====
|
3367
|
+
|
3368
|
+
|
3369
|
+
== Serialization model mappings
|
3370
|
+
|
3371
|
+
=== General
|
3372
|
+
|
3373
|
+
Lutaml::Model allows you to translate a data model into serialization models of
|
3374
|
+
various serialization formats.
|
3375
|
+
|
3376
|
+
Depending on the serialization format, different methods are supported for
|
3377
|
+
defining serialization and deserialization mappings.
|
3378
|
+
|
3379
|
+
A serialization model mapping is defined using a format-specific DSL block
|
3380
|
+
in this syntax:
|
3381
|
+
|
3382
|
+
[source,ruby]
|
3383
|
+
----
|
3384
|
+
class Example < Lutaml::Model::Serializable
|
3385
|
+
{format-short-name} do <1>
|
3386
|
+
# ...
|
3387
|
+
end
|
3388
|
+
end
|
3389
|
+
----
|
3390
|
+
<1> `{format-short-name}` is the serialization format short name.
|
1977
3391
|
|
1978
|
-
|
3392
|
+
There are two kinds of serialization models:
|
1979
3393
|
|
1980
|
-
|
3394
|
+
* Represents a singular model (maps to a Lutaml::Model::Serializable)
|
3395
|
+
* Represents a group/collection of models (maps to Lutaml::Model::Collection)
|
1981
3396
|
|
1982
|
-
|
1983
|
-
|
3397
|
+
A collection contains instances of singular models, and therefore is always
|
3398
|
+
inextricably linked to an underlying serialization format for singular models.
|
3399
|
+
For instance, JSONL represents a collection (itself being invalid JSON) that
|
3400
|
+
uses JSON for singular models.
|
1984
3401
|
|
1985
|
-
|
1986
|
-
|
3402
|
+
The supported serialization formats and their short names are defined as follows:
|
3403
|
+
|
3404
|
+
Model serialization formats::
|
3405
|
+
|
3406
|
+
`xml`::: XML
|
3407
|
+
`hsh`::: Hash
|
3408
|
+
+
|
3409
|
+
NOTE: Yes a 3-letter abbreviation for Hash!
|
3410
|
+
|
3411
|
+
`json`::: JSON
|
3412
|
+
`yaml`::: YAML
|
3413
|
+
`toml`::: TOML
|
3414
|
+
`key_value`::: Key-value format, a shorthand for all key-value formats (including
|
3415
|
+
JSON, YAML and TOML).
|
3416
|
+
|
3417
|
+
Collection serialization formats::
|
3418
|
+
|
3419
|
+
`jsonl`::: JSONL (JSON Lines)
|
3420
|
+
`yamls`::: YAML Stream (multi-document format)
|
1987
3421
|
|
1988
|
-
Serialization model mappings are defined under the `xml`, `hsh`, `json`, `yaml`,
|
1989
|
-
and `toml` blocks.
|
1990
3422
|
|
1991
3423
|
.Using the `xml`, `hsh`, `json`, `yaml`, `toml` and `key_value` blocks to define serialization mappings
|
3424
|
+
[example]
|
3425
|
+
====
|
1992
3426
|
[source,ruby]
|
1993
3427
|
----
|
1994
3428
|
class Example < Lutaml::Model::Serializable
|
@@ -2017,6 +3451,20 @@ class Example < Lutaml::Model::Serializable
|
|
2017
3451
|
end
|
2018
3452
|
end
|
2019
3453
|
----
|
3454
|
+
====
|
3455
|
+
|
3456
|
+
.Using the `jsonl` block to define serialization mappings to a collection
|
3457
|
+
[example]
|
3458
|
+
====
|
3459
|
+
[source,ruby]
|
3460
|
+
----
|
3461
|
+
class Example < Lutaml::Model::Collection
|
3462
|
+
jsonl do
|
3463
|
+
# ...
|
3464
|
+
end
|
3465
|
+
end
|
3466
|
+
----
|
3467
|
+
====
|
2020
3468
|
|
2021
3469
|
|
2022
3470
|
=== XML
|
@@ -2988,6 +4436,7 @@ HERE
|
|
2988
4436
|
----
|
2989
4437
|
====
|
2990
4438
|
|
4439
|
+
NOTE: For importing model mappings inside a `sequence` block, refer to <<import-model-mappings-inside-sequence, Importing model mappings inside a `sequence`>>.
|
2991
4440
|
|
2992
4441
|
[[xml-schema-location]]
|
2993
4442
|
==== Automatic support of `xsi:schemaLocation`
|
@@ -3453,11 +4902,20 @@ This XML snippet is in UTF-8.
|
|
3453
4902
|
|
3454
4903
|
==== General
|
3455
4904
|
|
3456
|
-
Key-value data models
|
3457
|
-
|
4905
|
+
Key-value data models share a similar structure where data is stored as
|
4906
|
+
key-value pairs.
|
3458
4907
|
|
3459
4908
|
`Lutaml::Model` works with these formats in a similar way.
|
3460
4909
|
|
4910
|
+
Key-value data models supported are identified by their short name:
|
4911
|
+
|
4912
|
+
`hsh`:: Hash (Ruby `Hash` class)
|
4913
|
+
`json`:: JSON
|
4914
|
+
`yaml`:: YAML
|
4915
|
+
`toml`:: TOML
|
4916
|
+
`key_value`:: A way to configure key-value mappings for all supported key-value data models.
|
4917
|
+
|
4918
|
+
|
3461
4919
|
==== Mapping
|
3462
4920
|
|
3463
4921
|
The `map` method is used to define key-value mappings.
|
@@ -3466,11 +4924,35 @@ Syntax:
|
|
3466
4924
|
|
3467
4925
|
[source,ruby]
|
3468
4926
|
----
|
3469
|
-
|
4927
|
+
{key_value_type_short} do <1>
|
3470
4928
|
map 'key_value_model_attribute_name', to: :name_of_attribute
|
3471
4929
|
end
|
3472
4930
|
----
|
4931
|
+
<1> `key_value_type_short` is the key-value data model's short name.
|
4932
|
+
|
4933
|
+
.Creating a key-value data model mapping for only the JSON format
|
4934
|
+
[example]
|
4935
|
+
====
|
4936
|
+
[source,ruby]
|
4937
|
+
----
|
4938
|
+
json do
|
4939
|
+
map :color, to: :color
|
4940
|
+
map :desc, to: :description
|
4941
|
+
end
|
4942
|
+
----
|
4943
|
+
====
|
3473
4944
|
|
4945
|
+
.Creating a key-value data model mapping for all key-value formats
|
4946
|
+
[example]
|
4947
|
+
====
|
4948
|
+
[source,ruby]
|
4949
|
+
----
|
4950
|
+
key_value do
|
4951
|
+
map :color, to: :color
|
4952
|
+
map :desc, to: :description
|
4953
|
+
end
|
4954
|
+
----
|
4955
|
+
====
|
3474
4956
|
|
3475
4957
|
==== Unified mapping
|
3476
4958
|
|
@@ -3494,19 +4976,11 @@ class CeramicModel < Lutaml::Model::Serializable
|
|
3494
4976
|
attribute :glaze, :string
|
3495
4977
|
attribute :description, :string
|
3496
4978
|
|
3497
|
-
key_value do
|
3498
|
-
map :color, to: color
|
4979
|
+
key_value do # or any other key-value data model
|
4980
|
+
map :color, to: :color
|
3499
4981
|
map :glz, to: :glaze
|
3500
4982
|
map :desc, to: :description
|
3501
4983
|
end
|
3502
|
-
|
3503
|
-
# Equivalent to the Hash, JSON, YAML, and TOML mappings.
|
3504
|
-
#
|
3505
|
-
# hsh and json and yaml and toml do
|
3506
|
-
# map :id, to: color
|
3507
|
-
# map :name, to: :full_name
|
3508
|
-
# map :status, to: :current_status
|
3509
|
-
# end
|
3510
4984
|
end
|
3511
4985
|
----
|
3512
4986
|
|
@@ -3537,13 +5011,7 @@ desc: A ceramic with a navy blue color and clear glaze.
|
|
3537
5011
|
|
3538
5012
|
==== Specific format mappings
|
3539
5013
|
|
3540
|
-
Specific key value formats can be mapping independently of other formats
|
3541
|
-
|
3542
|
-
* `hsh` for the Hash format
|
3543
|
-
* `json` for the JSON format
|
3544
|
-
* `yaml` for the YAML format
|
3545
|
-
* `toml` for the TOML format
|
3546
|
-
|
5014
|
+
Specific key value formats can be mapping independently of other formats.
|
3547
5015
|
|
3548
5016
|
.Using the `map` method to define key-value mappings per format
|
3549
5017
|
[example]
|
@@ -4477,106 +5945,447 @@ object as the value of a designated attribute.
|
|
4477
5945
|
----
|
4478
5946
|
====
|
4479
5947
|
|
4480
|
-
If a specified value path is not found, the corresponding attribute in the model
|
4481
|
-
will be assigned a `nil` value.
|
5948
|
+
If a specified value path is not found, the corresponding attribute in the model
|
5949
|
+
will be assigned a `nil` value.
|
5950
|
+
|
5951
|
+
.Attribute values set to `nil` when the `path_to_value` is not found
|
5952
|
+
[example]
|
5953
|
+
====
|
5954
|
+
In the following JSON content, the `path_to_value` of `[:extras, :sunroof]` and
|
5955
|
+
`[:extras, :drinks_cooler]` at the object `"gearbox"` would be set to `nil`.
|
5956
|
+
|
5957
|
+
[source,json]
|
5958
|
+
----
|
5959
|
+
{
|
5960
|
+
"components": {
|
5961
|
+
"engine": {
|
5962
|
+
"manufacturer": "Ford",
|
5963
|
+
"extras": {
|
5964
|
+
"sunroof": true,
|
5965
|
+
"drinks_cooler": true
|
5966
|
+
}
|
5967
|
+
},
|
5968
|
+
"gearbox": {
|
5969
|
+
"manufacturer": "Toyota"
|
5970
|
+
}
|
5971
|
+
}
|
5972
|
+
}
|
5973
|
+
----
|
5974
|
+
====
|
5975
|
+
|
5976
|
+
|
5977
|
+
.Using the `child_mappings` option to extract values from a key-value data model
|
5978
|
+
[example]
|
5979
|
+
====
|
5980
|
+
The following JSON contains 2 keys in schema named `foo` and `bar`.
|
5981
|
+
|
5982
|
+
[source,json]
|
5983
|
+
----
|
5984
|
+
{
|
5985
|
+
"schemas": {
|
5986
|
+
"foo": { <1>
|
5987
|
+
"path": { <2>
|
5988
|
+
"link": "link one",
|
5989
|
+
"name": "one"
|
5990
|
+
}
|
5991
|
+
},
|
5992
|
+
"bar": { <1>
|
5993
|
+
"path": { <2>
|
5994
|
+
"link": "link two",
|
5995
|
+
"name": "two"
|
5996
|
+
}
|
5997
|
+
}
|
5998
|
+
}
|
5999
|
+
}
|
6000
|
+
----
|
6001
|
+
<1> The keys `foo` and `bar` are to be mapped to the `id` attribute.
|
6002
|
+
<2> The nested `path.link` and `path.name` keys are used as the `link` and
|
6003
|
+
`name` attributes, respectively.
|
6004
|
+
|
6005
|
+
A model can be defined for this JSON as follows:
|
6006
|
+
|
6007
|
+
[source,ruby]
|
6008
|
+
----
|
6009
|
+
class Schema < Lutaml::Model::Serializable
|
6010
|
+
attribute :id, :string
|
6011
|
+
attribute :link, :string
|
6012
|
+
attribute :name, :string
|
6013
|
+
end
|
6014
|
+
|
6015
|
+
class ChildMappingClass < Lutaml::Model::Serializable
|
6016
|
+
attribute :schemas, Schema, collection: true
|
6017
|
+
|
6018
|
+
json do
|
6019
|
+
map "schemas", to: :schemas,
|
6020
|
+
child_mappings: {
|
6021
|
+
id: :key,
|
6022
|
+
link: %i[path link],
|
6023
|
+
name: %i[path name],
|
6024
|
+
}
|
6025
|
+
end
|
6026
|
+
end
|
6027
|
+
----
|
6028
|
+
|
6029
|
+
The output becomes:
|
6030
|
+
|
6031
|
+
[source,ruby]
|
6032
|
+
----
|
6033
|
+
> ChildMappingClass.from_json(json)
|
6034
|
+
> #<ChildMappingClass:0x0000000104ac7240
|
6035
|
+
@schemas=
|
6036
|
+
[#<Schema:0x0000000104ac6e30 @id="foo", @link="link one", @name="one">,
|
6037
|
+
#<Schema:0x0000000104ac58f0 @id="bar", @link="link two", @name="two">]>
|
6038
|
+
> ChildMappingClass.new(schemas: [Schema.new(id: "foo", link: "link one", name: "one"), Schema.new(id: "bar", link: "link two", name: "two")]).to_json
|
6039
|
+
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}}
|
6040
|
+
----
|
6041
|
+
|
6042
|
+
In this example:
|
6043
|
+
|
6044
|
+
* The `key` of each schema (`foo` and `bar`) is mapped to the `id` attribute.
|
6045
|
+
|
6046
|
+
* The nested `path.link` and `path.name` keys are mapped to the `link` and
|
6047
|
+
`name` attributes, respectively.
|
6048
|
+
====
|
6049
|
+
|
6050
|
+
=== Collection data models
|
6051
|
+
|
6052
|
+
==== General
|
6053
|
+
|
6054
|
+
Collection data models represent a group of models, mapping to an instance of
|
6055
|
+
Lutaml::Model::Collection.
|
6056
|
+
|
6057
|
+
Collection data models supported are identified by their short name:
|
6058
|
+
|
6059
|
+
`jsonl`:: JSONL (JSON Lines)
|
6060
|
+
`yamls`:: YAML Stream (multi-document format)
|
6061
|
+
|
6062
|
+
|
6063
|
+
==== Mapping
|
6064
|
+
|
6065
|
+
As with collections in general, the `map` method is used to define collection
|
6066
|
+
mappings.
|
6067
|
+
|
6068
|
+
Syntax:
|
6069
|
+
|
6070
|
+
[source,ruby]
|
6071
|
+
----
|
6072
|
+
class MySerializedCollection < Lutaml::Model::Collection
|
6073
|
+
instances {attribute}, ModelType
|
6074
|
+
|
6075
|
+
{collection_type_short} do
|
6076
|
+
map_instances to: {attribute}
|
6077
|
+
end
|
6078
|
+
end
|
6079
|
+
----
|
6080
|
+
|
6081
|
+
Where,
|
6082
|
+
|
6083
|
+
`{collection_type_short}`:: The short name of the collection type (e.g. `jsonl`, `yamls`).
|
6084
|
+
|
6085
|
+
`{attribute}`:: The name of the attribute in the collection that will hold the
|
6086
|
+
collection data.
|
6087
|
+
|
6088
|
+
`ModelType`:: The type of the model that will be used in the collection.
|
6089
|
+
|
6090
|
+
A singular model may also utilize collection data models in the following manner.
|
6091
|
+
|
6092
|
+
Syntax:
|
6093
|
+
|
6094
|
+
[source,ruby]
|
6095
|
+
----
|
6096
|
+
class MySerializedCollection < Lutaml::Model::Serializeable
|
6097
|
+
attribute {attribute}, ModelType, collection: true
|
6098
|
+
|
6099
|
+
{collection_type_short} do
|
6100
|
+
# Notice that there is no key_name i.e map <key_name>, to: <attribute_name>,
|
6101
|
+
# This is because in a collection there are no keys. Each object needs to be
|
6102
|
+
# mapped to the attribute.
|
6103
|
+
map to: {attribute}
|
6104
|
+
end
|
6105
|
+
end
|
6106
|
+
----
|
6107
|
+
|
6108
|
+
Where,
|
6109
|
+
|
6110
|
+
`{collection_type_short}`:: The short name of the collection type (e.g. `jsonl`, `yamls`).
|
6111
|
+
|
6112
|
+
`{attribute}`:: The name of the attribute in the collection that will hold the
|
6113
|
+
collection data.
|
6114
|
+
|
6115
|
+
`ModelType`:: The type of the model that will be used in the collection.
|
6116
|
+
|
6117
|
+
|
6118
|
+
==== JSONL
|
6119
|
+
|
6120
|
+
JSONL (short for JSON Lines) is a serialization format where each line
|
6121
|
+
represents a valid JSON object. The format is meant to be efficient for large
|
6122
|
+
datasets such as for streaming or batch processing.
|
6123
|
+
|
6124
|
+
It represents a collection of JSON objects encoded one object per line.
|
6125
|
+
|
6126
|
+
NOTE: The contents of JSONL itself is not valid JSON, but each line is a valid
|
6127
|
+
JSON.
|
6128
|
+
|
6129
|
+
Since JSONL contains JSON elements, the model specified with `instances` or
|
6130
|
+
`attribute` must support JSON.
|
6131
|
+
|
6132
|
+
Every line in a JSONL file is also a valid JSON object. If JSONL-specific
|
6133
|
+
mappings (through `jsonl`) are not defined in the model, the existing `json`
|
6134
|
+
mappings are used instead as a fallback for serialization and deserialization.
|
6135
|
+
|
6136
|
+
|
6137
|
+
.Parsing a JSONL collection using a collection
|
6138
|
+
[example]
|
6139
|
+
====
|
6140
|
+
[source,ruby]
|
6141
|
+
----
|
6142
|
+
class Person
|
6143
|
+
attribute :name, :string
|
6144
|
+
attribute :age, :integer
|
6145
|
+
attribute :id, :string
|
6146
|
+
end
|
6147
|
+
|
6148
|
+
class Directory < Lutaml::Model::Collection
|
6149
|
+
instances :persons, Person
|
6150
|
+
|
6151
|
+
jsonl do
|
6152
|
+
map_instances to: :persons
|
6153
|
+
end
|
6154
|
+
end
|
6155
|
+
|
6156
|
+
jsonl = <<~JSONL
|
6157
|
+
{"name":"John","age":30,"id":"abc-123"}
|
6158
|
+
{"name":"Jane","age":25,"id":"def-456"}
|
6159
|
+
JSONL
|
6160
|
+
|
6161
|
+
jsonl = Directory.from_jsonl(jsonl)
|
6162
|
+
|
6163
|
+
# => <Directory:0x00007fae4b0c9b10
|
6164
|
+
# @persons=[
|
6165
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
6166
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
6167
|
+
# ]>
|
6168
|
+
----
|
6169
|
+
====
|
6170
|
+
|
6171
|
+
.Parsing a JSONL collection using a singlular model
|
6172
|
+
[example]
|
6173
|
+
====
|
6174
|
+
[source,ruby]
|
6175
|
+
----
|
6176
|
+
class Person
|
6177
|
+
attribute :name, :string
|
6178
|
+
attribute :age, :integer
|
6179
|
+
attribute :id, :string
|
6180
|
+
end
|
6181
|
+
|
6182
|
+
class Directory < Lutaml::Model::Serializeable
|
6183
|
+
attribute :persons, Person, collection: true
|
6184
|
+
|
6185
|
+
jsonl do
|
6186
|
+
map_instances to: :persons
|
6187
|
+
end
|
6188
|
+
end
|
6189
|
+
|
6190
|
+
jsonl = <<~JSONL
|
6191
|
+
{"name":"John","age":30,"id":"abc-123"}
|
6192
|
+
{"name":"Jane","age":25,"id":"def-456"}
|
6193
|
+
JSONL
|
6194
|
+
|
6195
|
+
jsonl = Directory.from_jsonl(jsonl)
|
6196
|
+
|
6197
|
+
# => <Directory:0x00007fae4b0c9b10
|
6198
|
+
# @persons=[
|
6199
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
6200
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
6201
|
+
# ]>
|
6202
|
+
----
|
6203
|
+
====
|
6204
|
+
|
6205
|
+
.Parsing a JSONL collection relying on JSON mappings on instance model
|
6206
|
+
[example]
|
6207
|
+
====
|
6208
|
+
[source,ruby]
|
6209
|
+
----
|
6210
|
+
class Person
|
6211
|
+
attribute :name, :string
|
6212
|
+
attribute :age, :integer
|
6213
|
+
attribute :id, :string
|
6214
|
+
|
6215
|
+
json do
|
6216
|
+
map "full_name", to: :name
|
6217
|
+
map "age", to: :age
|
6218
|
+
map "id", to: :id
|
6219
|
+
end
|
6220
|
+
end
|
6221
|
+
|
6222
|
+
class Directory < Lutaml::Model::Collection
|
6223
|
+
instances :persons, Person
|
6224
|
+
|
6225
|
+
jsonl do
|
6226
|
+
map_instances to: :persons
|
6227
|
+
end
|
6228
|
+
end
|
6229
|
+
|
6230
|
+
jsonl = <<~JSONL
|
6231
|
+
{"full_name":"John Doe","age":30,"id":"abc-123"}
|
6232
|
+
{"full_name":"Jane Smith","age":25,"id":"def-456"}
|
6233
|
+
JSONL
|
6234
|
+
|
6235
|
+
jsonl = Directory.from_jsonl(jsonl)
|
6236
|
+
# => <Directory:0x00007fae4b0c9b10
|
6237
|
+
# @persons=[
|
6238
|
+
# <Person:0x00007fae4b0c9970 @name="John Doe", @age=30, @id="abc-123">,
|
6239
|
+
# <Person:0x00007fae4b0c9838 @name="Jane Smith", @age=25, @id="def-456">
|
6240
|
+
# ]>
|
6241
|
+
----
|
6242
|
+
====
|
6243
|
+
|
4482
6244
|
|
4483
|
-
|
4484
|
-
[example]
|
4485
|
-
====
|
4486
|
-
In the following JSON content, the `path_to_value` of `[:extras, :sunroof]` and
|
4487
|
-
`[:extras, :drinks_cooler]` at the object `"gearbox"` would be set to `nil`.
|
6245
|
+
==== YAML Stream
|
4488
6246
|
|
4489
|
-
|
4490
|
-
|
4491
|
-
|
4492
|
-
|
4493
|
-
"engine": {
|
4494
|
-
"manufacturer": "Ford",
|
4495
|
-
"extras": {
|
4496
|
-
"sunroof": true,
|
4497
|
-
"drinks_cooler": true
|
4498
|
-
}
|
4499
|
-
},
|
4500
|
-
"gearbox": {
|
4501
|
-
"manufacturer": "Toyota"
|
4502
|
-
}
|
4503
|
-
}
|
4504
|
-
}
|
4505
|
-
----
|
4506
|
-
====
|
6247
|
+
YAML Stream (short for YAML multi-document format) is a serialization format
|
6248
|
+
where each document is separated by a document separator (`---`). The format is
|
6249
|
+
meant to be efficient for large datasets such as for streaming or batch
|
6250
|
+
processing.
|
4507
6251
|
|
6252
|
+
It represents a collection of YAML documents encoded one document per stream.
|
4508
6253
|
|
4509
|
-
|
6254
|
+
NOTE: The contents of YAML Stream is valid YAML, where each document is a valid
|
6255
|
+
YAML document separated by document separators.
|
6256
|
+
|
6257
|
+
Since YAML Stream contains YAML elements, the model specified with `instances`
|
6258
|
+
or `attribute` must support YAML.
|
6259
|
+
|
6260
|
+
Every document in a YAML Stream file is also a valid YAML document. If YAML
|
6261
|
+
Stream-specific mappings (through `yamls`) are not defined in the model, the
|
6262
|
+
existing `yaml` mappings are used instead as a fallback for serialization and
|
6263
|
+
deserialization.
|
6264
|
+
|
6265
|
+
|
6266
|
+
.Parsing a YAML Stream collection using a collection
|
4510
6267
|
[example]
|
4511
6268
|
====
|
4512
|
-
|
4513
|
-
|
4514
|
-
[source,json]
|
4515
|
-
----
|
4516
|
-
{
|
4517
|
-
"schemas": {
|
4518
|
-
"foo": { <1>
|
4519
|
-
"path": { <2>
|
4520
|
-
"link": "link one",
|
4521
|
-
"name": "one"
|
4522
|
-
}
|
4523
|
-
},
|
4524
|
-
"bar": { <1>
|
4525
|
-
"path": { <2>
|
4526
|
-
"link": "link two",
|
4527
|
-
"name": "two"
|
4528
|
-
}
|
4529
|
-
}
|
4530
|
-
}
|
4531
|
-
}
|
6269
|
+
[source,ruby]
|
4532
6270
|
----
|
4533
|
-
|
4534
|
-
|
4535
|
-
|
6271
|
+
class Person
|
6272
|
+
attribute :name, :string
|
6273
|
+
attribute :age, :integer
|
6274
|
+
attribute :id, :string
|
6275
|
+
end
|
4536
6276
|
|
4537
|
-
|
6277
|
+
class Directory < Lutaml::Model::Collection
|
6278
|
+
instances :persons, Person
|
6279
|
+
|
6280
|
+
yamls do
|
6281
|
+
map_instances to: :persons
|
6282
|
+
end
|
6283
|
+
end
|
6284
|
+
|
6285
|
+
yamls = <<~YAMLS
|
6286
|
+
---
|
6287
|
+
name: John
|
6288
|
+
age: 30
|
6289
|
+
id: abc-123
|
6290
|
+
---
|
6291
|
+
name: Jane
|
6292
|
+
age: 25
|
6293
|
+
id: def-456
|
6294
|
+
YAMLS
|
6295
|
+
|
6296
|
+
yamls = Directory.from_yamls(yamls)
|
4538
6297
|
|
6298
|
+
# => <Directory:0x00007fae4b0c9b10
|
6299
|
+
# @persons=[
|
6300
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
6301
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
6302
|
+
# ]>
|
6303
|
+
----
|
6304
|
+
====
|
6305
|
+
|
6306
|
+
.Parsing a YAML Stream collection using a singlular model
|
6307
|
+
[example]
|
6308
|
+
====
|
4539
6309
|
[source,ruby]
|
4540
6310
|
----
|
4541
|
-
class
|
4542
|
-
attribute :id, :string
|
4543
|
-
attribute :link, :string
|
6311
|
+
class Person
|
4544
6312
|
attribute :name, :string
|
6313
|
+
attribute :age, :integer
|
6314
|
+
attribute :id, :string
|
4545
6315
|
end
|
4546
6316
|
|
4547
|
-
class
|
4548
|
-
attribute :
|
6317
|
+
class Directory < Lutaml::Model::Serializeable
|
6318
|
+
attribute :persons, Person, collection: true
|
4549
6319
|
|
4550
|
-
|
4551
|
-
|
4552
|
-
child_mappings: {
|
4553
|
-
id: :key,
|
4554
|
-
link: %i[path link],
|
4555
|
-
name: %i[path name],
|
4556
|
-
}
|
6320
|
+
yamls do
|
6321
|
+
map_instances to: :persons
|
4557
6322
|
end
|
4558
6323
|
end
|
4559
|
-
----
|
4560
6324
|
|
4561
|
-
|
6325
|
+
yamls = <<~YAMLS
|
6326
|
+
---
|
6327
|
+
name: John
|
6328
|
+
age: 30
|
6329
|
+
id: abc-123
|
6330
|
+
---
|
6331
|
+
name: Jane
|
6332
|
+
age: 25
|
6333
|
+
id: def-456
|
6334
|
+
YAMLS
|
4562
6335
|
|
4563
|
-
|
6336
|
+
yamls = Directory.from_yamls(yamls)
|
6337
|
+
|
6338
|
+
# => <Directory:0x00007fae4b0c9b10
|
6339
|
+
# @persons=[
|
6340
|
+
# <Person:0x00007fae4b0c9970 @name="John", @age=30, @id="abc-123">,
|
6341
|
+
# <Person:0x00007fae4b0c9838 @name="Jane", @age=25, @id="def-456">
|
6342
|
+
# ]>
|
4564
6343
|
----
|
4565
|
-
|
4566
|
-
|
4567
|
-
|
4568
|
-
|
4569
|
-
|
4570
|
-
|
4571
|
-
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}}
|
6344
|
+
====
|
6345
|
+
|
6346
|
+
.Parsing a YAML Stream collection relying on YAML mappings on instance model
|
6347
|
+
[example]
|
6348
|
+
====
|
6349
|
+
[source,ruby]
|
4572
6350
|
----
|
6351
|
+
class Person
|
6352
|
+
attribute :name, :string
|
6353
|
+
attribute :age, :integer
|
6354
|
+
attribute :id, :string
|
4573
6355
|
|
4574
|
-
|
6356
|
+
yaml do
|
6357
|
+
map "full_name", to: :name
|
6358
|
+
map "age", to: :age
|
6359
|
+
map "id", to: :id
|
6360
|
+
end
|
6361
|
+
end
|
4575
6362
|
|
4576
|
-
|
6363
|
+
class Directory < Lutaml::Model::Collection
|
6364
|
+
instances :persons, Person
|
4577
6365
|
|
4578
|
-
|
4579
|
-
|
6366
|
+
yamls do
|
6367
|
+
map_instances to: :persons
|
6368
|
+
end
|
6369
|
+
end
|
6370
|
+
|
6371
|
+
yamls = <<~YAMLS
|
6372
|
+
---
|
6373
|
+
full_name: John Doe
|
6374
|
+
age: 30
|
6375
|
+
id: abc-123
|
6376
|
+
---
|
6377
|
+
full_name: Jane Smith
|
6378
|
+
age: 25
|
6379
|
+
id: def-456
|
6380
|
+
YAMLS
|
6381
|
+
|
6382
|
+
yamls = Directory.from_yamls(yamls)
|
6383
|
+
# => <Directory:0x00007fae4b0c9b10
|
6384
|
+
# @persons=[
|
6385
|
+
# <Person:0x00007fae4b0c9970 @name="John Doe", @age=30, @id="abc-123">,
|
6386
|
+
# <Person:0x00007fae4b0c9838 @name="Jane Smith", @age=25, @id="def-456">
|
6387
|
+
# ]>
|
6388
|
+
----
|
4580
6389
|
====
|
4581
6390
|
|
4582
6391
|
=== Format-independent mechanisms
|
@@ -6997,9 +8806,91 @@ puts SomeModel.new.to_yaml
|
|
6997
8806
|
|
6998
8807
|
|
6999
8808
|
|
7000
|
-
==
|
8809
|
+
== Schema generation and import
|
7001
8810
|
|
7002
|
-
===
|
8811
|
+
=== Schema generation
|
8812
|
+
|
8813
|
+
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.
|
8814
|
+
|
8815
|
+
Currently, the following schema formats are supported:
|
8816
|
+
|
8817
|
+
* JSON Schema (https://json-schema.org/understanding-json-schema/[JSON Schema])
|
8818
|
+
* YAML Schema (https://yaml.org/spec/1.2/spec.html[YAML])
|
8819
|
+
|
8820
|
+
==== JSON Schema generation
|
8821
|
+
|
8822
|
+
The `Lutaml::Model::Schema.to_json` method generates a JSON Schema from a LutaML model class. The generated schema includes:
|
8823
|
+
|
8824
|
+
* Properties based on model attributes
|
8825
|
+
* Validation constraints (patterns, enumerations, etc.)
|
8826
|
+
* Support for polymorphic types
|
8827
|
+
* Support for inheritance
|
8828
|
+
* Support for choice attributes
|
8829
|
+
* Collection constraints
|
8830
|
+
|
8831
|
+
Example:
|
8832
|
+
|
8833
|
+
[source,ruby]
|
8834
|
+
----
|
8835
|
+
class Glaze < Lutaml::Model::Serializable
|
8836
|
+
attribute :color, :string
|
8837
|
+
attribute :finish, :string
|
8838
|
+
end
|
8839
|
+
|
8840
|
+
class Vase < Lutaml::Model::Serializable
|
8841
|
+
attribute :height, :float
|
8842
|
+
attribute :diameter, :float
|
8843
|
+
attribute :glaze, Glaze
|
8844
|
+
attribute :materials, :string, collection: true
|
8845
|
+
end
|
8846
|
+
|
8847
|
+
# Generate JSON schema
|
8848
|
+
schema = Lutaml::Model::Schema.to_json(
|
8849
|
+
Vase,
|
8850
|
+
id: "https://example.com/vase.schema.json",
|
8851
|
+
description: "A vase schema",
|
8852
|
+
pretty: true
|
8853
|
+
)
|
8854
|
+
|
8855
|
+
# Write to file
|
8856
|
+
File.write("vase.schema.json", schema)
|
8857
|
+
----
|
8858
|
+
|
8859
|
+
The generated schema will include definitions for all nested models and their attributes.
|
8860
|
+
|
8861
|
+
==== YAML Schema generation
|
8862
|
+
|
8863
|
+
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.
|
8864
|
+
|
8865
|
+
Example:
|
8866
|
+
|
8867
|
+
[source,ruby]
|
8868
|
+
----
|
8869
|
+
class Glaze < Lutaml::Model::Serializable
|
8870
|
+
attribute :color, :string
|
8871
|
+
attribute :finish, :string
|
8872
|
+
end
|
8873
|
+
|
8874
|
+
class Vase < Lutaml::Model::Serializable
|
8875
|
+
attribute :height, :float
|
8876
|
+
attribute :diameter, :float
|
8877
|
+
attribute :glaze, Glaze
|
8878
|
+
attribute :materials, :string, collection: true
|
8879
|
+
end
|
8880
|
+
|
8881
|
+
# Generate YAML schema
|
8882
|
+
schema = Lutaml::Model::Schema.to_yaml(
|
8883
|
+
Vase,
|
8884
|
+
id: "http://example.com/schemas/vase",
|
8885
|
+
description: "A vase schema",
|
8886
|
+
pretty: true
|
8887
|
+
)
|
8888
|
+
|
8889
|
+
# Write to file
|
8890
|
+
File.write("vase.schema.yaml", schema)
|
8891
|
+
----
|
8892
|
+
|
8893
|
+
=== Importing data models
|
7003
8894
|
|
7004
8895
|
Lutaml::Model provides a way to import data models defined from various formats
|
7005
8896
|
into the LutaML data modeling system.
|
@@ -7524,60 +9415,129 @@ puts output
|
|
7524
9415
|
----
|
7525
9416
|
====
|
7526
9417
|
|
7527
|
-
|
9418
|
+
|
9419
|
+
== Serialization adapters
|
7528
9420
|
|
7529
9421
|
=== General
|
7530
9422
|
|
7531
|
-
|
7532
|
-
serialization format.
|
9423
|
+
The LutaML component that serializes a model into a serialization format is
|
9424
|
+
called an adapter. A serialization format may be supported by multiple adapters.
|
9425
|
+
|
9426
|
+
An adapter typically:
|
7533
9427
|
|
7534
|
-
|
9428
|
+
* supports a specific serialization format
|
9429
|
+
* provides a set of methods to serialize and deserialize models and collections of models
|
9430
|
+
|
9431
|
+
LutaML, out of the box, supports the following serialization formats:
|
7535
9432
|
|
7536
9433
|
* XML (https://www.w3.org/TR/xmlschema-1/[W3C XML Schema (Second Edition)], XML 1.0)
|
7537
9434
|
* YAML (https://yaml.org/[YAML version 1.2])
|
7538
9435
|
* 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])
|
7539
9436
|
* TOML (https://toml.io/en[TOML version 1.0])
|
7540
|
-
* You can also create custom adapters for additional data formats. See link:docs/custom_adapters.adoc[Custom Adapters Guide] for detailed information.
|
7541
9437
|
|
7542
|
-
|
7543
|
-
|
9438
|
+
The adapter interface is also used to support certain transformation of models
|
9439
|
+
into an "end format", which is not a serialization format. For example, the
|
9440
|
+
`Lutaml::Model::HashAdapter` is used to convert a model into a hash format
|
9441
|
+
that is not a serialization format.
|
9442
|
+
|
9443
|
+
Users can extend LutaML by creating custom adapters for other serialization
|
9444
|
+
formats or for other data formats. The
|
9445
|
+
link:docs/custom_adapters.adoc[Custom Adapters Guide] describes this process in
|
9446
|
+
detail.
|
9447
|
+
|
9448
|
+
For certain serialization formats, LutaML provides multiple adapters to support
|
9449
|
+
different serialization libraries. Please refer to their specific sections for
|
9450
|
+
more information.
|
9451
|
+
|
7544
9452
|
|
7545
|
-
|
9453
|
+
=== Configuration
|
9454
|
+
|
9455
|
+
==== General
|
9456
|
+
|
9457
|
+
It is necessary to configure the adapter to be used for serialization and
|
9458
|
+
deserialization for a set of formats that the LutaML models will be transformed
|
9459
|
+
into.
|
9460
|
+
|
9461
|
+
There are two cases where you need to define such configuration:
|
9462
|
+
|
9463
|
+
* End-user usage of the LutaML models. This is the case where you are
|
9464
|
+
using LutaML models in your application and want to serialize them into a
|
9465
|
+
specific format. If you are a gem developer that relies on lutaml-model,
|
9466
|
+
this case does not apply to you, because the end-user of your gem should
|
9467
|
+
determine the adapter configuration.
|
9468
|
+
|
9469
|
+
* Testing purposes, e.g. RSpec. In order to run tests that involve verifying
|
9470
|
+
correctness of serialization, it is necessary to define adapter configuration.
|
9471
|
+
|
9472
|
+
There are two ways to specify a configuration:
|
9473
|
+
|
9474
|
+
* by providing a predefined symbol (preferred)
|
9475
|
+
* by providing the actual adapter classes
|
9476
|
+
|
9477
|
+
There is a default configuration for adapters for commonly used formats:
|
9478
|
+
|
9479
|
+
* YAML: `yaml_adapter_type` is set to `:standard_yaml`
|
9480
|
+
* JSON: `json_adapter_type` is set to `:standard_json`
|
9481
|
+
* Hash: `hash_adapter_type` is set to `:standard_hash`
|
9482
|
+
* XML: not defined
|
9483
|
+
* TOML: not defined
|
9484
|
+
|
9485
|
+
|
9486
|
+
==== Configure adapters through symbol choices
|
9487
|
+
|
9488
|
+
The end-user or a gem developer can copy and paste the following configuration
|
9489
|
+
into an early loading file in their application or gem.
|
9490
|
+
|
9491
|
+
This configuration is preferred over the class choices because it is more
|
9492
|
+
concise and does not require any `require` code specific to the internals
|
9493
|
+
of the LutaML runtime implementation.
|
9494
|
+
|
9495
|
+
Syntax:
|
7546
9496
|
|
7547
9497
|
[source,ruby]
|
7548
9498
|
----
|
7549
9499
|
require 'lutaml/model'
|
7550
|
-
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
7551
|
-
require 'lutaml/model/json_adapter/standard_json_adapter'
|
7552
|
-
require 'lutaml/model/toml_adapter/toml_rb_adapter'
|
7553
|
-
require 'lutaml/model/yaml_adapter/standard_yaml_adapter'
|
7554
9500
|
|
7555
9501
|
Lutaml::Model::Config.configure do |config|
|
7556
|
-
config.
|
7557
|
-
config.
|
7558
|
-
config.
|
7559
|
-
config.
|
7560
|
-
config.
|
9502
|
+
config.xml_adapter_type = :nokogiri # can be one of [:nokogiri, :ox, :oga]
|
9503
|
+
config.hash_adapter_type = :standard_hash
|
9504
|
+
config.yaml_adapter_type = :standard_yaml
|
9505
|
+
config.json_adapter_type = :standard_json # can be one of [:standard_json, :multi_json]
|
9506
|
+
config.toml_adapter_type = :toml_rb # can be one of [:toml_rb, :tomlib]
|
7561
9507
|
end
|
7562
9508
|
----
|
7563
9509
|
|
7564
|
-
You can also provide the adapter type by using symbols like
|
7565
9510
|
|
9511
|
+
==== Configure adapters through class choices
|
9512
|
+
|
9513
|
+
The end-uesr or a gem developer can copy and paste the following configuration
|
9514
|
+
into an early loading file in their application or gem.
|
9515
|
+
|
9516
|
+
Only the serialization formats used will require a configuration.
|
9517
|
+
|
9518
|
+
Syntax:
|
9519
|
+
|
9520
|
+
[example]
|
9521
|
+
====
|
7566
9522
|
[source,ruby]
|
7567
9523
|
----
|
7568
9524
|
require 'lutaml/model'
|
9525
|
+
require 'lutaml/model/xml/nokogiri_adapter'
|
9526
|
+
require 'lutaml/model/hash_adapter/standard_adapter'
|
9527
|
+
require 'lutaml/model/json/standard_adapter'
|
9528
|
+
require 'lutaml/model/yaml/standard_adapter'
|
9529
|
+
require 'lutaml/model/toml/toml_rb_adapter'
|
7569
9530
|
|
7570
9531
|
Lutaml::Model::Config.configure do |config|
|
7571
|
-
config.
|
7572
|
-
config.
|
7573
|
-
config.
|
7574
|
-
config.
|
7575
|
-
config.
|
9532
|
+
config.xml_adapter = Lutaml::Model::Xml::NokogiriAdapter
|
9533
|
+
config.hash_adapter = Lutaml::Model::HashAdapter::StandardAdapter
|
9534
|
+
config.yaml_adapter = Lutaml::Model::Yaml::StandardAdapter
|
9535
|
+
config.json_adapter = Lutaml::Model::Json::StandardAdapter
|
9536
|
+
config.toml_adapter = Lutaml::Model::Toml::TomlRbAdapter
|
7576
9537
|
end
|
7577
9538
|
----
|
9539
|
+
====
|
7578
9540
|
|
7579
|
-
NOTE: By default `yaml_adapter_type` and `json_adapter_type` are set to
|
7580
|
-
`:standard_yaml` and `:standard_json` respectively.
|
7581
9541
|
|
7582
9542
|
|
7583
9543
|
=== XML
|
@@ -7604,36 +9564,17 @@ Requires native extensions (i.e. compiled C code).
|
|
7604
9564
|
Requires the `ox` gem.
|
7605
9565
|
|
7606
9566
|
|
7607
|
-
.Using
|
7608
|
-
[source,ruby]
|
7609
|
-
----
|
7610
|
-
require 'lutaml/model'
|
7611
|
-
|
7612
|
-
Lutaml::Model::Config.configure do |config|
|
7613
|
-
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
7614
|
-
config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
|
7615
|
-
end
|
7616
|
-
----
|
7617
|
-
|
7618
|
-
.Using the Oga XML adapter
|
7619
|
-
[source,ruby]
|
7620
|
-
----
|
7621
|
-
require 'lutaml/model'
|
7622
|
-
|
7623
|
-
Lutaml::Model::Config.configure do |config|
|
7624
|
-
require 'lutaml/model/xml_adapter/oga_adapter'
|
7625
|
-
config.xml_adapter = Lutaml::Model::XmlAdapter::OgaAdapter
|
7626
|
-
end
|
7627
|
-
----
|
7628
|
-
|
7629
|
-
.Using the Ox XML adapter
|
9567
|
+
.Using an XML adapter
|
7630
9568
|
[source,ruby]
|
7631
9569
|
----
|
7632
9570
|
require 'lutaml/model'
|
7633
9571
|
|
7634
9572
|
Lutaml::Model::Config.configure do |config|
|
7635
|
-
|
7636
|
-
|
9573
|
+
config.xml_adapter = :nokogiri
|
9574
|
+
# or
|
9575
|
+
config.xml_adapter = :oga
|
9576
|
+
# or
|
9577
|
+
config.xml_adapter = :ox
|
7637
9578
|
end
|
7638
9579
|
----
|
7639
9580
|
|
@@ -7647,14 +9588,13 @@ YAML::
|
|
7647
9588
|
The Psych YAML parser and emitter for Ruby.
|
7648
9589
|
Included in the Ruby standard library.
|
7649
9590
|
|
7650
|
-
.Using
|
9591
|
+
.Using a YAML adapter
|
7651
9592
|
[source,ruby]
|
7652
9593
|
----
|
7653
9594
|
require 'lutaml/model'
|
7654
9595
|
|
7655
9596
|
Lutaml::Model::Config.configure do |config|
|
7656
|
-
|
7657
|
-
config.yaml_adapter = Lutaml::Model::YamlAdapter::StandardYamlAdapter
|
9597
|
+
config.yaml_adapter = :standard_yaml
|
7658
9598
|
end
|
7659
9599
|
----
|
7660
9600
|
|
@@ -7673,27 +9613,18 @@ MultiJson::
|
|
7673
9613
|
A gem that provides a common interface to multiple JSON libraries.
|
7674
9614
|
Requires the `multi_json` gem.
|
7675
9615
|
|
7676
|
-
.Using
|
9616
|
+
.Using a JSON adapter
|
7677
9617
|
[source,ruby]
|
7678
9618
|
----
|
7679
9619
|
require 'lutaml/model'
|
7680
9620
|
|
7681
9621
|
Lutaml::Model::Config.configure do |config|
|
7682
|
-
|
7683
|
-
|
9622
|
+
config.json_adapter = :standard_json
|
9623
|
+
# or
|
9624
|
+
config.json_adapter = :multi_json
|
7684
9625
|
end
|
7685
9626
|
----
|
7686
9627
|
|
7687
|
-
.Using the MultiJson adapter
|
7688
|
-
[source,ruby]
|
7689
|
-
----
|
7690
|
-
require 'lutaml/model'
|
7691
|
-
|
7692
|
-
Lutaml::Model::Config.configure do |config|
|
7693
|
-
require 'lutaml/model/json_adapter/multi_json_adapter'
|
7694
|
-
config.json_adapter = Lutaml::Model::JsonAdapter::MultiJsonAdapter
|
7695
|
-
end
|
7696
|
-
----
|
7697
9628
|
|
7698
9629
|
=== TOML
|
7699
9630
|
|
@@ -7712,33 +9643,82 @@ additional features.
|
|
7712
9643
|
Requires the `tomlib` gem.
|
7713
9644
|
|
7714
9645
|
|
7715
|
-
.Using
|
9646
|
+
.Using a TOML adapter
|
7716
9647
|
[source,ruby]
|
7717
9648
|
----
|
7718
9649
|
require 'lutaml/model'
|
7719
9650
|
|
7720
9651
|
Lutaml::Model::Config.configure do |config|
|
7721
|
-
|
7722
|
-
|
9652
|
+
config.toml_adapter = :toml_rb
|
9653
|
+
# or
|
9654
|
+
config.toml_adapter = :tomlib
|
7723
9655
|
end
|
7724
9656
|
----
|
7725
9657
|
|
7726
|
-
.Using the Tomlib adapter
|
7727
|
-
[source,ruby]
|
7728
|
-
----
|
7729
|
-
require 'lutaml/model'
|
7730
|
-
|
7731
|
-
Lutaml::Model::Config.configure do |config|
|
7732
|
-
config.toml_adapter = Lutaml::Model::TomlAdapter::TomlibAdapter
|
7733
|
-
require 'lutaml/model/toml_adapter/tomlib_adapter'
|
7734
|
-
end
|
7735
|
-
----
|
7736
9658
|
|
7737
9659
|
[[custom-adapters]]
|
7738
|
-
|
9660
|
+
== Custom serialization adapters
|
9661
|
+
|
9662
|
+
Lutaml::Model provides a flexible system for creating custom adapters to handle
|
9663
|
+
different data formats.
|
9664
|
+
|
9665
|
+
Please refer to link:docs/custom_adapters.adoc[Custom adapters] for details
|
9666
|
+
and examples.
|
9667
|
+
|
9668
|
+
|
9669
|
+
[[schema-generation]]
|
9670
|
+
== Schema generation
|
9671
|
+
|
9672
|
+
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.
|
9673
|
+
|
9674
|
+
Currently, the following schema formats are supported:
|
9675
|
+
|
9676
|
+
* JSON Schema (https://json-schema.org/understanding-json-schema/[JSON Schema])
|
9677
|
+
* YAML Schema (https://yaml.org/spec/1.2/spec.html[YAML])
|
9678
|
+
* XSD (https://w3.org/TR/xmlschema-1/[XML Schema Definition Language])
|
9679
|
+
* RELAX NG (https://relaxng.org/[RELAX NG])
|
9680
|
+
|
9681
|
+
The schema generation supports advanced features such as:
|
7739
9682
|
|
7740
|
-
|
7741
|
-
|
9683
|
+
* Validation constraints (patterns, enumerations, ranges)
|
9684
|
+
* Choice attributes with min/max constraints
|
9685
|
+
* Polymorphic types with oneOf validation
|
9686
|
+
* Collection constraints with minItems/maxItems
|
9687
|
+
|
9688
|
+
Please refer to link:docs/schema_generation.adoc[Schema Generation] for details
|
9689
|
+
and examples.
|
9690
|
+
|
9691
|
+
|
9692
|
+
[[schema-import]]
|
9693
|
+
== Schema import
|
9694
|
+
|
9695
|
+
Lutaml::Model provides functionality to import schema definitions into LutaML
|
9696
|
+
models. This allows you to create models from existing schema definitions.
|
9697
|
+
|
9698
|
+
Currently, the following schema formats are supported:
|
9699
|
+
|
9700
|
+
* XSD (https://w3.org/TR/xmlschema-1/[XML Schema Definition Language])
|
9701
|
+
* JSON/YAML Schema
|
9702
|
+
|
9703
|
+
Please refer to link:docs/schema_import.adoc[Schema Import] for details
|
9704
|
+
and examples.
|
9705
|
+
|
9706
|
+
|
9707
|
+
[[custom-registers]]
|
9708
|
+
=== Custom Registers
|
9709
|
+
|
9710
|
+
A *LutaML::Model* *Register* allows for dynamic modification and reconfiguration of model hierarchies without altering the original model definitions.
|
9711
|
+
For more information, refer to the link:docs/custom_registers.adoc[Custom Registers Guide].
|
9712
|
+
|
9713
|
+
NOTE: Before using the `Lutaml::Model::Register` instance, make sure to register it in `Lutaml::Model::GlobalRegister`.
|
9714
|
+
|
9715
|
+
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.
|
9716
|
+
|
9717
|
+
The default register can be set at the configuration level using the following syntax:
|
9718
|
+
|
9719
|
+
```ruby
|
9720
|
+
Lutaml::Model::Config.default_register = :default # the register id goes here.
|
9721
|
+
```
|
7742
9722
|
|
7743
9723
|
== Comparison with Shale
|
7744
9724
|
|
@@ -7853,6 +9833,11 @@ data models.
|
|
7853
9833
|
| No.
|
7854
9834
|
| Lutaml::Model supports attribute extraction from key-value data models.
|
7855
9835
|
|
9836
|
+
| Register
|
9837
|
+
| Yes. Supports three types of registers(<<custom-register, read more details>>) with different types of functionalities.
|
9838
|
+
| Supports `register` functionality for *Shale::Type* classes only.
|
9839
|
+
| `Lutaml::Model` registers both `Registrable` classes and `Lutaml::Model::Type` classes, offering a more comprehensive registration system.
|
9840
|
+
|
7856
9841
|
|===
|
7857
9842
|
|
7858
9843
|
|