Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » EMF » retain order of XML elements when using EMF
retain order of XML elements when using EMF [message #1806706] Tue, 14 May 2019 08:59 Go to next message
Zakir Meer is currently offline Zakir MeerFriend
Messages: 50
Registered: February 2016
Member
Hello Experts,

I am using EMF to parse my XML files (generated EMF code from XML schema).

While fetching the contents from the EMF model built by parsing the XML file -> I am not able to retain the order in which elements are defined in the XML file (especially the simple type in XML -> as they are changed to EAttributes)

index.php/fa/35557/0/

I am using eObject.eContents() API, which is only giving the complex type objects (from XML) as EMF converts xml complex types as EClass and corresponding EObjects are available. Where as in case of simple type, they are created as EAttributes

Quote:
I am looking for a API which gives me a list (in the same order) of elements which are defined in the xml file, irrespective of EMF converting them as EAttributes or EObjects


I will be glad if someone can give hints regarding this topic.

I have attached a zip file of the example which I have built

Re: retain order of XML elements when using EMF [message #1806712 is a reply to message #1806706] Tue, 14 May 2019 10:18 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 7655
Registered: July 2009
Senior Member
Hi

You should find that Ecore preserves the order of ordered element/attribute values so that you can process your read model correctly.

XML is a very inferior model representation, made more acceptable by use of XMI to support cross-references and XSD to impose some integrity on the arbitrary eXtension permitted by the XML philosophy. Since XSD was a retrofit, it has many complexities that support crazy irrelevant legacy XML practices. EMF has support for exploiting XSD so you should be able to impose a correct order when you write your Ecore model out again, but it can be hard and not usually worth the effort to maintain perfect legacy compatibility.

I suspect that you are just seeing something internally that surprises you. Once you get used to Ecore, you should find that what's in memory respects what you need.

Regards

Ed Willink
Re: retain order of XML elements when using EMF [message #1806717 is a reply to message #1806712] Tue, 14 May 2019 11:15 Go to previous messageGo to next message
Zakir Meer is currently offline Zakir MeerFriend
Messages: 50
Registered: February 2016
Member
Thanks for your response Ed Willink,

Here I want to make use of Dynamic EMF capability and fetch the contents in the same order in which they are defined in XML (want to use the EMF model just like a DOM node in few cases)

In below case I want to access : child1, child2, ... etc.,in the same order they are present

eObject.eContents() API just gives a list of all complex node elements (which are treated as EObjects). using eObject.eContents().get(0) , eObject.eContents().get(1), eObject.eContents().get(2) I can get the corresponding complex node elements


<shiporder>
  <orderperson>John Smith</orderperson>
  <shipto>
    <name>Ola Nordmann</name>
    <address>Langgt 23</address>
    <city>4000 Stavanger</city>
    <country>Norway</country>
  </shipto>
  <orderperson>John Smith</orderperson>
  <item>
    <title>Empire Burlesque</title>
    <note>Special Edition</note>
    <quantity>1</quantity>
    <price>10.90</price>
  </item>
    <orderperson>John Smith</orderperson>
 </shiporder> 


In my example here, orderperson tag contents are converted as attribute (and is not present in the eObject.eContents() API) and in this case I do not find an API to get the element based on the order in which it is specified in the XML
Re: retain order of XML elements when using EMF [message #1806718 is a reply to message #1806712] Tue, 14 May 2019 11:51 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33113
Registered: July 2009
Senior Member
There is no direct API that will given you that order. Any XML element or XML attribute can map either to an EAttribute or an EReference, so the order in eContents will only show you the EReferences.

You could, for your simple model use this type of approach to visit the XML attributes and the XML elements separately and in XML order:
					for (EStructuralFeature eStructuralFeature : shipOrderEObject.eClass()
							.getEAllStructuralFeatures()) {
						if (ExtendedMetaData.INSTANCE
								.getFeatureKind(eStructuralFeature) == ExtendedMetaData.ATTRIBUTE_FEATURE) {
							Object value = shipOrderEObject.eGet(eStructuralFeature);
							System.out.println(ExtendedMetaData.INSTANCE.getName(eStructuralFeature) + "='" + value + "'");
						}
					}

					for (EStructuralFeature eStructuralFeature : shipOrderEObject.eClass()
							.getEAllStructuralFeatures()) {
						if (ExtendedMetaData.INSTANCE
								.getFeatureKind(eStructuralFeature) == ExtendedMetaData.ELEMENT_FEATURE) {
							Object value = shipOrderEObject.eGet(eStructuralFeature);
							System.out.println("<" + ExtendedMetaData.INSTANCE.getName(eStructuralFeature) + ">=" + value);
						}
					}


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: retain order of XML elements when using EMF [message #1806732 is a reply to message #1806718] Tue, 14 May 2019 13:22 Go to previous messageGo to next message
Ed Willink is currently offline Ed WillinkFriend
Messages: 7655
Registered: July 2009
Senior Member
Hi

Your example is a classic case of crazy XML. Clearly for you

<orderperson>John Smith</orderperson>
  <item>
    <title>Empire Burlesque</title>
    <note>Special Edition</note>
    <quantity>1</quantity>
    <price>10.90</price>
  </item>


is really

<order person="John Smith">
    <item>
      <title>Empire Burlesque</title>
      <note>Special Edition</note>
      <quantity>1</quantity>
      <price>10.90</price>
    </item>
</order>


with order a true object rather than an accidental interspersal.

Your best choice is to use a proper Ecore-defined model so that order is a true element.

If you must stick with the perverse XML layout, you may experiment with the XSD capabilities and perhaps get what you want. I cannot give any positive advice since this is an area that I am aware of considerable power, but I have steadfastly avoided such challenges.

If EMF's XSD support doesn't work for you, you may use the XMLResourceFactoryImpl load which will allow you to see exactly what is loaded and convert it to a more respectable form internally.

Regards

Ed Willink
Re: retain order of XML elements when using EMF [message #1806742 is a reply to message #1806718] Tue, 14 May 2019 15:03 Go to previous messageGo to next message
Zakir Meer is currently offline Zakir MeerFriend
Messages: 50
Registered: February 2016
Member
Thanks for your response Ed Merks !!

I found that code snippet shared by you doesn't satisfy the use-case of retrieval of elements from the XML file in the same order they are defined (here I mean the mixed elements e.g. orderperson, shipto, item ...nodes)

As the code-snippet is iterating on EStructuralFeatures of a EClass, this will not be able to fetch all the XML elements in the same order they are defined as in XML there is a possibility to mix the tags (if in XML Schema a Choice is specified and max-occurs unbounded)

Please find the below screen shot:

index.php/fa/35568/0/

Re: retain order of XML elements when using EMF [message #1806766 is a reply to message #1806742] Wed, 15 May 2019 03:38 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33113
Registered: July 2009
Senior Member
Normally an XML Schema's model group in a complex type's complex content will map to an Ecore model's a feature-map-based attribute (like the one I see in your DocumentRoot called "mixed") and your sample XML suggests that there ought to be some repeating choice involved in the XML Schema representation of ShiporderType. But I don't see an indication of that in your ShiporderType. And in fact it's quite clear in your model that a ShiporderType can have exactly one orderperson element (that must appear first, though the deserializer actually doesn't care about mixed order). So what you show in your XML is simply not valid (sensible) according to your model because the model can only capture information about a single orderperson's value. As such, you seem to be somewhat confused in your expectations of what is sensible XML versus what is actually representable in your model. Looking at your XML Schema
<xs:element name="shiporder">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="orderperson" type="xs:string"/>
      <xs:element name="shipto">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="address" type="xs:string"/>
            <xs:element name="city" type="xs:string"/>
            <xs:element name="country" type="xs:string"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="item" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="title" type="xs:string"/>
            <xs:element name="note" type="xs:string" minOccurs="0"/>
            <xs:element name="quantity" type="xs:positiveInteger"/>
            <xs:element name="price" type="xs:decimal"/>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
    <xs:attribute name="orderid" type="xs:string" use="required"/>
  </xs:complexType>
</xs:element>
</xs:schema> 
It's again quite clear that an orderperson element can appear only once, which to me makes a lot more sense than the confusing (non-conforming) XML you are showing. The deserializer will happily deserialize this, but the only information left in your model will be the value of the last orderperson element in the XML.


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: retain order of XML elements when using EMF [message #1806790 is a reply to message #1806766] Wed, 15 May 2019 08:18 Go to previous messageGo to next message
Zakir Meer is currently offline Zakir MeerFriend
Messages: 50
Registered: February 2016
Member
Hi Ed,

sorry for the confusion.

Based on your previous reply, I tried creating a more complex example by modifying the xml schema and generated corresponding EMF model from it. But somehow I missed to attach it while I was replying to your message.

Please find the XML schema (latest) which allows to mix orderperson tag

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="shiporder">
  <xs:complexType>
            <xs:choice maxOccurs="unbounded" minOccurs="0">
      <xs:element name="orderperson" type="xs:string" maxOccurs="unbounded" />
      <xs:element name="shipto">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="name" type="xs:string" />
            <xs:element name="address" type="xs:string" />
            <xs:element name="city" type="xs:string" />
            <xs:element name="country" type="xs:string" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
      <xs:element name="item" maxOccurs="unbounded">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="title" type="xs:string" />
            <xs:element name="note" type="xs:string" minOccurs="0" />
            <xs:element name="quantity" type="xs:positiveInteger" />
            <xs:element name="price" type="xs:decimal" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:choice>
    <xs:attribute name="orderid" type="xs:string" use="required"/>
  </xs:complexType>
</xs:element>
</xs:schema> 


In between I used JDOM2 to check my expectations and this is working as expected (i.e. order of the retrieval of the mixed elements is same as present in the xml)

index.php/fa/35575/0/

In case of EMF, still I do not get the right results, as the retrieval is based on EAllStructuralFeatures which will give only unique list and can not handle the scenarios if different XML elements are mixed.
shipOrderEObject.eClass()
								.getEAllStructuralFeatures())

index.php/fa/35573/0/

Below is the result of execution:

<orderperson>=[John Smith, smith, john]
<shipto>=[Sample.impl.ShiptoTypeImpl@2f9f7dcf (name: Ola Nordmann, address: Langgt 23, city: 4000 Stavanger, country: Norway)]
<item>=[Sample.impl.ItemTypeImpl@747ddf94 (title: Empire Burlesque, note: Special Edition, quantity: 1, price: 10.90), Sample.impl.ItemTypeImpl@1bd4fdd (title: Hide your heart, note: null, quantity: 1, price: 9.90)]
orderid='889923'


FYI : I have attached the source code containing test case of EMF loading and JDOM2 loading.

p.s: I am a big fan of EMF and would prefer to use EMF for XML handling.. rather than using other XML dom technologies !! I guess there is a gap in the EMF API w.r..t fetching XML contents (in the same order they are defined in the file ) and due to this reason I am trying to clarify. I hope you do not take me in the other way :P

[Updated on: Wed, 15 May 2019 09:13]

Report message to a moderator

Re: retain order of XML elements when using EMF [message #1806799 is a reply to message #1806790] Wed, 15 May 2019 10:29 Go to previous messageGo to next message
Ed Merks is currently offline Ed MerksFriend
Messages: 33113
Registered: July 2009
Senior Member
You'll likely find this article useful:

https://www.theserverside.com/news/1364302/Binding-XML-to-Java

It demonstrates that EMF can handle any old XML, even without a schema just like is possible with DOM.

Here is some scaffolding to give you the general ideal of how you can handle all the overly complicated XML stuff to traverse a model's XML-based information in XML order.
public static void visit(EObject eObject)
  {
    System.out.println("Visiting" + eObject);

    EClass eClass = eObject.eClass();
    int contentKind = ExtendedMetaData.INSTANCE.getContentKind(eClass);
    switch (contentKind)
    {
      case ExtendedMetaData.MIXED_CONTENT:
      {
        List<EStructuralFeature> attributes = ExtendedMetaData.INSTANCE.getAttributes(eClass);
        for (EStructuralFeature attribute : attributes)
        {
          // It can only be ATTRIBUTE_FEATURE or ATTRIBUTE_WILDCARD_FEATURE.
          if (ExtendedMetaData.INSTANCE.getFeatureKind(attribute) == ExtendedMetaData.ATTRIBUTE_WILDCARD_FEATURE)
          {
            // The feature will be a FeatureMap.
            // These are all the attributes in this wildcard.
            FeatureMap featureMap = (FeatureMap)eObject.eGet(attribute);
            for (FeatureMap.Entry entry : featureMap)
            {
              EStructuralFeature eStructuralFeature = entry.getEStructuralFeature();
              System.out.println("visiting attribute " + ExtendedMetaData.INSTANCE.getName(eStructuralFeature));
              Object value = entry.getValue();
              if (eStructuralFeature instanceof EReference)
              {
                if (value != null)
                {
                  // The value is some EObject.
                  visit((EObject)value);
                }
              }
              else
              {
                // The value is that of a simple type.
              }
            }
          }
          else if (ExtendedMetaData.INSTANCE.getFeatureKind(attribute) == ExtendedMetaData.ATTRIBUTE_FEATURE)
          {
            System.out.println("visiting attribute " + ExtendedMetaData.INSTANCE.getName(attribute));
            // The value is just the value of the attribute.
            Object value = eObject.eGet(attribute);
          }
          else
          {
            // Something else but not purely XML.
          }
        }

        EAttribute mixedFeature = ExtendedMetaData.INSTANCE.getMixedFeature(eClass);
        FeatureMap featureMap = (FeatureMap)eObject.eGet(mixedFeature);
        for (FeatureMap.Entry entry : featureMap)
        {
          if (FeatureMapUtil.isCDATA(entry))
          {
            // Only a mixed content feature map can contain these.
          }
          else if (FeatureMapUtil.isComment(entry))
          {
            // Only a mixed content feature map can contain these.
          }
          else if (FeatureMapUtil.isProcessingInstruction(entry))
          {
            // Only a mixed content feature map can contain these.
          }
          else if (FeatureMapUtil.isText(entry))
          {
            // Only a mixed content feature map can contain these.
          }
          else
          {
            // This must be an element.
            EStructuralFeature eStructuralFeature = entry.getEStructuralFeature();
            System.out.println("visiting element " + ExtendedMetaData.INSTANCE.getName(eStructuralFeature));
            Object value = entry.getValue();
            if (eStructuralFeature instanceof EReference)
            {
              if (value != null)
              {
                // The value is some EObject.
                visit((EObject)value);
              }
            }
            else
            {
              // The value is that of a simple type.
            }
          }
        }

        break;
      }

      case ExtendedMetaData.SIMPLE_CONTENT:
      {
        EStructuralFeature simpleFeature = ExtendedMetaData.INSTANCE.getSimpleFeature(eClass);
        // The value is the simple content value.
        Object value = eObject.eGet(simpleFeature);
        break;
      }

      case ExtendedMetaData.EMPTY_CONTENT:
      {
        // There are only attributes, no elements.
        // See how attributes are handled for MIXED_CONTENT.
        break;
      }

      case ExtendedMetaData.ELEMENT_ONLY_CONTENT:
      {
        // There may be attribute, see how they are handled for MIXED_CONTENT.

        List<EStructuralFeature> elements = ExtendedMetaData.INSTANCE.getElements(eClass);
        for (EStructuralFeature element : elements)
        {
          // Ignore elements that delegate to groups.  They will be in a feature map of some group and be in the correct order in that group.
          if (ExtendedMetaData.INSTANCE.getGroup(element) == null)
          {
            int featureKind = ExtendedMetaData.INSTANCE.getFeatureKind(element);
            switch (featureKind)
            {
              case ExtendedMetaData.ELEMENT_FEATURE:
              {
                System.out.println("visiting element " + ExtendedMetaData.INSTANCE.getName(element));
                Object value = eObject.eGet(element);
                if (element instanceof EReference)
                {
                  if (value != null)
                  {
                    // The value is some EObject.
                    visit((EObject)value);
                  }
                }
                else
                {
                  // The value is that of a simple type.
                }
                break;
              }
              case ExtendedMetaData.GROUP_FEATURE:
              case ExtendedMetaData.ELEMENT_WILDCARD_FEATURE:
              {
                // The value must be a feature map and all the contents of that feature map are elements.
                FeatureMap featureMap = (FeatureMap)eObject.eGet(element);
                for (FeatureMap.Entry entry : featureMap)
                {
                  EStructuralFeature eStructuralFeature = entry.getEStructuralFeature();
                  System.out.println("visiting element " + ExtendedMetaData.INSTANCE.getName(eStructuralFeature));
                  Object value = entry.getValue();
                  if (eStructuralFeature instanceof EReference)
                  {
                    if (value != null)
                    {
                      // The value is some EObject.
                      visit((EObject)value);
                    }
                  }
                  else
                  {
                    // The value is that of a simple type.
                  }
                }
              }
            }
          }
        }

        break;
      }

      default:
      case ExtendedMetaData.UNSPECIFIED_CONTENT:
      {
        throw new RuntimeException("Only pure XML is supported");
      }
    }
  }


Ed Merks
Professional Support: https://www.macromodeling.com/
Re: retain order of XML elements when using EMF [message #1806815 is a reply to message #1806790] Wed, 15 May 2019 15:13 Go to previous message
Zakir Meer is currently offline Zakir MeerFriend
Messages: 50
Registered: February 2016
Member
Hi Ed Merks,

Thanks a lot for the information !! It was a very detailed explanation in the article which you shared.
Previous Topic:limitation of child extender ?
Next Topic:Duplicated @Override annotation in generated code
Goto Forum:
  


Current Time: Thu Mar 28 14:15:14 GMT 2024

Powered by FUDForum. Page generated in 0.03272 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top