This document describes how to import schemas into LutaML models.
Schema import
Overview
Lutaml::Model provides functionality to import schema definitions into LutaML models. This allows you to create models from existing schema definitions.
The following figure illustrates the process of importing an XML Schema model to create corresponding LutaML models.
╔════════════════════════════╗ ╔═══════════════════════╗
║ Serialization Models ║ ║ Core Model ║
╚════════════════════════════╝ ╚═══════════════════════╝
╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
┆ XML Schema (XSD/RNG/RNC) ┆ ┆ Model ┆
┆ │ ┆ ┌────────────────┐ ┆ │ ┆
┆ ┌──────┴──────┐ ┆ │ │ ┆ ┌────────┴──┐ ┆
┆ │ │ ┆ │ Schema │ ┆ │ │ ┆
┆ Models Value Types ┆──►│ Importing │──►┆ Models Value Types ┆
┆ │ │ ┆ │ │ ┆ │ │ ┆
┆ │ │ ┆ └────────────────┘ ┆ │ │ ┆
┆ ┌────┴────┐ ┌─┴─┐ ┆ │ ┆ │ ┌──────┴──┐ ┆
┆ │ │ │ │ ┆ │ ┆ │ │ │ ┆
┆ Element Value xs:string ┆ │ ┆ │ String Integer ┆
┆ Attribute Type xs:date ┆ │ ┆ │ Date Float ┆
┆ Union Complex xs:boolean ┆ │ ┆ │ Time Boolean ┆
┆ Sequence Choice xs:anyURI ┆ │ ┆ │ ┆
╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ │ ┆ └──────┐ ┆
│ ┆ │ ┆
│ ┆ Contains ┆
│ ┆ more Models ┆
│ ┆ (recursive) ┆
│ ┆ ┆
│ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
│ ┌────────────────┐
│ │ │
│ │ Model │
└──────────► │ Transformation │
│ & │
│ Mapping Rules │
│ │
└────────────────┘Currently, the following schema formats are supported for import:
XML Schema (XSD) import
W3C XSD is a schema language designed to define the structure of XML documents, alongside other XML schema languages like DTD, RELAX NG, and Schematron.
Lutaml::Model supports the import of XSD schema files to define information models that can be used to parse and generate XML documents.
Specifically, the Lutaml::Model::Schema#from_xml method loads XML Schema files (XSD, .xsd) and generates Ruby files (.rb) that inherit from Lutaml::Model::Serializable that are saved to disk.
Syntax
Lutaml::Model::Schema.from_xml(
xsd_schema, (1)
options: options (2)
)| 1 | The xsd_schema is the XML Schema string to be converted to model files. |
| 2 | The options hash is an optional argument.
|
If both create_files and load_classes are provided, the create_files argument will take priority and generate files without loading them! |
Generated model structure
The generated LutaML models consists of two different kind of Ruby classes depending on the XSD schema:
- XSD "SimpleTypes"
-
converted into classes that inherit from
Lutaml::Model::Type::Value, which define the data types with restrictions and other validations of these values. - XSD "ComplexTypes"
-
converted into classes that inherit from
Lutaml::Model::Serializablethat model according to the defined structure.
Lutaml::Model uses the lutaml-xsd gem to automatically resolve the include and import elements, enabling Lutaml-Model to generate the corresponding model files.
This auto-resolving feature allows seamless integration of these files into your models without the need for manual resolution of includes and imports.
Example
xsd_schema = <<~XSD
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- XML schema definition here -->
<xs:element name="User">
<xs:complexType>
<xs:sequence>
<xs:element name="id" type="xs:positiveInteger"/>
<xs:element name="age" type="xs:positiveInteger"/>
<xs:element name="token" type="xs:token"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
XSD
options = {
# These are all optional:
output_dir: 'path/to/directory',
namespace: 'http://example.com/namespace',
prefix: "example-prefix",
location: "http://example.com/example.xsd",
# or
# location: "path/to/schema/directory"
create_files: true, # Default: false
# OR
load_classes: true, # Default: false
}
# generates the files in the output_dir | default_dir
Lutaml::Model::Schema.from_xml(xsd_schema, options: options)Working with generated models
You can use the models directly if you set load_classes: true:
# Generate and load the models
Lutaml::Model::Schema.from_xml(xsd_schema, options: {load_classes: true})
# Create a new User instance
user = User.new(id: 1112, age: 29, token: "u9dId901dp13f")
# Serialize to XML
xml = user.to_xml
# => "<User>\n <id>1112</id>\n <age>29</age>\n <token>u9dId901dp13f</token>\n</User>"
# Parse from XML
parsed_user = User.from_xml(xml)
parsed_user.id # => 1112
parsed_user.age # => 29
parsed_user.token # => "u9dId901dp13f"Alternatively, you could directly load the generated Ruby files into your application by requiring them:
Lutaml::Model::Schema.from_xml(xsd_schema, options: {output_dir: 'path/to/directory', create_files: true})
require_relative 'path/to/directory/*.rb'JSON/YAML Schema import
Lutaml::Model supports importing JSON Schema definitions to generate Ruby model classes. This enables you to create Ruby models that match your JSON Schema, supporting schema-driven development and interoperability.
Overview
The Lutaml::Model::Schema::JsonSchema.generate_model_classes method takes a JSON Schema (as a Ruby hash) and generates Ruby class definitions for each schema in the $defs section.
-
Each generated class inherits from
Lutaml::Model::Serializable. -
Attributes are created based on the schema’s properties.
-
The output is a hash mapping definition names to Ruby class code (as strings).
Usage
require 'lutaml/model/schema/json_schema'
require 'json'
# Load your JSON Schema (as a Ruby hash)
schema = JSON.parse(File.read("your_schema.json"))
# Generate Ruby model class definitions as strings
model_classes = Lutaml::Model::Schema::JsonSchema.generate_model_classes(schema)
# model_classes is a hash mapping definition names to Ruby class code
puts model_classes["YourDefinitionName"]Example
Given a JSON Schema with a $defs section:
{
"$defs": {
"Person": {
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" }
}
}
}
}The generated Ruby class will look like:
class Person < Lutaml::Model::Serializable
attribute "name", :string
attribute "age", :integer
end Polymorphic Classes and oneOf Support
Polymorphism allows you to define a common interface for multiple classes, enabling them to be used interchangeably. In JSON Schema, polymorphism is often represented using the oneOf keyword, which specifies that a value must validate against exactly one of the given schemas.
For example:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/PolymorphicModel",
"$defs": {
"PolymorphicModel": {
"type": "object",
"additionalProperties": false,
"properties": {
"shape": {
"type": ["object", "null"],
"oneOf": [
{ "$ref": "#/$defs/Circle" },
{ "$ref": "#/$defs/Square" },
{ "$ref": "#/$defs/Shape" }
]
}
}
},
"Circle": {
"type": "object",
"additionalProperties": false,
"properties": {
"area": { "type": ["number", "null"] },
"radius": { "type": ["number", "null"] }
}
},
"Square": {
"type": "object",
"additionalProperties": false,
"properties": {
"area": { "type": ["number", "null"] },
"side": { "type": ["number", "null"] }
}
},
"Shape": {
"type": "object",
"additionalProperties": false,
"properties": {
"area": { "type": ["number", "null"] }
}
}
}
}Detecting the Parent Class
When using oneOf, you can infer the parent class by identifying the common attributes shared by all referenced schemas. In the example above, all referenced types (Circle, Square, Shape) have an area property, so a Shape parent class can be defined with this attribute. The subclasses (Circle, Square) then add their specific attributes.
This pattern allows Lutaml::Model to:
-
Detect the parent class by finding the intersection of attributes in all
oneOfschemas. -
Generate a base class with the shared attributes.
-
Generate subclasses for each specific schema, inheriting from the base class and adding unique attributes.
Example Ruby Mapping
class Shape < Lutaml::Model::Serializable
attribute :area, :float
end
class Circle < Shape
attribute :radius, :float
end
class Square < Shape
attribute :side, :float
end
class PolymorphicModel < Lutaml::Model::Serializable
attribute :shape, :Shape, polymorphic: [Circle, Square]
endThis approach enables polymorphic deserialization and validation, matching the intent of the JSON