Search code examples
xmlxsltxmi

keep the hierarchy (order) of an XML file after transformation with XSLT


I'm trying to transform an XML file into another but I'm having a problem with keeping the same hierarchy of the input file.

Here's the input:

<XMI xmi.version='1.2' xmlns:UML="org.omg.xmi.namespace.UML">
  <XMI.content>
    <UML:Model xmi.id='eee_1045467100313_135436_1' name='Data'>
      <UML:Namespace.ownedElement>
        <UML:Package xmi.id='_9_0_bc102e5_1427365805826_580042_23' name='migration_kbir'>
          <UML:Namespace.ownedElement>
            <UML:Package xmi.id='_9_0_bc102e5_1427365805826_580042_22' name='migration_s8ir'>
              <UML:Package xmi.id='_9_0_bc102e5_1427365805826_580042_22' name='migration_s8ir2'>
                <UML:Class xmi.id='_9_0_bc102e5_1427367042666_255023_151' name='Employee'>
                  <UML:Classifier.feature>
                    <UML:Attribute xmi.id='_9_0_bc102e5_1427367052819_893122_168' name='cin'>
                    </UML:Attribute>
                  </UML:Classifier.feature>
                </UML:Class>
                <UML:Class xmi.id='_9_0_64701d4_1429716452808_363115_43' name='AssoEmpl'>
                </UML:Class>
                <UML:Generalization xmi.id='_9_0_64701d4_1429719509242_100032_198' child='_9_0_64701d4_1429719498101_197360_182' parent='_9_0_64701d4_1429716437842_892182_26' />
              </UML:Package>
            </UML:Package>
          </UML:Namespace.ownedElement>
        </UML:Package>
      </UML:Namespace.ownedElement>
    </UML:Model>
  </XMI.content>
</XMI>

Here's what I get as an output:

<xmi:XMI xmlns:xmi="http://www.omg.org/XMI" xmlns:UML="org.omg.xmi.namespace.UML"
  xmi.version="2.1" timestamp="">
  <packagedElement xmi:type="uml:package"
    xmi.id="_9_0_bc102e5_1427365805826_580042_23" name="migration_kbir" />
  <packagedElement xmi:type="uml:package"
    xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir" />
  <packagedElement xmi:type="uml:package"
    xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir2">
    <packagedElement xmi:type="uml:Class"
      xmi.id="_9_0_bc102e5_1427367042666_255023_151" name="Employee">
      <generalization xmi.id="_9_0_bc102e5_1427367042666_255023_151">
        <general xmi:type="uml:Class" />
      </generalization>
      <ownedAttribute xmi.id="_9_0_bc102e5_1427367052819_893122_168"
        name="cin" />
    </packagedElement>
    <packagedElement xmi:type="uml:Class"
      xmi.id="_9_0_64701d4_1429716452808_363115_43" name="AssoEmpl">
      <generalization xmi.id="_9_0_64701d4_1429716452808_363115_43">
        <general xmi:type="uml:Class" />
      </generalization>
    </packagedElement>
  </packagedElement>
</xmi:XMI>

but I'm expecting something like this:

<xmi:XMI xmlns:xmi="http://www.omg.org/XMI" xmlns:UML="org.omg.xmi.namespace.UML" xmi.version="2.1" timestamp="">
  <packagedElement xmi:type="uml:package" xmi.id="_9_0_bc102e5_1427365805826_580042_23" name="migration_kbir">
      <packagedElement xmi:type="uml:package"xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir">
          <packagedElement xmi:type="uml:package"xmi.id="_9_0_bc102e5_1427365805826_580042_22" name="migration_s8ir2">
              <packagedElement xmi:type="uml:Class"xmi.id="_9_0_bc102e5_1427367042666_255023_151" name="Employee">
                  <generalization xmi.id="_9_0_bc102e5_1427367042666_255023_151">
                    <general xmi:type="uml:Class" />
                  </generalization>
                  <ownedAttribute xmi.id="_9_0_bc102e5_1427367052819_893122_168"name="cin" />
              </packagedElement>
              <packagedElement xmi:type="uml:Class" xmi.id="_9_0_64701d4_1429716452808_363115_43" name="AssoEmpl">
                  <generalization xmi.id="_9_0_64701d4_1429716452808_363115_43">
                    <general xmi:type="uml:Class" />
                  </generalization>
              </packagedElement>
          </packagedElement>
      </packagedElement>
  </packagedElement>
</xmi:XMI>

and finally here's my transformation file:

<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:UML="org.omg.xmi.namespace.UML"
  xmlns:xmi="http://www.omg.org/XMI"
