Jakub is a Java EE developer since 2005 and occasionally a project manager, working currently with Iterate AS. He's highly interested in developer productivity (and tools like Maven and AOP/AspectJ), web frameworks, Java portals, testing and performance and works a lot with IBM technologies. A native to Czech Republic, he lives now in Oslo, Norway. Jakub is a DZone MVB and is not an employee of DZone and has posted 155 posts at DZone. You can read more from them at their website. View Full User Profile

EMF: Reading a Model from XML – How to Correctly Declare the Namespace

01.05.2011
| 13332 views |
  • submit to reddit

When you use the Eclipse Modeling Framework (EMF) to read a model instance from an XML file, such as a webservice call message payload, it’s essential for EMF to be able to match the root XML element with the model’s “ePackage” that should be used for (re)constructing the model instance from the XML and this is done by matching the root element’s and the ePackage’s namespaces (as in XSD). So it’s very important to have proper configuration of EMF and proper content of the XML. Since there ale multiple variations of both, there are more ways to get it wrong than right. To learn what I’ve discovered regarding the valid combinations suitable at different situations, read on.

EMF micro-introduction

In Java you can define models by creating classes with attributes (such as Book with a name, an Author, a Library) and connecting them together. You can then create an instance of the model by creating instances of the classes (Book: Clean Code, Author: Uncle Bob, …). The meta-model you use for defining the models is defined by the Java language and consists of classes etc.

EMF is Java API which makes it also possible to define models and create, save, and load their instances in various formats (for example native EMF, UML, XSD). The main differences to Java are that models can be defined not only statically but also dynamically at runtime (and I suppose that the syntax may be richer than Java’s as EMF is more general-purpose), that EMF supports multiple formats for model and model instance input and output, that there is a better reflection-like API, and that you can easily generate editors for your models. The generic meta-model for defining models consists of elements like EClass, EAttribute, EOperation etc. and these elements are grouped in so-called EPackages, each identified uniquely by a namespace URI as you might know it from XSD. Instances of a model are composed of EObjects (and supporting classes like EList). Each of this elements is represented by a Java class.

Nano-glossary: Feature (or structural feature) is, simply said, an attribute.

Assumptions

In this post I’ll suppose that you’ve already defined your model, for example dynamically by reading it from an XSD or statically by generating it based on UML, and that you want to read an XML file representing an instance of the model and transform it into the runtime EMF representation composed of EObjects. The XML may be for example a part of a SOAP message.

For simplicity we may assume that all your model elements are within a single EPackage, under a single namespace, though there is no problem with having them distributed among multiple packages provided that each top-most XML element belonging to another package has proper namespace information so that EMF can match it to the correct EPackage.

Proper EMF configurations and namespace declarations

There are basically three options regarding the XML root element’s namespace declaration:

  1. It declares its namespace with an explicit prefix
  2. It declares its namespace without any prefix, i.e. it declares its namespace as the default one since that on
  3. There is no namespace information at all in the XML

Based on where you’re getting the XML from you may encounter any of these situation and you will need to configure EMF accordingly. If you get EMF wrong then you will get strange results such as

  • null
  • a valid root EObject without any attributes for the nested elements or the failure “unknown feature”
  • an EObject without any EClass information (signifying that EMF failed to match the root XML element with the EPackage that you wanted and used its defaults instead)

1. XML root element in a namespace with a prefix (xmlns:ns=”…”>)

EMF normally expects the input XML to have the target namespace declared with a prefix on the root element and the nested elements to have neither the prefix nor any namespace declaration (and thus these local elements are unqualified in the XSD language). Example:

<myRootElement xmlns:ns="http://example.com/myXml" someAttribute="value">
<myAnotherElement anotherAttribute="value 2"/>
<myRootElement>

This is what you would get if you saved an EMF model using its serialization API (Resource.save(..)).

The root element’s namespace – http://example.com/myXml in this case – must correspond to the namespace URI of a registered EPackage in the EMF package registry (via put(namespaceUri, anEPackageInstance)).

2. XML root element in the default namespace(<elm xmlns=”…”>)

Another, a bit more tricky option is to declare the namespace without any prefix at all:

<myRootElement xmlns="http://example.com/myXml" someAttribute="value">
<myAnotherElement anotherAttribute="value 2"/>
</myRootElement>

Beware that having the root element without any prefix and with a declaration of the default NS as here may cause a strange behavior, namely the root object being loaded but without any content, producing the error “unknown feature” for the 1st nested element.

It’s important to know how namespaces and qualified elements work. In the case #1 (explicit prefix) you have only the root element in a namespace (NS) while the nested elements, having no prefix, have null NS. Therefore if you declare a default NS on the root element you have a totally different situation because now all elements without a prefix – including the nested ones – have a namespace. And from the point of view of XML and thus also EMF the elements “myAnotherElement from the namespace XY” and “myAnotherElement from the null namespace” are completely different and you will thus get the rather confusing error “unknown feature myAnotherElement”.

The solution is to tell EMF that it should suppose that the nested elements are qualified with a namespace name instead of having a null NS. I don’t know how to do that in general but if you load your model from an XSD then you can do it by adding the attribute ‘elementFormDefault=”qualified”‘ to the xsd:schema element to tell it that even local elements (those within a complexType) should be qualified:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/myXml"
elementFormDefault="qualified">
<xsd:complexType name="myRootElement">
<xsd:sequence>
<xsd:element name="myAnotherElement" type="xsd:string" />
...
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

(Setting the elementFormDefault attribute was really the only change I had to do to get EMF working again.)

This tip comes from a forum post and its response. See the XSD spec. – 3.1 Target Namespaces & Unqualified Locals for details.

