Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Authoring XML Schemas for use with EMF"

(Simple Type to DataType or Enum)
m (Simple Type to DataType or Enum)
Line 104: Line 104:
  
 
===Simple Type to DataType or Enum===
 
===Simple Type to DataType or Enum===
 +
 +
An EDataType will be created if there are no enumerations:
  
 
{{AnnotatedSourceCode}}
 
{{AnnotatedSourceCode}}
 
|-
 
{{CodeCell|1}}...
 
  
 
{{AnnotatedLine}}
 
{{AnnotatedLine}}
Line 146: Line 145:
 
|}
 
|}
  
Enum Case:
+
If the restriction includes enumerations an EEnum is created:
  
 
{{AnnotatedSourceCode}}
 
{{AnnotatedSourceCode}}

Revision as of 14:18, 18 June 2007

This article focuses on authoring and using XML Schemas for the purpose of generating an EMF model.

Introduction

An EMF Model (files of type *.genmodel) is your hub for importing/exporting other metamodel formats, linking to other EMF models, and generating implementation code. Out of the box, EMF can import XML Schema Definitions (XSDs) into Ecore Models (files of type *.ecore). Similarly, EMF can export Ecore Models to XSDs. But without an understanding of the differences between these two metamodels, and without knowing how to control the mapping process, the results will often be less than desired.

Luckily, both Ecore models and XML Schemas have support for annotations. Annotations can be almost anything, but for this discussion, annotations serve two purposes:

  • Provide additional information when transforming one metamodel to another
  • Provide additional information on how to serialize/deserialize your model objects

EMF supports transformations in either direction (to or from XSDs). Currently, if you start with an Ecore model, round-tripping to XSD and back to Ecore should be lossless. However, Ecore supports some structures that have no XSD equivalent, such as multiple inheritance. Information will be lost in such cases. Also, if you start with an XSD, not all of the information is captured in the Ecore model. Therefore, "round-tripping" XSD->Ecore->XSD is not a recommended practice as of the Europa release. Information might get lost, and almost certainly will get shuffled around.

This article will focus only on the practice of authoring (and annotating) XSDs and importing them into Ecore models. However, it can be a useful learning tool to model certain structures first in Ecore, and then exporting to see the annotated XSD equivalent.

Reasons to use XSDs with EMF

Here are a few reasons why XSDs are used with EMF:
  • The XSD already exists
  • You must ship an XSD
  • Using an XSD is one way to customize how EMF models are persisted

Important Differences between XSDs and Ecore Models

Multiple Inheritance

Global Elements and Attributes

The Transformation Process

The overall process looks like:
  1. An EMF Model is created and associated with one or more XSDs
  2. When the Wizard finishes, the conversion process creates *.ecore files
  3. At this point, EMF will not read your XSDs again. All information needed by EMF has been tucked away somewhere else.
  4. If you modify your schemas, the transformation can be performed again from the .genmodel Editor. Affected Ecore models are completely replaced.

Which Parts of an XML Schema are Processed?

Without being told otherwise, EMF will process your Schema, all of its globally declared Elements, and all Types contained therein. Other content (e.g. globally-declared Attributes, AttributeGroups, and Groups) is ignored by EMF. These entries are just macros that Elements and Types can reference, effectively in-lining a copy. If two Types reference the same global Attribute, AttributeGroup, or Element, EMF sees them as two distinct features which happen to have identical information.

What is the Output of the Process?

The Process generates, updates, or replaces the following:
  1. Ecore Models
    Schemas become Packages; Simple/Complex Types become EClasses, EDataTypes, and EEnums; Elements and Attributes become EStructuralFeatures
    Annotations in your Ecore Models
    Ecore annotations are added in several places to capture the way your model should be serialized/deserialized. These annotations are what enable EMF to read and write XML documents that don't conform to EMF's default serialization format.
    A Helper EClass named "DocumentRoot"
    In certain cases, EMF must create a DocumentRoot class to store additional information.
    Helper EStructuralFeatures, such as feature maps with the suffix "group"
    To handle XSD features like substitution groups, the substitution rule to be used when writing must be known. To handle these cases, your Class' EReferences don't hold onto the actual content, but are instead derived from these "group" references which contains keys in addition to the actual referenced object.
    Your EMF Model
    References to the schemas you imported are kept so that you can invoke a reload later. Generations options are configured the first time with different defaults, under the assumption that you'll be reading and writing to XML files.

Mapping XSD to Ecore

Mapping Schema to EPackage

Each XML Schema is mapped to an EPackage.

Mapping Simple and Complex Type to Classes, DataTypes, and Enums

Types in a Schema are mapped to Classifiers in Ecore (one of EClass, EDataType, EEnum). A Complex Type always results in a Class, but a Simple Type can be mapped to either a DataType or an Enum, depending on the presence of restricted values.

Simple Type to DataType or Enum

An EDataType will be created if there are no enumerations:


<xs:simpleType EDataType
[Default Mappings]
name="RGB" -> Name (unless overridden)
[Override Default Mapping]
ecore:ignore="true" Ignores this type
ecore:name="overrideName" Determines the name
ecore:instanceClass="package.JavaType" Determines the fully qualified java Class or Interface
... >
<xs:restriction base="xs:string"/> Determines the Instance Class Name in many cases
</xs:simpleType>

