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.

Table 1. Support of missing value types in different technologies
Technology Missing value type Realized as

Lutaml::Model

empty value

Ruby empty string ("")

non-existent value

Ruby NilClass (nil)

undefined value

class Uninitialized

XML element

empty value

XML blank element: <status></status> or <status/>

non-existent value

XML blank element with attribute xsi:nil: <status xsi:nil="true"/>

undefined value

the XML element is omitted

XML attribute

empty value

XML blank attribute: status=""

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 null value

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.

Mapping of missing value types between Lutaml::Model and YAML
┌─────────────────────────────────────────────────────────────────────────────┐
│  ╔════════════════════════╗                    ╔════════════════════════╗   │
│  ║  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.

Mapping of missing value types between Lutaml::Model and TOML
┌─────────────────────────────────────────────────────────────────────────────┐
│  ╔════════════════════════╗                    ╔════════════════════════╗   │
│  ║  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.

Table 2. Handling of missing value types in Lutaml::Model data types
LutaML value type Cardinality (1 or n) Missing value type Ruby value

Collection attribute

collection

empty value

[] (Array)

non-existent value

nil (NilClass)

undefined value

No assigned value

:string

single

empty value

"" (String)

non-existent value

nil (NilClass)

undefined value

No assigned value

:integer

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:float

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:boolean

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:date

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:time_without_date

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:date_time

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:time

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:decimal

single

empty value

N/A

non-existent value

nil (NilClass)

undefined value

No assigned value

:hash

single

empty value

{} (Hash)

non-existent value

nil (NilClass)

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.

Table 3. Varied handling of missing values in supported serialization formats
Serialization format Cardinality (1 or n) Missing value type Example

XML

collection

empty collection

the XML blank element: <status></status> or <status/>

non-existent collection

a blank element with attribute xsi:nil: <status xsi:nil="true"/>

undefined collection

the XML element is omitted

single

empty value

the XML blank element: <status></status> or <status/>

non-existent value

a blank element with attribute xsi:nil: <status xsi:nil="true"/>

undefined value

the XML element is omitted

JSON

collection

empty collection

an empty array ([])

non-existent collection

the value null

undefined collection

the key is omitted

single

empty value

an empty string ("")

non-existent value

the value null

undefined value

the key is omitted

YAML

collection

empty collection

an empty array ([])

non-existent collection

the value null

undefined collection

the key is omitted

single

empty value

an empty string ("")

non-existent value

the value null

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:

from pairs

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.
to pairs

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.

Table 4. Available missing value types in different mapping commands
Map command Missing value types available (key in from: direction, value in the to: direction)

XML element map_element

:empty, :omitted, :nil

XML attribute map_attribute

:empty, :omitted

Hash map, map_content

:empty, :omitted, :nil

JSON map, map_content

:empty, :omitted, :nil

YAML map, map_content

:empty, :omitted, :nil

TOML map, map_content

:empty, :omitted

For instance, TOML does not support the notion of "null" and therefore the missing value type of 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 }
  }
end

Each 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 from and to mappings are empty, omitted, and nil.

  • The values in the mappings can also be empty, omitted, and nil.

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 }
  }
end
Table 5. Default missing value mapping configuration for single attributes in XML elements

Direction

Map rule

XML source

Model target

from:

empty: :nil

blank XML element (<status/>)

nil

omitted: :omitted

absent XML element

omitted from the model

nil: :nil

blank XML element with attribute xsi:nil (<status xsi:nil=true/>)

nil value in the model

Direction

Map rule

Model source

XML target

to:

empty: :empty

empty string ("")

blank XML element

omitted: :omitted

omitted in the model

XML element not rendered

nil: :nil

nil value in the model

blank XML element with attribute xsi:nil (<status xsi:nil=true/>)

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 }
  }
end
Table 6. Default missing value mapping configuration for collection attributes in XML elements

Direction

Map rule

XML source

Model target

from:

empty: :nil

blank XML element (<status/>)

empty array ([])

omitted: :omitted

absent XML element

omitted from the model

nil: :nil

blank XML element with attribute xsi:nil (<status xsi:nil=true/>)

nil value in the model

Direction

Map rule

Model source

XML target

to:

empty: :empty

empty array ([])

blank XML element

omitted: :omitted

omitted in the model

XML element not rendered

nil: :nil

nil value in the model

blank XML element with attribute xsi:nil (<status xsi:nil=true/>)

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 }
  }
end
Table 7. Default missing value mapping configuration for single attributes in XML attributes

Direction

Map rule

XML source

Model target

from: (source only supports empty and omitted)

empty: :nil

blank XML attribute (status="")

nil

omitted: :omitted

absent XML attribute

omitted from the model

Direction

Map rule

Model source

XML target

to: (target only accepts empty and omitted)

empty: :empty

empty string ("")

blank XML attribute (status="")

omitted: :omitted

omitted in the model

XML attribute not rendered

nil: :empty

nil

