lutaml-model 0.7.1 → 0.7.2

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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.rubocop_todo.yml +49 -48
  4. data/Gemfile +4 -1
  5. data/README.adoc +791 -143
  6. data/RELEASE_NOTES.adoc +346 -0
  7. data/docs/custom_adapters.adoc +144 -0
  8. data/lib/lutaml/model/attribute.rb +17 -11
  9. data/lib/lutaml/model/config.rb +48 -42
  10. data/lib/lutaml/model/error/polymorphic_error.rb +7 -2
  11. data/lib/lutaml/model/format_registry.rb +41 -0
  12. data/lib/lutaml/model/hash/document.rb +11 -0
  13. data/lib/lutaml/model/hash/mapping.rb +19 -0
  14. data/lib/lutaml/model/hash/mapping_rule.rb +9 -0
  15. data/lib/lutaml/model/hash/standard_adapter.rb +17 -0
  16. data/lib/lutaml/model/hash/transform.rb +8 -0
  17. data/lib/lutaml/model/hash.rb +21 -0
  18. data/lib/lutaml/model/json/document.rb +11 -0
  19. data/lib/lutaml/model/json/mapping.rb +19 -0
  20. data/lib/lutaml/model/json/mapping_rule.rb +9 -0
  21. data/lib/lutaml/model/{json_adapter → json}/multi_json_adapter.rb +4 -5
  22. data/lib/lutaml/model/{json_adapter/standard_json_adapter.rb → json/standard_adapter.rb} +5 -3
  23. data/lib/lutaml/model/json/transform.rb +8 -0
  24. data/lib/lutaml/model/json.rb +21 -0
  25. data/lib/lutaml/model/key_value_document.rb +27 -0
  26. data/lib/lutaml/model/mapping/key_value_mapping.rb +8 -4
  27. data/lib/lutaml/model/mapping/mapping.rb +13 -0
  28. data/lib/lutaml/model/mapping/mapping_rule.rb +7 -6
  29. data/lib/lutaml/model/serialization_adapter.rb +22 -0
  30. data/lib/lutaml/model/serialize.rb +146 -521
  31. data/lib/lutaml/model/services/logger.rb +54 -0
  32. data/lib/lutaml/model/services/transformer.rb +48 -0
  33. data/lib/lutaml/model/services.rb +2 -0
  34. data/lib/lutaml/model/toml/document.rb +11 -0
  35. data/lib/lutaml/model/toml/mapping.rb +27 -0
  36. data/lib/lutaml/model/toml/mapping_rule.rb +9 -0
  37. data/lib/lutaml/model/{toml_adapter → toml}/toml_rb_adapter.rb +3 -3
  38. data/lib/lutaml/model/toml/tomlib_adapter.rb +19 -0
  39. data/lib/lutaml/model/toml/transform.rb +8 -0
  40. data/lib/lutaml/model/toml.rb +30 -0
  41. data/lib/lutaml/model/transform/key_value_transform.rb +291 -0
  42. data/lib/lutaml/model/transform/xml_transform.rb +239 -0
  43. data/lib/lutaml/model/transform.rb +78 -0
  44. data/lib/lutaml/model/type/value.rb +6 -9
  45. data/lib/lutaml/model/uninitialized_class.rb +1 -1
  46. data/lib/lutaml/model/utils.rb +30 -0
  47. data/lib/lutaml/model/version.rb +1 -1
  48. data/lib/lutaml/model/{xml_adapter → xml}/builder/nokogiri.rb +2 -2
  49. data/lib/lutaml/model/{xml_adapter → xml}/builder/oga.rb +10 -10
  50. data/lib/lutaml/model/{xml_adapter → xml}/builder/ox.rb +1 -1
  51. data/lib/lutaml/model/{xml_adapter/xml_document.rb → xml/document.rb} +6 -7
  52. data/lib/lutaml/model/xml/element.rb +32 -0
  53. data/lib/lutaml/model/xml/mapping.rb +410 -0
  54. data/lib/lutaml/model/xml/mapping_rule.rb +141 -0
  55. data/lib/lutaml/model/xml/nokogiri_adapter.rb +232 -0
  56. data/lib/lutaml/model/{xml_adapter → xml}/oga/document.rb +1 -1
  57. data/lib/lutaml/model/{xml_adapter → xml}/oga/element.rb +3 -1
  58. data/lib/lutaml/model/xml/oga_adapter.rb +171 -0
  59. data/lib/lutaml/model/xml/ox_adapter.rb +215 -0
  60. data/lib/lutaml/model/xml/transform.rb +8 -0
  61. data/lib/lutaml/model/{xml_adapter → xml}/xml_attribute.rb +1 -1
  62. data/lib/lutaml/model/{xml_adapter → xml}/xml_element.rb +6 -3
  63. data/lib/lutaml/model/{xml_adapter → xml}/xml_namespace.rb +1 -1
  64. data/lib/lutaml/model/xml.rb +31 -0
  65. data/lib/lutaml/model/xml_adapter/element.rb +11 -25
  66. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +6 -223
  67. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +13 -163
  68. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +10 -207
  69. data/lib/lutaml/model/yaml/document.rb +10 -0
  70. data/lib/lutaml/model/yaml/mapping.rb +19 -0
  71. data/lib/lutaml/model/yaml/mapping_rule.rb +9 -0
  72. data/lib/lutaml/model/{yaml_adapter/standard_yaml_adapter.rb → yaml/standard_adapter.rb} +4 -3
  73. data/lib/lutaml/model/yaml/transform.rb +8 -0
  74. data/lib/lutaml/model/yaml.rb +21 -0
  75. data/lib/lutaml/model.rb +39 -4
  76. data/lutaml-model.gemspec +0 -4
  77. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +4 -4
  78. data/spec/lutaml/model/cdata_spec.rb +7 -7
  79. data/spec/lutaml/model/custom_bibtex_adapter_spec.rb +598 -0
  80. data/spec/lutaml/model/custom_vobject_adapter_spec.rb +1226 -0
  81. data/spec/lutaml/model/group_spec.rb +18 -7
  82. data/spec/lutaml/model/hash/adapter_spec.rb +255 -0
  83. data/spec/lutaml/model/json_adapter_spec.rb +6 -6
  84. data/spec/lutaml/model/key_value_mapping_spec.rb +25 -1
  85. data/spec/lutaml/model/mixed_content_spec.rb +24 -24
  86. data/spec/lutaml/model/multiple_mapping_spec.rb +5 -5
  87. data/spec/lutaml/model/ordered_content_spec.rb +6 -6
  88. data/spec/lutaml/model/polymorphic_spec.rb +178 -0
  89. data/spec/lutaml/model/root_mappings_spec.rb +3 -3
  90. data/spec/lutaml/model/schema/xml_compiler_spec.rb +6 -6
  91. data/spec/lutaml/model/serializable_spec.rb +179 -103
  92. data/spec/lutaml/model/toml_adapter_spec.rb +6 -6
  93. data/spec/lutaml/model/toml_spec.rb +51 -0
  94. data/spec/lutaml/model/transformation_spec.rb +72 -15
  95. data/spec/lutaml/model/uninitialized_class_spec.rb +96 -0
  96. data/spec/lutaml/model/xml/namespace_spec.rb +57 -0
  97. data/spec/lutaml/model/xml/xml_element_spec.rb +1 -1
  98. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +2 -2
  99. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +2 -2
  100. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +2 -2
  101. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +6 -6
  102. data/spec/lutaml/model/xml_adapter_spec.rb +6 -6
  103. data/spec/lutaml/model/xml_mapping_rule_spec.rb +3 -3
  104. data/spec/lutaml/model/xml_mapping_spec.rb +26 -14
  105. data/spec/lutaml/model/xml_spec.rb +63 -0
  106. data/spec/lutaml/model/yaml_adapter_spec.rb +3 -5
  107. data/spec/spec_helper.rb +3 -3
  108. metadata +64 -59
  109. data/lib/lutaml/model/json_adapter/json_document.rb +0 -20
  110. data/lib/lutaml/model/json_adapter/json_object.rb +0 -28
  111. data/lib/lutaml/model/loggable.rb +0 -15
  112. data/lib/lutaml/model/mapping/json_mapping.rb +0 -17
  113. data/lib/lutaml/model/mapping/toml_mapping.rb +0 -25
  114. data/lib/lutaml/model/mapping/xml_mapping.rb +0 -389
  115. data/lib/lutaml/model/mapping/xml_mapping_rule.rb +0 -139
  116. data/lib/lutaml/model/mapping/yaml_mapping.rb +0 -17
  117. data/lib/lutaml/model/mapping.rb +0 -14
  118. data/lib/lutaml/model/toml_adapter/toml_document.rb +0 -20
  119. data/lib/lutaml/model/toml_adapter/toml_object.rb +0 -28
  120. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +0 -20
  121. data/lib/lutaml/model/toml_adapter.rb +0 -6
  122. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +0 -20
  123. data/lib/lutaml/model/yaml_adapter/yaml_object.rb +0 -28
  124. data/lib/lutaml/model/yaml_adapter.rb +0 -8
