Project

xmi

0.0
The project is in a healthy, maintained state
XMI data model parser
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 0.12.2
~> 13.0
~> 3.11
~> 1.58

Runtime

 Project Readme

XMI

Purpose

This Ruby object mapper is a module designed to convert XMI (XML Metadata Interchange) files into Ruby objects. It can also leverage the nokogiri gem for XML parsing and provides methods to dynamically generate Ruby classes and modules based on the XML structure, particularly those related to EA (Enterprise Architect) MDG (Model Driven Generation) extensions.

Installation

Requirements

  • Ruby 3.0 or later

  • lutaml-model 0.8.0 or later (for Lutaml::Xml::Namespace support)

Installing the gem

Add this line to your application’s Gemfile:

gem "xmi"

And then execute:

$ bundle install

Or install it yourself as:

$ gem install xmi

Usage

Convert XMI file into Ruby objects

To convert XMI file into Ruby objects, run:

xml = "path/to/your/file.xmi"
xml_content = File.read(xml)
xmi_root_model = Xmi::Sparx::Root.parse_xml(xml_content)

This method takes the path to an XMI file and generate the Ruby objects.

Loading Extensions and Generate Ruby Classes and Modules Dynamically

To load an extension from an XML file, use the load_extension method:

Xmi::EaRoot.load_extension("path/to/your/extension.xml")

xml = "path/to/your/file.xmi"
xml_content = File.read(xml)
xmi_root_model = Xmi::Sparx::Root.parse_xml(xml_content)

Xmi::EaRoot.load_extension takes the path to an XML file and generate the Ruby classes and modules defined in XML file dynamically. Then, you can generate Ruby objects by Xmi::Sparx::Root.parse_xml.

Output Classes and Modules Generated from Extension into Ruby Files

You can also generate Ruby files directly from the XMI content:

Xmi::EaRoot.load_extension(
  input_xml_path: 'path/to/your/custom_extension.xml',
  module_name: 'CustomModule'
)
Xmi::EaRoot.output_rb_file('path/to/output_file.rb')

This approach allows you to save the dynamically generated Ruby code to a file for further use.

Create Extension XML File

If you would like to create an extension, which allows to be loaded later, you can create an extension XML file.

For example, you would like to create an extension:

  • A Module named Mymodule.

  • A class named Klass under this module.

  • The class Klass has two attributes: base_apply_attribute and tag.

First, you create an extension XML file mymodule.xml as the following:

<?xml version='1.0' encoding='UTF-8'?>
<MYMODULE version="1.0">
  <UMLProfiles>
    <UMLProfile profiletype="uml2">
      <Documentation name="MYMODULE" version="1.0"
      URI="http://www.test.com/profiles/MYMODULE/1.0" />
      <Content>
        <Stereotypes>
          <Stereotype name="klass">
            <AppliesTo>
              <Apply type="ApplyAttribute" />
            </AppliesTo>
            <TaggedValues>
              <Tag name="tag" type="String" description="" unit="" values="" default=""/>
            </TaggedValues>
          </Stereotype>
        </Stereotypes>
        <TaggedValueTypes />
        <ViewDefinitions />
        <Metamodel />
      </Content>
    </UMLProfile>
  </UMLProfiles>
</MYMODULE>
  • The attribute name in the Documentation defines the module name.

  • The attribute URI in the Documentation defines the namespace.

  • The attribute version in the Documentation defines the version.

  • The attribute name in the Stereotype defines the class name.

  • The attribute type in the Apply defines the attribute name with prefix base_.

  • The attribute name in the Tag defines the attribute name.

To load the extension, you can use the following code:

mymodule_xml = "mymodule.xml"
Xmi::EaRoot.load_extension(mymodule_xml)

After you load the extension, a class Klass have been generated in the module Mymodule.

module Xmi
  class EaRoot
    module Mymodule
      class Klass < Lutaml::Model::Serializable
        attribute :base_apply_attribute, :string
        attribute :tag, :string

        xml do
          root "import"

          map_attribute "base_ApplyAttribute", to: :base_apply_attribute
          map_attribute "tag", to: :tag
        end
      end
    end
  end
end

Namespace Architecture

General

The XMI library normalizes all input namespace versions to a canonical version (20131001) before parsing. This allows the library to handle XMI files with different namespace versions (2011, 2013, 2016) using a single set of model classes.

Namespace Normalization

The normalization is performed by [SparxRoot.replace_xmlns](lib/xmi/sparx.rb:1158) which rewrites namespace URIs in the input XML:

Example 1. Example of namespace normalization
<!-- Input XMI with various namespace versions -->
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20110701"
         xmlns:uml="http://www.omg.org/spec/UML/20161101">
  <!-- content -->
</xmi:XMI>

<!-- After normalization, becomes -->
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20131001"
         xmlns:uml="http://www.omg.org/spec/UML/20131001">
  <!-- content -->
</xmi:XMI>

Namespace Classes

Namespace classes are defined in [lib/xmi/namespace/omg.rb](lib/xmi/namespace/omg.rb:1):

  • Version-specific classes: Xmi20110701, Uml20131001, etc.

  • Version-agnostic aliases: Xmi, Uml, UmlDi, UmlDc

The aliases inherit from the 20131001 versions (the normalized version):

Example 2. Alias class definitions
class Xmi < Lutaml::Xml::Namespace
  uri "http://www.omg.org/spec/XMI/20131001"
  prefix_default "xmi"