>
  <xsl:output indent="yes" method="xml" />

  <xsl:template match="/">
    <xmi:XMI>
      <xsl:attribute name="xmi.version">2.1</xsl:attribute>
      <xsl:attribute name="timestamp">
        <xsl:value-of select="@timestamp" />
      </xsl:attribute>
      <xsl:for-each select="//UML:Package">
        <packagedElement>
          <xsl:attribute name="xmi:type">uml:package</xsl:attribute>
          <xsl:attribute name="xmi.id">
            <xsl:value-of select="@xmi.id" />
          </xsl:attribute>
          <xsl:attribute name="name">
            <xsl:value-of select="@name" />
          </xsl:attribute>
          <xsl:choose>
            <xsl:when test="UML:Class">
              <xsl:for-each select="UML:Class">
                <packagedElement>
                  <xsl:attribute name="xmi:type">uml:Class</xsl:attribute>
                  <xsl:attribute name="xmi.id">
                    <xsl:value-of select="@xmi.id" />
                  </xsl:attribute>
                  <xsl:attribute name="name">
                    <xsl:value-of select="@name" />
                  </xsl:attribute>
                  <generalization>
                    <xsl:attribute name="xmi.id">
                      <xsl:value-of select="@xmi.id" />
                    </xsl:attribute>
                    <general xmi:type="uml:Class" />
                  </generalization>
                  <xsl:for-each select="UML:Classifier.feature/UML:Attribute">
                    <ownedAttribute>
                      <xsl:attribute name="xmi.id">
                        <xsl:value-of select="@xmi.id" />
                      </xsl:attribute>
                      <xsl:attribute name="name">
                        <xsl:value-of select="@name" />
                      </xsl:attribute>
                    </ownedAttribute>
                  </xsl:for-each>
                </packagedElement>
              </xsl:for-each>
            </xsl:when>
          </xsl:choose>
        </packagedElement>
      </xsl:for-each>
    </xmi:XMI>
  </xsl:template>
</xsl:stylesheet>

What should I do in the transformation code to keep the hierarchy of my input file?


Solution

  • When you do:

    <xsl:for-each select="//UML:Package">
    

    you are selecting all UML:Package elements, anywhere in the document, and treating them as siblings. Since that's not what you want, you need to change your approach and apply templates recursively instead.

    I have made a quick adjustment to your existing styleheet, see if that works for you:

    <xsl:stylesheet xmlns:UML="org.omg.xmi.namespace.UML" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes" method="xml"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/">
        <xmi:XMI xmi.version="2.1">
            <xsl:attribute name="xmi.version">2.1</xsl:attribute>
            <xsl:attribute name="timestamp">
                <xsl:value-of select="@timestamp"/>
            </xsl:attribute>
            <xsl:apply-templates/>  
        </xmi:XMI>  
    </xsl:template> 
    
    <xsl:template match="UML:Package">      
        <packagedElement>
          <xsl:attribute name="xmi:type">uml:package</xsl:attribute>
          <xsl:attribute name="xmi.id">
            <xsl:value-of select="@xmi.id"/>
          </xsl:attribute>
          <xsl:attribute name="name">
            <xsl:value-of select="@name"/>
          </xsl:attribute>
          <xsl:choose>
            <xsl:when test="UML:Class">
              <xsl:for-each select="UML:Class">
                <packagedElement>
                  <xsl:attribute name="xmi:type">uml:Class</xsl:attribute>
                  <xsl:attribute name="xmi.id">
                    <xsl:value-of select="@xmi.id"/>
                  </xsl:attribute>
                  <xsl:attribute name="name">
                    <xsl:value-of select="@name"/>
                  </xsl:attribute>
                  <generalization>
                    <xsl:attribute name="xmi.id">
                      <xsl:value-of select="@xmi.id"/>
                    </xsl:attribute>
                    <general xmi:type="uml:Class"/>
                  </generalization>
                  <xsl:for-each select="UML:Classifier.feature/UML:Attribute">
                    <ownedAttribute>
                      <xsl:attribute name="xmi.id">
                        <xsl:value-of select="@xmi.id"/>
                      </xsl:attribute>
                      <xsl:attribute name="name">
                        <xsl:value-of select="@name"/>
                      </xsl:attribute>
                    </ownedAttribute>
                  </xsl:for-each>
                </packagedElement>
              </xsl:for-each>
            </xsl:when>
          </xsl:choose>
          <xsl:apply-templates/>  
        </packagedElement>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Note:

    1. You should learn how to add attributes directly to literal result elements, and how to use the attribute value template. This could simplify your stylesheet considerably.

    2. This:

      <xsl:attribute name="timestamp">
          <xsl:value-of select="@timestamp"/>
      </xsl:attribute>
      

      makes no sense in the context of the root node that has no attributes.