General
Different information models define different primitive value types, and the same goes for the notions of the "missing values" family:
- the empty value
-
the value is present but empty
- the non-existent value
-
the value is not present
- the undefined value
-
the value is not defined
There are also different ways to represent these missing values when the attribute accepts a single value or a collection of values.
| Technology | Missing value type | Realized as |
|---|---|---|
Lutaml::Model | empty value | Ruby empty string ( |
non-existent value | Ruby | |
undefined value |
| |
XML element | empty value | XML blank element: |
non-existent value | XML blank element with attribute | |
undefined value | the XML element is omitted | |
XML attribute | empty value | XML blank attribute: |
non-existent value | the XML attribute is omitted | |
undefined value | the XML attribute is omitted | |
JSON | empty value | JSON empty string ( |
non-existent value | JSON | |
undefined value | the JSON key is omitted | |
TOML | empty value | TOML empty string |
non-existent value | the TOML key is omitted since TOML does not support the concept of null. | |
undefined value | the TOML key is omitted |
The Uninitialized class is a special Lutaml::Model construct, it is not supported by normal Ruby objects. |
The challenge for the developer is how to represent fully compatible semantics using interoperable data models across different technologies.
Lutaml::Model provides you with several mechanisms to retain the missing values semantics. An example mapping is shown in the following diagram.
┌─────────────────────────────────────────────────────────────────────────────┐
│ ╔════════════════════════╗ ╔════════════════════════╗ │
│ ║ Lutaml::Model Values ║ ║ YAML Values ║ │
│ ╠════════════════════════╣ ╠════════════════════════╣ │
│ ║ ║ mapping ║ ║ │
│ ║ "empty" ║◀──────────────────▶║ "empty" ║ │
│ ║ (empty string, []) ║ to empty ║ (empty string, []) ║ │
│ ║ ║ ║ ║ │
│ ╟────────────────────────╢ ╟────────────────────────╢ │
│ ║ ║ mapping ║ ║ │
│ ║ "non-existent" ║◀──────────────────▶║ "non-existent" ║ │
│ ║ (nil) ║ to non-existent ║ (null) ║ │
│ ║ ║ ║ ║ │
│ ╟────────────────────────╢ ╟────────────────────────╢ │
│ ║ ║ mapping ║ ║ │
│ ║ "undefined" ║◀──────────────────▶║ "undefined" ║ │
│ ║ (uninitialized) ║ to undefined ║ (key omitted) ║ │
│ ║ ║ ║ ║ │
│ ╚════════════════════════╝ ╚════════════════════════╝ │
└─────────────────────────────────────────────────────────────────────────────┘In the case where the interoperating technologies do not support the full spectrum of missing value types, it is necessary for the developer to understand any such behavior and relevant handling.
┌─────────────────────────────────────────────────────────────────────────────┐
│ ╔════════════════════════╗ ╔════════════════════════╗ │
│ ║ Lutaml::Model Values ║ ║ TOML Values ║ │
│ ╠════════════════════════╣ ╠════════════════════════╣ │
│ ║ ║ mapping ║ ║ │
│ ║ "empty" ║◀──────────────────▶║ "empty" ║ │
│ ║ (empty string, []) ║ to empty ║ (empty string, []) ║ │
│ ║ ║ ║ ║ │
│ ╟────────────────────────╢ ╟────────────────────────╢ │
│ ║ ║ mapping ║ ║ │
│ ║ "non-existent" ║◀──────────────────▶║ ║ │
│ ║ (nil) ║ to undefined ║ "undefined" ║ │
│ ║ ║ ║ (key omitted) ║ │
│ ╟────────────────────────╢ ║ ║ │
│ ║ ║ mapping ║ ║ │
│ ║ "undefined" ║─────(one-way)─────▶║ TOML does not ║ │
│ ║ (uninitialized) ║ to undefined ║ support NULL ║ │
│ ║ ║ ║ ║ │
│ ╚════════════════════════╝ ╚════════════════════════╝ │
└─────────────────────────────────────────────────────────────────────────────┘There are the following additional challenges that a developer must take into account of:
-
Single attribute value vs collection attribute value. Different technologies treat single/collection values differently.
-
External schemas and systems that interoperate with serializations from Lutaml::Model. Many schemas and systems adopt "different" conventions for representing missing value semantics (sometimes very awkward ones).
The solution for the first challenge is to understand the behavior of the different technologies used. The default mappings are described in Value representation in Lutaml::Model and Value representation in serialization formats.
Value representation in Lutaml::Model
The following table summarizes the behavior of the Lutaml::Model in regards of the "missing values" family.
| LutaML value type | Cardinality (1 or n) | Missing value type | Ruby value |
|---|---|---|---|
Collection attribute | collection | empty value |
|
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value |
|
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value | N/A |
non-existent value |
| ||
undefined value | No assigned value | ||
| single | empty value |
|
non-existent value |
| ||
undefined value | No assigned value |
Value representation in serialization formats
Every serialization format uses a different information model to represent these missing values.
Some serialization formats support all 3 types of missing values, while others only support a subset of them.
| Serialization format | Cardinality (1 or n) | Missing value type | Example |
|---|---|---|---|
XML | collection | empty collection | the XML blank element: |
non-existent collection | a blank element with attribute | ||
undefined collection | the XML element is omitted | ||
single | empty value | the XML blank element: | |
non-existent value | a blank element with attribute | ||
undefined value | the XML element is omitted | ||
JSON | collection | empty collection | an empty array ( |
non-existent collection | the value | ||
undefined collection | the key is omitted | ||
single | empty value | an empty string ( | |
non-existent value | the value | ||
undefined value | the key is omitted | ||
YAML | collection | empty collection | an empty array ( |
non-existent collection | the value | ||
undefined collection | the key is omitted | ||
single | empty value | an empty string ( | |
non-existent value | the value | ||
undefined value | the key is omitted | ||
TOML | collection | empty collection | an empty array ( |
non-existent collection | TOML does not support the concept of "null" | ||
undefined collection | the key is omitted | ||
single | empty value | an empty string ( | |
non-existent value | TOML does not support the concept of "null" | ||
undefined value | the key is omitted |
Missing value mapping
General
Lutaml::Model provides a comprehensive way to handle the missing values family across different serialization formats.
The value_map option as applied to serialization mapping rules allow users to meticulously define how each and every missing value should be mapped from a serialization format to a Lutaml::Model object.
The value_map option is used to define mappings for both from and to values:
frompairs-
A hash of key-value pairs that determines the mapping of a missing value at the serialization format ("from") to a LutaML Model missing value where this mapping applies. The key is the missing value type in the serialization format, and the value is the missing value type in the LutaML Model.
In other words, used when converting the serialized format into a Lutaml::Model Ruby object. topairs-
A hash of key-value pairs that determines the mapping of a LutaML Model ("to") missing to a missing value choice at the serialization format where this mapping applies. The key is the missing value type in the LutaML Model, and the value is the missing value type in the serialization format.
In other words, used when converting a Lutaml::Model Ruby object into the serialized format.
Syntax:
{map_command} 'format-key', to: :attribute_name, value_map: { (1)
from: {
{format-missing-value-n}: {model-missing-value-n}, (2)
{format-missing-value-m}: {model-missing-value-m},
{format-missing-value-o}: {model-missing-value-o}
},
to: {
{model-missing-value-n}: {format-missing-value-n}, (3)
{model-missing-value-m}: {format-missing-value-m},
{model-missing-value-o}: {format-missing-value-o}
}
}| 1 | The {map_command} is a mapping rule with the actual command depending on the serialization format. |
| 2 | In the from mapping, the keys are the missing value types in the serialization format. |
| 3 | In the to mapping, the keys are the missing value types in the LutaML Model. |
The missing value type mapping differs per serialization format, as serialization formats may not fully support all missing value types.
The availability of from and to keys and values depend on the types of missing values supported by that particular serialization format.
The available values for from and to for serialization formats are presented below, where the allowed values are to be used in the direction of the format. That means if the format supports :empty, it can be used as a key in from: direction, and the value in the to: direction (see {format-missing-value-n}) in the syntax.
| Map command | Missing value types available (key in from: direction, value in the to: direction) |
|---|---|
XML element |
|
XML attribute |
|
Hash |
|
JSON |
|
YAML |
|
TOML |
|
nil cannot be used; therefore in a from: value map, it is not possible to indicate nil: {model-missing-value}. In an XML mapping block, it is possible to do the following.
xml do
map_element 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :nil, omitted: :omitted, nil: :nil }
}
endEach serialization format has specific behavior when handling values such as empty, omitted, and nil.
Users can specify the mapping for both from and to values using the value_map option in the attribute definition.
-
The keys that can be used in the
fromandtomappings areempty,omitted, andnil. -
The values in the mappings can also be
empty,omitted, andnil.
Since nil is not supported in TOML, so mappings like nil: {any_option} or {any_option}: :nil will not work in TOML. |
In a collection attribute, the values of value_map also depend on the initialize_empty setting, where an omitted value in the serialization format can still lead to a nil or an empty array [] at the attribute-level (instead of the mapping-level). |
Default value maps for serialization formats
The table below describes the default value_map configurations for supported serialization formats.
Default value map for XML element (single attribute)
attribute :attr, :string
xml do
map_element 'key', to: :attr, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endDirection | Map rule | XML source | Model target |
|---|---|---|---|
|
| blank XML element ( |
|
| absent XML element | omitted from the model | |
| blank XML element with attribute |
| |
Direction | Map rule | Model source | XML target |
|
| empty string ( | blank XML element |
| omitted in the model | XML element not rendered | |
|
| blank XML element with attribute |
Default value map for XML element (collection attribute)
attribute :attr, :string, collection: true
xml do
map_element 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endDirection | Map rule | XML source | Model target |
|---|---|---|---|
|
| blank XML element ( | empty array ( |
| absent XML element | omitted from the model | |
| blank XML element with attribute |
| |
Direction | Map rule | Model source | XML target |
|
| empty array ( | blank XML element |
| omitted in the model | XML element not rendered | |
|
| blank XML element with attribute |
Default value map for XML attribute (single attribute)
attribute :attr, :string
xml do
map_attribute 'attr_name', to: :attr, value_map: {
from: { empty: :nil, omitted: :omitted },
to: { empty: :empty, nil: :empty, omitted: :omitted }
}
endDirection | Map rule | XML source | Model target |
|---|---|---|---|
|
| blank XML attribute ( |
|
| absent XML attribute | omitted from the model | |
Direction | Map rule | Model source | XML target |
|
| empty string ( | blank XML attribute ( |
| omitted in the model | XML attribute not rendered | |
|
| blank XML attribute ( |
Default value map for XML attribute (collection attribute)
attribute :attr, :string, collection: true
xml do
map_attribute 'attr_name', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted },
to: { empty: :empty, nil: :omitted, omitted: :omitted }
}
endDirection | Map rule | XML source | Model target |
|---|---|---|---|
|
| blank XML attribute ( | empty array ( |
| absent XML attribute | omitted from the model | |
Direction | Map rule | Model source | XML target |
|
| empty array ( | blank XML attribute ( |
| omitted in the model | XML attribute not rendered | |
|
| XML attribute not rendered |
Default value map for YAML (single attribute)
attribute :attr, :string
yaml do
map 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endDirection | Map rule | XML source | Model target |
|---|---|---|---|
|
| empty string in YAML ( | empty string ( |
| absent YAML key | omitted from the model | |
| YAML |
| |
Direction | Map rule | Model source | XML target |
|
| empty string ( | empty string in YAML ( |
| omitted in the model | YAML key omitted | |
|
| YAML |
In order to treat a YAML value like status: '' to nil, the mapping of value_map: { from: { empty: :nil } } can be applied. |
Default value map for YAML (collection attribute)
attribute :attr, :string, collection: true
yaml do
map 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endDirection | Map rule | YAML source | Model target |
|---|---|---|---|
|
| empty YAML array ( | empty array ( |
| absent YAML key | omitted from the model | |
| YAML |
| |
Direction | Map rule | Model source | YAML target |
|
| empty array ( | empty YAML array ( |
| omitted in the model | YAML key omitted | |
|
| YAML |
If the YAML key for the collection attribute is omitted, it will be treated as nil or an empty array depending on the initialize_empty setting. |
Default value map for JSON (single attribute)
attribute :attr, :string
json do
map 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endDirection | Map rule | JSON source | Model target |
|---|---|---|---|
|
| empty string in JSON ( | empty string ( |
| absent JSON key | omitted from the model | |
| JSON |
| |
Direction | Map rule | Model source | JSON target |
|
| empty string ( | empty string in JSON ( |
| omitted in the model | JSON key omitted | |
|
| JSON |
Default value map for JSON (collection attribute)
attribute :attr, :string, collection: true
json do
map 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endDirection | Map rule | JSON source | Model target |
|---|---|---|---|
|
| empty JSON array ( | empty array ( |
| absent JSON key | omitted from the model | |
| JSON |
| |
Direction | Map rule | Model source | JSON target |
|
| empty array ( | empty JSON array ( |
| omitted in the model | JSON key omitted | |
|
| JSON |
Default value map for TOML (single attribute)
TOML does not support the concept of nil and therefore the mapping of from: direction with nil to will not work in TOML.
The nil mapping is only supported in the to: direction (model to TOML).
attribute :attr, :string
toml do
map 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted },
to: { empty: :empty, omitted: :omitted, nil: :omitted }
}
endDirection | Map rule | TOML source | Model target |
|---|---|---|---|
|
| empty string in TOML ( | empty string ( |
| absent TOML key | omitted from the model | |
Direction | Map rule | Model source | TOML target |
|
| empty string ( | empty string in TOML ( |
| omitted in the model | TOML key omitted | |
|
| TOML key omitted |
Default value map for TOML (collection attribute)
TOML does not support the concept of nil and therefore the mapping of from: direction with nil to will not work in TOML.
The nil mapping is only supported in the to: direction (model to TOML).
attribute :attr, :string, collection: true
toml do
map 'key', to: :attr, value_map: {
from: { empty: :empty, omitted: :omitted },
to: { empty: :empty, omitted: :omitted, nil: :omitted }
}
endDirection | Map rule | TOML source | Model target |
|---|---|---|---|
|
| empty TOML array ( | empty array ( |
| absent TOML key | omitted from the model | |
Direction | Map rule | Model source | TOML target |
|
| empty array ( | empty TOML array ( |
| omitted in the model | TOML key omitted | |
|
| TOML key omitted |
Replacing missing values type mapping with value_map
The value_map option can be defined to meticulously map for each serialization format as follows.
value_map with from and to valuesclass ExampleClass < Lutaml::Model::Serializable
attribute :status, :string
xml do
map_element 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :nil, omitted: :omitted, nil: :nil }
}
end
hsh | json | yaml | toml | key_value do
map 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :nil, omitted: :omitted, nil: :nil }
}
end
end
yaml = <<~YAML
---
status: ''
YAML
ExampleClass.from_yaml(yaml)
# => #<ExampleClass:0x000000011954efb0 @status=nil>
yaml1 = <<~YAML
---
YAML
ExampleClass.from_yaml(yaml1)
# => #<ExampleClass:0x000000011954efb0 @status=uninitialized>
yaml2 = <<~YAML
---
status:
YAML
ExampleClass.from_yaml(yaml2)
# => #<ExampleClass:0x000000011954efb0 @status=nil>When defining an attribute with collection: true, the attribute will behave as follows:
attribute :status, :string, collection: trueHere’s an example of how you can use the value_map with a collection attribute.
value_map with a collection attributeclass ExampleClass < Lutaml::Model::Serializable
attribute :status, :string, collection: true
xml do
map_element 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :nil, omitted: :omitted, nil: :nil }
}
end
hsh | json | yaml | key_value do
map 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :nil, omitted: :omitted, nil: :nil }
}
end
toml do
map 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted },
to: { empty: :nil, omitted: :omitted, nil: :omitted }
}
end
end
yaml = <<~YAML
---
status: ['new', 'assigned']
YAML
y = ExampleClass.from_yaml(yaml)
# => #<ExampleClass:0x000000011954efb0 @status=["new", "assigned"]> Specific overrides of value map (render_* and treat_*)
General
There are times that one may want to simply override handling of selective missing value types rather than re-define the entire value map.
The :render_* and :treat_* options are simple switches that override the default value map provided for the different serialization formats.
Syntax:
{map_command} 'format-key', to: :attribute_name, (1)
:render_{model-value}: :as_{format-value}, (2)
# ...
:treat_{format-value}: :as_{model-value}, (3)
# ...| 1 | The {map_command} is a mapping rule with the actual command depending on the serialization format. The attribute of attribute_name may be a single or a collection value. |
| 2 | The :render_* mapping overrides the default value map for missing value types in model-to-serialization. |
| 3 | The :treat_* mapping overrides the default value map for missing value types in serialization-to-model. |
Specifically,
-
The
:render_{model-value}: :as_{format-value}options are used to override the default behavior of rendering missing value types into the serialization format.{model-value}-
specifies the missing value type in the LutaML Model.
{format-value}-
specifies the missing value type in the serialization format.
-
The
:treat_{format-value}: :as_{model-value}options are used to override the default behavior of importing missing value types into the model.{format-value}-
specifies the missing value type in the serialization format.
{model-value}-
specifies the missing value type in the LutaML Model.
In effect, the default value_map is overriden by the :render_* and :treat_* directives.
Given the default mapping for an XML element, the :render_* and :treat_* options can be used to selectively override behavior.
xml do
map_element 'key', to: :attr, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :nil },
to: { empty: :empty, omitted: :omitted, nil: :nil }
}
endBy changing to this:
xml do
map_element 'key', to: :attr,
render_nil: :as_empty, (1)
treat_omitted: :as_nil (2)
end| 1 | This overrides the to: direction nil: :nil mapping. |
| 2 | This overrides the from: direction omitted: :omitted mapping |
The resulting value map would be:
xml do
map_element 'status', to: :status, value_map: {
from: { empty: :nil, omitted: :omitted, nil: :empty }, (1)
to: { empty: :nil, omitted: :nil, nil: :nil } (2)
}
end| 1 | See that nil: :nil is now nil: :empty. |
| 2 | See that omitted: :omitted is now omitted: :nil |
render_nil
General
:render_nil is a specially handled case of the :render_* pattern due to legacy. It is used to override default value map behavior for the nil model value.
Format-specific terminology
The terms :as_blank and :as_empty are format-specific and not interchangeable. See render_empty for detailed explanation. |
render_nil accepts these values:
:as_empty-
(key-value formats only) If the value is nil, render as explicit empty value (
[]or""). :as_blank-
(XML only) If the value is nil, render as blank XML element (
<tag/>). :as_nil-
If the value is nil, render as nil (XML:
xsi:nil="true", key-value:null). :omit-
If the value is nil, omit the element, attribute, or key entirely.
true-
(legacy) Setting
render_nil: truewill render the attribute as an empty element if the attribute isnil. For XML, this behaves like:as_blank. For key-value formats, this behaves like:as_empty.
Syntax:
xml do
map_element 'key_value_model_attribute_name', to: :name_of_attribute, render_nil: {option}
endhsh | json | yaml | toml do
map 'key_value_model_attribute_name', to: :name_of_attribute, render_nil: {option}
end Render nil as true
render_nil: true option to render an attribute value of nil as an empty elementclass Ceramic < Lutaml::Model::Serializable
attribute :type, :string
attribute :glaze, :string
xml do
map_element 'type', to: :type, render_nil: true
map_element 'glaze', to: :glaze
end
json do
map 'type', to: :type, render_nil: true
map 'glaze', to: :glaze
end
end> Ceramic.new.to_json
> # { 'type': null }
> Ceramic.new(type: "Porcelain", glaze: "Clear").to_json
> # { 'type': 'Porcelain', 'glaze': 'Clear' }> Ceramic.new.to_xml
> # <Ceramic><type></type></Ceramic>
> Ceramic.new(type: "Porcelain", glaze: "Clear").to_xml
> # <Ceramic><type>Porcelain</type><glaze>Clear</glaze></Ceramic>render_nil: true option to render an empty attribute collection of nil as an empty elementclass Ceramic < Lutaml::Model::Serializable
attribute :type, :string
attribute :glazes, :string, collection: true
xml do
map_element 'type', to: :type, render_nil: true
map_element 'glazes', to: :glazes, render_nil: true
end
json do
map 'type', to: :type, render_nil: true
map 'glazes', to: :glazes, render_nil: true
end
end> Ceramic.new.to_json
> # { 'type': null, 'glazes': [] }
> Ceramic.new(type: "Porcelain", glazes: ["Clear"]).to_json
> # { 'type': 'Porcelain', 'glazes': ['Clear'] }> Ceramic.new.to_xml
> # <Ceramic><type></type><glazes></glazes></Ceramic>
> Ceramic.new(type: "Porcelain", glazes: ["Clear"]).to_xml
> # <Ceramic><type>Porcelain</type><glazes>Clear</glazes></Ceramic> Render nil as omit
Using render_nil: :omit with a nil value will omit the key from XML and key-value formats.
class SomeModel < Lutaml::Model::Serializable
attribute :coll, :string, collection: true
xml do
root "some-model"
map_element 'collection', to: :coll, render_nil: :omit
end
key_value do
map 'collection', to: :coll, render_nil: :omit
end
end
puts SomeModel.new.coll
# => nil
puts SomeModel.new.to_xml
# =>
# <some-model></some-model>
puts SomeModel.new.to_yaml
# =>
# --- Render nil as nil
Using render_nil: :as_nil with a nil value will create an empty element with xsi:nil attribute in XML and create a key with explicit null value in key-value formats.
TOML does not support this option. |
class SomeModel < Lutaml::Model::Serializable
attribute :coll, :string, collection: true
xml do
root "some-model"
map_element 'collection', to: :coll, render_nil: :as_nil
end
hsh | json | yaml do
map 'collection', to: :coll, render_nil: :as_nil
end
end
puts SomeModel.new.coll
# => nil
puts SomeModel.new.to_xml
# =>
# <some-model xsi:xmlns="..."><collection xsi:nil="true"/></some-model>
puts SomeModel.new.to_yaml
# =>
# ---
# coll: null Render nil as blank
Using render_nil: :as_blank in XML mappings will create a blank element, and using render_nil: :as_empty in key-value mappings will create a key with an explicit empty array.
:as_blank is XML-specific (creates blank XML elements like <tag/>), while :as_empty is key-value-specific (creates explicit empty values like []). These terms are format-specific and not interchangeable. |
class SomeModel < Lutaml::Model::Serializable
attribute :coll, :string, collection: true
xml do
root "some-model"
map_element 'collection', to: :coll, render_nil: :as_blank (1)
end
key_value do
map 'collection', to: :coll, render_nil: :as_empty (2)
end
end
puts SomeModel.new.coll
# => nil
puts SomeModel.new.to_xml
# =>
# <some-model><collection/></some-model>
puts SomeModel.new.to_yaml
# =>
# ---
# coll: []| 1 | XML uses :as_blank to create a blank element <collection/> |
| 2 | Key-value formats use :as_empty to create explicit empty array [] |
render_empty
General
:render_empty is a specially handled case of the :render_* pattern due to legacy. It is used to override default value map behavior for empty model values.
Format-specific terminology
The terms :as_blank and :as_empty are format-specific and not interchangeable. |
:as_blank-
XML-specific. Creates a blank XML element (
<tag/>or<tag></tag>). Only valid inxmlmapping blocks (map_element,map_attribute,map_content). :as_empty-
Key-value-specific. Creates an explicit empty value (
[]for arrays,""for strings). Only valid injson,yaml,toml,hsh, andkey_valuemapping blocks.
Using the wrong term for a format will raise an IncorrectMappingArgumentsError:
-
Using
:as_emptyin XML mappings → Error: "`:as_empty` is not supported for XML mappings. Use:as_blankinstead." * Using:as_blankin key-value mappings → Error: "`:as_blank` is not supported for key-value mappings. Use:as_emptyinstead."
Supported values
render_empty accepts these values:
:as_empty-
(key-value formats only) Render as explicit empty value.
:as_blank-
(XML only) Render as blank XML element.
:as_nil-
Render as nil (XML:
xsi:nil="true", key-value:null). :omit-
Omit the element, attribute, or key entirely.
Syntax:
xml do
map_element 'key_value_model_attribute_name', to: :name_of_attribute, render_empty: {option}
endhsh | json | yaml | toml do
map 'key_value_model_attribute_name', to: :name_of_attribute, render_empty: {option}
end Render empty as omit
Using render_empty: :omit with an empty value or empty collection will omit the key from XML and key-value formats.
class SomeModel < Lutaml::Model::Serializable
attribute :coll, :string, collection: true
xml do
root "some-model"
map_element 'collection', to: :coll, render_empty: :omit
end
key_value do
map 'collection', to: :coll, render_empty: :omit
end
end
puts SomeModel.new(coll: []).coll
# => []
puts SomeModel.new.to_xml
# =>
# <some-model></some-model>
puts SomeModel.new.to_yaml
# =>
# --- Render empty as nil
Using render_empty: :as_nil will create an empty element with the xsi:nil attribute in XML, and create a key with explicit null value in key-value formats.
TOML does not support this option. |
class SomeModel < Lutaml::Model::Serializable
attribute :coll, :string, collection: true
xml do
root "some-model"
map_element 'collection', to: :coll, render_empty: :as_nil
end
hsh | json | yaml do
map 'collection', to: :coll, render_empty: :as_nil
end
end
puts SomeModel.new(coll: []).coll
# => []
puts SomeModel.new.to_xml
# =>
# <some-model xsi:xmlns="..."><collection xsi:nil="true"/></some-model>
puts SomeModel.new.to_yaml
# =>
# ---
# coll: null Render empty as blank/empty
Using render_empty: :as_blank in XML mappings will create a blank element, and using render_empty: :as_empty in key-value mappings will create a key with an explicit empty array.
:as_blank is XML-specific (creates blank XML elements like <tag/>), while :as_empty is key-value-specific (creates explicit empty values like []). These terms are format-specific and not interchangeable. |
class SomeModel < Lutaml::Model::Serializable
attribute :coll, :string, collection: true
xml do
root "some-model"
map_element 'collection', to: :coll, render_empty: :as_blank (1)
end
key_value do
map 'collection', to: :coll, render_empty: :as_empty (2)
end
end
puts SomeModel.new(coll: []).coll
# => []
puts SomeModel.new.to_xml
# =>
# <some-model><collection/></some-model>
puts SomeModel.new.to_yaml
# =>
# ---
# coll: []| 1 | XML uses :as_blank to create a blank element <collection/> |
| 2 | Key-value formats use :as_empty to create explicit empty array [] |