data/README.adoc CHANGED
@@ -17,7 +17,7 @@ for:
17
17
 
18
18
  It provides simple, flexible and comprehensive mechanisms for defining
19
19
  information models with attributes and types, and the serialization of them
20
- to/from serialization formats including JSON, XML, YAML, and TOML.
20
+ to/from serialization formats including Hash, JSON, XML, YAML, and TOML.
21
21
 
22
22
  For serialization formats, it uses an adapter pattern to support multiple
23
23
  libraries for each format, providing flexibility and extensibility for your data
@@ -33,12 +33,13 @@ Lutaml::Model are provided in <<migrate-from-shale>>.
33
33
  == Features
34
34
 
35
35
  * Define models with attributes and types
36
- * Serialize and deserialize models to/from JSON, XML, YAML, and TOML
36
+ * Serialize and deserialize models to/from Hash, JSON, XML, YAML, and TOML
37
37
  * Support for multiple serialization libraries (e.g., `toml-rb`, `tomlib`)
38
38
  * Configurable adapters for different serialization formats
39
39
  * Support for collections and default values
40
40
  * Custom serialization/deserialization methods
41
41
  * XML namespaces and mappings
42
+ * Create custom adapters for additional data formats (see <<custom-adapters>>)
42
43
 
43
44
 
44
45
  == Data modeling in a nutshell
@@ -496,7 +497,7 @@ serialization format and returns the object to be assigned to the `Value` class'
496
497
  `to_{format}`:: Serializes the object to a string of the serialization format.
497
498
 
498
499
  The `{format}` part of the method name is the serialization format in lowercase
499
- (e.g. `json`, `xml`, `yaml`, `toml`).
500
+ (e.g. `hash`, `json`, `xml`, `yaml`, `toml`).
500
501
 
501
502
  .Using custom serialization methods to handle a high-precision date-time type
502
503
  [example]
@@ -1051,16 +1052,16 @@ class Reference < Lutaml::Model::Serializable
1051
1052
 
1052
1053
  xml do
1053
1054
  map_attribute "reference-type", to: :_class, polymorphic_map: {
1054
- "document-ref" => "PolymorphicSpec::Base::DocumentReference",
1055
- "anchor-ref" => "PolymorphicSpec::Base::AnchorReference",
1055
+ "document-ref" => "DocumentReference",
1056
+ "anchor-ref" => "AnchorReference",
1056
1057
  }
1057
1058
  map_element "name", to: :name
1058
1059
  end
1059
1060
 
1060
1061
  key_value do
1061
1062
  map "_class", to: :_class, polymorphic_map: {
1062
- "Document" => "PolymorphicSpec::Base::DocumentReference",
1063
- "Anchor" => "PolymorphicSpec::Base::AnchorReference",
1063
+ "Document" => "DocumentReference",
1064
+ "Anchor" => "AnchorReference",
1064
1065
  }
1065
1066
  map "name", to: :name
1066
1067
  end
@@ -1979,15 +1980,15 @@ For the following XML snippet:
1979
1980
  === General
1980
1981
 
1981
1982
  Lutaml::Model allows you to translate a data model into serialization models of
1982
- various serialization formats including XML, JSON, YAML, and TOML.
1983
+ various serialization formats including XML, Hash, JSON, YAML, and TOML.
1983
1984
 
1984
1985
  Depending on the serialization format, different methods are supported for
1985
1986
  defining serialization and deserialization mappings.
1986
1987
 
1987
- Serialization model mappings are defined under the `xml`, `json`, `yaml`, and
1988
- `toml` blocks.
1988
+ Serialization model mappings are defined under the `xml`, `hsh`, `json`, `yaml`,
1989
+ and `toml` blocks.
1989
1990
 
1990
- .Using the `xml`, `json`, `yaml`, `toml` and `key_value` blocks to define serialization mappings
1991
+ .Using the `xml`, `hsh`, `json`, `yaml`, `toml` and `key_value` blocks to define serialization mappings
1991
1992
  [source,ruby]
1992
1993
  ----
1993
1994
  class Example < Lutaml::Model::Serializable
@@ -1995,6 +1996,10 @@ class Example < Lutaml::Model::Serializable
1995
1996
  # ...
1996
1997
  end
1997
1998
 
1999
+ hsh do
2000
+ # ...
2001
+ end
2002
+
1998
2003
  json do
1999
2004
  # ...
2000
2005
  end
@@ -3448,7 +3453,7 @@ This XML snippet is in UTF-8.
3448
3453
 
3449
3454
  ==== General
3450
3455
 
3451
- Key-value data models like JSON, YAML, and TOML all share a similar structure
3456
+ Key-value data models like Hash, JSON, YAML, and TOML all share a similar structure
3452
3457
  where data is stored as key-value pairs.
3453
3458
 
3454
3459
  `Lutaml::Model` works with these formats in a similar way.
@@ -3461,7 +3466,7 @@ Syntax:
3461
3466
 
3462
3467
  [source,ruby]
3463
3468
  ----
3464
- json | yaml | toml | key_value do
3469
+ hsh | json | yaml | toml | key_value do
3465
3470
  map 'key_value_model_attribute_name', to: :name_of_attribute
3466
3471
  end
3467
3472
  ----
@@ -3470,7 +3475,7 @@ end
3470
3475
  ==== Unified mapping
3471
3476
 
3472
3477
  The `key_value` method is a streamlined way to map all attributes for
3473
- serialization into key-value formats including JSON, YAML, and TOML.
3478
+ serialization into key-value formats including Hash, JSON, YAML, and TOML.
3474
3479
 
3475
3480
  If there is no definite differentiation between the key value formats, the
3476
3481
  `key_value` method simplifies defining mappings and improves code readability.
@@ -3495,9 +3500,9 @@ class CeramicModel < Lutaml::Model::Serializable
3495
3500
  map :desc, to: :description
3496
3501
  end
3497
3502
 
3498
- # Equivalent to the JSON, YAML, and TOML mappings.
3503
+ # Equivalent to the Hash, JSON, YAML, and TOML mappings.
3499
3504
  #
3500
- # json and yaml and toml do
3505
+ # hsh and json and yaml and toml do
3501
3506
  # map :id, to: color
3502
3507
  # map :name, to: :full_name
3503
3508
  # map :status, to: :current_status
@@ -3534,6 +3539,7 @@ desc: A ceramic with a navy blue color and clear glaze.
3534
3539
 
3535
3540
  Specific key value formats can be mapping independently of other formats, including:
3536
3541
 
3542
+ * `hsh` for the Hash format
3537
3543
  * `json` for the JSON format
3538
3544
  * `yaml` for the YAML format
3539
3545
  * `toml` for the TOML format
@@ -3548,6 +3554,11 @@ class Example < Lutaml::Model::Serializable
3548
3554
  attribute :name, :string
3549
3555
  attribute :value, :integer
3550
3556
 
3557
+ hsh do
3558
+ map 'name', to: :name
3559
+ map 'value', to: :value
3560
+ end
3561
+
3551
3562
  json do
3552
3563
  map 'name', to: :name
3553
3564
  map 'value', to: :value
@@ -3610,7 +3621,7 @@ Syntax:
3610
3621
 
3611
3622
  [source,ruby]
3612
3623
  ----
3613
- json | yaml | toml | key_value do
3624
+ hsh | json | yaml | toml | key_value do
3614
3625
  map_all to: :name_of_attribute
3615
3626
  end
3616
3627
  ----
@@ -3623,6 +3634,10 @@ end
3623
3634
  class Document < Lutaml::Model::Serializable
3624
3635
  attribute :content, :string
3625
3636
 
3637
+ hsh do
3638
+ map_all to: :content
3639
+ end
3640
+
3626
3641
  json do
3627
3642
  map_all to: :content
3628
3643
  end
@@ -3796,7 +3811,7 @@ Syntax:
3796
3811
  class SomeKeyedCollection < Lutaml::Model::Serializable
3797
3812
  attribute :name_of_attribute, AttributeValueType, collection: true
3798
3813
 