Notice that qualified, in XML terms, means “Associated with a namespace, either by the use of a declared prefix or via a default namespace declaration”. All “unqualified” elements and attributes are in no (null) namespace. By default, all global elements and attributes are qualified.

Why you may need this
  • Either you receive input in this format
  • Or you receive input without any namespace information and you want to add it there with the least effort (adding just a namespace declaration is easier than also needing to change element’s namespace prefix)

3. No namespace information in the XML (<elm>)

This is the worst case because the input XML doesn’t describe itself sufficiently and you need to know or guess the right EPackage to parse it, it would be certainly better if it had a namespace information. But sometimes you are not able to influence the input yet still want to read it with EMF.

<myRootElement someAttribute="value">
<myAnotherElement anotherAttribute="value 2"/>
</myRootElement>

If the root XML element has no prefix and no associated namespace information then EMF won’t be
able to match it with the corresponding model EPackage and thus won’t be able to load it properly unless you register the target EPackage with the ResourceSet’s/global package registry (also) under the null URI:

for (EPackage ePackage: eCorePackages) {
resourceSet.getPackageRegistry().put(null, ePackage);
// alternatively could call EPackage.Registry.INSTANCE.put(..)
}

This tip comes from an Eclipse forum thread.

General notes on XML saving/loading in EMF

When loading/saving model instance from/to a XML you will likely want to pass some of the following options to the save or load methods:

Map<String, Object> options = new HashMap<String, Object>();
options.put(XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);
// options.put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, Boolean.TRUE);
options.put(XMLResource.OPTION_ENCODING, "UTF-8");
  1. OPTION_EXTENDED_META_DATA – create nested elements rather than attributes; I’m not sure whether it has any effect when loading
  2. OPTION_RECORD_UNKNOWN_FEATURE – when loading and an unknown element is encountered (see #2), do not throw the “unknown feature” exception but simply skip it
  3. OPTION_ENCODING – the encoding of the generated XML, default is ASCII; not sure what it does when loading

Environment

EMF version: 2.2.1

Summary

EMF needs to map the root element of an XML being loaded to a registered EPacakge by matching their namespaces. In the XML, the namespace may be declared with a prefix or it may be the default namespace – in which case EMF must be informed that the nested elements are qualified too – or, finally, there may be no namespace information, in which case you need to register the “default EPackage” to be used under the null namespace URI.

The documentation and JavaDoc of EMF 2.2 is not very good but fortunately you can find most answers in the Eclipse and EclipseZone forums.

In the next post I’ll describe how to create a dynamic EMF model based on XSD schemas and how to use it to load model instances from XML either into the standard EObjects or into EMF SDO implementation’s EDataObjects.

 

From http://theholyjava.wordpress.com/2011/01/03/emf-reading-a-model-from-xml-how-to-correctly-delcare-a-namespace-variants/

Published at DZone with permission of Jakub Holý, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Arun Prakash replied on Fri, 2011/05/20 - 1:00pm

Hi Jakub,

Thanks for the good and well explained post. unfortunately, some of the solutions do not work for me.

 For instance when i create ecore models, i get the following feature version in my model ... 

<code>

<ecore:EPackage xmi:version="2.0"
    xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="something"
    nsURI="http://www.example.com/something" nsPrefix="sm">

</code>

 

I tried using your suggestions (options.put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, Boolean.TRUE);) with both true and false options. It didnt work.

 

The second thing i tried was to load without a default namespace. In my code before i put the EPackage in resourceset registry, i set the nsURI and the  nsPrefix of the EPackage to null.

<code>

                EPackage ePackageToRegister = itEPackegesToRegister.next();

                String nsURI = null;

                if (resourceHasNamespaceSet)
                {
                    nsURI = ePackageToRegister.getNsURI();
                }
                else
                {
                    // ePackageToRegister.setNsPrefix(null);
                    ePackageToRegister.setNsURI(null);
                }

                loadResourceSet.getPackageRegistry().put(nsURI, ePackageToRegister);

              Resource loadResource = loadResourceSet.getResource(URI.createFileURI(resourcePath), true);

</code>

when the  loadOnDemand parameter set to true, i get 

org.eclipse.emf.ecore.xmi.PackageNotFoundException: Package with uri 'null' not found.

when the loadOnDemand is set to false, i get a null loadResource.

 

My load options are 

 

        OPTIONS.put(XMLResource.OPTION_EXTENDED_META_DATA, Boolean.TRUE);
        OPTIONS.put(XMLResource.OPTION_RECORD_UNKNOWN_FEATURE, Boolean.FALSE);

 

I hope you can help with this, because the other non elegant approach would be add the xmlns attribute to the given xml file, which i dont want to do all the time.

 

Thanks for reading ..

 

Regards

Arun

Arun Prakash replied on Sat, 2011/05/21 - 1:57am

update .. before i get the resource, i check packageRegistry of the resourceset for the EPackage registered under null, and it returns the correct EPackage..

 

Object temp = loadResourceSet.getPackageRegistry().get(null); 

Resource loadResource = loadResourceSet.getResource(URI.createFileURI(resourcePath), true); 

 Now i am completey confused why it doesnt work.

 All help is appreciated.

 regards

Arun

Arun Prakash replied on Sat, 2011/05/21 - 5:53am

UPDATE & SOLVED

I finally found out what works ... use createResource and create a resource first and then load it with the options instead of using the getResource of the resourceSet which tries to load the resource with default options. 

        Resource loadResource = loadResourceSet.createResource(URI.createFileURI(resourcePath));

        try
        {
            loadResource.load(options);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

 Regards

Arun

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.