Lutaml::Model maps serialization constructs (map_element, map_attribute, map_content, key-value map) to model attributes. When the wire shape and the model shape disagree, custom methods bridge the gap at the mapping site instead of hand-rolled to_h/from_h code.
with: — per-rule custom methods
Declare both directions on the rule. The methods live on the model (instance methods); the names are symbols.
class Requirement
include Lutaml::Model::Serialize
attribute :guidance, :string
attribute :purpose, :string
xml do
element "requirement"
map_element "component", to: :guidance,
with: { from: :component_from_guidance, to: :component_to_guidance }
end
def component_from_guidance(model, value, ctx = nil)
# `from` receives (model, value[, context]); assign the model attribute
model.guidance = value
end
def component_to_guidance(model, parent, doc = nil, ctx = nil)
# `to` receives (model, parent[, doc[, context]]); write into parent/doc
parent.add_element("component", model.guidance, type: "guidance")
end
endNotes:
-
The
frommethod assigns the attribute (it receives the whole model); thetomethod writes the output document. -
The optional trailing
contextparameter receives the options hash passed toModel.from_xml(data, context: {…})/model.to_xml(context: {…})— it is forwarded when the method declares it (lutaml-model#550). -
Callables work in key-value mappings:
with: { from: →(value) { … } }maps and assigns the return value; an exact-arity-2 callable receives the context as its second argument.
transform: — value-level transforms
transform: converts values (not documents). A Hash form (transform: { from: →(v) {…}, to: →(v) {…} }) wraps the value in both directions without touching assignment; a ValueTransformer subclass receives from(value, format) / to(value, format) and is shared by every rule that references it.
Type-level serialization
A type class (a Lutaml::Model::Type::Value subclass) can carry its own format behavior, so reuse lives on the type rather than the parent mapping:
class XmiNsType < Lutaml::Model::Type::String
xml do
namespace XmiNs # bind the type's own namespace
end
endLutaml::Model::Type::Value.register_format_type_serializer(format, type_class, to:, from:) registers per-format serialize/deserialize procs for a type class; resolution walks the hierarchy.
of_* and the conversion entry points
Model.of_<format>(parsed_document, options) is the documented seam between adapter parsing and mapping application: the adapter (from_<format>) parses the wire data into a document object, and of_<format> (class method) applies the mappings to build instances. It is public API for tooling that already holds a parsed document — e.g. re-applying mappings to a reparsed tree — and is what from_<format> delegates to.
The per-instance counterparts are to_<format> (serialize through the mappings) and as_<format> (render without register-specific side effects). Prefer the generated from_*/to_* methods; reach for of_* only when you interpose between parse and mapping.
Common recipes
-
One wire element, several attributes dispatched by an attribute value (
component/@type): onewith:pair per attribute whosefrominspects the dispatch attribute and whosetoemits only for its type. (Polymorphic class dispatch has first-class support viapolymorphic:.) -
Key remapping, nesting shifts, delimiter splitting: prefer
transform:,child_mappings, androot/prefixoptions before custom methods — the mapping stays declarative and reviewable.