end

class Uml < Lutaml::Xml::Namespace
  uri "http://www.omg.org/spec/UML/20131001"
  prefix_default "uml"
end

This allows models to use clean namespace references without worrying about specific versions:

Example 3. Using namespace aliases in model definitions
class MyModel < Lutaml::Model::Serializable
  xml do
    root "Model"
    namespace ::Xmi::Namespace::Omg::Uml
    namespace_scope [
      ::Xmi::Namespace::Omg::Xmi,
      ::Xmi::Namespace::Omg::Uml,
      ::Xmi::Namespace::Omg::UmlDi,
    ]
  end
end

Namespace-Qualified Mapping Declaration

All element and attribute mappings explicitly declare their namespace to ensure proper XML parsing and serialization. This is required even when types have xml_namespace declared.

Example 4. Element mapping with namespace
xml do
  root "Model"
  namespace ::Xmi::Namespace::Omg::Uml

  # Element mapping with explicit namespace
  map_element "packagedElement", to: :packaged_element,
              namespace: "http://www.omg.org/spec/UML/20131001",
              prefix: "uml"
end

Attribute Namespace Handling

For attributes with namespace prefixes in XML (e.g., <uml:Model xmi:id="…​">), both namespace: and prefix: parameters are required in map_attribute, even when the attribute type has xml_namespace declared.

Example 5. Attribute mapping with namespace prefix
xml do
  root "Model"
  namespace ::Xmi::Namespace::Omg::Uml

  # XMI-typed attribute with explicit namespace declaration
  map_attribute "id", to: :id,
                namespace: "http://www.omg.org/spec/XMI/20131001",
                prefix: "xmi"

  # Regular attribute without namespace prefix
  map_attribute "name", to: :name
end

Without explicit namespace declarations in map_attribute, attributes with namespace prefixes will parse as nil.

Sparx Systems Namespaces

Sparx-specific namespaces are defined in [lib/xmi/namespace/sparx.rb](lib/xmi/namespace/sparx.rb:1):

  • SysPhS - System Physical Systems profile

  • GML - Geography Markup Language profile

  • EaUml - Enterprise Architect UML extensions

  • CustomProfile - Custom profile support

  • CityGML - City Geography Markup Language

Example 6. Using Sparx namespaces
xml do
  root "ModelicaParameter"
  namespace ::Xmi::Namespace::Sparx::SysPhS

  map_attribute "base_Package", to: :base_package
  map_attribute "name", to: :name
end

Extension Namespaces

Dynamically loaded extensions (via [EaRoot.load_extension](lib/xmi/ea_root.rb:54)) also use namespace-qualified mappings:

Example 7. Extension with namespace mapping
map_element "ApplicationSchema", to: :gml_application_schema,
            namespace: "http://www.sparxsystems.com/profiles/GML/1.0",
            prefix: "GML"

Limitation

This module is designed to work with XMI files generated by Enterprise Architect. It may not work with other XMI files.

Enterprise Architect Quirks

Enterprise Architect (EA) generates XMI files with several non-standard behaviors that this library handles through preprocessing.

OMG Namespace Version Normalization

OMG publishes XMI and UML specifications with dated namespace URIs (e.g., http://www.omg.org/spec/XMI/20110701, 20131001, 20161101). While these represent different specification versions, the core XMI structure is compatible across versions.

This library normalizes all OMG namespace versions to the canonical 20131001 version during parsing, allowing a single set of model classes to handle all versions.

<!-- Input with mixed versions -->
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20110701"
         xmlns:uml="http://www.omg.org/spec/UML/20161101">

<!-- After normalization -->
<xmi:XMI xmlns:xmi="http://www.omg.org/spec/XMI/20131001"
         xmlns:uml="http://www.omg.org/spec/UML/20131001">

EA’s Misuse of the xmlns Attribute

In standard XML, the xmlns attribute has special meaning—it declares the default namespace for an element and its descendants. The XML specification reserves this attribute name for namespace declarations.

However, Enterprise Architect incorrectly uses xmlns as a regular data attribute on certain stereotype elements, storing arbitrary URI values that have nothing to do with XML namespace declarations. This is a violation of XML conventions.

This quirk has been observed on: - GML:ApplicationSchema (Geography Markup Language profile) - CityGML:ApplicationSchema (City Geography Markup Language profile)

<!-- EA-generated XMI with xmlns as a data attribute -->
<GML:ApplicationSchema xmlns="http://some-uri-value"
                       targetNamespace="http://example.org/ns">

This creates parsing conflicts because XML libraries treat xmlns as a reserved keyword. This library works around the issue by renaming the xmlns attribute to altered_xmlns before parsing:

<!-- After preprocessing -->
<GML:ApplicationSchema altered_xmlns="http://some-uri-value"
                       targetNamespace="http://example.org/ns">

The corresponding model classes (e.g., Xmi::Sparx::Gml::ApplicationSchema) define an altered_xmlns attribute to receive this value.

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to [rubygems.org](https://rubygems.org).

Contributing

Bug reports and pull requests are welcome on GitHub at USERNAME/xmi. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](USERNAME/xmi/blob/master/CODE_OF_CONDUCT.md).

Code of Conduct

Everyone interacting in the Xmi project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](USERNAME/xmi/blob/master/CODE_OF_CONDUCT.md).