blank XML attribute (status="")

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 }
  }
end
Table 8. Default missing value mapping configuration for collection attributes in XML attributes

Direction

Map rule

XML source

Model target

from: (source only supports empty and omitted)

empty: :empty

blank XML attribute (status="")

empty array ([])

omitted: :omitted

absent XML attribute

omitted from the model

Direction

Map rule

Model source

XML target

to: (target only accepts empty and omitted)

empty: :empty

empty array ([])

blank XML attribute (status="")

omitted: :omitted

omitted in the model

XML attribute not rendered

nil: :omitted

nil

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 }
  }
end
Table 9. Default missing value mapping configuration for single attributes in YAML

Direction

Map rule

XML source

Model target

from:

empty: :empty

empty string in YAML (status: or status: "")

empty string ("")

omitted: :omitted

absent YAML key

omitted from the model

nil: :nil

YAML null

nil

Direction

Map rule

Model source

XML target

to:

empty: :empty

empty string ("")

empty string in YAML (status:)

omitted: :omitted

omitted in the model

YAML key omitted

nil: :nil

nil

YAML null

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 }
  }
end
Table 10. Default missing value mapping configuration for collection attributes in YAML

Direction

Map rule

YAML source

Model target

from:

empty: :empty

empty YAML array (status: or status: [])

empty array ([])

omitted: :omitted

absent YAML key

omitted from the model

nil: :nil

YAML null

nil

Direction

Map rule

Model source

YAML target

to:

empty: :empty

empty array ([])

empty YAML array (status: [])

omitted: :omitted

omitted in the model

YAML key omitted

nil: :nil

nil

YAML null

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 }
  }
end
Table 11. Default missing value mapping configuration for single attributes in JSON

Direction

Map rule

JSON source

Model target

from:

empty: :empty

empty string in JSON ("status" : "")

empty string ("")

omitted: :omitted

absent JSON key

omitted from the model

nil: :nil

JSON null

nil

Direction

Map rule

Model source

JSON target

to:

empty: :empty

empty string ("")

empty string in JSON ("status" : "")

omitted: :omitted

omitted in the model

JSON key omitted

nil: :nil

nil

JSON null

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 }
  }
end
Table 12. Default missing value mapping configuration for collection attributes in JSON

Direction

Map rule

JSON source

Model target

from:

empty: :empty

empty JSON array ("status": [])

empty array ([])

omitted: :omitted

absent JSON key

omitted from the model

nil: :nil

JSON null

nil

Direction

Map rule

Model source

JSON target

to:

empty: :empty

empty array ([])

empty JSON array ("status": [])

omitted: :omitted

omitted in the model

JSON key omitted

nil: :nil

nil

JSON null

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 }
  }
end
Table 13. Default missing value mapping configuration for single attributes in TOML

Direction

Map rule

TOML source

Model target

from: (source only supports empty and omitted)

empty: :empty

empty string in TOML ([status] with no value)

empty string ("")

omitted: :omitted

absent TOML key

omitted from the model

Direction

Map rule

Model source

TOML target

to: (source only supports empty and omitted)

empty: :empty

empty string ("")

empty string in TOML ([status] with no value)

omitted: :omitted

omitted in the model

TOML key omitted

nil: :omitted

nil

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 }
  }
end
Table 14. Default missing value mapping configuration for collection attributes in TOML

Direction

Map rule

TOML source

Model target

from: (source only supports empty and omitted)

empty: :empty

empty TOML array ([status] with no value)

empty array ([])

omitted: :omitted

absent TOML key

omitted from the model

Direction

Map rule

Model source

TOML target

to: (source only supports empty and omitted)

empty: :empty

empty array ([])

empty TOML array ([status] with no value)

omitted: :omitted

omitted in the model

TOML key omitted

nil: :omitted

nil

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.

Using value_map with from and to values
class 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: true

Here’s an example of how you can use the value_map with a collection attribute.

Example 1. Using value_map with a collection attribute
class 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 }
  }
end

By 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: true will render the attribute as an empty element if the attribute is nil. 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}
end
hsh | json | yaml | toml do
  map 'key_value_model_attribute_name', to: :name_of_attribute, render_nil: {option}
end

Render nil as true

Example 2. Using the render_nil: true option to render an attribute value of nil as an empty element
class 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>
Example 3. Using the render_nil: true option to render an empty attribute collection of nil as an empty element
class 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 in xml mapping blocks (map_element, map_attribute, map_content).

:as_empty

Key-value-specific. Creates an explicit empty value ([] for arrays, "" for strings). Only valid in json, yaml, toml, hsh, and key_value mapping blocks.

Using the wrong term for a format will raise an IncorrectMappingArgumentsError:

  • Using :as_empty in XML mappings → Error: "`:as_empty` is not supported for XML mappings. Use :as_blank instead." * Using :as_blank in key-value mappings → Error: "`:as_blank` is not supported for key-value mappings. Use :as_empty instead."

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}
end
hsh | 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 []