Overview
The XmlNamespace class provides declarative namespace metadata following W3C XML Namespace and XSD specifications.
Benefits:
-
Reusable namespace definitions across models
-
Full XSD generation support
-
Control over qualification behavior
-
Documentation and versioning
-
Multi-schema support via imports/includes
Creating namespace classes
Inherit from Lutaml::Model::XmlNamespace and use DSL methods:
class MyNamespace < Lutaml::Model::XmlNamespace
uri 'namespace-uri' # Required
schema_location 'schema-url' # Optional
prefix_default 'prefix' # Optional
element_form_default :qualified # Optional
attribute_form_default :unqualified # Optional
version '1.0' # Optional
documentation 'Schema description' # Optional
imports OtherNamespace # Optional
includes 'schema-file.xsd' # Optional
endDSL methods reference
uri
Sets the namespace URI that uniquely identifies this namespace.
Syntax:
uri 'namespace-uri-string'This is the fundamental identifier used in xmlns declarations.
class CeramicNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/ceramic/v1'
end
# Results in XML: xmlns="https://example.com/schemas/ceramic/v1"
# Or with prefix: xmlns:cer="https://example.com/schemas/ceramic/v1" schema_location
Sets the URL where the XSD schema file can be found.
Syntax:
schema_location 'schema-url-or-path'Used in xsi:schemaLocation attributes for schema validation.
class CeramicNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/ceramic/v1'
schema_location 'https://example.com/schemas/ceramic/v1/ceramic.xsd'
end prefix_default
Sets the default prefix for this namespace.
Syntax:
prefix_default 'prefix' # or :prefix (symbol)The prefix can be overridden at runtime when creating namespace instances.
class CeramicNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/ceramic/v1'
prefix_default 'cer'
end
# Default usage: xmlns:cer="https://..."
# Runtime override: xmlns:ceramic="https://..." element_form_default
Controls whether locally declared elements must be namespace-qualified by default.
Syntax:
element_form_default :qualified # or :unqualified (default)Values:
:qualified-
Local elements must include namespace prefix
:unqualified-
Local elements have no namespace prefix (default)
class CeramicNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/ceramic/v1'
prefix_default 'cer'
element_form_default :qualified
end
# With :qualified, child elements get: <cer:type>...</cer:type>
# With :unqualified, child elements get: <type>...</type> attribute_form_default
Controls whether locally declared attributes must be namespace-qualified by default.
Syntax:
attribute_form_default :qualified # or :unqualified (default)Values:
:qualified-
Local attributes must include namespace prefix
:unqualified-
Local attributes have no namespace prefix (default)
| Per W3C conventions, attributes are typically unqualified. |
class CeramicNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/ceramic/v1'
prefix_default 'cer'
attribute_form_default :unqualified # Typically left unqualified
end imports
Declares dependencies on other namespaces via XSD import directive.
Syntax:
imports OtherNamespace1, OtherNamespace2, ...Used when referencing types from other namespaces.
class AddressNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/address/v1'
prefix_default 'addr'
end
class ContactNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/contact/v1'
prefix_default 'contact'
imports AddressNamespace # Import address namespace
end
# Generates in XSD:
# <xs:import namespace="https://example.com/schemas/address/v1"
# schemaLocation="..." /> includes
Declares schema components from the same namespace via XSD include directive.
Syntax:
includes 'schema-file.xsd', 'another-file.xsd', ...Used for modular schema organization within the same namespace.
class ContactNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/contact/v1'
prefix_default 'contact'
includes 'contact-common.xsd', 'contact-extensions.xsd'
end
# Generates in XSD:
# <xs:include schemaLocation="contact-common.xsd" />
# <xs:include schemaLocation="contact-extensions.xsd" /> version
Sets the schema version for documentation and tracking.
Syntax:
version 'version-string'class ContactNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/contact/v1'
version '1.0.0'
end
# Used in XSD: <xs:schema version="1.0.0"> documentation
Provides human-readable description for XSD annotation.
Syntax:
documentation 'description text'class ContactNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/contact/v1'
documentation "Contact information schema for Example Corp"
end
# Generates in XSD:
# <xs:annotation>
# <xs:documentation>Contact information schema for Example Corp</xs:documentation>
# </xs:annotation>Complete namespace example
# Define dependent namespace
class AddressNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/address/v1'
schema_location 'https://example.com/schemas/address/v1/address.xsd'
prefix_default 'addr'
end
# Define main namespace with all features
class ContactNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/contact/v1'
schema_location 'https://example.com/schemas/contact/v1/contact.xsd'
prefix_default 'contact'
element_form_default :qualified
attribute_form_default :unqualified
version '1.0'
documentation "Contact information schema for Example Corp"
imports AddressNamespace
includes 'contact-common.xsd', 'contact-types.xsd'
end
# Use namespace in model
class Person < Lutaml::Model::Serializable
attribute :name, :string
attribute :email, :string
xml do
element "person"
namespace ContactNamespace
sequence do
map_element "name", to: :name
map_element "email", to: :email
end
end
end
person = Person.new(name: "John Doe", email: "john@example.com")
puts person.to_xml
# => <contact:person xmlns:contact="https://example.com/schemas/contact/v1">
# <contact:name>John Doe</contact:name>
# <contact:email>john@example.com</contact:email>
# </contact:person>Runtime prefix override
Namespace prefixes can be overridden at runtime when building namespace instances.
class ContactNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/schemas/contact/v1'
prefix_default 'contact'
end
# Use default prefix
class Person < Lutaml::Model::Serializable
xml do
element "person"
namespace ContactNamespace # Uses 'contact' prefix
end
end
# Override prefix at mapping level
class ShortPerson < Lutaml::Model::Serializable
xml do
element "person"
namespace ContactNamespace, 'c' # Override to 'c' prefix
end
end
Person.new.to_xml
# => <contact:person xmlns:contact="...">...</contact:person>
ShortPerson.new.to_xml
# => <c:person xmlns:c="...">...</c:person>Additional XSD types
Lutaml::Model supports additional XSD types for specialized data handling:
Duration type
The :duration type handles ISO 8601 duration values conforming to xs:duration.
Duration format: P[n]Y[n]M[n]DT[n]H[n]M[n]S
Where,
P-
Required prefix indicating period
[n]Y-
Years (optional)
[n]M-
Months (optional, before T)
[n]D-
Days (optional)
T-
Time prefix (required if time components present)
[n]H-
Hours (optional)
[n]M-
Minutes (optional, after T)
[n]S-
Seconds (optional, can include decimals)
:duration typeclass ProcessingTask < Lutaml::Model::Serializable
attribute :processing_time, :duration
xml do
root "task"
map_element "processingTime", to: :processing_time
end
end
# Valid durations
task1 = ProcessingTask.new(processing_time: "P1Y2M3D")
# 1 year, 2 months, 3 days
task2 = ProcessingTask.new(processing_time: "PT4H5M6S")
# 4 hours, 5 minutes, 6 seconds
task3 = ProcessingTask.new(processing_time: "P1Y2M3DT4H5M6S")
# Combined
task4 = ProcessingTask.new(processing_time: "PT0.5S")
# 0.5 seconds
puts task1.to_xml
# => <task><processingTime>P1Y2M3D</processingTime></task>URI type
The :uri type handles Uniform Resource Identifiers conforming to xs:anyURI.
:uri typeclass Resource < Lutaml::Model::Serializable
attribute :homepage, :uri
attribute :schema_location, :uri
xml do
root "resource"
map_element "homepage", to: :homepage
map_attribute "schemaLocation", to: :schema_location
end
end
resource = Resource.new(
homepage: "https://example.com/page",
schema_location: "https://example.com/schema.xsd"
)
puts resource.to_xml
# => <resource schemaLocation="https://example.com/schema.xsd">
# <homepage>https://example.com/page</homepage>
# </resource>QName type
The :qname type handles XML qualified names conforming to xs:QName.
A QName consists of an optional namespace prefix and a local name, separated by a colon.
:qname typeclass Reference < Lutaml::Model::Serializable
attribute :ref_type, :qname
attribute :target, :qname
xml do
root "reference"
map_attribute "type", to: :ref_type
map_element "target", to: :target
end
end
ref = Reference.new(
ref_type: "xsd:string",
target: "ns:elementName"
)
puts ref.to_xml
# => <reference type="xsd:string">
# <target>ns:elementName</target>
# </reference>
# Accessing QName components
qname = Lutaml::Model::Type::QName.new("prefix:localName")
puts qname.prefix # => "prefix"
puts qname.local_name # => "localName"Base64Binary type
The :base64_binary type handles base64-encoded binary data conforming to xs:base64Binary.
:base64_binary typeclass Attachment < Lutaml::Model::Serializable
attribute :content, :base64_binary
attribute :filename, :string
xml do
root "attachment"
map_element "content", to: :content
map_attribute "filename", to: :filename
end
end
# Encoding binary data
binary_data = "Hello World"
encoded = Lutaml::Model::Type::Base64Binary.encode(binary_data)
attachment = Attachment.new(
content: encoded,
filename: "hello.txt"
)
puts attachment.to_xml
# => <attachment filename="hello.txt">
# <content>SGVsbG8gV29ybGQ=</content>
# </attachment>
# Decoding
decoded = Lutaml::Model::Type::Base64Binary.decode(attachment.content)
# => "Hello World"HexBinary type
The :hex_binary type handles hexadecimal-encoded binary data conforming to xs:hexBinary.
:hex_binary typeclass Checksum < Lutaml::Model::Serializable
attribute :hash_value, :hex_binary
attribute :algorithm, :string
xml do
root "checksum"
map_element "value", to: :hash_value
map_attribute "algorithm", to: :algorithm
end
end
# Encoding binary data
binary_data = "Hello"
encoded = Lutaml::Model::Type::HexBinary.encode(binary_data)
checksum = Checksum.new(
hash_value: encoded,
algorithm: "SHA256"
)
puts checksum.to_xml
# => <checksum algorithm="SHA256">
# <value>48656c6c6f</value>
# </checksum>
# Decoding
decoded = Lutaml::Model::Type::HexBinary.decode(checksum.hash_value)
# => "Hello"Type-level namespaces
Overview
Custom Type::Value classes and Serializable models can declare their namespace using the xml_namespace directive.
This enables automatic namespace qualification based on attribute types, following W3C XML Namespace specifications.
Type-level namespaces work in both serialization and deserialization, ensuring proper round-trip behavior with namespace-qualified elements and attributes.
Use cases
Type-level namespaces are ideal for:
-
Standard vocabularies: Dublin Core, FOAF, RSS/Atom properties
-
Custom XSD types: Domain-specific types with their own namespaces
-
Multi-namespace documents: Office Open XML, XHTML with embedded metadata
-
Reusable components: Types shared across multiple models and schemas
Type::Value xml_namespace directive
Custom value types declare their namespace using class-level directives:
Syntax:
class CustomType < Lutaml::Model::Type::Value
xml_namespace CustomNamespace (1)
xsd_type 'CustomType' (2)
def self.cast(value)
# Type conversion logic
end
end| 1 | Associates an XmlNamespace class with this type |
| 2 | Sets the XSD type name for schema generation (optional) |
Where,
xml_namespace-
Class-level directive that accepts an
XmlNamespaceclass. This namespace will be automatically applied when serializing or deserializing elements and attributes of this type, unless overridden by explicit mapping namespace. xsd_type-
Class-level directive that sets the XSD type name used during schema generation. If not specified, inherits
default_xsd_typefrom the parent class (e.g.,xs:stringforType::String).
How Type namespaces work
During serialization (to_xml)
When an element or attribute uses a custom type with namespace:
-
Type’s namespace is consulted if no explicit mapping namespace
-
Namespace URI and prefix are added to document root
-
Element/attribute is prefixed according to namespace resolution priority
-
Works with both simple types (
Type::Value) and complex types (Serializable)
During deserialization (from_xml)
When parsing namespace-qualified XML:
-
Namespace-qualified elements/attributes are matched against type namespaces
-
Both prefixed (
dc:title) and default namespace elements are recognized -
Type casting occurs after namespace matching
-
Works seamlessly with inherited namespaces and explicit mappings
Simple example with deserialization
class EmailNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/types/email'
prefix_default 'email'
end
class EmailType < Lutaml::Model::Type::String
xml_namespace EmailNamespace
xsd_type 'EmailAddress'
def self.cast(value)
email = super(value)
unless email.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
raise Lutaml::Model::TypeError, "Invalid email: #{email}"
end
email.downcase
end
end
class Contact < Lutaml::Model::Serializable
attribute :email, EmailType
xml do
root 'contact'
map_element 'email', to: :email # Uses EmailNamespace automatically
end
end
# Serialization:
contact = Contact.new(email: "USER@EXAMPLE.COM")
xml = contact.to_xml
puts xml
# => <contact xmlns:email="https://example.com/types/email">
# <email:email>user@example.com</email:email>
# </contact>
# Deserialization:
parsed = Contact.from_xml(xml)
parsed.email # => "user@example.com" (type casting applied)
parsed == contact # => true (round-trip successful)Serializable namespace directive
Models declare their namespace using the namespace directive:
Syntax:
class CustomModel < Lutaml::Model::Serializable
namespace CustomNamespace
attribute :value, :string
xml do
root 'customElement'
map_element 'value', to: :value
end
endComplete working example
# Define Dublin Core namespace
class DublinCoreNamespace < Lutaml::Model::XmlNamespace
uri 'http://purl.org/dc/elements/1.1/'
prefix_default 'dc'
end
# Define document namespace
class DocumentNamespace < Lutaml::Model::XmlNamespace
uri 'https://example.com/document'
prefix_default 'doc'
end
# Define custom type with Dublin Core namespace
class DcTitleType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace
xsd_type 'titleType'
end
class DcCreatorType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace
xsd_type 'creatorType'
end
# Use types in document model
class Document < Lutaml::Model::Serializable
namespace DocumentNamespace
attribute :title, DcTitleType
attribute :creator, DcCreatorType
attribute :content, :string # Regular attribute, no type namespace
xml do
root 'document'
map_element 'title', to: :title # Becomes <dc:title> from Type
map_element 'creator', to: :creator # Becomes <dc:creator> from Type
map_element 'content', to: :content # No namespace (unqualified local)
end
end
# Serialization:
doc = Document.new(
title: 'Example Document',
creator: 'John Doe',
content: 'Document content here'
)
xml = doc.to_xml
puts xml
# Output:
# <doc:document
# xmlns:doc="https://example.com/document"
# xmlns:dc="http://purl.org/dc/elements/1.1/">
# <dc:title>Example Document</dc:title>
# <dc:creator>John Doe</dc:creator>
# <content>Document content here</content>
# </doc:document>
# Deserialization (round-trip):
parsed = Document.from_xml(xml)
parsed.title # => "Example Document"
parsed.creator # => "John Doe"
parsed.content # => "Document content here"
parsed == doc # => trueNamespace resolution priority
Namespace is determined by priority (highest to lowest):
For elements (map_element)
-
Model-level namespace (from the attribute’s type class)
# Child model declares its own namespace class Title < Lutaml::Model::Serializable xml do element 'title' namespace DublinCoreNamespace end end # Parent uses child's namespace class Document < Lutaml::Model::Serializable attribute :title, Title map_element 'title', to: :title # Uses Title's namespace end -
Type-level namespace (from
Type::Valueclass)class TitleType < Lutaml::Model::Type::String xml_namespace DublinCoreNamespace end attribute :title, TitleType map_element 'title', to: :title # Uses DublinCoreNamespace -
Form default qualification (from
element_form_default)If
:qualified: inherits parent namespace
If:unqualified: no namespace (default)
For attributes (map_attribute)
-
Type-level namespace (from
Type::Valueclass)class XsiTypeType < Lutaml::Model::Type::String xml_namespace XsiNamespace end attribute :type, XsiTypeType map_attribute 'type', to: :type # Uses XsiNamespace, becomes xsi:type -
No namespace (W3C default)
Per W3C specifications: unprefixed attributes are NEVER in a namespace.
Only explicitly qualified attributes have namespaces.
| Per W3C specifications, unprefixed XML attributes do NOT inherit their parent element’s namespace. Only explicitly qualified attributes have namespaces. This is critical for W3C compliance and correct round-tripping. |
Type-level namespaces work in both serialization (to_xml) and deserialization (from_xml), ensuring proper round-trip behavior with namespace-qualified elements and attributes. |
Multi-namespace example
Type-level namespaces excel when working with multi-namespace documents:
# 1. Define namespace classes
class CorePropertiesNamespace < Lutaml::Model::XmlNamespace
uri 'http://schemas.openxmlformats.org/package/2006/metadata/core-properties'
prefix_default 'cp'
end
class DublinCoreNamespace < Lutaml::Model::XmlNamespace
uri 'http://purl.org/dc/elements/1.1/'
prefix_default 'dc'
end
class DCTermsNamespace < Lutaml::Model::XmlNamespace
uri 'http://purl.org/dc/terms/'
prefix_default 'dcterms'
end
class XsiNamespace < Lutaml::Model::XmlNamespace
uri 'http://www.w3.org/2001/XMLSchema-instance'
prefix_default 'xsi'
end
# 2. Define Type::Value classes with namespaces
class DcTitleType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace
xsd_type 'titleType'
end
class DcCreatorType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace
xsd_type 'creatorType'
end
class CpLastModifiedByType < Lutaml::Model::Type::String
namespace CorePropertiesNamespace
end
class CpRevisionType < Lutaml::Model::Type::Integer
namespace CorePropertiesNamespace
end
class XsiTypeType < Lutaml::Model::Type::String
xml_namespace XsiNamespace
xsd_type 'type'
end
# 3. Define complex Model types with namespaces
class DctermsCreatedType < Lutaml::Model::Serializable
namespace DCTermsNamespace
attribute :value, :date_time
attribute :type, XsiTypeType
xml do
root 'created'
# This becomes xsi:type from XsiTypeType
map_attribute 'type', to: :type
map_content to: :value
end
end
class DctermsModifiedType < Lutaml::Model::Serializable
namespace DCTermsNamespace
attribute :value, :date_time
attribute :type, XsiTypeType
xml do
root 'modified'
map_attribute 'type', to: :type
map_content to: :value
end
end
# 4. Define root model
class CoreProperties < Lutaml::Model::Serializable
namespace CorePropertiesNamespace
attribute :title, DcTitleType
attribute :creator, DcCreatorType
attribute :last_modified_by, CpLastModifiedByType
attribute :revision, CpRevisionType
attribute :created, DctermsCreatedType
attribute :modified, DctermsModifiedType
xml do
root 'coreProperties'
# Type namespaces automatically applied
map_element 'title', to: :title # Becomes <dc:title>
map_element 'creator', to: :creator # Becomes <dc:creator>
map_element 'lastModifiedBy', to: :last_modified_by # Becomes <cp:lastModifiedBy>
map_element 'revision', to: :revision # Becomes <cp:revision>
map_element 'created', to: :created # Becomes <dcterms:created>
map_element 'modified', to: :modified # Becomes <dcterms:modified>
end
end
# Serialization: Create and serialize
props = CoreProperties.new(
title: 'Untitled',
creator: 'Uniword',
last_modified_by: 'Uniword',
revision: 1,
created: DctermsCreatedType.new(
value: DateTime.parse('2025-11-13T17:11:03Z'),
type: 'dcterms:W3CDTF'
),
modified: DctermsModifiedType.new(
value: DateTime.parse('2025-11-13T17:11:03Z'),
type: 'dcterms:W3CDTF'
)
)
puts props.to_xmlSerialization output with four namespaces:
<cp:coreProperties
xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<dc:title>Untitled</dc:title>
<dc:creator>Uniword</dc:creator>
<cp:lastModifiedBy>Uniword</cp:lastModifiedBy>
<cp:revision>1</cp:revision>
<dcterms:created xsi:type="dcterms:W3CDTF">2025-11-13T17:11:03Z</dcterms:created>
<dcterms:modified xsi:type="dcterms:W3CDTF">2025-11-13T17:11:03Z</dcterms:modified>
</cp:coreProperties>Deserialization: Round-trip parsing:
# Parse the generated XML
parsed_props = CoreProperties.from_xml(props.to_xml)
# All namespaces are correctly resolved
parsed_props.title # => "Untitled" (from dc:title)
parsed_props.creator # => "Uniword" (from dc:creator)
parsed_props.last_modified_by # => "Uniword" (from cp:lastModifiedBy)
parsed_props.revision # => 1 (from cp:revision)
parsed_props.created.value # => DateTime "2025-11-13T17:11:03Z"
parsed_props.created.type # => "dcterms:W3CDTF" (from xsi:type)
# Verify round-trip equality
parsed_props === props # => trueThis demonstrates:
-
Four different namespaces (
cp,dc,dcterms,xsi) in a single document -
Type-level namespace for simple value types (
DcTitleType,DcCreatorType) -
Model-level namespace for complex types (
DctermsCreatedType,DctermsModifiedType) -
Attribute namespace qualification (
xsi:typefromXsiTypeType) -
Full round-trip support - Type namespaces work in both serialization and deserialization
-
W3C-compliant namespace resolution following the priority rules
Namespace priority examples
class DefaultType < Lutaml::Model::Type::String
namespace DefaultNamespace
end
class Model < Lutaml::Model::Serializable
attribute :value, DefaultType
xml do
root 'model'
# Case 1: Type namespace used (no explicit namespace)
map_element 'value1', to: :value
# => <default:value1>
# Case 2: Explicit namespace overrides Type namespace
map_element 'value2', to: :value, namespace: OtherNamespace
# => <other:value2>
# Case 3: Inherit parent namespace, ignoring Type namespace
map_element 'value3', to: :value, namespace: :inherit
# => <model_ns:value3> (if Model has namespace)
end
endAttribute namespace handling
Attribute namespace handling differs from elements per W3C specifications.
Attributes only belong to a namespace when explicitly qualified with a prefix. Unprefixed attributes are not considered to be in any namespace per W3C XML Namespace.
class XsiTypeType < Lutaml::Model::Type::String
xml_namespace XsiNamespace
xsd_type 'type'
end
class Product < Lutaml::Model::Serializable
attribute :id, :string
attribute :schema_type, XsiTypeType
xml do
root 'product'
map_attribute 'id', to: :id # Unqualified: id="..."
map_attribute 'type', to: :schema_type # Qualified: xsi:type="..."
end
end
# Serialization:
product = Product.new(id: "P001", schema_type: "ProductType")
puts product.to_xml
# => <product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
# id="P001"
# xsi:type="ProductType"/>Note that id is unprefixed (no namespace per W3C), while type uses the xsi: prefix from XsiTypeType.
XML attribute explicit declaration of XSD type
General
The :xsd_type attribute option allows explicit control over the XSD type used in schema generation.
This is particularly useful for:
-
Reference types using
xs:IDandxs:IDREF -
Custom XSD types not directly mapped to Lutaml types
-
Override automatic type inference
Syntax:
attribute :attr_name, Type, xsd_type: 'xs:typeName'Priority order
When determining XSD type, Lutaml::Model uses this priority:
-
Explicit
:xsd_typeoption on attribute (highest priority) -
Type’s
xsd_typeclass method -
Default type inference (lowest priority)
:xsd_type for ID and IDREFclass Product < Lutaml::Model::Serializable
attribute :product_id, :string, xsd_type: 'xs:ID'
attribute :category_ref, :string, xsd_type: 'xs:IDREF'
attribute :related_refs, :string, collection: true, xsd_type: 'xs:IDREFS'
xml do
element 'product'
map_attribute 'id', to: :product_id
map_attribute 'categoryRef', to: :category_ref
map_attribute 'relatedRefs', to: :related_refs
end
end
# Generated XSD uses:
# <xs:attribute name="id" type="xs:ID"/>
# <xs:attribute name="categoryRef" type="xs:IDREF"/>
# <xs:attribute name="relatedRefs" type="xs:IDREFS"/>:xsd_type for custom typesclass Document < Lutaml::Model::Serializable
attribute :language, :string, xsd_type: 'xs:language'
attribute :content_type, :string, xsd_type: 'xs:token'
attribute :normalized_text, :string, xsd_type: 'xs:normalizedString'
xml do
element 'document'
map_attribute 'lang', to: :language
map_attribute 'contentType', to: :content_type
map_element 'text', to: :normalized_text
end
end
# Generated XSD declares proper XSD built-in typesUse with Reference type
The Type::Reference type can utilize :xsd_type for proper XSD generation:
class Catalog < Lutaml::Model::Serializable
attribute :catalog_id, { ref: [Catalog, :id] }, xsd_type: 'xs:ID'
attribute :parent_ref, { ref: [Catalog, :id] }, xsd_type: 'xs:IDREF'
xml do
element 'catalog'
map_attribute 'id', to: :catalog_id
map_attribute 'parent', to: :parent_ref
end
endXSD schema generation support
Type-level namespaces are fully integrated with XSD generation:
-
Type namespaces generate proper
xs:importdeclarations -
xsd_typedirective controls the type name in generated schemas -
Namespace URIs are included in schema imports
-
Schema locations are preserved from
XmlNamespacedefinitions
# After defining DcTitleType and Document from previous example
xsd = Lutaml::Model::Schema.to_xml(
Document,
namespace: DocumentNamespace.uri,
prefix: DocumentNamespace.prefix_default
)
# Generated XSD includes:
# <xs:import namespace="http://purl.org/dc/elements/1.1/"
# schemaLocation="..." />
#
# And uses dc:titleType for the title element typeBest practices
When using Type-level namespaces:
-
Define namespace classes first: Create all
XmlNamespaceclasses before defining types -
Use descriptive type names: Set
xsd_typefor custom types to generate meaningful schemas -
Leverage namespace resolution: Let type namespaces handle qualification automatically instead of explicit mapping namespaces
-
Test round-trips: Verify that serialization and deserialization work correctly with your namespace configuration
-
Document namespaces: Use XmlNamespace
documentationfor schema clarity
Migration from deprecated namespace directive
Overview
The namespace directive for Type::Value classes has been renamed to xml_namespace to better indicate its XML-specific nature and reserve namespace for future semantic namespace features.
Use xml_namespace instead of namespace for all Type::Value classes. |
Deprecated syntax
class DcTitleType < Lutaml::Model::Type::String
namespace DublinCoreNamespace # ⚠️ DEPRECATED
xsd_type 'titleType'
endDeprecation warning shown:
[DEPRECATION] Type::Value.namespace is deprecated. Use xml_namespace instead.
This will be removed in version 1.0.0Current syntax
class DcTitleType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace # ✅ CURRENT
xsd_type 'titleType'
endWhy the change?
The renaming serves several important purposes:
-
XML-specific clarity: The
xml_namespacename clearly indicates this is XML serialization configuration, not a general semantic namespace -
Future compatibility: Reserves
namespacefor semantic namespace features like JSON-LD@contextand RDF IRI mappings -
Consistency: Aligns with format-specific naming patterns throughout Lutaml::Model
-
Disambiguation: Distinguishes Type-level namespace from Model-level
namespacedirective
Migration steps
-
Locate Type::Value classes with namespace: Search your codebase for classes inheriting from
Lutaml::Model::Type::Value(or built-in types likeType::String) that usenamespace -
Replace directive: Change
namespacetoxml_namespace -
Test round-trip: Verify serialization and deserialization work correctly
-
No other changes needed: Functionality is identical
Complete migration example
class DublinCoreNamespace < Lutaml::Model::XmlNamespace
uri 'http://purl.org/dc/elements/1.1/'
prefix_default 'dc'
end
class DcTitleType < Lutaml::Model::Type::String
namespace DublinCoreNamespace # ⚠️ DEPRECATED
xsd_type 'titleType'
end
class DcCreatorType < Lutaml::Model::Type::String
namespace DublinCoreNamespace # ⚠️ DEPRECATED
xsd_type 'creatorType'
end
class Document < Lutaml::Model::Serializable
attribute :title, DcTitleType
attribute :creator, DcCreatorType
xml do
root 'document'
map_element 'title', to: :title
map_element 'creator', to: :creator
end
endclass DublinCoreNamespace < Lutaml::Model::XmlNamespace
uri 'http://purl.org/dc/elements/1.1/'
prefix_default 'dc'
end
class DcTitleType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace # ✅ CURRENT
xsd_type 'titleType'
end
class DcCreatorType < Lutaml::Model::Type::String
xml_namespace DublinCoreNamespace # ✅ CURRENT
xsd_type 'creatorType'
end
class Document < Lutaml::Model::Serializable
attribute :title, DcTitleType
attribute :creator, DcCreatorType
xml do
root 'document'
map_element 'title', to: :title
map_element 'creator', to: :creator
end
endMigration from xml block
The old xml block approach in Type::Value is deprecated:
class EmailType < Lutaml::Model::Type::String
xml do
namespace EmailNamespace # DEPRECATED
end
def self.xsd_type
'EmailAddress'
end
endclass EmailType < Lutaml::Model::Type::String
xml_namespace EmailNamespace # Directive, not block
xsd_type 'EmailAddress' # Directive, not method
endBenefits of new approach:
-
Cleaner, more declarative syntax
-
Consistent with XmlNamespace DSL pattern
-
Better performance (no block evaluation)
-
Enhanced XSD generation support
-
Full deserialization support
| The xml block approach still works with a deprecation warning for backward compatibility but lacks full deserialization support. |
Namespace scope consolidation
General
The namespace_scope directive controls where namespace declarations appear in serialized XML. By default, namespaces are declared on the elements where they are used. With namespace_scope, you can consolidate multiple namespace declarations at a parent element for cleaner, more compact XML.
This feature is particularly useful for:
-
Multi-namespace documents: Documents using several namespaces throughout
-
Reduced XML verbosity: Consolidate namespace declarations at logical scoping levels
-
Standards compliance: W3C-compliant namespace handling with declaration consolidation
-
Cleaner output: More readable XML with namespaces declared once
Syntax
xml do
namespace RootNamespace
namespace_scope [Namespace1, Namespace2, Namespace3]
endWhere,
namespace_scope-
Array of
XmlNamespaceclass objects. These namespaces will be declared once at the root element. Any namespaces NOT in this list will be declared locally on elements that use them.
How it works
Namespaces IN scope:
-
Declared once at the root element
-
All child elements using these namespaces omit local declarations
-
Cleaner, more compact XML output
Namespaces NOT in scope:
-
Declared locally on the first element that uses them
-
Each usage includes namespace declaration
-
More verbose but clearly scoped
Basic example
class VcardNamespace < Lutaml::Model::XmlNamespace
uri "urn:ietf:params:xml:ns:vcard-4.0"
prefix_default "vcard"
end
class DcNamespace < Lutaml::Model::XmlNamespace
uri "http://purl.org/dc/elements/1.1/"
prefix_default "dc"
end
class DcTitleType < Lutaml::Model::Type::String
xml_namespace DcNamespace
end
class Vcard < Lutaml::Model::Serializable
attribute :version, :string
attribute :title, DcTitleType
xml do
root "vCard"
namespace VcardNamespace
namespace_scope [VcardNamespace, DcNamespace] (1)
map_element "version", to: :version
map_element "title", to: :title
end
end
vcard = Vcard.new(
version: "4.0",
title: "Dr. John Doe"
)
puts vcard.to_xml| 1 | Both namespaces declared at root element |
Output with namespace_scope:
<vcard:vCard xmlns:vcard="urn:ietf:params:xml:ns:vcard-4.0"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<vcard:version>4.0</vcard:version>
<dc:title>Dr. John Doe</dc:title>
</vcard:vCard>Without namespace_scope, dc namespace declared locally:
<vcard:vCard xmlns:vcard="urn:ietf:params:xml:ns:vcard-4.0">
<vcard:version>4.0</vcard:version>
<dc:title xmlns:dc="http://purl.org/dc/elements/1.1/">Dr. John Doe</dc:title>
</vcard:vCard>Complete vCard example
class VcardNamespace < Lutaml::Model::XmlNamespace
uri "urn:ietf:params:xml:ns:vcard-4.0"
prefix_default "vcard"
end
class DcNamespace < Lutaml::Model::XmlNamespace
uri "http://purl.org/dc/elements/1.1/"
prefix_default "dc"
end
class DctermsNamespace < Lutaml::Model::XmlNamespace
uri "http://purl.org/dc/terms/"
prefix_default "dcterms"
end
# Define types with Dublin Core namespaces
class DcTitleType < Lutaml::Model::Type::String
xml_namespace DcNamespace
end
class DctermsCreatedType < Lutaml::Model::Type::DateTime
xml_namespace DctermsNamespace
end
class VcardVersion < Lutaml::Model::Type::String
xml_namespace VcardNamespace
end
class Vcard < Lutaml::Model::Serializable
attribute :version, VcardVersion
attribute :title, DcTitleType
attribute :full_name, :string
attribute :created, DctermsCreatedType
xml do
root "vCard"
namespace VcardNamespace
namespace_scope [VcardNamespace, DcNamespace, DctermsNamespace] (1)
map_element "version", to: :version
map_element "title", to: :title
map_element "fn", to: :full_name
map_element "created", to: :created
end
end
vcard = Vcard.new(
version: "4.0",
title: "Contact: Dr. John Doe",
full_name: "Dr. John Doe",
created: DateTime.parse("2024-06-01T12:00:00Z")
)
puts vcard.to_xml| 1 | Consolidate all three namespaces at root element |
Output with namespace_scope:
<vcard:vCard xmlns:vcard="urn:ietf:params:xml:ns:vcard-4.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/">
<vcard:version>4.0</vcard:version>
<dc:title>Contact: Dr. John Doe</dc:title>
<vcard:fn>Dr. John Doe</vcard:fn>
<dcterms:created>2024-06-01T12:00:00+00:00</dcterms:created>
</vcard:vCard>Without namespace_scope, each namespace declared locally where used:
<vcard:vCard xmlns:vcard="urn:ietf:params:xml:ns:vcard-4.0">
<vcard:version>4.0</vcard:version>
<dc:title xmlns:dc="http://purl.org/dc/elements/1.1/">Contact: Dr. John Doe</dc:title>
<vcard:fn>Dr. John Doe</vcard:fn>
<dcterms:created xmlns:dcterms="http://purl.org/dc/terms/">2024-06-01T12:00:00+00:00</dcterms:created>
</vcard:vCard>Selective namespace consolidation
You can choose which namespaces to consolidate and which to keep local:
class Vcard < Lutaml::Model::Serializable
attribute :version, VcardVersion
attribute :title, DcTitleType
attribute :created, DctermsCreatedType
xml do
root "vCard"
namespace VcardNamespace
namespace_scope [VcardNamespace, DcNamespace] (1)
map_element "version", to: :version
map_element "title", to: :title
map_element "created", to: :created
end
end| 1 | Only vcard and dc namespaces at root; dcterms declared locally |
Output:
<vcard:vCard xmlns:vcard="urn:ietf:params:xml:ns:vcard-4.0"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<vcard:version>4.0</vcard:version>
<dc:title>Contact: Dr. John Doe</dc:title>
<dcterms:created xmlns:dcterms="http://purl.org/dc/terms/">2024-06-01T12:00:00+00:00</dcterms:created>
</vcard:vCard>The dcterms namespace is declared locally on the <dcterms:created> element because it was not included in namespace_scope.
When to use namespace_scope
Use namespace_scope when:
-
Multiple namespaces are used throughout the document
-
You prefer cleaner XML with consolidated declarations
-
Namespaces should be declared at a logical scoping level
-
The document structure benefits from upfront namespace declarations
Do NOT use namespace_scope when:
-
Single namespace documents (not needed)
-
Namespaces are rarely used (local declaration is clearer)
-
External consumers expect local namespace declarations
-
Namespace declarations should be deferred to actual usage points
Round-trip behavior
Type-level namespaces with namespace_scope work correctly in both serialization and deserialization:
# Serialize with consolidated namespaces
vcard = Vcard.new(
version: "4.0",
title: "Dr. John Doe",
created: DateTime.parse("2024-06-01T12:00:00Z")
)
xml = vcard.to_xml
# Namespaces declared at root
# Deserialize - namespaces correctly resolved
parsed = Vcard.from_xml(xml)
parsed == vcard # => true
# All attributes correctly parsed regardless of where namespace was declared
parsed.version # => "4.0"
parsed.title # => "Dr. John Doe"
parsed.created # => DateTime "2024-06-01T12:00:00Z"