When creating or modifying lutaml-model classes, attributes MUST be declared BEFORE xml mappings. This is the single most important rule for contributors.
1. The Rule
# CORRECT - Attributes FIRST
class MyClass < Lutaml::Model::Serializable
attribute :my_attr, MyType # Declare attribute first
xml do
element 'myElem'
namespace Namespaces::WordProcessingML
map_element 'elem', to: :my_attr # Map after
end
end
# WRONG - Will NOT serialize
class MyClass < Lutaml::Model::Serializable
xml do
map_element 'elem', to: :my_attr # Mapping before attribute
end
attribute :my_attr, MyType # Too late! Framework doesn't know it exists
end
2. Why This Matters
Lutaml-model builds its internal schema by reading attribute declarations sequentially:
-
When the class body is evaluated, Ruby executes statements top to bottom
-
The
xml doblock registers mappings with the framework -
If
xml docomes first, the framework processes mappings before knowing attributes exist -
The framework cannot resolve
to: :my_attrbecause the attribute has not been declared yet -
Result: Serialization produces empty XML, deserialization fails silently
This was the root cause of complete document serialization failure during v1.1.0 development.
3. Additional Rules
- Use
mixed_contentfor elements with nested content -
Elements that contain a mix of text and child elements need
mixed_contentenabled. - Only one namespace declaration per element level
-
Do not declare multiple namespaces at the same element level.
- Use
render_default: truefor required optional elements -
Optional elements that must always appear in output should use
render_default: true. - Use
||=in initialize, not= -
In
initializeaftersuper, use||=to preserve lutaml-model parsed values:
def initialize(**attrs)
super
@items ||= [] # Preserves parsed values
# NOT: @items = [] # Overwrites parsed values!
end