3799
- json | yaml | toml | key_value do
3814
+ hsh | json | yaml | toml | key_value do
3800
3815
  map to: :name_of_attribute, <1>
3801
3816
  root_mappings: { <2>
3802
3817
  # `:key` is a reserved keyword
@@ -3870,7 +3885,7 @@ Syntax:
3870
3885
  class SomeKeyedCollection < Lutaml::Model::Serializable
3871
3886
  attribute :name_of_attribute, AttributeValueType, collection: true
3872
3887
 
3873
- json | yaml | toml | key_value do
3888
+ hsh | json | yaml | toml | key_value do
3874
3889
  map to: :name_of_attribute,
3875
3890
  root_mappings: {
3876
3891
  value_type_attribute_name_for_key: :key, <1>
@@ -4008,7 +4023,7 @@ Syntax:
4008
4023
  class SomeKeyedCollection < Lutaml::Model::Serializable
4009
4024
  attribute :name_of_attribute, AttributeValueType, collection: true
4010
4025
 
4011
- json | yaml | toml | key_value do
4026
+ hsh | json | yaml | toml | key_value do
4012
4027
  map to: :name_of_attribute,
4013
4028
  root_mappings: {
4014
4029
  value_type_attribute_name_for_key: :key, <1>
@@ -4214,7 +4229,7 @@ Syntax:
4214
4229
  class SomeKeyedCollection < Lutaml::Model::Serializable
4215
4230
  attribute :name_of_attribute, AttributeValueType, collection: true
4216
4231
 
4217
- json | yaml | toml | key_value do
4232
+ hsh | json | yaml | toml | key_value do
4218
4233
  map to: :name_of_attribute,
4219
4234
  root_mappings: {
4220
4235
  value_type_attribute_name_for_key: :key, <1>
@@ -4378,7 +4393,7 @@ end
4378
4393
  NOTE: This feature is for key-value data model serialization only.
4379
4394
 
4380
4395
  The `child_mappings` option is used to extract results from a key-value
4381
- serialization data model (JSON, YAML, TOML) into a `Lutaml::Model::Serializable`
4396
+ serialization data model (Hash, JSON, YAML, TOML) into a `Lutaml::Model::Serializable`
4382
4397
  object (collection or not).
4383
4398
 
4384
4399
  The values are extracted from the key-value data model using the list of keys
@@ -4391,7 +4406,7 @@ Syntax:
4391
4406
  class SomeObject < Lutaml::Model::Serializable
4392
4407
  attribute :name_of_attribute, AttributeValueType, collection: true
4393
4408
 
4394
- json | yaml | toml | key_value do
4409
+ hsh | json | yaml | toml | key_value do
4395
4410
  map 'key_value_model_attribute_name', to: :name_of_attribute,
4396
4411
  child_mappings: {
4397
4412
  value_type_attribute_name_1: <1>
@@ -4639,7 +4654,7 @@ being retrieved from the model.
4639
4654
  Attribute-level `import`:: The transformation `Proc` for the value when it is
4640
4655
  being assigned to the model.
4641
4656
 
4642
- `{key_value_formats}`:: The serialization format (e.g. `json`, `yaml`, `toml`,
4657
+ `{key_value_formats}`:: The serialization format (e.g. `hsh`, `json`, `yaml`, `toml`,
4643
4658
  `key_value`) for which the mapping is defined.
4644
4659
 
4645
4660
  Mapping-level `transform`:: The option to define a transformation for the
@@ -4738,18 +4753,18 @@ format-specific custom methods.
4738
4753
  ╔════════════════════════════╗ ╔════════════════════════════╗
4739
4754
  ║ Serialization Format Value ║ ║ Serialization Format Value ║
4740
4755
  ╚════════════════════════════╝ ╚════════════════════════════╝
4741
- | |
4742
-
4756
+ |
4757
+ |
4743
4758
  ╔════════════════════════════╗ ╔════════════════════════════╗
4744
4759
  ║ Mapping Transform ║ ║ Mapping Transform ║
4745
4760
  ╚════════════════════════════╝ ╚════════════════════════════╝
4746
- | |
4747
-
4761
+ |
4762
+ |
4748
4763
  ╔════════════════════════════╗ ╔════════════════════════════╗
4749
4764
  ║ Attribute Transform ║ ║ Attribute Transform ║
4750
4765
  ╚════════════════════════════╝ ╚════════════════════════════╝
4751
- | |
4752
-
4766
+ |
4767
+ |
4753
4768
  ╔════════════════════════════╗ ╔════════════════════════════╗
4754
4769
  ║ Model Attribute Value ║ ║ Model Attribute Value ║
4755
4770
  ╚════════════════════════════╝ ╚════════════════════════════╝
@@ -4994,7 +5009,7 @@ xml do
4994
5009
  map_attribute 'name_of_attribute', to: :name_of_attribute, render_default: true
4995
5010
  end
4996
5011
 
4997
- json | yaml | toml | key_value do
5012
+ hsh | json | yaml | toml | key_value do
4998
5013
  map 'name_of_attribute', to: :name_of_attribute, render_default: true
4999
5014
  end
5000
5015
  ----
@@ -5071,7 +5086,7 @@ Syntax:
5071
5086
 
5072
5087
  [source,ruby]
5073
5088
  ----
5074
- json | yaml | toml | key_value do
5089
+ hsh | json | yaml | toml | key_value do
5075
5090
  map ["name1", "name2"], to: :attribute_name
5076
5091
  end
5077
5092
 
@@ -5185,7 +5200,7 @@ Syntax:
5185
5200
 
5186
5201
  [source,ruby]
5187
5202
  ----
5188
- xml | json | yaml | toml do
5203
+ xml | hsh | json | yaml | toml do
5189
5204
  map 'key_value_model_attribute_name', to: :name_of_attribute, delegate: :model_to_delegate_to
5190
5205
  end
5191
5206
  ----
@@ -5466,7 +5481,7 @@ NOTE: For `NokogiriAdapter`, we can also call `to_xml` on `value.node.adapter_no
5466
5481
  .Key-value data model serialization with custom methods
5467
5482
  [source,ruby]
5468
5483
  ----
5469
- json | yaml | toml do
5484
+ hsh | json | yaml | toml do
5470
5485
  map 'attribute_name', to: :name_of_attribute, with: {
5471
5486
  to: :method_name_to_serialize,
5472
5487
  from: :method_name_to_deserialize
@@ -5533,12 +5548,122 @@ the undefined value:: the value is not defined
5533
5548
  There are also different ways to represent these missing values
5534
5549
  when the attribute accepts a single value or a collection of values.
5535
5550
 
5551
+ .Support of missing value types in different technologies
5552
+ |===
5553
+ | Technology | Missing value type | Realized as
5554
+
5555
+ .3+| Lutaml::Model
5556
+ | empty value | Ruby empty string (`""`)
5557
+ | non-existent value | Ruby `NilClass` (`nil`)
5558
+ | undefined value | `class Uninitialized`
5559
+
5560
+ .3+| XML element
5561
+ | empty value | XML blank element: `<status></status>` or `<status/>`
5562
+ | non-existent value | XML blank element with attribute `xsi:nil`: `<status xsi:nil="true"/>`
5563
+ | undefined value | the XML element is omitted
5564
+
5565
+ .3+| XML attribute
5566
+ | empty value | XML blank attribute: `status=""`
5567
+ | non-existent value | the XML attribute is omitted
5568
+ | undefined value | the XML attribute is omitted
5569
+
5570
+ .3+| JSON
5571
+ | empty value | JSON empty string (`""`)
5572
+ | non-existent value | JSON `null` value
5573
+ | undefined value | the JSON key is omitted
5574
+
5575
+ .3+| TOML
5576
+ | empty value | TOML empty string
5577
+ | non-existent value | the TOML key is omitted since TOML does not support the concept of null.
5578
+ | undefined value | the TOML key is omitted
5579
+
5580
+ |===
5581
+
5582
+ NOTE: The `Uninitialized` class is a special Lutaml::Model construct, it is not
5583
+ supported by normal Ruby objects.
5584
+
5585
+ The challenge for the developer is how to represent fully compatible semantics
5586
+ using interoperable data models across different technologies.
5587
+
5588
+ Lutaml::Model provides you with several mechanisms to retain the missing values
5589
+ semantics. An example mapping is shown in the following diagram.
5590
+
5591
+ .Mapping of missing value types between Lutaml::Model and YAML
5592
+ [source]
5593
+ ----
5594
+ ┌─────────────────────────────────────────────────────────────────────────────┐
5595
+ │ ╔════════════════════════╗ ╔════════════════════════╗ │
5596
+ │ ║ Lutaml::Model Values ║ ║ YAML Values ║ │
5597
+ │ ╠════════════════════════╣ ╠════════════════════════╣ │
5598
+ │ ║ ║ mapping ║ ║ │
5599
+ │ ║ "empty" ║◀──────────────────▶║ "empty" ║ │
5600
+ │ ║ (empty string, []) ║ to empty ║ (empty string, []) ║ │
5601
+ │ ║ ║ ║ ║ │
5602
+ │ ╟────────────────────────╢ ╟────────────────────────╢ │
5603
+ │ ║ ║ mapping ║ ║ │
5604
+ │ ║ "non-existent" ║◀──────────────────▶║ "non-existent" ║ │
5605
+ │ ║ (nil) ║ to non-existent ║ (null) ║ │
5606
+ │ ║ ║ ║ ║ │
5607
+ │ ╟────────────────────────╢ ╟────────────────────────╢ │
5608
+ │ ║ ║ mapping ║ ║ │
5609
+ │ ║ "undefined" ║◀──────────────────▶║ "undefined" ║ │
5610
+ │ ║ (uninitialized) ║ to undefined ║ (key omitted) ║ │
5611
+ │ ║ ║ ║ ║ │
5612
+ │ ╚════════════════════════╝ ╚════════════════════════╝ │
5613
+ └─────────────────────────────────────────────────────────────────────────────┘
5614
+ ----
5615
+
5616
+ In the case where the interoperating technologies do not support the full spectrum
5617
+ of missing value types, it is necessary for the developer to understand any
5618
+ such behavior and relevant handling.
5619
+
5620
+ .Mapping of missing value types between Lutaml::Model and TOML
5621
+ [source]
5622
+ ----
5623
+ ┌─────────────────────────────────────────────────────────────────────────────┐
5624
+ │ ╔════════════════════════╗ ╔════════════════════════╗ │
5625
+ │ ║ Lutaml::Model Values ║ ║ TOML Values ║ │
5626
+ │ ╠════════════════════════╣ ╠════════════════════════╣ │
5627
+ │ ║ ║ mapping ║ ║ │
5628
+ │ ║ "empty" ║◀──────────────────▶║ "empty" ║ │
5629
+ │ ║ (empty string, []) ║ to empty ║ (empty string, []) ║ │
5630
+ │ ║ ║ ║ ║ │
5631
+ │ ╟────────────────────────╢ ╟────────────────────────╢ │
5632
+ │ ║ ║ mapping ║ ║ │
5633
+ │ ║ "non-existent" ║◀──────────────────▶║ ║ │
5634
+ │ ║ (nil) ║ to undefined ║ "undefined" ║ │
5635
+ │ ║ ║ ║ (key omitted) ║ │
5636
+ │ ╟────────────────────────╢ ║ ║ │
5637
+ │ ║ ║ mapping ║ ║ │
5638
+ │ ║ "undefined" ║─────(one-way)─────▶║ TOML does not ║ │
5639
+ │ ║ (uninitialized) ║ to undefined ║ support NULL ║ │
5640
+ │ ║ ║ ║ ║ │
5641
+ │ ╚════════════════════════╝ ╚════════════════════════╝ │
5642
+ └─────────────────────────────────────────────────────────────────────────────┘
5643
+ ----
5644
+
5645
+ There are the following additional challenges that a developer must take into account of:
5646
+
5647
+ * Single attribute value vs collection attribute value. Different technologies
5648
+ treat single/collection values differently.
5649
+
5650
+ * External schemas and systems that interoperate with serializations from
5651
+ Lutaml::Model. Many schemas and systems adopt "different" conventions for
5652
+ representing missing value semantics (sometimes very awkward ones).
5653
+
5654
+ The solution for the first challenge is to understand the behavior of the
5655
+ different technologies used. The default mappings are described in <<value_representation_in_lutaml-model>>
5656
+ and <<value_representation_in_serialization_formats>>.
5657
+
5658
+
5659
+ [[value_representation_in_lutaml-model]]
5536
5660
  ==== Value representation in Lutaml::Model
5537
5661
 
5538
5662
  The following table summarizes the behavior of the Lutaml::Model
5539
5663
  in regards of the "missing values" family.
5540
5664
 
5541
- [cols="1,1,1,1", options="header"]
5665
+ .Handling of missing value types in Lutaml::Model data types
5666
+ [cols="1,1,2,2"]
5542
5667
  |===
5543
5668
  | LutaML value type | Cardinality (1 or n) | Missing value type | Ruby value
5544
5669
 
@@ -5556,55 +5681,49 @@ in regards of the "missing values" family.
5556
5681
 
5557
5682
  .3+| `:integer`
5558
5683
  .3+| single
5559
- | empty value | `0` (Integer)
5684
+ | empty value | N/A
5560
5685
  | non-existent value | `nil` (NilClass)
5561
5686
  | undefined value | No assigned value
5562
5687
 
5563
5688
  .3+| `:float`
5564
5689
  .3+| single
5565
- | empty value | `0.0` (Float)
5690
+ | empty value | N/A
5566
5691
  | non-existent value | `nil` (NilClass)
5567
5692
  | undefined value | No assigned value
5568
5693
 
5569
5694
  .3+| `:boolean`
5570
5695
  .3+| single
5571
- | empty value | `false` (FalseClass)
5696
+ | empty value | N/A
5572
5697
  | non-existent value | `nil` (NilClass)
5573
5698
  | undefined value | No assigned value
5574
5699
 
5575
5700
  .3+| `:date`
5576
5701
  .3+| single
5577
- | empty value | `Date.new(0)` (Date)
5578
- | non-existent value | `nil` (NilClass)
5579
- | undefined value | No assigned value
5580
-
5581
- .3+| `:date`
5582
- .3+| single
5583
- | empty value | `Date.new(0)` (Date)
5702
+ | empty value | N/A
5584
5703
  | non-existent value | `nil` (NilClass)
5585
5704
  | undefined value | No assigned value
5586
5705
 
5587
5706
  .3+| `:time_without_date`
5588
5707
  .3+| single
5589
- | empty value | `Time.new(0)` (Time)
5708
+ | empty value | N/A
5590
5709
  | non-existent value | `nil` (NilClass)
5591
5710
  | undefined value | No assigned value
5592
5711
 
5593
5712
  .3+| `:date_time`
5594
5713
  .3+| single
5595
- | empty value | `Time.new(0)` (Time)
5714
+ | empty value | N/A
5596
5715
  | non-existent value | `nil` (NilClass)
5597
5716
  | undefined value | No assigned value
5598
5717
 
5599
5718
  .3+| `:time`
5600
5719
  .3+| single
5601
- | empty value | `Time.new(0)` (Time)
5720
+ | empty value | N/A
5602
5721
  | non-existent value | `nil` (NilClass)
5603
5722
  | undefined value | No assigned value
5604
5723
 
5605
5724
  .3+| `:decimal`
5606
5725
  .3+| single
5607
- | empty value | `BigDecimal.new(0)` (BigDecimal)
5726
+ | empty value | N/A
5608
5727
  | non-existent value | `nil` (NilClass)
5609
5728
  | undefined value | No assigned value
5610
5729
 
@@ -5617,6 +5736,7 @@ in regards of the "missing values" family.
5617
5736
  |===
5618
5737
 
5619
5738
 
5739
+ [[value_representation_in_serialization_formats]]
5620
5740
  ==== Value representation in serialization formats
5621
5741
 
5622
5742
  Every serialization format uses a different information model to represent
@@ -5674,6 +5794,8 @@ while others only support a subset of them.
5674
5794
 
5675
5795
  ==== Missing value mapping
5676
5796
 
5797
+ ===== General
5798
+
5677
5799
  Lutaml::Model provides a comprehensive way to handle the missing values family
5678
5800
  across different serialization formats.
5679
5801
 
@@ -5689,7 +5811,8 @@ A hash of key-value pairs that determines the mapping of a missing value at the
5689
5811
  The key is the missing value type in the serialization format, and the value is
5690
5812
  the missing value type in the LutaML Model.
5691
5813
  +
5692
- NOTE: In other words, used when converting the serialized format into a Ruby object.
5814
+ NOTE: In other words, used when converting the serialized format into a
5815
+ Lutaml::Model Ruby object.
5693
5816
 
5694
5817
  `to` pairs::
5695
5818
  A hash of key-value pairs that determines the mapping of a LutaML Model ("to") missing
@@ -5697,7 +5820,8 @@ to a missing value choice at the serialization format where this mapping applies
5697
5820
  The key is the missing value type in the LutaML Model, and the value is the
5698
5821
  missing value type in the serialization format.
5699
5822
  +
5700
- NOTE: In other words, used when converting Ruby objects back into the serialized format.
5823
+ NOTE: In other words, used when converting a Lutaml::Model Ruby object into the
5824
+ serialized format.
5701
5825
 
5702
5826
  Syntax:
5703
5827
 
@@ -5705,14 +5829,14 @@ Syntax:
5705
5829
  ----
5706
5830
  {map_command} 'format-key', to: :attribute_name, value_map: { <1>
5707
5831
  from: {
5708
- {format-missing-value-1}: {model-missing-value-1}, <2>
5709
- {format-missing-value-2}: {model-missing-value-2},
5710
- {format-missing-value-3}: {model-missing-value-3}
5832
+ {format-missing-value-n}: {model-missing-value-n}, <2>
5833
+ {format-missing-value-m}: {model-missing-value-m},
5834
+ {format-missing-value-o}: {model-missing-value-o}
5711
5835
  },
5712
5836
  to: {
5713
- {model-missing-value-1}: {format-missing-value-1}, <3>
5714
- {model-missing-value-2}: {format-missing-value-2},
5715
- {model-missing-value-3}: {format-missing-value-3}
5837
+ {model-missing-value-n}: {format-missing-value-n}, <3>
5838
+ {model-missing-value-m}: {format-missing-value-m},
5839
+ {model-missing-value-o}: {format-missing-value-o}
5716
5840
  }
5717
5841
  }
5718
5842
  ----
@@ -5726,6 +5850,29 @@ Model.
5726
5850
  The missing value type mapping differs per serialization format,
5727
5851
  as serialization formats may not fully support all missing value types.
5728
5852
 
5853
+ The availability of `from` and `to` keys and values depend on the types of
5854
+ missing values supported by that particular serialization format.
5855
+
5856
+ The available values for `from` and `to` for serialization formats
5857
+ are presented below, where the allowed values are to be used in the direction
5858
+ of the format. That means if the format supports `:empty`, it can be used
5859
+ as a key in `from:` direction, and the value in the `to:` direction (see `{format-missing-value-n}`) in the syntax.
5860
+
5861
+ .Available missing value types in different mapping commands
5862
+ [cols="a,2a"]
5863
+ |===
5864
+ | Map command | Missing value types available (key in `from:` direction, value in the `to:` direction)
5865
+
5866
+ | XML element `map_element` | `:empty`, `:omitted`, `:nil`
5867
+ | XML attribute `map_attribute` | `:empty`, `:omitted`
5868
+ | Hash `map`, `map_content` | `:empty`, `:omitted`, `:nil`
5869
+ | JSON `map`, `map_content` | `:empty`, `:omitted`, `:nil`
5870
+ | YAML `map`, `map_content` | `:empty`, `:omitted`, `:nil`
5871
+ | TOML `map`, `map_content` | `:empty`, `:omitted`
5872
+
5873
+ |===
5874
+
5875
+
5729
5876
  [example]
5730
5877
  For instance, TOML does not support the notion of "null" and therefore the
5731
5878
  missing value type of `nil` cannot be used; therefore in a `from:`
@@ -5760,46 +5907,538 @@ Users can specify the mapping for both `from` and `to` values using the
5760
5907
  NOTE: Since `nil` is not supported in TOML, so mappings like `nil:
5761
5908
  {any_option}` or `{any_option}: :nil` will not work in TOML.
5762
5909
 
5910
+ NOTE: In a collection attribute, the values of `value_map` also depend on the
5911
+ `initialize_empty` setting, where an omitted value in the serialization format can still lead to a `nil`
5912
+ or an empty array `[]` at the attribute-level (instead of the mapping-level).
5913
+
5914
+
5915
+
5916
+ ===== Default value maps for serialization formats
5917
+
5763
5918
  The table below describes the default `value_map` configurations for supported
5764
5919
  serialization formats.
5765
5920
 
5766
- .Default missing value mapping configuration for single attribute to serialization formats
5767
- [cols="1,1,1,1", options="header"]
5921
+ // TODO: Find a place to write this
5922
+ // XML Handling::
5923
+ // - `<status>new</status>` will be treated as `["new"]`.
5924
+ // - `<status>new</status><status>assigned</status>` will be treated as `["new", "assigned"]`.
5925
+
5926
+ ====== Default value map for XML element (single attribute)
5927
+
5928
+ [source,ruby]
5929
+ ----
5930
+ attribute :attr, :string
5931
+
5932
+ xml do
5933
+ map_element 'key', to: :attr, value_map: {
5934
+ from: { empty: :nil, omitted: :omitted, nil: :nil },
5935
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
5936
+ }
5937
+ end
5938
+ ----
5939
+
5940
+ .Default missing value mapping configuration for single attributes in XML elements
5941
+ [cols="a,a,a,a"]
5942
+ |===
5943
+
5944
+ h| Direction h| Map rule h| XML source h| Model target
5945
+
5946
+ .3+|`from:`
5947
+ | `empty: :nil`
5948
+ | blank XML element (`<status/>`)
5949
+ | `nil`
5950
+
5951
+ | `omitted: :omitted`
5952
+ | absent XML element
5953
+ | omitted from the model
5954
+
5955
+ | `nil: :nil`
5956
+ | blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
5957
+ | `nil` value in the model
5958
+
5959
+
5960
+ h| Direction h| Map rule h| Model source h| XML target
5961
+
5962
+ .3+|`to:`
5963
+ | `empty: :empty`
5964
+ | empty string (`""`)
5965
+ | blank XML element
5966
+
5967
+ | `omitted: :omitted`
5968
+ | omitted in the model
5969
+ | XML element not rendered
5970
+
5971
+ | `nil: :nil`
5972
+ | `nil` value in the model
5973
+ | blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
5974
+
5975
+ |===
5976
+
5977
+ ====== Default value map for XML element (collection attribute)
5978
+
5979
+ [source,ruby]
5980
+ ----
5981
+ attribute :attr, :string, collection: true
5982
+
5983
+ xml do
5984
+ map_element 'key', to: :attr, value_map: {
5985
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
5986
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
5987
+ }
5988
+ end
5989
+ ----
5990
+
5991
+ .Default missing value mapping configuration for collection attributes in XML elements
5992
+ [cols="a,a,a,a"]
5768
5993
  |===
5769
- | Serialization format | `from` value | `to` value | Description
5770
5994
 
5771
- .3+| XML (supports all 3 types)
5772
- | `empty` | `nil` |
5773
- * Element: An empty value in the model is treated as an element with attribute `xsi:nil` during serialization.
5774
- * Attribute: An empty value in the model is treated as an empty string in the attribute.
5995
+ h| Direction h| Map rule h| XML source h| Model target
5996
+
5997
+ .3+|`from:`
5998
+ | `empty: :nil`
5999
+ | blank XML element (`<status/>`)
6000
+ | empty array (`[]`)
6001
+
6002
+ | `omitted: :omitted`
6003
+ | absent XML element
6004
+ | omitted from the model
6005
+
6006
+ | `nil: :nil`
6007
+ | blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
6008
+ | `nil` value in the model
5775
6009
 
5776
- | `omitted` | `omitted` |
5777
- * Element: If the value is omitted in the model, the element is omitted from the XML output.
5778
- * Attribute: If the value is omitted in the model, the attribute is omitted from the XML output.
5779
6010
 
5780
- | `nil` | `nil` |
5781
- * Element: A `nil` value in the model is serialized as an element with attribute `xsi:nil`.
5782
- * Attribute: A `nil` value in the model is treated as an empty string in the attribute.
6011
+ h| Direction h| Map rule h| Model source h| XML target
6012
+
6013
+ .3+|`to:`
6014
+ | `empty: :empty`
6015
+ | empty array (`[]`)
6016
+ | blank XML element
6017
+
6018
+ | `omitted: :omitted`
6019
+ | omitted in the model
6020
+ | XML element not rendered
6021
+
6022
+ | `nil: :nil`
6023
+ | `nil` value in the model
6024
+ | blank XML element with attribute `xsi:nil` (`<status xsi:nil=true/>`)
6025
+
6026
+ |===
5783
6027
 
5784
- .3+| YAML (supports all 3 types)
5785
- | `empty` | `empty` | An empty string (`""`) in the model is deserialized as `empty` (an empty YAML string).
5786
- | `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the YAML output.
5787
- | `nil` | `nil` | A `nil` value in the model is serialized as YAML `null`.
6028
+ ====== Default value map for XML attribute (single attribute)
5788
6029
 
5789
- .3+| JSON (supports all 3 types)
5790
- | `empty` | `empty` | An empty string (`""`) in the model is serialized as `empty` (an empty JSON string).
5791
- | `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the JSON output.
5792
- | `nil` | `nil` | A `nil` value in the model is serialized as JSON `null`.
6030
+ [source,ruby]
6031
+ ----
6032
+ attribute :attr, :string
5793
6033
 
5794
- .3+| TOML (does not support `nil`)
5795
- | `empty` | `empty` | An empty string (`""`) in the model is serialized as `empty` (an empty TOML string).
5796
- | `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the TOML output.
5797
- | `nil` | `omitted` | In TOML, `nil` is represented as `omitted` due to TOML limitations.
6034
+ xml do
6035
+ map_attribute 'attr_name', to: :attr, value_map: {
6036
+ from: { empty: :nil, omitted: :omitted },
6037
+ to: { empty: :empty, nil: :empty, omitted: :omitted }
6038
+ }
6039
+ end
6040
+ ----
5798
6041
 
6042
+ .Default missing value mapping configuration for single attributes in XML attributes
6043
+ [cols="a,a,a,a"]
5799
6044
  |===
5800
6045
 
6046
+ h| Direction h| Map rule h| XML source h| Model target
6047
+
6048
+ .2+|`from:` (source only supports `empty` and `omitted`)
6049
+ | `empty: :nil`
6050
+ | blank XML attribute (`status=""`)
6051
+ | `nil`
6052
+
6053
+ | `omitted: :omitted`
6054
+ | absent XML attribute
6055
+ | omitted from the model
6056
+
6057
+ h| Direction h| Map rule h| Model source h| XML target
6058
+
6059
+ .3+|`to:` (target only accepts `empty` and `omitted`)
6060
+ | `empty: :empty`
6061
+ | empty string (`""`)
6062
+ | blank XML attribute (`status=""`)
6063
+
6064
+ | `omitted: :omitted`
6065
+ | omitted in the model
6066
+ | XML attribute not rendered
6067
+
6068
+ | `nil: :empty`
6069
+ | `nil`
6070
+ | blank XML attribute (`status=""`)
6071
+
6072
+ |===
6073
+
6074
+ ====== Default value map for XML attribute (collection attribute)
6075
+
6076
+ [source,ruby]
6077
+ ----
6078
+ attribute :attr, :string, collection: true
6079
+
6080
+ xml do
6081
+ map_attribute 'attr_name', to: :attr, value_map: {
6082
+ from: { empty: :empty, omitted: :omitted },
6083
+ to: { empty: :empty, nil: :omitted, omitted: :omitted }
6084
+ }
6085
+ end
6086
+ ----
6087
+
6088
+ .Default missing value mapping configuration for collection attributes in XML attributes
6089
+ [cols="a,a,a,a"]
6090
+ |===
6091
+
6092
+ h| Direction h| Map rule h| XML source h| Model target
6093
+
6094
+ .2+|`from:` (source only supports `empty` and `omitted`)
6095
+ | `empty: :empty`
6096
+ | blank XML attribute (`status=""`)
6097
+ | empty array (`[]`)
6098
+
6099
+ | `omitted: :omitted`
6100
+ | absent XML attribute
6101
+ | omitted from the model
6102
+
6103
+ h| Direction h| Map rule h| Model source h| XML target
5801
6104
 
5802
- The `value_map` option can be defined for each serialization format as follows.
6105
+ .3+|`to:` (target only accepts `empty` and `omitted`)
6106
+ | `empty: :empty`
6107
+ | empty array (`[]`)
6108
+ | blank XML attribute (`status=""`)
6109
+
6110
+ | `omitted: :omitted`
6111
+ | omitted in the model
6112
+ | XML attribute not rendered
6113
+
6114
+ | `nil: :omitted`
6115
+ | `nil`
6116
+ | XML attribute not rendered
6117
+
6118
+ |===
6119
+
6120
+
6121
+
6122
+ ====== Default value map for YAML (single attribute)
6123
+
6124
+ [source,ruby]
6125
+ ----
6126
+ attribute :attr, :string
6127
+
6128
+ yaml do
6129
+ map 'key', to: :attr, value_map: {
6130
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
6131
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
6132
+ }
6133
+ end
6134
+ ----
6135
+
6136
+ .Default missing value mapping configuration for single attributes in YAML
6137
+ [cols="a,a,a,a"]
6138
+ |===
6139
+
6140
+ h| Direction h| Map rule h| XML source h| Model target
6141
+
6142
+ .3+|`from:`
6143
+ | `empty: :empty`
6144
+ | empty string in YAML (`status:` or `status: ""`)
6145
+ | empty string (`""`)
6146
+
6147
+ | `omitted: :omitted`
6148
+ | absent YAML key
6149
+ | omitted from the model
6150
+
6151
+ | `nil: :nil`
6152
+ | YAML `null`
6153
+ | `nil`
6154
+
6155
+ h| Direction h| Map rule h| Model source h| XML target
6156
+
6157
+ .3+|`to:`
6158
+ | `empty: :empty`
6159
+ | empty string (`""`)
6160
+ | empty string in YAML (`status:`)
6161
+
6162
+ | `omitted: :omitted`
6163
+ | omitted in the model
6164
+ | YAML key omitted
6165
+
6166
+ | `nil: :nil`
6167
+ | `nil`
6168
+ | YAML `null`
6169
+
6170
+ |===
6171
+
6172
+ NOTE: In order to treat a YAML value like `status: ''` to `nil`, the mapping of
6173
+ `value_map: { from: { empty: :nil } }` can be applied.
6174
+
6175
+
6176
+ ====== Default value map for YAML (collection attribute)
6177
+
6178
+ [source,ruby]
6179
+ ----
6180
+ attribute :attr, :string, collection: true
6181
+
6182
+ yaml do
6183
+ map 'key', to: :attr, value_map: {
6184
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
6185
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
6186
+ }
6187
+ end
6188
+ ----
6189
+
6190
+ .Default missing value mapping configuration for collection attributes in YAML
6191
+ [cols="a,a,a,a"]
6192
+ |===
6193
+
6194
+ h| Direction h| Map rule h| YAML source h| Model target
6195
+
6196
+ .3+|`from:`
6197
+ | `empty: :empty`
6198
+ | empty YAML array (`status:` or `status: []`)
6199
+ | empty array (`[]`)
6200
+
6201
+ | `omitted: :omitted`
6202
+ | absent YAML key
6203
+ | omitted from the model
6204
+
6205
+ | `nil: :nil`
6206
+ | YAML `null`
6207
+ | `nil`
6208
+
6209
+ h| Direction h| Map rule h| Model source h| YAML target
6210
+
6211
+ .3+|`to:`
6212
+ | `empty: :empty`
6213
+ | empty array (`[]`)
6214
+ | empty YAML array (`status: []`)
6215
+
6216
+ | `omitted: :omitted`
6217
+ | omitted in the model
6218
+ | YAML key omitted
6219
+
6220
+ | `nil: :nil`
6221
+ | `nil`
6222
+ | YAML `null`
6223
+
6224
+ |===
6225
+
6226
+ NOTE: If the YAML key for the collection attribute is omitted, it will be treated
6227
+ as `nil` or an empty array depending on the `initialize_empty` setting.
6228
+
6229
+
6230
+ ====== Default value map for JSON (single attribute)
6231
+
6232
+ [source,ruby]
6233
+ ----
6234
+ attribute :attr, :string
6235
+
6236
+ json do
6237
+ map 'key', to: :attr, value_map: {
6238
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
6239
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
6240
+ }
6241
+ end
6242
+ ----
6243
+
6244
+ .Default missing value mapping configuration for single attributes in JSON
6245
+ [cols="a,a,a,a"]
6246
+ |===
6247
+
6248
+ h| Direction h| Map rule h| JSON source h| Model target
6249
+
6250
+ .3+|`from:`
6251
+ | `empty: :empty`
6252
+ | empty string in JSON (`"status" : ""`)
6253
+ | empty string (`""`)
6254
+
6255
+ | `omitted: :omitted`
6256
+ | absent JSON key
6257
+ | omitted from the model
6258
+
6259
+ | `nil: :nil`
6260
+ | JSON `null`
6261
+ | `nil`
6262
+
6263
+ h| Direction h| Map rule h| Model source h| JSON target
6264
+
6265
+ .3+|`to:`
6266
+ | `empty: :empty`
6267
+ | empty string (`""`)
6268
+ | empty string in JSON (`"status" : ""`)
6269
+
6270
+ | `omitted: :omitted`
6271
+ | omitted in the model
6272
+ | JSON key omitted
6273
+
6274
+ | `nil: :nil`
6275
+ | `nil`
6276
+ | JSON `null`
6277
+
6278
+ |===
6279
+
6280
+
6281
+
6282
+ ====== Default value map for JSON (collection attribute)
6283
+
6284
+ [source,ruby]
6285
+ ----
6286
+ attribute :attr, :string, collection: true
6287
+
6288
+ json do
6289
+ map 'key', to: :attr, value_map: {
6290
+ from: { empty: :empty, omitted: :omitted, nil: :nil },
6291
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
6292
+ }
6293
+ end
6294
+ ----
6295
+
6296
+ .Default missing value mapping configuration for collection attributes in JSON
6297
+ [cols="a,a,a,a"]
6298
+ |===
6299
+
6300
+ h| Direction h| Map rule h| JSON source h| Model target
6301
+
6302
+ .3+|`from:`
6303
+ | `empty: :empty`
6304
+ | empty JSON array (`"status": []`)
6305
+ | empty array (`[]`)
6306
+
6307
+ | `omitted: :omitted`
6308
+ | absent JSON key
6309
+ | omitted from the model
6310
+
6311
+ | `nil: :nil`
6312
+ | JSON `null`
6313
+ | `nil`
6314
+
6315
+ h| Direction h| Map rule h| Model source h| JSON target
6316
+
6317
+ .3+|`to:`
6318
+ | `empty: :empty`
6319
+ | empty array (`[]`)
6320
+ | empty JSON array (`"status": []`)
6321
+
6322
+ | `omitted: :omitted`
6323
+ | omitted in the model
6324
+ | JSON key omitted
6325
+
6326
+ | `nil: :nil`
6327
+ | `nil`
6328
+ | JSON `null`
6329
+
6330
+ |===
6331
+
6332
+ ====== Default value map for TOML (single attribute)
6333
+
6334
+ TOML does not support the concept of `nil` and therefore the mapping of `from:`
6335
+ direction with `nil` to will not work in TOML.
6336
+
6337
+ The `nil` mapping is only supported in the `to:` direction (model to TOML).
6338
+
6339
+ [source,ruby]
6340
+ ----
6341
+ attribute :attr, :string
6342
+
6343
+ toml do
6344
+ map 'key', to: :attr, value_map: {
6345
+ from: { empty: :empty, omitted: :omitted },
6346
+ to: { empty: :empty, omitted: :omitted, nil: :omitted }
6347
+ }
6348
+ end
6349
+ ----
6350
+
6351
+ .Default missing value mapping configuration for single attributes in TOML
6352
+ [cols="a,a,a,a"]
6353
+ |===
6354
+
6355
+ h| Direction h| Map rule h| TOML source h| Model target
6356
+
6357
+ .2+|`from:` (source only supports `empty` and `omitted`)
6358
+ | `empty: :empty`
6359
+ | empty string in TOML (`[status]` with no value)
6360
+ | empty string (`""`)
6361
+
6362
+ | `omitted: :omitted`
6363
+ | absent TOML key
6364
+ | omitted from the model
6365
+
6366
+ h| Direction h| Map rule h| Model source h| TOML target
6367
+
6368
+ .3+|`to:` (source only supports `empty` and `omitted`)
6369
+ | `empty: :empty`
6370
+ | empty string (`""`)
6371
+ | empty string in TOML (`[status]` with no value)
6372
+
6373
+ | `omitted: :omitted`
6374
+ | omitted in the model
6375
+ | TOML key omitted
6376
+
6377
+ | `nil: :omitted`
6378
+ | `nil`
6379
+ | TOML key omitted
6380
+
6381
+ |===
6382
+
6383
+
6384
+
6385
+ ====== Default value map for TOML (collection attribute)
6386
+
6387
+ TOML does not support the concept of `nil` and therefore the mapping of `from:`
6388
+ direction with `nil` to will not work in TOML.
6389
+
6390
+ The `nil` mapping is only supported in the `to:` direction (model to TOML).
6391
+
6392
+ [source,ruby]
6393
+ ----
6394
+ attribute :attr, :string, collection: true
6395
+
6396
+ toml do
6397
+ map 'key', to: :attr, value_map: {
6398
+ from: { empty: :empty, omitted: :omitted },
6399
+ to: { empty: :empty, omitted: :omitted, nil: :omitted }
6400
+ }
6401
+ end
6402
+ ----
6403
+
6404
+ .Default missing value mapping configuration for collection attributes in TOML
6405
+ [cols="a,a,a,a"]
6406
+ |===
6407
+
6408
+ h| Direction h| Map rule h| TOML source h| Model target
6409
+
6410
+ .2+|`from:` (source only supports `empty` and `omitted`)
6411
+ | `empty: :empty`
6412
+ | empty TOML array (`[status]` with no value)
6413
+ | empty array (`[]`)
6414
+
6415
+ | `omitted: :omitted`
6416
+ | absent TOML key
6417
+ | omitted from the model
6418
+
6419
+ h| Direction h| Map rule h| Model source h| TOML target
6420
+
6421
+ .3+|`to:` (source only supports `empty` and `omitted`)
6422
+ | `empty: :empty`
6423
+ | empty array (`[]`)
6424
+ | empty TOML array (`[status]` with no value)
6425
+
6426
+ | `omitted: :omitted`
6427
+ | omitted in the model
6428
+ | TOML key omitted
6429
+
6430
+ | `nil: :omitted`
6431
+ | `nil`
6432
+ | TOML key omitted
6433
+
6434
+ |===
6435
+
6436
+
6437
+
6438
+ ===== Replacing missing values type mapping with `value_map`
6439
+
6440
+ The `value_map` option can be defined to meticulously map for each serialization
6441
+ format as follows.
5803
6442
 
5804
6443
  [example]
5805
6444
  ====
@@ -5816,7 +6455,7 @@ class ExampleClass < Lutaml::Model::Serializable
5816
6455
  }
5817
6456
  end
5818
6457
 
5819
- json | yaml | toml | key_value do
6458
+ hsh | json | yaml | toml | key_value do
5820
6459
  map 'status', to: :status, value_map: {
5821
6460
  from: { empty: :nil, omitted: :omitted, nil: :nil },
5822
6461
  to: { empty: :nil, omitted: :omitted, nil: :nil }
@@ -5857,57 +6496,6 @@ When defining an attribute with `collection: true`, the attribute will behave as
5857
6496
  attribute :status, :string, collection: true
5858
6497
  ----
5859
6498
 
5860
- - **XML Handling**:
5861
- - `<status />` will be treated as `[]` (an empty array).
5862
- - `<status>new</status>` will be treated as `["new"]`.
5863
- - `<status>new</status><status>assigned</status>` will be treated as `["new", "assigned"]`.
5864
-
5865
- - **YAML Handling**:
5866
- - A value like `status: ''` in YAML will be deserialized as `nil` when `value_map: { from: { empty: :nil } }` is applied.
5867
- - If the collection is omitted, it will be initialized as `nil` or an empty array depending on the `initialize_empty` setting.
5868
-
5869
- - **JSON Handling**:
5870
- - A `status: []` in the serialized output will correspond to an empty array when `value_map: { from: { empty: :empty } }` is applied.
5871
-
5872
- - **TOML Handling**:
5873
- - TOML treats empty collections similarly to other formats, but `nil` values are not supported so we can not read or write nil values in TOML.
5874
-
5875
- .Default missing value mapping configuration for collection attributes to serialization formats
5876
- [cols="1,1,1,1", options="header"]
5877
- |===
5878
- | Serialization format | `from` value | `to` value | Description
5879
-
5880
- .3+| XML (supports all 3 types)
5881
- | `empty` | `empty` |
5882
- * Element: An empty array (`[]`) in the model is treated a blank element (`<x/>`)
5883
- * Attribute: An empty array (`[]`) in the model is treated as an empty string in the attribute.
5884
-
5885
- | `omitted` | `omitted` |
5886
- * Element: If the value is omitted in the model, the element is omitted from the XML output.
5887
- * Attribute: If the value is omitted in the model, the attribute is omitted from the XML output.
5888
-
5889
- | `nil` | `nil` |
5890
- * Element: A `nil` value in the model is serialized as an element with attribute `xsi:nil`.
5891
- * Attribute: A `nil` value in the model is treated as an empty string in the attribute.
5892
-
5893
- .3+| YAML (supports all 3 types)
5894
- | `empty` | `empty` | An empty array (`[]`) in the model is deserialized as `empty` (an empty YAML array).
5895
- | `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the YAML output.
5896
- | `nil` | `nil` | A `nil` value in the model is serialized as YAML `null`.
5897
-
5898
- .3+| JSON (supports all 3 types)
5899
- | `empty` | `empty` | An empty array (`[]`) in the model is serialized as `empty` (an empty JSON array).
5900
- | `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the JSON output.
5901
- | `nil` | `nil` | A `nil` value in the model is serialized as JSON `null`.
5902
-
5903
- .3+| TOML (does not support `nil`)
5904
- | `empty` | `empty` | An empty array (`[]`) in the model is serialized as `empty` (an empty TOML array).
5905
- | `omitted` | `omitted` | If the value is omitted in the model, the key is omitted from the TOML output.
5906
- | `nil` | `omitted` | In TOML, `nil` is represented as `omitted` due to TOML limitations.
5907
-
5908
- |===
5909
-
5910
-
5911
6499
  Here's an example of how you can use the `value_map` with a collection attribute.
5912
6500
 
5913
6501
  .Using `value_map` with a collection attribute
@@ -5925,12 +6513,19 @@ class ExampleClass < Lutaml::Model::Serializable
5925
6513
  }
5926
6514
  end
5927
6515
 
5928
- json | yaml | toml | key_value do
6516
+ hsh | json | yaml | key_value do
5929
6517
  map 'status', to: :status, value_map: {
5930
6518
  from: { empty: :nil, omitted: :omitted, nil: :nil },
5931
6519
  to: { empty: :nil, omitted: :omitted, nil: :nil }
5932
6520
  }
5933
6521
  end
6522
+
6523
+ toml do
6524
+ map 'status', to: :status, value_map: {
6525
+ from: { empty: :nil, omitted: :omitted },
6526
+ to: { empty: :nil, omitted: :omitted, nil: :omitted }
6527
+ }
6528
+ end
5934
6529
  end
5935
6530
 
5936
6531
  yaml = <<~YAML
@@ -5943,9 +6538,9 @@ y = ExampleClass.from_yaml(yaml)
5943
6538
  ----
5944
6539
  ====
5945
6540
 
5946
- TODO: Need to improve this example.
6541
+ // TODO: Need to improve this example.
5947
6542
 
5948
- ==== Specific overrides of value map
6543
+ ==== Specific overrides of value map (`render_*` and `treat_*`)
5949
6544
 
5950
6545
  ===== General
5951
6546
 
@@ -5982,6 +6577,52 @@ missing value types into the model.
5982
6577
  `{format-value}`::: specifies the missing value type in the serialization format.
5983
6578
  `{model-value}`::: specifies the missing value type in the LutaML Model.
5984
6579
 
6580
+ In effect, the default `value_map` is overriden by the `:render_*` and `:treat_*`
6581
+ directives.
6582
+
6583
+ [example]
6584
+ ====
6585
+ Given the default mapping for an XML element, the `:render_*` and `:treat_*`
6586
+ options can be used to selectively override behavior.
6587
+
6588
+ [source,ruby]
6589
+ ----
6590
+ xml do
6591
+ map_element 'key', to: :attr, value_map: {
6592
+ from: { empty: :nil, omitted: :omitted, nil: :nil },
6593
+ to: { empty: :empty, omitted: :omitted, nil: :nil }
6594
+ }
6595
+ end
6596
+ ----
6597
+
6598
+ By changing to this:
6599
+
6600
+ [source,ruby]
6601
+ ----
6602
+ xml do
6603
+ map_element 'key', to: :attr,
6604
+ render_nil: :as_empty, <1>
6605
+ treat_omitted: :as_nil <2>
6606
+ end
6607
+ ----
6608
+ <1> This overrides the `to:` direction `nil: :nil` mapping.
6609
+ <2> This overrides the `from:` direction `omitted: :omitted` mapping
6610
+
6611
+ The resulting value map would be:
6612
+
6613
+ [source,ruby]
6614
+ ----
6615
+ xml do
6616
+ map_element 'status', to: :status, value_map: {
6617
+ from: { empty: :nil, omitted: :omitted, nil: :empty }, <1>
6618
+ to: { empty: :nil, omitted: :nil, nil: :nil } <2>
6619
+ }
6620
+ end
6621
+ ----
6622
+ <1> See that `nil: :nil` is now `nil: :empty`.
6623
+ <2> See that `omitted: :omitted` is now `omitted: :nil`
6624
+ ====
6625
+
5985
6626
 
5986
6627
  ==== `render_nil`
5987
6628
 
@@ -6014,7 +6655,7 @@ end
6014
6655
 
6015
6656
  [source,ruby]
6016
6657
  ----
6017
- json | yaml | toml do
6658
+ hsh | json | yaml | toml do
6018
6659
  map 'key_value_model_attribute_name', to: :name_of_attribute, render_nil: {option}
6019
6660
  end
6020
6661
  ----
@@ -6153,7 +6794,7 @@ class SomeModel < Lutaml::Model::Serializable
6153
6794
  map_element 'collection', to: :coll, render_nil: :as_nil
6154
6795
  end
6155
6796
 
6156
- json | yaml do
6797
+ hsh | json | yaml do
6157
6798
  map 'collection', to: :coll, render_nil: :as_nil
6158
6799
  end
6159
6800
  end
@@ -6238,7 +6879,7 @@ end
6238
6879
 
6239
6880
  [source,ruby]
6240
6881
  ----
6241
- json | yaml | toml do
6882
+ hsh | json | yaml | toml do
6242
6883
  map 'key_value_model_attribute_name', to: :name_of_attribute, render_empty: {option}
6243
6884
  end
6244
6885
  ----
@@ -6297,7 +6938,7 @@ class SomeModel < Lutaml::Model::Serializable
6297
6938
  map_element 'collection', to: :coll, render_empty: :as_nil
6298
6939
  end
6299
6940
 
6300
- json | yaml do
6941
+ hsh | json | yaml do
6301
6942
  map 'collection', to: :coll, render_empty: :as_nil
6302
6943
  end
6303
6944
  end
@@ -6896,6 +7537,7 @@ Lutaml::Model supports the following serialization formats:
6896
7537
  * YAML (https://yaml.org/[YAML version 1.2])
6897
7538
  * JSON (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/[ECMA-404 The JSON Data Interchange Standard], unofficial link: https://www.json.org[JSON])
6898
7539
  * 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.
6899
7541
 
6900
7542
  You will need to specify the configuration for the adapter you want to use. The
6901
7543
  easiest way is to copy and paste the following configuration into your code.
@@ -6912,6 +7554,7 @@ require 'lutaml/model/yaml_adapter/standard_yaml_adapter'
6912
7554
 
6913
7555
  Lutaml::Model::Config.configure do |config|
6914
7556
  config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
7557
+ config.hash_adapter = Lutaml::Model::YamlAdapter::StandardHashAdapter
6915
7558
  config.yaml_adapter = Lutaml::Model::YamlAdapter::StandardYamlAdapter
6916
7559
  config.json_adapter = Lutaml::Model::JsonAdapter::StandardJsonAdapter
6917
7560
  config.toml_adapter = Lutaml::Model::TomlAdapter::TomlRbAdapter
@@ -6926,6 +7569,7 @@ require 'lutaml/model'
6926
7569
 
6927
7570
  Lutaml::Model::Config.configure do |config|
6928
7571
  config.xml_adapter_type = :nokogiri # can be one of [:nokogiri, :ox, :oga]
7572
+ config.hash_adapter_type = :standard_hash
6929
7573
  config.yaml_adapter_type = :standard_yaml
6930
7574
  config.json_adapter_type = :standard_json # can be one of [:standard_json, :multi_json]
6931
7575
  config.toml_adapter_type = :toml_rb # can be one of [:toml_rb, :tomlib]
@@ -7015,7 +7659,6 @@ end
7015
7659
  ----
7016
7660
 
7017
7661
 
7018
-
7019
7662
  === JSON
7020
7663
 
7021
7664
  Lutaml::Model supports the following JSON adapters:
@@ -7091,6 +7734,11 @@ Lutaml::Model::Config.configure do |config|
7091
7734
  end
7092
7735
  ----
7093
7736
 
7737
+ [[custom-adapters]]
7738
+ === Custom Adapters
7739
+
7740
+ Lutaml::Model provides a flexible system for creating custom adapters to handle different data formats.
7741
+ For more information See link:docs/custom_adapters.adoc[Custom Adapters Guide]
7094
7742
 
7095
7743
  == Comparison with Shale
7096
7744