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.
0 stars 0 forks source link

Generate model from model description. #9

Open andrew2net opened 1 week ago

andrew2net commented 1 week ago

I'm trying to reimplement the Relaton data model with Shale. Shale is limited, especially in inheritance, multi-inheritance, recurrence, cloise, and repeated groups. I found some workarounds but the code becomes more complicated than if we don't use Shale. Also, it can parse and produce XML that is not valid against grammar. I started thinking that we could use the major Shale idea and spend our efforts on implementing a gem that reads RNG grammar files, creates Ruby objects for each defined element in the RNG files, and then parses and renders various serialization formats. So once the grammar is updated, the Relaton model will be updated immediately without any code changes. With the Shale, we will have pain after each data model update. I believe that we need to implement a Domain Specific Language (DSL) that allows us to describe data models in a way we do in the grammar. For example the definition in the grammar:

  ...
  <define name="FullNameType">
    <optional>
      <ref name="name_abbreviation"/>
    </optional>
    <choice>
      <group>
        <zeroOrMore>
          <ref name="prefix"/>
        </zeroOrMore>
        <zeroOrMore>
          <ref name="forename"/>
        </zeroOrMore>
        <optional>
          <ref name="formatted-initials"/>
        </optional>
        <ref name="surname"/>
        <zeroOrMore>
          <ref name="addition"/>
        </zeroOrMore>
      </group>
      <ref name="completeName"/>
    </choice>
    <zeroOrMore>
      <ref name="biblionote"/>
    </zeroOrMore>
    <zeroOrMore>
      <ref name="variantname"/>
    </zeroOrMore>
  </define>
  ...

could be implemented using DSL:

class FullNameType < Lutaml::Model::Serializable
  attribute :abbreviation, NameAbbreviation, optional: true

  choice do
    group do
      attribute :prefix, Prefix, collection: :zero_or_more
      attribute :forename, Forename, collection: :zero_or_more
      attribute :formatted-initials, FormattedInitials, optional: true
      attribute :surname, Surname
      attribute :addition, Addition, collection: :zero_or_more
    end

    attribute :completeName, CompleteName
  end

  attribute :biblionote, BiblionNote, collection: :zero_or_more
  attribute :variantname, VariantName, collection: :zero_or_more
  ...
end

Next, we can implement a model creator like:

model = Lutaml::Model.open("biblio.rng")

and use the model to parse and serialize objects

bibitem = mode.from_xml File.read("bibitem.xml")
json = model.to_json bibitem
ronaldtse commented 1 week ago

Agree @andrew2net .

For zeroAndMore, oneAndMore, we should just use cardinality such as min, max to set.

e.g.

class FullNameType < Lutaml::Model::Serializable
  attribute :abbreviation, NameAbbreviation, optional: true

  choice do
    group do
      attribute :prefix, Prefix, collection: [0, :inf]
      attribute :forename, Forename, collection: [0, :inf]
      attribute :formatted-initials, FormattedInitials, optional: true
      attribute :surname, Surname
      attribute :addition, Addition, collection: [0, :inf]
    end

    attribute :completeName, CompleteName
  end

  attribute :biblionote, BiblionNote, collection: [0, :inf]
  attribute :variantname, VariantName, collection: [0, :inf]
  ...
end
andrew2net commented 1 week ago

@ronaldtse Yeah, this is just an idea. It requires working out a lot of details. For example, we may need to map our Relaton classes to data model attributes:

bibitem = mode.from_xml(File.read("bibitem.xml")) do
  model :btitle, Relaton::Bib::Title
  ...
end