lutaml / lutaml-model

LutaML Model is the Ruby data modeler part of the LutaML data modeling suite. It supports creating serialization object models (XML, YAML, JSON, TOML) and mappings to and from them.
Other
2 stars 0 forks source link

Implement XSD parsing and lutaml-model generation functionality #37

Open ronaldtse opened 2 months ago

ronaldtse commented 2 months ago

This functionality is called "compile" in Shale, where it generates Shale classes in Ruby source code for a given XSD.

We can do the following:

ronaldtse commented 2 weeks ago

This is urgently needed for:

HassanAkbar commented 2 weeks ago

This is urgently needed for:

@ronaldtse I'm working on it

ronaldtse commented 2 weeks ago

@HassanAkbar and there is an XSD Schema for XSD itself:

We can use Lutaml::Model to parse XSD schemas.

ronaldtse commented 2 weeks ago

Thanks @HassanAkbar, this is how Shale works:

Given an (one or more) XSD schema:

require 'shale'
require 'shale/schema'

schema1 = <<~SCHEMA
<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:bar="http://bar.com"
  elementFormDefault="qualified"
>
  <xs:import namespace="http://bar.com" />

  <xs:element name="Person" type="Person" />

  <xs:complexType name="Person">
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element ref="bar:Address" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>
SCHEMA

schema2 = <<~SCHEMA
<xs:schema
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:bar="http://bar.com"
  targetNamespace="http://bar.com"
  elementFormDefault="qualified"
>
  <xs:element name="Address" type="bar:Address" />

  <xs:complexType name="Address">
    <xs:sequence>
      <xs:element name="Street" type="xs:string" />
      <xs:element name="City" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>
SCHEMA

mapping = {
  nil => 'Api::Foo', # no namespace
  'http://bar.com' => 'Api::Bar',
}

result = Shale::Schema.from_xml(
  [schema1, schema2],
  namespace_mapping: mapping
)

result.each do |name, model|
  puts "# ----- #{name}.rb -----"
  puts model
end

The output is:

# ----- api/bar/address.rb -----
require 'shale'
module Api
  module Bar
    class Address < Shale::Mapper
      attribute :street, Shale::Type::String
      attribute :city, Shale::Type::String

      xml do
        root 'Address'
        namespace 'http://bar.com', 'bar'
        map_element 'Street', to: :street
        map_element 'City', to: :city
      end
    end
  end
end

# ----- api/foo/person.rb -----
require 'shale'
require_relative '../bar/address'
module Api
  module Foo
    class Person < Shale::Mapper
      attribute :name, Shale::Type::String
      attribute :address, Api::Bar::Address

      xml do
        root 'Person'
        map_element 'Name', to: :name
        map_element 'Address', to: :address, prefix: 'bar', namespace: 'http://bar.com'
      end
    end
  end
end

We don't need to print it out, instead, we want to create those files directly with code.