lutaml-model 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +4 -4
- data/README.adoc +837 -184
- data/lib/lutaml/model/attribute.rb +9 -3
- data/lib/lutaml/model/error/collection_true_missing_error.rb +16 -0
- data/lib/lutaml/model/error/multiple_mappings_error.rb +6 -0
- data/lib/lutaml/model/error.rb +2 -0
- data/lib/lutaml/model/key_value_mapping.rb +26 -4
- data/lib/lutaml/model/key_value_mapping_rule.rb +15 -4
- data/lib/lutaml/model/loggable.rb +15 -0
- data/lib/lutaml/model/mapping_rule.rb +2 -2
- data/lib/lutaml/model/serialize.rb +59 -15
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +3 -3
- data/lib/lutaml/model/xml_mapping.rb +4 -0
- data/lib/lutaml/model/xml_mapping_rule.rb +2 -5
- data/lib/lutaml/model.rb +1 -0
- data/spec/lutaml/model/root_mappings_spec.rb +297 -0
- data/spec/lutaml/model/serializable_spec.rb +41 -6
- data/spec/lutaml/model/with_child_mapping_spec.rb +182 -0
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +66 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +8 -0
- metadata +9 -5
data/README.adoc
CHANGED
@@ -1977,6 +1977,843 @@ end
|
|
1977
1977
|
----
|
1978
1978
|
====
|
1979
1979
|
|
1980
|
+
|
1981
|
+
|
1982
|
+
==== Collection with keyed elements (keyed collection)
|
1983
|
+
|
1984
|
+
===== General
|
1985
|
+
|
1986
|
+
NOTE: This feature is for key-value data model serialization and deserialization
|
1987
|
+
only.
|
1988
|
+
|
1989
|
+
The `map` method with the `root_mappings` option is used for key-value data that
|
1990
|
+
is keyed using an attribute value.
|
1991
|
+
|
1992
|
+
In other words, the key of a key-value pair in a collection is actually the
|
1993
|
+
value of an attribute that belongs to the value.
|
1994
|
+
|
1995
|
+
Simply put, the following two data structures are considered to have the same
|
1996
|
+
data:
|
1997
|
+
|
1998
|
+
[[collection-keyed-by-value]]
|
1999
|
+
.A YAML collection as a keyed object, each key with value of the `id` attribute
|
2000
|
+
[source,yaml]
|
2001
|
+
----
|
2002
|
+
---
|
2003
|
+
vase1:
|
2004
|
+
name: Imperial Vase
|
2005
|
+
bowl2:
|
2006
|
+
name: 18th Century Bowl
|
2007
|
+
----
|
2008
|
+
|
2009
|
+
[[collection-unkeyed-by-value]]
|
2010
|
+
.A YAML collection as an array, the `id` attribute value located inside each element
|
2011
|
+
[source,yaml]
|
2012
|
+
----
|
2013
|
+
---
|
2014
|
+
- id: vase1
|
2015
|
+
name: Imperial Vase
|
2016
|
+
- id: bowl2
|
2017
|
+
name: 18th Century Bowl
|
2018
|
+
----
|
2019
|
+
|
2020
|
+
There are key difference between these two data structures:
|
2021
|
+
|
2022
|
+
* The <<collection-keyed-by-value,keyed object>> (first data structure) ensures
|
2023
|
+
uniqueness of the `id` attribute value across the collection, while the
|
2024
|
+
<<collection-unkeyed-by-value,array>> (second data structure) does not.
|
2025
|
+
|
2026
|
+
* The value of the `id` attribute in the first data structure *exists outside*
|
2027
|
+
of the formal structure of the data object, instead, it *only exists at the
|
2028
|
+
collection level*. On the other hand, the value *exists inside* the structure of
|
2029
|
+
the data object in the second data structure.
|
2030
|
+
|
2031
|
+
The `map` method with the `root_mappings` option, in practice, parses the first
|
2032
|
+
data structure in the same way that you would access / manipulate the second
|
2033
|
+
data structure, while retaining the serialization semantics of using an
|
2034
|
+
attribute as key.
|
2035
|
+
|
2036
|
+
As a result, usage of lutaml-model across both types of collections are
|
2037
|
+
identical (except when serialized).
|
2038
|
+
|
2039
|
+
|
2040
|
+
Syntax:
|
2041
|
+
|
2042
|
+
[source,ruby]
|
2043
|
+
----
|
2044
|
+
class SomeKeyedCollection < Lutaml::Model::Serializable
|
2045
|
+
attribute :name_of_attribute, AttributeValueType, collection: true
|
2046
|
+
|
2047
|
+
json | yaml | toml | key_value do
|
2048
|
+
map to: :name_of_attribute, <1>
|
2049
|
+
root_mappings: { <2>
|
2050
|
+
# `:key` is a reserved keyword
|
2051
|
+
value_type_attribute_name_for_key: :key, <3>
|
2052
|
+
# `:value` is a reserved keyword (and optional)
|
2053
|
+
value_type_attribute_name_for_value: :value, <4>
|
2054
|
+
# `[path name]` represents the path to access the value in the
|
2055
|
+
# serialization data model to be assigned to
|
2056
|
+
# `AttributeValueType.value_type_attribute_name_for_custom_type`
|
2057
|
+
value_type_attribute_name_for_custom_type: [path name] <5>
|
2058
|
+
}
|
2059
|
+
end
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
class AttributeValueType < Lutaml::Model::Serializable
|
2063
|
+
attribute :value_type_attribute_name_for_key, :string
|
2064
|
+
attribute :value_type_attribute_name_for_value, :string
|
2065
|
+
attribute :value_type_attribute_name_for_custom_type, CustomType
|
2066
|
+
end
|
2067
|
+
----
|
2068
|
+
<1> The `map` option indicates that this class represents the root of the
|
2069
|
+
serialization object being passed in. The `name_of_attribute` is the name
|
2070
|
+
of the attribute that will hold the collection data. (Mandatory)
|
2071
|
+
<2> The `root_mappings` keyword specifies what the collection key represents and
|
2072
|
+
and value for model. (Mandatory)
|
2073
|
+
<3> The `key` keyword specifies the attribute name of the individual collection
|
2074
|
+
object type that represents its key used in the collection. (Mandatory)
|
2075
|
+
<4> The `value` keyword specifies the attribute name of the individual collection
|
2076
|
+
object type that represents its data used in the collection. (Optional, if
|
2077
|
+
not specified, the entire object is used as the value.)
|
2078
|
+
<5> The `value_type_attribute_name_for_custom_type` is the name of the attribute
|
2079
|
+
inside the individual collection object (`AttributeValueType`) that will hold
|
2080
|
+
the value accessible in the serialization data model fetched at `[path name]`.
|
2081
|
+
|
2082
|
+
The mapping syntax here is similar to that of <<attribute-extraction>> except
|
2083
|
+
that the `:key` and `:value` keywords are allowed in addition to `{path}`.
|
2084
|
+
|
2085
|
+
|
2086
|
+
There are 3 cases when working with a keyed collection:
|
2087
|
+
|
2088
|
+
. Case 1: Only move the "key" into the collection object.
|
2089
|
+
|
2090
|
+
. Case 2: Move the "key" into the collection object, override all other
|
2091
|
+
mappings. Maps `:key` and another attribute, then we override all the other
|
2092
|
+
mappings (clean slate)
|
2093
|
+
|
2094
|
+
. Case 3: Move the "key" into the collection object to an attribute, map the
|
2095
|
+
entire "value" to another attribute of the collection object.
|
2096
|
+
|
2097
|
+
|
2098
|
+
===== Case 1: Only move the "key" into the collection object
|
2099
|
+
|
2100
|
+
In this case, the "key" of the keyed collection is moved into the collection
|
2101
|
+
object, and all other mappings are left as they are.
|
2102
|
+
|
2103
|
+
When the "key" is moved into the collection object, the following happens:
|
2104
|
+
|
2105
|
+
* The "key" of the keyed collection maps to a particular attribute of the
|
2106
|
+
collection's instance object.
|
2107
|
+
* The "value" of the keyed collection (with its various content) maps to the
|
2108
|
+
collection's instance object following the collection's instance object type's
|
2109
|
+
default mappings.
|
2110
|
+
|
2111
|
+
The `root_mappings` option **should only contain one mapping**, and the mapping
|
2112
|
+
must lead **to the `:key` keyword**.
|
2113
|
+
|
2114
|
+
Syntax:
|
2115
|
+
|
2116
|
+
[source,ruby]
|
2117
|
+
----
|
2118
|
+
class SomeKeyedCollection < Lutaml::Model::Serializable
|
2119
|
+
attribute :name_of_attribute, AttributeValueType, collection: true
|
2120
|
+
|
2121
|
+
json | yaml | toml | key_value do
|
2122
|
+
map to: :name_of_attribute,
|
2123
|
+
root_mappings: {
|
2124
|
+
value_type_attribute_name_for_key: :key, <1>
|
2125
|
+
}
|
2126
|
+
end
|
2127
|
+
end
|
2128
|
+
|
2129
|
+
class AttributeValueType < Lutaml::Model::Serializable
|
2130
|
+
attribute :value_type_attribute_name_for_key, :string
|
2131
|
+
attribute :value_type_attribute_name_for_value, :string
|
2132
|
+
attribute :value_type_attribute_name_for_custom_type, CustomType
|
2133
|
+
end
|
2134
|
+
----
|
2135
|
+
<1> The `:key` keyword specifies that the "key" of the keyed collection maps
|
2136
|
+
to the `value_type_attribute_name_for_key` attribute of the collection's
|
2137
|
+
instance object (i.e. `AttributeValueType`).
|
2138
|
+
|
2139
|
+
|
2140
|
+
|
2141
|
+
.Using `map` with `root_mappings` (only `key`) to map a keyed collection into individual models
|
2142
|
+
[example]
|
2143
|
+
====
|
2144
|
+
Given this data:
|
2145
|
+
|
2146
|
+
[source,yaml]
|
2147
|
+
----
|
2148
|
+
---
|
2149
|
+
vase1:
|
2150
|
+
name: Imperial Vase
|
2151
|
+
bowl2:
|
2152
|
+
name: 18th Century Bowl
|
2153
|
+
----
|
2154
|
+
|
2155
|
+
A model can be defined for this YAML as follows:
|
2156
|
+
|
2157
|
+
[source,ruby]
|
2158
|
+
----
|
2159
|
+
# This is a normal Lutaml::Model class
|
2160
|
+
class Ceramic < Lutaml::Model::Serializable
|
2161
|
+
attribute :ceramic_id, :string
|
2162
|
+
attribute :ceramic_name, :string
|
2163
|
+
|
2164
|
+
key_value do
|
2165
|
+
map 'id', to: :ceramic_id
|
2166
|
+
map 'name', to: :ceramic_name
|
2167
|
+
end
|
2168
|
+
end
|
2169
|
+
|
2170
|
+
# This is Lutaml::Model class that represents the collection of Ceramic objects
|
2171
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
2172
|
+
attribute :ceramics, Ceramic, collection: true
|
2173
|
+
|
2174
|
+
key_value do
|
2175
|
+
map to: :ceramics, # All data goes to the `ceramics` attribute
|
2176
|
+
root_mappings: {
|
2177
|
+
# The key of an object in this collection is mapped to the ceramic_id
|
2178
|
+
# attribute of the Ceramic object.
|
2179
|
+
ceramic_id: :key # "key" is a reserved keyword
|
2180
|
+
}
|
2181
|
+
end
|
2182
|
+
end
|
2183
|
+
----
|
2184
|
+
|
2185
|
+
[source,ruby]
|
2186
|
+
----
|
2187
|
+
# Parsing the YAML collection with dynamic data keys
|
2188
|
+
> ceramic_collection = CeramicCollection.from_yaml(yaml)
|
2189
|
+
> #<CeramicCollection:0x0000000104ac7240
|
2190
|
+
@ceramics=
|
2191
|
+
[#<Ceramic:0x0000000104ac6e30 @ceramic_id="vase1", @ceramic_name="Imperial Vase">,
|
2192
|
+
#<Ceramic:0x0000000104ac58f0 @ceramic_id="bowl2", @ceramic_name="18th Century Bowl">]
|
2193
|
+
|
2194
|
+
# NOTE: When an individual Ceramic object is serialized, the `id` attribute is
|
2195
|
+
# 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.
|
2196
|
+
> first_ceramic = ceramic_collection.ceramics.first
|
2197
|
+
> puts first_ceramic.to_yaml
|
2198
|
+
=>
|
2199
|
+
# ---
|
2200
|
+
# id: vase1
|
2201
|
+
# name: Imperial Vase
|
2202
|
+
|
2203
|
+
# NOTE: When in a collection, the `ceramic_id` attribute is used to key the data,
|
2204
|
+
# and it disappears from the individual object.
|
2205
|
+
> puts ceramic_collection.to_yaml
|
2206
|
+
=>
|
2207
|
+
# ---
|
2208
|
+
# vase1:
|
2209
|
+
# name: Imperial Vase
|
2210
|
+
# bowl2:
|
2211
|
+
# name: 18th Century Bowl
|
2212
|
+
|
2213
|
+
# NOTE: When the collection is serialized, the `ceramic_id` attribute is used to
|
2214
|
+
# key the data. This is defined through the `map` with `root_mappings` method in
|
2215
|
+
# CeramicCollection.
|
2216
|
+
> new_collection = CeramicCollection.new(ceramics: [
|
2217
|
+
Ceramic.new(ceramic_id: "vase1", ceramic_name: "Imperial Vase"),
|
2218
|
+
Ceramic.new(ceramic_id: "bowl2", ceramic_name: "18th Century Bowl")
|
2219
|
+
])
|
2220
|
+
> puts new_collection.to_yaml
|
2221
|
+
=>
|
2222
|
+
# ---
|
2223
|
+
# vase1:
|
2224
|
+
# name: Imperial Vase
|
2225
|
+
# bowl2:
|
2226
|
+
# name: 18th Century Bowl
|
2227
|
+
----
|
2228
|
+
====
|
2229
|
+
|
2230
|
+
|
2231
|
+
|
2232
|
+
===== Case 2: Mapping the `key` and complex ``value``s
|
2233
|
+
|
2234
|
+
In this use case, the "key" of the keyed collection is moved into the collection
|
2235
|
+
object, and all other mappings are overridden.
|
2236
|
+
|
2237
|
+
When more than one mapping rule exists in the `root_mappings` option, the
|
2238
|
+
`root_mappings` option will override all other mappings in the collection object.
|
2239
|
+
|
2240
|
+
When the "key" is moved into the collection object, the following happens:
|
2241
|
+
|
2242
|
+
* The "key" of the keyed collection maps to a particular attribute of the
|
2243
|
+
collection's instance object.
|
2244
|
+
|
2245
|
+
* The data of the "value" of the keyed collection have their own mappings
|
2246
|
+
overridden by the new mapping rules of the `root_mappings` option.
|
2247
|
+
|
2248
|
+
The `root_mappings` option **can contain more than one mapping**, with one of
|
2249
|
+
the mapping rules leading **to the `:key` keyword**.
|
2250
|
+
|
2251
|
+
|
2252
|
+
Syntax:
|
2253
|
+
|
2254
|
+
[source,ruby]
|
2255
|
+
----
|
2256
|
+
class SomeKeyedCollection < Lutaml::Model::Serializable
|
2257
|
+
attribute :name_of_attribute, AttributeValueType, collection: true
|
2258
|
+
|
2259
|
+
json | yaml | toml | key_value do
|
2260
|
+
map to: :name_of_attribute,
|
2261
|
+
root_mappings: {
|
2262
|
+
value_type_attribute_name_for_key: :key, <1>
|
2263
|
+
value_type_attribute_name_for_value_data_1: "serialization_format_name_1", <2>
|
2264
|
+
value_type_attribute_name_for_value_data_2: "serialization_format_name_2",
|
2265
|
+
value_type_attribute_name_for_value_data_3: ["path name", ...] <3>
|
2266
|
+
# ...
|
2267
|
+
}
|
2268
|
+
end
|
2269
|
+
end
|
2270
|
+
|
2271
|
+
class AttributeValueType < Lutaml::Model::Serializable
|
2272
|
+
attribute :value_type_attribute_name_for_key, :string
|
2273
|
+
attribute :value_type_attribute_name_for_value_data_1, :string
|
2274
|
+
attribute :value_type_attribute_name_for_value_data_2, SomeType
|
2275
|
+
attribute :value_type_attribute_name_for_value_data_3, MoreType
|
2276
|
+
# ...
|
2277
|
+
end
|
2278
|
+
----
|
2279
|
+
<1> The `:key` keyword specifies that the "key" of the keyed collection maps
|
2280
|
+
to the `value_type_attribute_name_for_key` attribute of the collection's
|
2281
|
+
instance object (i.e. `AttributeValueType`).
|
2282
|
+
<2> The `serialization_format_name_1` target specifies that the
|
2283
|
+
`serialization_format_name_2` key of the keyed collection value maps to the
|
2284
|
+
`value_type_attribute_name_for_value_data_1` attribute of the collection's
|
2285
|
+
instance object.
|
2286
|
+
<3> The `[path name]` target specifies to fetch from `[path name]` in the
|
2287
|
+
serialization data model to be assigned to the
|
2288
|
+
`value_type_attribute_name_for_value_data_3` attribute of the collection's
|
2289
|
+
instance object.
|
2290
|
+
|
2291
|
+
When the `root_mappings` mapping contains more than one mapping rule that is not
|
2292
|
+
to `:key` or `:value`, the `root_mappings` mapping will override all other
|
2293
|
+
mappings in the collection object. This means that unmapped attributes in
|
2294
|
+
`root_mappings` will not be incorporated in the collection instance objects.
|
2295
|
+
|
2296
|
+
.Using `map` with `root_mappings` (`key` and complex `value`) to map a keyed collection into individual models
|
2297
|
+
[example]
|
2298
|
+
====
|
2299
|
+
|
2300
|
+
[source,yaml]
|
2301
|
+
----
|
2302
|
+
"vase1":
|
2303
|
+
type: "vase"
|
2304
|
+
details:
|
2305
|
+
name: "Imperial Vase"
|
2306
|
+
insignia: "Tang Tianbao"
|
2307
|
+
urn:
|
2308
|
+
primary: "urn:ceramic:vase:vase1"
|
2309
|
+
"bowl2":
|
2310
|
+
type: "bowl"
|
2311
|
+
details:
|
2312
|
+
name: "18th Century Bowl"
|
2313
|
+
insignia: "Ming Wanli"
|
2314
|
+
urn:
|
2315
|
+
primary: "urn:ceramic:bowl:bowl2"
|
2316
|
+
----
|
2317
|
+
|
2318
|
+
A model can be defined for this YAML as follows:
|
2319
|
+
|
2320
|
+
[source,ruby]
|
2321
|
+
----
|
2322
|
+
# This is a normal Lutaml::Model class
|
2323
|
+
class CeramicDetails < Lutaml::Model::Serializable
|
2324
|
+
attribute :name, :string
|
2325
|
+
attribute :insignia, :string
|
2326
|
+
|
2327
|
+
key_value do
|
2328
|
+
map 'name', to: :name
|
2329
|
+
map 'insignia', to: :insignia
|
2330
|
+
end
|
2331
|
+
end
|
2332
|
+
|
2333
|
+
# This is a normal Lutaml::Model class
|
2334
|
+
class Ceramic < Lutaml::Model::Serializable
|
2335
|
+
attribute :ceramic_id, :string
|
2336
|
+
attribute :ceramic_type, :string
|
2337
|
+
attribute :ceramic_details, CeramicDetails
|
2338
|
+
attribute :ceramic_urn, :string
|
2339
|
+
|
2340
|
+
key_value do
|
2341
|
+
map 'id', to: :ceramic_id
|
2342
|
+
map 'type', to: :ceramic_type
|
2343
|
+
map 'details', to: :ceramic_details
|
2344
|
+
map 'urn', to: :ceramic_urn
|
2345
|
+
end
|
2346
|
+
end
|
2347
|
+
|
2348
|
+
# This is Lutaml::Model class that represents the collection of Ceramic objects
|
2349
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
2350
|
+
attribute :ceramics, Ceramic, collection: true
|
2351
|
+
|
2352
|
+
key_value do
|
2353
|
+
map to: :ceramics, # All data goes to the `ceramics` attribute
|
2354
|
+
root_mappings: {
|
2355
|
+
# The key of an object in this collection is mapped to the ceramic_id
|
2356
|
+
# attribute of the Ceramic object.
|
2357
|
+
# (e.g. `vase1`, `bowl2`)
|
2358
|
+
ceramic_id: :key,
|
2359
|
+
ceramic_type: :type,
|
2360
|
+
ceramic_details: "details",
|
2361
|
+
ceramic_urn: ["urn", "primary"]
|
2362
|
+
}
|
2363
|
+
end
|
2364
|
+
end
|
2365
|
+
----
|
2366
|
+
|
2367
|
+
The output becomes:
|
2368
|
+
|
2369
|
+
[source,ruby]
|
2370
|
+
----
|
2371
|
+
> ceramics_collection = CeramicCollection.from_yaml(yaml)
|
2372
|
+
=> #<CeramicCollection:0x0000000107a2cf30
|
2373
|
+
@ceramics=
|
2374
|
+
[#<Ceramic:0x0000000107a2cf30
|
2375
|
+
@ceramic_id="vase1",
|
2376
|
+
@ceramic_type="vase",
|
2377
|
+
@ceramic_details=
|
2378
|
+
#<CeramicDetails:0x0000000107a2cf30
|
2379
|
+
@name="Imperial Vase",
|
2380
|
+
@insignia="Tang Tianbao">,
|
2381
|
+
@ceramic_urn="urn:ceramic:vase:vase1">,
|
2382
|
+
#<Ceramic:0x0000000107a2cf30
|
2383
|
+
@ceramic_id="bowl2",
|
2384
|
+
@ceramic_type="bowl",
|
2385
|
+
@ceramic_details=
|
2386
|
+
#<CeramicDetails:0x0000000107a2cf30
|
2387
|
+
@name="18th Century Bowl",
|
2388
|
+
@insignia="Ming Wanli">
|
2389
|
+
@ceramic_urn="urn:ceramic:bowl:bowl2">]
|
2390
|
+
|
2391
|
+
> first_ceramic = ceramics_collection.ceramics.first
|
2392
|
+
> puts first_ceramic.to_yaml
|
2393
|
+
=>
|
2394
|
+
# ---
|
2395
|
+
# id: vase1
|
2396
|
+
# type: vase
|
2397
|
+
# details:
|
2398
|
+
# name: Imperial Vase
|
2399
|
+
# insignia: Tang Tianbao
|
2400
|
+
# urn: urn:ceramic:vase:vase1
|
2401
|
+
|
2402
|
+
> new_collection = CeramicCollection.new(ceramics: [
|
2403
|
+
Ceramic.new(ceramic_id: "vase1",
|
2404
|
+
ceramic_type: "vase",
|
2405
|
+
ceramic_urn: "urn:ceramic:vase:vase1",
|
2406
|
+
ceramic_details: CeramicDetails.new(
|
2407
|
+
name: "Imperial Vase", insignia: "Tang Tianbao")
|
2408
|
+
),
|
2409
|
+
Ceramic.new(ceramic_id: "bowl2",
|
2410
|
+
ceramic_type: "bowl",
|
2411
|
+
ceramic_urn: "urn:ceramic:vase:bowl2",
|
2412
|
+
ceramic_details: CeramicDetails.new(
|
2413
|
+
name: "18th Century Bowl", insignia: "Ming Wanli")
|
2414
|
+
)
|
2415
|
+
])
|
2416
|
+
> new_collection.to_yaml
|
2417
|
+
>
|
2418
|
+
# ---
|
2419
|
+
# vase1:
|
2420
|
+
# type: vase
|
2421
|
+
# details:
|
2422
|
+
# name: Imperial Vase
|
2423
|
+
# insignia: Tang Tianbao
|
2424
|
+
# urn:
|
2425
|
+
# primary: urn:ceramic:vase:vase1
|
2426
|
+
# bowl2:
|
2427
|
+
# type: bowl
|
2428
|
+
# details:
|
2429
|
+
# name: 18th Century Bowl
|
2430
|
+
# insignia: Ming Wanli
|
2431
|
+
# urn:
|
2432
|
+
# primary: urn:ceramic:bowl:bowl2
|
2433
|
+
----
|
2434
|
+
====
|
2435
|
+
|
2436
|
+
|
2437
|
+
===== Case 3: Mapping the `key` and delegating `value` to an inner object
|
2438
|
+
|
2439
|
+
In this use case, the "key" of the keyed collection is moved into the collection
|
2440
|
+
object to an attribute, and the entire "value" of the keyed collection is mapped
|
2441
|
+
to another attribute of the collection object.
|
2442
|
+
|
2443
|
+
When the "key" is moved into the collection object, the following happens:
|
2444
|
+
|
2445
|
+
* The "key" of the keyed collection maps to a particular attribute of the
|
2446
|
+
collection's instance object.
|
2447
|
+
|
2448
|
+
* The data of the "value" of the keyed collection will be entirely mapped into
|
2449
|
+
an attribute of the collection's instance object.
|
2450
|
+
|
2451
|
+
* The original mapping of the "value" attribute of the collection's instance
|
2452
|
+
object is retained.
|
2453
|
+
|
2454
|
+
The `root_mappings` option **should only contain two mappings**, and the mappings
|
2455
|
+
must lead **to both the `:key` and `:value` keywords**.
|
2456
|
+
|
2457
|
+
|
2458
|
+
Syntax:
|
2459
|
+
|
2460
|
+
[source,ruby]
|
2461
|
+
----
|
2462
|
+
class SomeKeyedCollection < Lutaml::Model::Serializable
|
2463
|
+
attribute :name_of_attribute, AttributeValueType, collection: true
|
2464
|
+
|
2465
|
+
json | yaml | toml | key_value do
|
2466
|
+
map to: :name_of_attribute,
|
2467
|
+
root_mappings: {
|
2468
|
+
value_type_attribute_name_for_key: :key, <1>
|
2469
|
+
value_type_attribute_name_for_value: :value <2>
|
2470
|
+
}
|
2471
|
+
end
|
2472
|
+
end
|
2473
|
+
|
2474
|
+
class AttributeValueType < Lutaml::Model::Serializable
|
2475
|
+
attribute :value_type_attribute_name_for_key, :string
|
2476
|
+
attribute :value_type_attribute_name_for_value, SomeObject
|
2477
|
+
end
|
2478
|
+
----
|
2479
|
+
<1> The `:key` keyword specifies that the "key" of the keyed collection maps
|
2480
|
+
to the `value_type_attribute_name_for_key` attribute of the collection's
|
2481
|
+
instance object (i.e. `AttributeValueType`).
|
2482
|
+
<2> The `:value` keyword specifies that the entire "value" of the keyed
|
2483
|
+
collection maps to the `value_type_attribute_name_for_value` attribute of the
|
2484
|
+
collection's instance object (i.e. `SomeObject`).
|
2485
|
+
|
2486
|
+
When the `root_mappings` mapping contains more than one mapping rule, the
|
2487
|
+
`root_mappings` mapping will override all other mappings in the collection
|
2488
|
+
object. This means that unmapped attributes in `root_mappings` will not be
|
2489
|
+
incorporated in the collection instance objects.
|
2490
|
+
|
2491
|
+
|
2492
|
+
|
2493
|
+
.Using `map` with `root_mappings` (`key` and `value`) to map a keyed collection into individual models
|
2494
|
+
[example]
|
2495
|
+
====
|
2496
|
+
Given this data:
|
2497
|
+
|
2498
|
+
[source,yaml]
|
2499
|
+
----
|
2500
|
+
---
|
2501
|
+
vase1:
|
2502
|
+
name: Imperial Vase
|
2503
|
+
insignia: "Tang Tianbao"
|
2504
|
+
bowl2:
|
2505
|
+
name: 18th Century Bowl
|
2506
|
+
insignia: "Ming Wanli"
|
2507
|
+
----
|
2508
|
+
|
2509
|
+
A model can be defined for this YAML as follows:
|
2510
|
+
|
2511
|
+
[source,ruby]
|
2512
|
+
----
|
2513
|
+
# This is a normal Lutaml::Model class
|
2514
|
+
class CeramicDetails < Lutaml::Model::Serializable
|
2515
|
+
attribute :name, :string
|
2516
|
+
attribute :insignia, :string
|
2517
|
+
|
2518
|
+
key_value do
|
2519
|
+
map 'name', to: :name
|
2520
|
+
map 'insignia', to: :insignia
|
2521
|
+
end
|
2522
|
+
end
|
2523
|
+
|
2524
|
+
# This is a normal Lutaml::Model class
|
2525
|
+
class Ceramic < Lutaml::Model::Serializable
|
2526
|
+
attribute :ceramic_id, :string
|
2527
|
+
attribute :ceramic_details, CeramicDetails
|
2528
|
+
|
2529
|
+
key_value do
|
2530
|
+
map 'id', to: :ceramic_id
|
2531
|
+
map 'details', to: :ceramic_details
|
2532
|
+
end
|
2533
|
+
end
|
2534
|
+
|
2535
|
+
# This is Lutaml::Model class that represents the collection of Ceramic objects
|
2536
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
2537
|
+
attribute :ceramics, Ceramic, collection: true
|
2538
|
+
|
2539
|
+
key_value do
|
2540
|
+
map to: :ceramics, # All data goes to the `ceramics` attribute
|
2541
|
+
root_mappings: {
|
2542
|
+
# The key of an object in this collection is mapped to the ceramic_id
|
2543
|
+
# attribute of the Ceramic object.
|
2544
|
+
# (e.g. `vase1`, `bowl2`)
|
2545
|
+
ceramic_id: :key,
|
2546
|
+
# The value of an object in this collection is mapped to the
|
2547
|
+
# ceramic_details attribute of the Ceramic object.
|
2548
|
+
# (e.g. `name: 18th Century Bowl`, `insignia: "Ming Wanli"`
|
2549
|
+
ceramic_details: :value
|
2550
|
+
}
|
2551
|
+
end
|
2552
|
+
end
|
2553
|
+
----
|
2554
|
+
|
2555
|
+
[source,ruby]
|
2556
|
+
----
|
2557
|
+
# Parsing the YAML collection with dynamic data keys
|
2558
|
+
> ceramic_collection = CeramicCollection.from_yaml(yaml)
|
2559
|
+
> #<CeramicCollection:0x0000000104ac7240
|
2560
|
+
@ceramics=
|
2561
|
+
[#<Ceramic:0x0000000104ac6e30
|
2562
|
+
@ceramic_id="vase1",
|
2563
|
+
@ceramic_details=
|
2564
|
+
#<CeramicDetails:0x0000000104ac6e30
|
2565
|
+
@name="Imperial Vase",
|
2566
|
+
@insignia="Tang Tianbao">,
|
2567
|
+
#<Ceramic:0x0000000104ac58f0
|
2568
|
+
@ceramic_id="bowl2",
|
2569
|
+
@ceramic_details=
|
2570
|
+
#<CeramicDetails:0x0000000104ac58f0
|
2571
|
+
@name="18th Century Bowl",
|
2572
|
+
@insignia="Ming Wanli">]
|
2573
|
+
|
2574
|
+
# NOTE: When an individual Ceramic object is serialized, the `id` attribute is
|
2575
|
+
# the original key in the incoming YAML data.
|
2576
|
+
> first_ceramic = ceramic_collection.ceramics.first
|
2577
|
+
> puts first_ceramic.to_yaml
|
2578
|
+
=>
|
2579
|
+
# ---
|
2580
|
+
# id: vase1
|
2581
|
+
# details:
|
2582
|
+
# name: Imperial Vase
|
2583
|
+
# insignia: Tang Tianbao
|
2584
|
+
|
2585
|
+
# NOTE: When in a collection, the `ceramic_id` attribute is used to key the data,
|
2586
|
+
# and it disappears from the individual object.
|
2587
|
+
> puts ceramic_collection.to_yaml
|
2588
|
+
=>
|
2589
|
+
# ---
|
2590
|
+
# vase1:
|
2591
|
+
# name: Imperial Vase
|
2592
|
+
# insignia: Tang Tianbao
|
2593
|
+
# bowl2:
|
2594
|
+
# name: 18th Century Bowl
|
2595
|
+
# insignia: Ming Wanli
|
2596
|
+
|
2597
|
+
# NOTE: When the collection is serialized, the `ceramic_id` attribute is used to
|
2598
|
+
# key the data. This is defined through the `map` with `root_mappings` method in
|
2599
|
+
# CeramicCollection.
|
2600
|
+
> new_collection = CeramicCollection.new(ceramics: [
|
2601
|
+
Ceramic.new(ceramic_id: "vase1",
|
2602
|
+
ceramic_details: CeramicDetails.new(
|
2603
|
+
name: "Imperial Vase", insignia: "Tang Tianbao")
|
2604
|
+
),
|
2605
|
+
Ceramic.new(ceramic_id: "bowl2",
|
2606
|
+
ceramic_details: CeramicDetails.new(
|
2607
|
+
name: "18th Century Bowl", insignia: "Ming Wanli")
|
2608
|
+
)
|
2609
|
+
])
|
2610
|
+
> puts new_collection.to_yaml
|
2611
|
+
=>
|
2612
|
+
# ---
|
2613
|
+
# vase1:
|
2614
|
+
# name: Imperial Vase
|
2615
|
+
# insignia: Tang Tianbao
|
2616
|
+
# bowl2:
|
2617
|
+
# name: 18th Century Bowl
|
2618
|
+
# insignia: Ming Wanli
|
2619
|
+
----
|
2620
|
+
====
|
2621
|
+
|
2622
|
+
|
2623
|
+
[[attribute-extraction]]
|
2624
|
+
==== Attribute extraction
|
2625
|
+
|
2626
|
+
NOTE: This feature is for key-value data model serialization only.
|
2627
|
+
|
2628
|
+
The `child_mappings` option is used to extract results from a key-value
|
2629
|
+
serialization data model (JSON, YAML, TOML) into a `Lutaml::Model::Serializable`
|
2630
|
+
object (collection or not).
|
2631
|
+
|
2632
|
+
The values are extracted from the key-value data model using the list of keys
|
2633
|
+
provided.
|
2634
|
+
|
2635
|
+
Syntax:
|
2636
|
+
|
2637
|
+
[source,ruby]
|
2638
|
+
----
|
2639
|
+
class SomeObject < Lutaml::Model::Serializable
|
2640
|
+
attribute :name_of_attribute, AttributeValueType, collection: true
|
2641
|
+
|
2642
|
+
json | yaml | toml | key_value do
|
2643
|
+
map 'key_value_model_attribute_name', to: :name_of_attribute,
|
2644
|
+
child_mappings: {
|
2645
|
+
value_type_attribute_name_1: <1>
|
2646
|
+
{path_to_value_1}, <2>
|
2647
|
+
value_type_attribute_name_2:
|
2648
|
+
{path_to_value_2},
|
2649
|
+
# ...
|
2650
|
+
}
|
2651
|
+
end
|
2652
|
+
end
|
2653
|
+
----
|
2654
|
+
<1> The `value_type_attribute_name_1` is the attribute name in the
|
2655
|
+
`AttributeValueType` model. The value of this attribute will be assigned the key
|
2656
|
+
of the hash in the key-value data model.
|
2657
|
+
|
2658
|
+
<2> The `path_to_value_1` is an array of keys that represent the path to the
|
2659
|
+
value in the key-value serialization data model. The keys are used to extract the value from
|
2660
|
+
the key-value serialization data model and assign it to the attribute in the
|
2661
|
+
`AttributeValueType` model.
|
2662
|
+
+
|
2663
|
+
The `path_to_value` is in a nested array format with each value a symbol or a
|
2664
|
+
string, where each symbol represents a key to traverse down. The last key in the
|
2665
|
+
path is the value to be extracted.
|
2666
|
+
|
2667
|
+
.Determining the path to value in a key-value data model
|
2668
|
+
[example]
|
2669
|
+
====
|
2670
|
+
The following JSON contains 2 keys in schema named `engine` and `gearbox`.
|
2671
|
+
|
2672
|
+
[source,json]
|
2673
|
+
----
|
2674
|
+
{
|
2675
|
+
"components": {
|
2676
|
+
"engine": {
|
2677
|
+
"manufacturer": "Ford",
|
2678
|
+
"model": "V8"
|
2679
|
+
},
|
2680
|
+
"gearbox": {
|
2681
|
+
"manufacturer": "Toyota",
|
2682
|
+
"model": "4-speed"
|
2683
|
+
}
|
2684
|
+
}
|
2685
|
+
}
|
2686
|
+
----
|
2687
|
+
|
2688
|
+
The path to value for the `engine` schema is `[:components, :engine]` and for
|
2689
|
+
the `gearbox` schema is `[:components, :gearbox]`.
|
2690
|
+
====
|
2691
|
+
|
2692
|
+
In `path_to_value`, the `:key` and `:value` are reserved instructions used to
|
2693
|
+
assign the key or value of the serialization data respectively as the value to
|
2694
|
+
the attribute.
|
2695
|
+
|
2696
|
+
[example]
|
2697
|
+
====
|
2698
|
+
In the following JSON content, the `path_to_value` for the object keys named
|
2699
|
+
`engine` and `gearbox` will utilize the `:key` keyword to assign the key of the
|
2700
|
+
object as the value of a designated attribute.
|
2701
|
+
|
2702
|
+
[source,json]
|
2703
|
+
----
|
2704
|
+
{
|
2705
|
+
"components": {
|
2706
|
+
"engine": { /*...*/ },
|
2707
|
+
"gearbox": { /*...*/ }
|
2708
|
+
}
|
2709
|
+
}
|
2710
|
+
----
|
2711
|
+
====
|
2712
|
+
|
2713
|
+
If a specified value path is not found, the corresponding attribute in the model
|
2714
|
+
will be assigned a `nil` value.
|
2715
|
+
|
2716
|
+
.Attribute values set to `nil` when the `path_to_value` is not found
|
2717
|
+
[example]
|
2718
|
+
====
|
2719
|
+
In the following JSON content, the `path_to_value` of `[:extras, :sunroof]` and
|
2720
|
+
`[:extras, :drinks_cooler]` at the object `"gearbox"` would be set to `nil`.
|
2721
|
+
|
2722
|
+
[source,json]
|
2723
|
+
----
|
2724
|
+
{
|
2725
|
+
"components": {
|
2726
|
+
"engine": {
|
2727
|
+
"manufacturer": "Ford",
|
2728
|
+
"extras": {
|
2729
|
+
"sunroof": true,
|
2730
|
+
"drinks_cooler": true
|
2731
|
+
}
|
2732
|
+
},
|
2733
|
+
"gearbox": {
|
2734
|
+
"manufacturer": "Toyota"
|
2735
|
+
}
|
2736
|
+
}
|
2737
|
+
}
|
2738
|
+
----
|
2739
|
+
====
|
2740
|
+
|
2741
|
+
|
2742
|
+
.Using the `child_mappings` option to extract values from a key-value data model
|
2743
|
+
[example]
|
2744
|
+
====
|
2745
|
+
The following JSON contains 2 keys in schema named `foo` and `bar`.
|
2746
|
+
|
2747
|
+
[source,json]
|
2748
|
+
----
|
2749
|
+
{
|
2750
|
+
"schemas": {
|
2751
|
+
"foo": { <1>
|
2752
|
+
"path": { <2>
|
2753
|
+
"link": "link one",
|
2754
|
+
"name": "one"
|
2755
|
+
}
|
2756
|
+
},
|
2757
|
+
"bar": { <1>
|
2758
|
+
"path": { <2>
|
2759
|
+
"link": "link two",
|
2760
|
+
"name": "two"
|
2761
|
+
}
|
2762
|
+
}
|
2763
|
+
}
|
2764
|
+
}
|
2765
|
+
----
|
2766
|
+
<1> The keys `foo` and `bar` are to be mapped to the `id` attribute.
|
2767
|
+
<2> The nested `path.link` and `path.name` keys are used as the `link` and
|
2768
|
+
`name` attributes, respectively.
|
2769
|
+
|
2770
|
+
A model can be defined for this JSON as follows:
|
2771
|
+
|
2772
|
+
[source,ruby]
|
2773
|
+
----
|
2774
|
+
class Schema < Lutaml::Model::Serializable
|
2775
|
+
attribute :id, :string
|
2776
|
+
attribute :link, :string
|
2777
|
+
attribute :name, :string
|
2778
|
+
end
|
2779
|
+
|
2780
|
+
class ChildMappingClass < Lutaml::Model::Serializable
|
2781
|
+
attribute :schemas, Schema, collection: true
|
2782
|
+
|
2783
|
+
json do
|
2784
|
+
map "schemas", to: :schemas,
|
2785
|
+
child_mappings: {
|
2786
|
+
id: :key,
|
2787
|
+
link: %i[path link],
|
2788
|
+
name: %i[path name],
|
2789
|
+
}
|
2790
|
+
end
|
2791
|
+
end
|
2792
|
+
----
|
2793
|
+
|
2794
|
+
The output becomes:
|
2795
|
+
|
2796
|
+
[source,ruby]
|
2797
|
+
----
|
2798
|
+
> ChildMappingClass.from_json(json)
|
2799
|
+
> #<ChildMappingClass:0x0000000104ac7240
|
2800
|
+
@schemas=
|
2801
|
+
[#<Schema:0x0000000104ac6e30 @id="foo", @link="link one", @name="one">,
|
2802
|
+
#<Schema:0x0000000104ac58f0 @id="bar", @link="link two", @name="two">]>
|
2803
|
+
> ChildMappingClass.new(schemas: [Schema.new(id: "foo", link: "link one", name: "one"), Schema.new(id: "bar", link: "link two", name: "two")]).to_json
|
2804
|
+
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}}
|
2805
|
+
----
|
2806
|
+
|
2807
|
+
In this example:
|
2808
|
+
|
2809
|
+
* The `key` of each schema (`foo` and `bar`) is mapped to the `id` attribute.
|
2810
|
+
|
2811
|
+
* The nested `path.link` and `path.name` keys are mapped to the `link` and
|
2812
|
+
`name` attributes, respectively.
|
2813
|
+
====
|
2814
|
+
|
2815
|
+
|
2816
|
+
|
1980
2817
|
[[separate-serialization-model]]
|
1981
2818
|
=== Separate serialization model
|
1982
2819
|
|
@@ -2468,190 +3305,6 @@ end
|
|
2468
3305
|
====
|
2469
3306
|
|
2470
3307
|
|
2471
|
-
[[attribute-extraction]]
|
2472
|
-
==== Attribute extraction (for key-value data models only)
|
2473
|
-
|
2474
|
-
NOTE: This feature is for key-value data model serialization only.
|
2475
|
-
|
2476
|
-
The `child_mappings` option is used to extract results from a key-value data
|
2477
|
-
model (JSON, YAML, TOML) into a `Lutaml::Model` collection.
|
2478
|
-
|
2479
|
-
The values are extracted from the key-value data model using the list of keys
|
2480
|
-
provided.
|
2481
|
-
|
2482
|
-
Syntax:
|
2483
|
-
|
2484
|
-
[source,ruby]
|
2485
|
-
----
|
2486
|
-
json | yaml | toml do
|
2487
|
-
map 'key_value_model_attribute_name', to: :name_of_attribute,
|
2488
|
-
child_mappings: {
|
2489
|
-
key_attribute_name_1: <1>
|
2490
|
-
{path_to_value_1}, <2>
|
2491
|
-
key_attribute_name_2:
|
2492
|
-
{path_to_value_2},
|
2493
|
-
# ...
|
2494
|
-
}
|
2495
|
-
end
|
2496
|
-
----
|
2497
|
-
<1> The `key_attribute_name_1` is the attribute name in the model. The value of
|
2498
|
-
this attribute will be assigned the key of the hash in the key-value data model.
|
2499
|
-
|
2500
|
-
<2> The `path_to_value_1` is an array of keys that represent the path to the
|
2501
|
-
value in the key-value data model. The keys are used to extract the value from
|
2502
|
-
the key-value data model and assign it to the attribute in the model.
|
2503
|
-
|
2504
|
-
The `path_to_value` is in a nested array format with each value a symbol, where
|
2505
|
-
each symbol represents a key to traverse down. The last key in the path is the
|
2506
|
-
value to be extracted.
|
2507
|
-
|
2508
|
-
.Determining the path to value in a key-value data model
|
2509
|
-
[example]
|
2510
|
-
====
|
2511
|
-
The following JSON contains 2 keys in schema named `engine` and `gearbox`.
|
2512
|
-
|
2513
|
-
[source,json]
|
2514
|
-
----
|
2515
|
-
{
|
2516
|
-
"components": {
|
2517
|
-
"engine": {
|
2518
|
-
"manufacturer": "Ford",
|
2519
|
-
"model": "V8"
|
2520
|
-
},
|
2521
|
-
"gearbox": {
|
2522
|
-
"manufacturer": "Toyota",
|
2523
|
-
"model": "4-speed"
|
2524
|
-
}
|
2525
|
-
}
|
2526
|
-
}
|
2527
|
-
----
|
2528
|
-
|
2529
|
-
The path to value for the `engine` schema is `[:components, :engine]` and for
|
2530
|
-
the `gearbox` schema is `[:components, :gearbox]`.
|
2531
|
-
====
|
2532
|
-
|
2533
|
-
In `path_to_value`, the `:key` and `:value` are reserved instructions used to
|
2534
|
-
assign the key or value of the serialization data respectively as the value to
|
2535
|
-
the attribute.
|
2536
|
-
|
2537
|
-
[example]
|
2538
|
-
====
|
2539
|
-
In the following JSON content, the `path_to_value` for the object keys named
|
2540
|
-
`engine` and `gearbox` will utilize the `:key` keyword to assign the key of the
|
2541
|
-
object as the value of a designated attribute.
|
2542
|
-
|
2543
|
-
[source,json]
|
2544
|
-
----
|
2545
|
-
{
|
2546
|
-
"components": {
|
2547
|
-
"engine": { /*...*/ },
|
2548
|
-
"gearbox": { /*...*/ }
|
2549
|
-
}
|
2550
|
-
}
|
2551
|
-
----
|
2552
|
-
====
|
2553
|
-
|
2554
|
-
If a specified value path is not found, the corresponding attribute in the model
|
2555
|
-
will be assigned a `nil` value.
|
2556
|
-
|
2557
|
-
.Attribute values set to `nil` when the `path_to_value` is not found
|
2558
|
-
[example]
|
2559
|
-
====
|
2560
|
-
In the following JSON content, the `path_to_value` of `[:extras, :sunroof]` and
|
2561
|
-
`[:extras, :drinks_cooler]` at the object `"gearbox"` would be set to `nil`.
|
2562
|
-
|
2563
|
-
[source,json]
|
2564
|
-
----
|
2565
|
-
{
|
2566
|
-
"components": {
|
2567
|
-
"engine": {
|
2568
|
-
"manufacturer": "Ford",
|
2569
|
-
"extras": {
|
2570
|
-
"sunroof": true,
|
2571
|
-
"drinks_cooler": true
|
2572
|
-
}
|
2573
|
-
},
|
2574
|
-
"gearbox": {
|
2575
|
-
"manufacturer": "Toyota"
|
2576
|
-
}
|
2577
|
-
}
|
2578
|
-
}
|
2579
|
-
----
|
2580
|
-
====
|
2581
|
-
|
2582
|
-
|
2583
|
-
.Using the `child_mappings` option to extract values from a key-value data model
|
2584
|
-
[example]
|
2585
|
-
====
|
2586
|
-
The following JSON contains 2 keys in schema named `foo` and `bar`.
|
2587
|
-
|
2588
|
-
[source,json]
|
2589
|
-
----
|
2590
|
-
{
|
2591
|
-
"schemas": {
|
2592
|
-
"foo": { <1>
|
2593
|
-
"path": { <2>
|
2594
|
-
"link": "link one",
|
2595
|
-
"name": "one"
|
2596
|
-
}
|
2597
|
-
},
|
2598
|
-
"bar": { <1>
|
2599
|
-
"path": { <2>
|
2600
|
-
"link": "link two",
|
2601
|
-
"name": "two"
|
2602
|
-
}
|
2603
|
-
}
|
2604
|
-
}
|
2605
|
-
}
|
2606
|
-
----
|
2607
|
-
<1> The keys `foo` and `bar` are to be mapped to the `id` attribute.
|
2608
|
-
<2> The nested `path.link` and `path.name` keys are used as the `link` and
|
2609
|
-
`name` attributes, respectively.
|
2610
|
-
|
2611
|
-
A model can be defined for this JSON as follows:
|
2612
|
-
|
2613
|
-
[source,ruby]
|
2614
|
-
----
|
2615
|
-
class Schema < Lutaml::Model::Serializable
|
2616
|
-
attribute :id, :string
|
2617
|
-
attribute :link, :string
|
2618
|
-
attribute :name, :string
|
2619
|
-
end
|
2620
|
-
|
2621
|
-
class ChildMappingClass < Lutaml::Model::Serializable
|
2622
|
-
attribute :schemas, Schema, collection: true
|
2623
|
-
|
2624
|
-
json do
|
2625
|
-
map "schemas", to: :schemas,
|
2626
|
-
child_mappings: {
|
2627
|
-
id: :key,
|
2628
|
-
link: %i[path link],
|
2629
|
-
name: %i[path name],
|
2630
|
-
}
|
2631
|
-
end
|
2632
|
-
end
|
2633
|
-
----
|
2634
|
-
|
2635
|
-
The output becomes:
|
2636
|
-
|
2637
|
-
[source,ruby]
|
2638
|
-
----
|
2639
|
-
> ChildMappingClass.from_json(json)
|
2640
|
-
> #<ChildMappingClass:0x0000000104ac7240
|
2641
|
-
@schemas=
|
2642
|
-
[#<Schema:0x0000000104ac6e30 @id="foo", @link="link one", @name="one">,
|
2643
|
-
#<Schema:0x0000000104ac58f0 @id="bar", @link="link two", @name="two">]>
|
2644
|
-
> ChildMappingClass.new(schemas: [Schema.new(id: "foo", link: "link one", name: "one"), Schema.new(id: "bar", link: "link two", name: "two")]).to_json
|
2645
|
-
> #{"schemas"=>{"foo"=>{"path"=>{"link"=>"link one", "name"=>"one"}}, {"bar"=>{"path"=>{"link"=>"link two", "name"=>"two"}}}}
|
2646
|
-
----
|
2647
|
-
|
2648
|
-
In this example:
|
2649
|
-
|
2650
|
-
* The `key` of each schema (`foo` and `bar`) is mapped to the `id` attribute.
|
2651
|
-
|
2652
|
-
* The nested `path.link` and `path.name` keys are mapped to the `link` and
|
2653
|
-
`name` attributes, respectively.
|
2654
|
-
====
|
2655
3308
|
|
2656
3309
|
|
2657
3310
|
== Validation
|