General
Key-value data models share a similar structure where data is stored as key-value pairs.
Lutaml::Model works with these formats in a similar way.
Key-value data models supported are identified by their short name:
hsh-
Hash (Ruby
Hashclass) json-
JSON
yaml-
YAML
toml-
TOML
key_value-
A way to configure key-value mappings for all supported key-value data models.
Mapping
The map method is used to define key-value mappings.
Syntax:
{key_value_type_short} do (1)
map 'key_value_model_attribute_name', to: :name_of_attribute
end| 1 | key_value_type_short is the key-value data model’s short name. |
json do
map :color, to: :color
map :desc, to: :description
endkey_value do
map :color, to: :color
map :desc, to: :description
endUnified mapping
The key_value method is a streamlined way to map all attributes for serialization into key-value formats including Hash, JSON, YAML, and TOML.
If there is no definite differentiation between the key value formats, the key_value method simplifies defining mappings and improves code readability.
map method to define the same mappings across all key-value formatsThis example shows how to define a key-value data model with the key_value method which maps the same attributes across all key-value formats.
class CeramicModel < Lutaml::Model::Serializable
attribute :color, :string
attribute :glaze, :string
attribute :description, :string
key_value do # or any other key-value data model
map :color, to: :color
map :glz, to: :glaze
map :desc, to: :description
end
end{
"color": "Navy Blue",
"glz": "Clear",
"desc": "A ceramic with a navy blue color and clear glaze."
}color: Navy Blue
glz: Clear
desc: A ceramic with a navy blue color and clear glaze.> CeramicModel.from_json(json)
> #<CeramicModel:0x0000000104ac7240 @color="Navy Blue", @glaze="Clear", @description="A ceramic with a navy blue color and clear glaze.">
> CeramicModel.new(color: "Navy Blue", glaze: "Clear", description: "A ceramic with a navy blue color and clear glaze.").to_json
> #{"color"=>"Navy Blue", "glz"=>"Clear", "desc"=>"A ceramic with a navy blue color and clear glaze."}Specific format mappings
Specific key value formats can be mapping independently of other formats.
map method to define key-value mappings per formatclass Example < Lutaml::Model::Serializable
attribute :name, :string
attribute :value, :integer
hsh do
map 'name', to: :name
map 'value', to: :value
end
json do
map 'name', to: :name
map 'value', to: :value
end
yaml do
map 'name', to: :name
map 'value', to: :value
end
toml do
map 'name', to: :name
map 'value', to: :value
end
end{
"name": "John Doe",
"value": 28
}> Example.from_json(json)
> #<Example:0x0000000104ac7240 @name="John Doe", @value=28>
> Example.new(name: "John Doe", value: 28).to_json
> #{"name"=>"John Doe", "value"=>28}Mapping all key-value content
The map_all tag captures and maps all content within a serialization format into a single attribute in the target Ruby object.
The use case for map_all is to tell Lutaml::Model to not parse the content at all, and instead handle it as a raw string.
| The corresponding method for XML is at [xml-map-all]. |
| Notice that usage of mapping all will lead to incompatibility between serialization formats, i.e. the raw string content will not be portable as objects are across different formats. |
This is useful when the content needs to be handled as-is without parsing into individual attributes.
The map_all tag is exclusive and cannot be combined with other mappings, ensuring it captures the entire content.
An error is raised if map_all is defined alongside any other mapping in the same mapping context. |
Syntax:
hsh | json | yaml | toml | key_value do
map_all to: :name_of_attribute
endmap_all to capture all content across different formatsclass Document < Lutaml::Model::Serializable
attribute :content, :string
hsh do
map_all to: :content
end
json do
map_all to: :content
end
yaml do
map_all to: :content
end
toml do
map_all to: :content
end
endFor JSON:
{
"sections": [
{ "title": "Introduction", "text": "Chapter 1" },
{ "title": "Conclusion", "text": "Final chapter" }
],
"metadata": {
"author": "John Doe",
"date": "2024-01-15"
}
}For YAML:
sections:
- title: Introduction
text: Chapter 1
- title: Conclusion
text: Final chapter
metadata:
author: John Doe
date: 2024-01-15The content is preserved exactly as provided:
> doc = Document.from_json(json_content)
> puts doc.content
> # "{\"sections\":[{\"title\":\"Introduction\",\"text\":\"Chapter 1\"},{\"title\":\"Conclusion\",\"text\":\"Final chapter\"}],\"metadata\":{\"author\":\"John Doe\",\"date\":\"2024-01-15\"}}"
> doc = Document.from_yaml(yaml_content)
> puts doc.content
> # "sections:\n - title: Introduction\n text: Chapter 1\n - title: Conclusion\n text: Final chapter\nmetadata:\n author: John Doe\n date: 2024-01-15\n"Nested attribute mappings
The map method can also be used to map nested key-value data models by referring to a Lutaml::Model class as an attribute class.
class Glaze < Lutaml::Model::Serializable
attribute :color, :string
attribute :temperature, :integer
json do
map 'color', to: :color
map 'temperature', to: :temperature
end
end
class Ceramic < Lutaml::Model::Serializable
attribute :type, :string
attribute :glaze, Glaze
json do
map 'type', to: :type
map 'glaze', to: :glaze
end
end{
"type": "Porcelain",
"glaze": {
"color": "Clear",
"temperature": 1050
}
}> Ceramic.from_json(json)
> #<Ceramic:0x0000000104ac7240 @type="Porcelain", @glaze=#<Glaze:0x0000000104ac7240 @color="Clear", @temperature=1050>>
> Ceramic.new(type: "Porcelain", glaze: Glaze.new(color: "Clear", temperature: 1050)).to_json
> #{"type"=>"Porcelain", "glaze"=>{"color"=>"Clear", "temperature"=>1050}}Collection with keyed elements (keyed collection)
General
| This feature is for key-value data model serialization and deserialization only. |
The map method with the root_mappings option is used for key-value data that is keyed using an attribute value.
In other words, the key of a key-value pair in a collection is actually the value of an attribute that belongs to the value.
Simply put, the following two data structures are considered to have the same data:
id attribute---
vase1:
name: Imperial Vase
bowl2:
name: 18th Century Bowlid attribute value located inside each element---
- id: vase1
name: Imperial Vase
- id: bowl2
name: 18th Century BowlThere are key difference between these two data structures:
-
The keyed object (first data structure) ensures uniqueness of the
idattribute value across the collection, while the array (second data structure) does not. -
The value of the
idattribute in the first data structure exists outside of the formal structure of the data object, instead, it only exists at the collection level. On the other hand, the value exists inside the structure of the data object in the second data structure.
The map method with the root_mappings option, in practice, parses the first data structure in the same way that you would access / manipulate the second data structure, while retaining the serialization semantics of using an attribute as key.
As a result, usage of lutaml-model across both types of collections are identical (except when serialized).
Syntax:
class SomeKeyedCollection < Lutaml::Model::Serializable
attribute :name_of_attribute, AttributeValueType, collection: true
hsh | json | yaml | toml | key_value do
map to: :name_of_attribute, (1)
root_mappings: { (2)
# `:key` is a reserved keyword
value_type_attribute_name_for_key: :key, (3)
# `:value` is a reserved keyword (and optional)
value_type_attribute_name_for_value: :value, (4)
# `[path name]` represents the path to access the value in the
# serialization data model to be assigned to
# `AttributeValueType.value_type_attribute_name_for_custom_type`
value_type_attribute_name_for_custom_type: [path name] (5)
}
end
end
class AttributeValueType < Lutaml::Model::Serializable
attribute :value_type_attribute_name_for_key, :string
attribute :value_type_attribute_name_for_value, :string
attribute :value_type_attribute_name_for_custom_type, CustomType
end| 1 | The map option indicates that this class represents the root of the serialization object being passed in. The name_of_attribute is the name of the attribute that will hold the collection data. (Mandatory) |
| 2 | The root_mappings keyword specifies what the collection key represents and and value for model. (Mandatory) |
| 3 | The key keyword specifies the attribute name of the individual collection object type that represents its key used in the collection. (Mandatory) |
| 4 | The value keyword specifies the attribute name of the individual collection object type that represents its data used in the collection. (Optional, if not specified, the entire object is used as the value.) |
| 5 | The value_type_attribute_name_for_custom_type is the name of the attribute inside the individual collection object (AttributeValueType) that will hold the value accessible in the serialization data model fetched at [path name]. |
The mapping syntax here is similar to that of Attribute extraction except that the :key and :value keywords are allowed in addition to {path}.
There are 3 cases when working with a keyed collection:
-
Case 1: Only move the "key" into the collection object.
-
Case 2: Move the "key" into the collection object, override all other mappings. Maps
:keyand another attribute, then we override all the other mappings (clean slate) -
Case 3: Move the "key" into the collection object to an attribute, map the entire "value" to another attribute of the collection object.
Case 1: Only move the "key" into the collection object
In this case, the "key" of the keyed collection is moved into the collection object, and all other mappings are left as they are.
When the "key" is moved into the collection object, the following happens:
-
The "key" of the keyed collection maps to a particular attribute of the collection’s instance object.
-
The "value" of the keyed collection (with its various content) maps to the collection’s instance object following the collection’s instance object type’s default mappings.
The root_mappings option should only contain one mapping, and the mapping must lead to the :key keyword.
Syntax:
class SomeKeyedCollection < Lutaml::Model::Serializable
attribute :name_of_attribute, AttributeValueType, collection: true
hsh | json | yaml | toml | key_value do
map to: :name_of_attribute,
root_mappings: {
value_type_attribute_name_for_key: :key, (1)
}
end
end
class AttributeValueType < Lutaml::Model::Serializable
attribute :value_type_attribute_name_for_key, :string
attribute :value_type_attribute_name_for_value, :string
attribute :value_type_attribute_name_for_custom_type, CustomType
end| 1 | The :key keyword specifies that the "key" of the keyed collection maps to the value_type_attribute_name_for_key attribute of the collection’s instance object (i.e. AttributeValueType). |
map with root_mappings (only key) to map a keyed collection into individual modelsGiven this data:
---
vase1:
name: Imperial Vase
bowl2:
name: 18th Century BowlA model can be defined for this YAML as follows:
# This is a normal Lutaml::Model class
class Ceramic < Lutaml::Model::Serializable
attribute :ceramic_id, :string
attribute :ceramic_name, :string
key_value do
map 'id', to: :ceramic_id
map 'name', to: :ceramic_name
end
end
# This is Lutaml::Model class that represents the collection of Ceramic objects
class CeramicCollection < Lutaml::Model::Serializable
attribute :ceramics, Ceramic, collection: true
key_value do
map to: :ceramics, # All data goes to the `ceramics` attribute
root_mappings: {
# The key of an object in this collection is mapped to the ceramic_id
# attribute of the Ceramic object.
ceramic_id: :key # "key" is a reserved keyword
}
end
end# Parsing the YAML collection with dynamic data keys
> ceramic_collection = CeramicCollection.from_yaml(yaml)
> #<CeramicCollection:0x0000000104ac7240
@ceramics=
[#<Ceramic:0x0000000104ac6e30 @ceramic_id="vase1", @ceramic_name="Imperial Vase">,
#<Ceramic:0x0000000104ac58f0 @ceramic_id="bowl2", @ceramic_name="18th Century Bowl">]
# NOTE: When an individual Ceramic object is serialized, the `id` attribute is
# the original key in the incoming YAML data, and because there were no mappings defined along with the `:key`, everyting is mapped to the `Ceramic` object using the mappings defined in the `Ceramic` class.
> first_ceramic = ceramic_collection.ceramics.first
> puts first_ceramic.to_yaml
=>
# ---
# id: vase1
# name: Imperial Vase
# NOTE: When in a collection, the `ceramic_id` attribute is used to key the data,
# and it disappears from the individual object.
> puts ceramic_collection.to_yaml
=>
# ---
# vase1:
# name: Imperial Vase
# bowl2:
# name: 18th Century Bowl
# NOTE: When the collection is serialized, the `ceramic_id` attribute is used to
# key the data. This is defined through the `map` with `root_mappings` method in
# CeramicCollection.
> new_collection = CeramicCollection.new(ceramics: [
Ceramic.new(ceramic_id: "vase1", ceramic_name: "Imperial Vase"),
Ceramic.new(ceramic_id: "bowl2", ceramic_name: "18th Century Bowl")
])
> puts new_collection.to_yaml
=>
# ---
# vase1:
# name: Imperial Vase
# bowl2:
# name: 18th Century Bowl Case 2: Mapping the key and complex values
In this use case, the "key" of the keyed collection is moved into the collection object, and all other mappings are overridden.
When more than one mapping rule exists in the root_mappings option, the root_mappings option will override all other mappings in the collection object.
When the "key" is moved into the collection object, the following happens:
-
The "key" of the keyed collection maps to a particular attribute of the collection’s instance object.
-
The data of the "value" of the keyed collection have their own mappings overridden by the new mapping rules of the
root_mappingsoption.
The root_mappings option can contain more than one mapping, with one of the mapping rules leading to the :key keyword.
Syntax:
class SomeKeyedCollection < Lutaml::Model::Serializable
attribute :name_of_attribute, AttributeValueType, collection: true
hsh | json | yaml | toml | key_value do
map to: :name_of_attribute,
root_mappings: {
value_type_attribute_name_for_key: :key, (1)
value_type_attribute_name_for_value_data_1: "serialization_format_name_1", (2)
value_type_attribute_name_for_value_data_2: "serialization_format_name_2",
value_type_attribute_name_for_value_data_3: ["path name", ...] (3)
# ...
}
end
end
class AttributeValueType < Lutaml::Model::Serializable
attribute :value_type_attribute_name_for_key, :string
attribute :value_type_attribute_name_for_value_data_1, :string
attribute :value_type_attribute_name_for_value_data_2, SomeType
attribute :value_type_attribute_name_for_value_data_3, MoreType
# ...
end| 1 | The :key keyword specifies that the "key" of the keyed collection maps to the value_type_attribute_name_for_key attribute of the collection’s instance object (i.e. AttributeValueType). |
| 2 | The serialization_format_name_1 target specifies that the serialization_format_name_2 key of the keyed collection value maps to the value_type_attribute_name_for_value_data_1 attribute of the collection’s instance object. |
| 3 | The [path name] target specifies to fetch from [path name] in the serialization data model to be assigned to the value_type_attribute_name_for_value_data_3 attribute of the collection’s instance object. |
When the root_mappings mapping contains more than one mapping rule that is not to :key or :value, the root_mappings mapping will override all other mappings in the collection object. This means that unmapped attributes in root_mappings will not be incorporated in the collection instance objects.
map with root_mappings (key and complex value) to map a keyed collection into individual models"vase1":
type: "vase"
details:
name: "Imperial Vase"
insignia: "Tang Tianbao"
urn:
primary: "urn:ceramic:vase:vase1"
"bowl2":
type: "bowl"
details:
name: "18th Century Bowl"
insignia: "Ming Wanli"
urn:
primary: "urn:ceramic:bowl:bowl2"A model can be defined for this YAML as follows:
# This is a normal Lutaml::Model class
class CeramicDetails < Lutaml::Model::Serializable
attribute :name, :string
attribute :insignia, :string
key_value do
map 'name', to: :name
map 'insignia', to: :insignia
end
end
# This is a normal Lutaml::Model class
class Ceramic < Lutaml::Model::Serializable
attribute :ceramic_id, :string
attribute :ceramic_type, :string
attribute :ceramic_details, CeramicDetails
attribute :ceramic_urn, :string
key_value do
map 'id', to: :ceramic_id
map 'type', to: :ceramic_type
map 'details', to: :ceramic_details
map 'urn', to: :ceramic_urn
end
end
# This is Lutaml::Model class that represents the collection of Ceramic objects
class CeramicCollection < Lutaml::Model::Serializable
attribute :ceramics, Ceramic, collection: true
key_value do
map to: :ceramics, # All data goes to the `ceramics` attribute
root_mappings: {
# The key of an object in this collection is mapped to the ceramic_id
# attribute of the Ceramic object.
# (e.g. `vase1`, `bowl2`)
ceramic_id: :key,
ceramic_type: :type,
ceramic_details: "details",
ceramic_urn: ["urn", "primary"]
}
end
endThe output becomes:
> ceramics_collection = CeramicCollection.from_yaml(yaml)
=> #<CeramicCollection:0x0000000107a2cf30
@ceramics=
[#<Ceramic:0x0000000107a2cf30
@ceramic_id="vase1",
@ceramic_type="vase",
@ceramic_details=
#<CeramicDetails:0x0000000107a2cf30
@name="Imperial Vase",
@insignia="Tang Tianbao">,
@ceramic_urn="urn:ceramic:vase:vase1">,
#<Ceramic:0x0000000107a2cf30
@ceramic_id="bowl2",
@ceramic_type="bowl",
@ceramic_details=
#<CeramicDetails:0x0000000107a2cf30
@name="18th Century Bowl",
@insignia="Ming Wanli">
@ceramic_urn="urn:ceramic:bowl:bowl2">]
> first_ceramic = ceramics_collection.ceramics.first
> puts first_ceramic.to_yaml
=>
# ---
# id: vase1
# type: vase
# details:
# name: Imperial Vase
# insignia: Tang Tianbao
# urn: urn:ceramic:vase:vase1
> new_collection = CeramicCollection.new(ceramics: [
Ceramic.new(ceramic_id: "vase1",
ceramic_type: "vase",
ceramic_urn: "urn:ceramic:vase:vase1",
ceramic_details: CeramicDetails.new(
name: "Imperial Vase", insignia: "Tang Tianbao")
),
Ceramic.new(ceramic_id: "bowl2",
ceramic_type: "bowl",
ceramic_urn: "urn:ceramic:vase:bowl2",
ceramic_details: CeramicDetails.new(
name: "18th Century Bowl", insignia: "Ming Wanli")
)
])
> new_collection.to_yaml
>
# ---
# vase1:
# type: vase
# details:
# name: Imperial Vase
# insignia: Tang Tianbao
# urn:
# primary: urn:ceramic:vase:vase1
# bowl2:
# type: bowl
# details:
# name: 18th Century Bowl
# insignia: Ming Wanli
# urn:
# primary: urn:ceramic:bowl:bowl2 Case 3: Mapping the key and delegating value to an inner object
In this use case, the "key" of the keyed collection is moved into the collection object to an attribute, and the entire "value" of the keyed collection is mapped to another attribute of the collection object.
When the "key" is moved into the collection object, the following happens:
-
The "key" of the keyed collection maps to a particular attribute of the collection’s instance object.
-
The data of the "value" of the keyed collection will be entirely mapped into an attribute of the collection’s instance object.
-
The original mapping of the "value" attribute of the collection’s instance object is retained.
The root_mappings option should only contain two mappings, and the mappings must lead to both the :key and :value keywords.
Syntax:
class SomeKeyedCollection < Lutaml::Model::Serializable
attribute :name_of_attribute, AttributeValueType, collection: true
hsh | json | yaml | toml | key_value do
map to: :name_of_attribute,
root_mappings: {
value_type_attribute_name_for_key: :key, (1)
value_type_attribute_name_for_value: :value (2)
}
end
end
class AttributeValueType < Lutaml::Model::Serializable
attribute :value_type_attribute_name_for_key, :string
attribute :value_type_attribute_name_for_value, SomeObject
end| 1 | The :key keyword specifies that the "key" of the keyed collection maps to the value_type_attribute_name_for_key attribute of the collection’s instance object (i.e. AttributeValueType). |
| 2 | The :value keyword specifies that the entire "value" of the keyed collection maps to the value_type_attribute_name_for_value attribute of the collection’s instance object (i.e. SomeObject). |
When the root_mappings mapping contains more than one mapping rule, the root_mappings mapping will override all other mappings in the collection object. This means that unmapped attributes in root_mappings will not be incorporated in the collection instance objects.
map with root_mappings (key and value) to map a keyed collection into individual modelsGiven this data:
---
vase1:
name: Imperial Vase
insignia: "Tang Tianbao"
bowl2:
name: 18th Century Bowl
insignia: "Ming Wanli"A model can be defined for this YAML as follows:
# This is a normal Lutaml::Model class
class CeramicDetails < Lutaml::Model::Serializable
attribute :name, :string
attribute :insignia, :string
key_value do
map 'name', to: :name
map 'insignia', to: :insignia
end
end
# This is a normal Lutaml::Model class
class Ceramic < Lutaml::Model::Serializable
attribute :ceramic_id, :string
attribute :ceramic_details, CeramicDetails
key_value do
map 'id', to: :ceramic_id
map 'details', to: :ceramic_details
end
end
# This is Lutaml::Model class that represents the collection of Ceramic objects
class CeramicCollection < Lutaml::Model::Serializable
attribute :ceramics, Ceramic, collection: true
key_value do
map to: :ceramics, # All data goes to the `ceramics` attribute
root_mappings: {
# The key of an object in this collection is mapped to the ceramic_id
# attribute of the Ceramic object.
# (e.g. `vase1`, `bowl2`)
ceramic_id: :key,
# The value of an object in this collection is mapped to the
# ceramic_details attribute of the Ceramic object.
# (e.g. `name: 18th Century Bowl`, `insignia: "Ming Wanli"`
ceramic_details: :value
}
end
end# Parsing the YAML collection with dynamic data keys
> ceramic_collection = CeramicCollection.from_yaml(yaml)
> #<CeramicCollection:0x0000000104ac7240
@ceramics=
[#<Ceramic:0x0000000104ac6e30
@ceramic_id="vase1",
@ceramic_details=
#<CeramicDetails:0x0000000104ac6e30
@name="Imperial Vase",
@insignia="Tang Tianbao">,
#<Ceramic:0x0000000104ac58f0
@ceramic_id="bowl2",
@ceramic_details=
#<CeramicDetails:0x0000000104ac58f0
@name="18th Century Bowl",
@insignia="Ming Wanli">]
# NOTE: When an individual Ceramic object is serialized, the `id` attribute is
# the original key in the incoming YAML data.
> first_ceramic = ceramic_collection.ceramics.first
> puts first_ceramic.to_yaml
=>
# ---
# id: vase1
# details:
# name: Imperial Vase
# insignia: Tang Tianbao
# NOTE: When in a collection, the `ceramic_id` attribute is used to key the data,
# and it disappears from the individual object.
> puts ceramic_collection.to_yaml
=>
# ---
# vase1:
# name: Imperial Vase
# insignia: Tang Tianbao
# bowl2:
# name: 18th Century Bowl
# insignia: Ming Wanli
# NOTE: When the collection is serialized, the `ceramic_id` attribute is used to
# key the data. This is defined through the `map` with `root_mappings` method in
# CeramicCollection.
> new_collection = CeramicCollection.new(ceramics: [
Ceramic.new(ceramic_id: "vase1",
ceramic_details: CeramicDetails.new(
name: "Imperial Vase", insignia: "Tang Tianbao")
),
Ceramic.new(ceramic_id: "bowl2",
ceramic_details: CeramicDetails.new(
name: "18th Century Bowl", insignia: "Ming Wanli")
)
])
> puts new_collection.to_yaml
=>
# ---
# vase1:
# name: Imperial Vase
# insignia: Tang Tianbao
# bowl2:
# name: 18th Century Bowl
# insignia: Ming WanliAttribute extraction
| This feature is for key-value data model serialization only. |
The child_mappings option is used to extract results from a key-value serialization data model (Hash, JSON, YAML, TOML) into a Lutaml::Model::Serializable object (collection or not).
The values are extracted from the key-value data model using the list of keys provided.
Syntax:
class SomeObject < Lutaml::Model::Serializable
attribute :name_of_attribute, AttributeValueType, collection: true
hsh | json | yaml | toml | key_value do
map 'key_value_model_attribute_name', to: :name_of_attribute,
child_mappings: {
value_type_attribute_name_1: (1)
{path_to_value_1}, (2)
value_type_attribute_name_2:
{path_to_value_2},
# ...
}
end
end| 1 | The value_type_attribute_name_1 is the attribute name in the AttributeValueType model. The value of this attribute will be assigned the key of the hash in the key-value data model. |
| 2 | The path_to_value_1 is an array of keys that represent the path to the value in the key-value serialization data model. The keys are used to extract the value from the key-value serialization data model and assign it to the attribute in the AttributeValueType model. The |
The following JSON contains 2 keys in schema named engine and gearbox.
{
"components": {
"engine": {
"manufacturer": "Ford",
"model": "V8"
},
"gearbox": {
"manufacturer": "Toyota",
"model": "4-speed"
}
}
}The path to value for the engine schema is [:components, :engine] and for the gearbox schema is [:components, :gearbox].
In path_to_value, the :key and :value are reserved instructions used to assign the key or value of the serialization data respectively as the value to the attribute.
In the following JSON content, the path_to_value for the object keys named engine and gearbox will utilize the :key keyword to assign the key of the object as the value of a designated attribute.
{
"components": {
"engine": { /*...*/ },
"gearbox": { /*...*/ }
}
}If a specified value path is not found, the corresponding attribute in the model will be assigned a nil value.
nil when the path_to_value is not foundIn the following JSON content, the path_to_value of [:extras, :sunroof] and [:extras, :drinks_cooler] at the object "gearbox" would be set to nil.
{
"components": {
"engine": {
"manufacturer": "Ford",
"extras": {
"sunroof": true,
"drinks_cooler": true
}
},
"gearbox": {
"manufacturer": "Toyota"
}
}
}child_mappings option to extract values from a key-value data modelThe following JSON contains 2 keys in schema named foo and bar.
{
"schemas": {
"foo": { (1)
"path": { (2)
"link": "link one",
"name": "one"
}
},
"bar": { (1)
"path": { (2)
"link": "link two",
"name": "two"
}
}
}
}| 1 | The keys foo and bar are to be mapped to the id attribute. |
| 2 | The nested path.link and path.name keys are used as the link and name attributes, respectively. |
A model can be defined for this JSON as follows:
class Schema < Lutaml::Model::Serializable
attribute :id, :string
attribute :link, :string
attribute :name, :string
end
class ChildMappingClass < Lutaml::Model::Serializable
attribute :schemas, Schema, collection: true
json do
map "schemas", to: :schemas,
child_mappings: {
id: :key,
link: %i[path link],
name: %i[path name],
}
end
endThe output becomes:
> ChildMappingClass.from_json(json)
> #<ChildMappingClass:0x0000000104ac7240
@schemas=
[#<Schema:0x0000000104ac6e30 @id="foo", @link="link one", @name="one">,
#<Schema:0x0000000104ac58f0 @id="bar", @link="link two", @name="two">]>
> ChildMappingClass.new(schemas: [Schema.new(id: "foo", link: "link one", name: "one"), Schema.new(id: "bar", link: "link two", name: "two")]).to_json
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}}In this example:
-
The
keyof each schema (fooandbar) is mapped to theidattribute. -
The nested
path.linkandpath.namekeys are mapped to thelinkandnameattributes, respectively.