If the restriction includes enumerations an EEnum is created:


<xs:simpleType EEnum
name="Align" -> Name (unless overridden)
[Overriding Default Mappings]
ecore:ignore="(true)" Ignores this Simple Type
ecore:name="$name|override" Overrides the Name, normally determined by name
...
>
<xs:restriction>
<xs:enumeration EEnum Literal (for each xs:enumeration)
value="Left"
ecore:value="auto|int" Overrides the Literal's Value
ecore:name="auto|override" Overrides the Literal's Name, normally determined by value
...
/>
<xs:enumeration value="Center"/>
<xs:enumeration value="Right"/>
</xs:restriction>
</xs:simpleType>
...

Mapping Attributes and Elements to EAttributes and EReferences

Attribute to Attribute

Simple attributes in your schema (attributes that are not references) are mapped to EAttributes. The upper bound on the EAttribute of this type must be 1.

In a document the attribute would appear as:


<Event message="Something happened"
...
</Event>


XML Schema Description
<xsd:complexType name="Event">
<xsd:sequence>
<xsd:attribute EAttribute
[Default Mappings]
name="message" -> Name (unless overridden)
type="xsd:string" -> EType (unless overridden?)
default="hello world" -> Default Value Literal
use="optional|required|prohibited" "Required" raises the Lower Bound of the EAttribute to be 1
Note that Unsettable is true by default
[Overriding default mappings]
ecore:ignore="<t>false</u>|true" Instructs EMF not to perform any mapping
ecore:name="overrideName" Override's the name
[Ecore-only information]
ecore:unsettable="false|true" True is assumed
ecore:suppressedGetVisibility="false|true" Hides the generated interface's accessor method
ecore:suppressedSetVisibility="false|true" Hides the generated interface's set method
ecore:suppressedIsSetVisibility="false|true" Hides the generated interface's is set method
ecore:suppressedUnsetVisibility="false|true" Hides the generated interface's unset method
ecore:transient="false|true" False is assumed
ecore:volatile="false|true" False is assumed
ecore:derived="false|true" False is assumed
...
/>
</xsd:sequence>
</xsd:complexType>

Element to Attribute

Elements which are Simple Types must be mapped to EAttributes. Exceptions are strings that are references to other Objects. Elements may occur multiple times, and they are the only way to represent multi-valued attributes.

Example Document:

...
<Book>
<Author>Randy Hudson</Author>
<Author>Author #2</Author>
...
</Book>
...


XML Schema Description
<xsd:complexType name="Book">
<xsd:sequence>
<xsd:element EAttribute
[Default Mappings]
name="author" The EAttribute's name will default to this value
type="string" -> EType (must be a Simple Type)
default="John Doe"/> -> Default Value Literal
minOccurs="0|1|n" -> Lower Bound
maxOccurs="0|1|n|unbounded" -> Upper Bound
nillable="false|true" Setting to true allows null to occur as a value. Note that java primitives like int are not Objects
[Overriding default mappings]
ecore:ignore="<t>false</u>|true" Instructs EMF not to perform any mapping
ecore:name="overrideName" Override's the name
ecore:lowerBound = "$minOccurs$|n" Overrides Lower Bound, normally determined by minOccurs
ecore:upperBound = "$maxOccurs$|n" Overrides Upper Bound, normally determined by maxOccurs
[Ecore Specific]
ecore:unsettable="false|true" True is assumed
ecore:suppressedGetVisibility="false|true" Hides the generated interface's accessor method
ecore:suppressedSetVisibility="false|true" Hides the generated interface's set method
ecore:suppressedIsSetVisibility="false|true" Hides the generated interface's is set method
ecore:suppressedUnsetVisibility="false|true" Hides the generated interface's unset method
ecore:transient="false|true" False is assumed
ecore:volatile="false|true" False is assumed
ecore:derived="false|true" False is assumed
</xsd:sequence>
</xsd:complexType>

Element to Contained EReference

Elements with Complex Types can only be mapped to EReferences. Since Elements represent nested data in a document, it's no surprise that the EReference must be contained. EReferences which are not contained are saved as URIs to some other location, which means that a Simple Type is sufficient.

In a document, contained content could appear as:


<List>
<Item text="This is item 1"/>
<Item text="This is item 2"/>
</List>


XML Schema Description
<xsd:complexType name="Container">
<xsd:sequence>
<xsd:element EReference
name="child" ->Name
type="tns:Child" ->EType (Must be a Complex Type)
minOccurs="0|1|n" -> Lower Bound
maxOccurs="0|1|n|unbounded" -> Upper Bound
[Overriding default mappings]
ecore:ignore="<t>false</u>|true" Instructs EMF not to perform any mapping
ecore:name="overrideName" Overrides Name, normally determined by name
ecore:opposite="none|opposite" Indicates an Inverse EReference using the name of the xsd:element or xsd:attribute declared at the Type
ecore:lowerBound = "$minOccurs$|n" Overrides Lower Bound, normally determined by minOccurs
ecore:upperBound = "$maxOccurs$|n" Overrides Upper Bound, normally determined by maxOccurs
[Ecore Specific]
ecore:ordered="false|true" True is assumed
ecore:changeable="false|true" True is assumed
...
/>
</xsd:sequence>
</xsd:complexType>

Element to EReference

Attribute to EReference

Back to the top