Sarah
Sarah

Reputation: 11

XSLT Parent child Organization Hierarchy not working properly

I have an XML structure with an organization hierarchy. The <pkEntity> element is a parent ID of org and <entityParent> is the child of org. I don't know the depth of the parent/child combination. I need to transform in below mentioned state:

 <EntityDimCollection>
<EntityDim>
<pkEntity>-9</pkEntity>
<entityParent>-7</entityParent>
<entityCode>Own_CP</entityCode>

<entityType>OT</entityType>
<essEntityCode>un.Own_CP</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>-8</pkEntity>
<entityParent>-7</entityParent>
<entityCode>Alternatives</entityCode>

<entityType>OT</entityType>
<essEntityCode>un.Alternatives</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>8555</pkEntity>
<entityParent>-8</entityParent>
<entityCode>Ex_BABRO</entityCode>

<entityType>CF</entityType>
<essEntityCode>un.Ex_BABRO</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>8752</pkEntity>
<entityParent>-8</entityParent>
<entityCode>Ex_SY</entityCode>

<entityType>CF</entityType>
<essEntityCode>un.Ex_SY</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>9731</pkEntity>
<entityParent>-8</entityParent>
<entityCode>NOR</entityCode>

<entityType>LE</entityType>
<essEntityCode>un.NOR</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1649940</pkEntity>
<entityParent>9731</entityParent>
<entityCode>NO</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.NO</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1838293</pkEntity>
<entityParent>1649940</entityParent>
<entityCode>UKONORWAY</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.UKONORWAY</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1127251</pkEntity>
<entityParent>1838293</entityParent>
<entityCode>2BUS</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.2BUS</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1127274</pkEntity>
<entityParent>1127251</entityParent>
<entityCode>3BUS_B</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.3BUS_B</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1988187</pkEntity>
<entityParent>1127274</entityParent>
<entityCode>4BUS_B</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.4BUS_B</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1988188</pkEntity>
<entityParent>1988187</entityParent>
<entityCode>5ADM_B</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.5ADM_B</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1988189</pkEntity>
<entityParent>1988187</entityParent>
<entityCode>5FVK_B</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.5FVK_B</essEntityCode>

</EntityDim>
<EntityDim>
<pkEntity>1988190</pkEntity>
<entityParent>1988189</entityParent>
<entityCode>61_FVK_B</entityCode>

<entityType>CG</entityType>
<essEntityCode>un.NOR.61_FVK_B</essEntityCode>

</EntityDim>
</EntityDimCollection>

and with Mark Veenstra solution I got output like this

<?xml version="1.0" encoding="UTF-8"?>
<client:LMSDetails xmlns:client="http://foo/bar">
   <client:ParentID>-9</client:ParentID>
   <client:ChildID>-7</client:ChildID>
   <client:name/>
   <client:identifier>OT</client:identifier>
   <client:isActive/>
   <client:ParentID>-8</client:ParentID>
   <client:ChildID>-7</client:ChildID>
   <client:name/>
   <client:identifier>OT</client:identifier>
   <client:isActive/>
   <client:children>
      <client:ParentID>8555</client:ParentID>
      <client:ChildID>-8</client:ChildID>
      <client:name/>
      <client:identifier>CF</client:identifier>
      <client:isActive/>
   </client:children>
   <client:children>
      <client:ParentID>8752</client:ParentID>
      <client:ChildID>-8</client:ChildID>
      <client:name/>
      <client:identifier>CF</client:identifier>
      <client:isActive/>
   </client:children>
   <client:children>
      <client:ParentID>9731</client:ParentID>
      <client:ChildID>-8</client:ChildID>
      <client:name/>
      <client:identifier>LE</client:identifier>
      <client:isActive/>
      <client:children>
         <client:ParentID>1649940</client:ParentID>
         <client:ChildID>9731</client:ChildID>
         <client:name/>
         <client:identifier>CG</client:identifier>
         <client:isActive/>
         <client:children>
            <client:ParentID>1838293</client:ParentID>
            <client:ChildID>1649940</client:ChildID>
            <client:name/>
            <client:identifier>CG</client:identifier>
            <client:isActive/>
            <client:children>
               <client:ParentID>1127251</client:ParentID>
               <client:ChildID>1838293</client:ChildID>
               <client:name/>
               <client:identifier>CG</client:identifier>
               <client:isActive/>
               <client:children>
                  <client:ParentID>1127274</client:ParentID>
                  <client:ChildID>1127251</client:ChildID>
                  <client:name/>
                  <client:identifier>CG</client:identifier>
                  <client:isActive/>
                  <client:children>
                     <client:ParentID>1988187</client:ParentID>
                     <client:ChildID>1127274</client:ChildID>
                     <client:name/>
                     <client:identifier>CG</client:identifier>
                     <client:isActive/>
                     <client:children>
                        <client:ParentID>1988188</client:ParentID>
                        <client:ChildID>1988187</client:ChildID>
                        <client:name/>
                        <client:identifier>CG</client:identifier>
                        <client:isActive/>
                     </client:children>
                     <client:children>
                        <client:ParentID>1988189</client:ParentID>
                        <client:ChildID>1988187</client:ChildID>
                        <client:name/>
                        <client:identifier>CG</client:identifier>
                        <client:isActive/>
                        <client:children>
                           <client:ParentID>1988190</client:ParentID>
                           <client:ChildID>1988189</client:ChildID>
                           <client:name/>
                           <client:identifier>CG</client:identifier>
                           <client:isActive/>
                        </client:children>
                     </client:children>
                  </client:children>
               </client:children>
            </client:children>
         </client:children>
      </client:children>
   </client:children>
   <client:ParentID>8555</client:ParentID>
   <client:ChildID>-8</client:ChildID>
   <client:name/>
   <client:identifier>CF</client:identifier>
   <client:isActive/>
   <client:ParentID>8752</client:ParentID>
   <client:ChildID>-8</client:ChildID>
   <client:name/>
   <client:identifier>CF</client:identifier>
   <client:isActive/>
   <client:ParentID>9731</client:ParentID>
   <client:ChildID>-8</client:ChildID>
   <client:name/>
   <client:identifier>LE</client:identifier>
   <client:isActive/>
   <client:children>
      <client:ParentID>1649940</client:ParentID>
      <client:ChildID>9731</client:ChildID>
      <client:name/>
      <client:identifier>CG</client:identifier>
      <client:isActive/>
      <client:children>
         <client:ParentID>1838293</client:ParentID>
         <client:ChildID>1649940</client:ChildID>
         <client:name/>
         <client:identifier>CG</client:identifier>
         <client:isActive/>
         <client:children>
            <client:ParentID>1127251</client:ParentID>
            <client:ChildID>1838293</client:ChildID>
            <client:name/>
            <client:identifier>CG</client:identifier>
            <client:isActive/>
            <client:children>
               <client:ParentID>1127274</client:ParentID>
               <client:ChildID>1127251</client:ChildID>
               <client:name/>
               <client:identifier>CG</client:identifier>
               <client:isActive/>
               <client:children>
                  <client:ParentID>1988187</client:ParentID>
                  <client:ChildID>1127274</client:ChildID>
                  <client:name/>
                  <client:identifier>CG</client:identifier>
                  <client:isActive/>
                  <client:children>
                     <client:ParentID>1988188</client:ParentID>
                     <client:ChildID>1988187</client:ChildID>
                     <client:name/>
                     <client:identifier>CG</client:identifier>
                     <client:isActive/>
                  </client:children>
                  <client:children>
                     <client:ParentID>1988189</client:ParentID>
                     <client:ChildID>1988187</client:ChildID>
                     <client:name/>
                     <client:identifier>CG</client:identifier>
                     <client:isActive/>
                     <client:children>
                        <client:ParentID>1988190</client:ParentID>
                        <client:ChildID>1988189</client:ChildID>
                        <client:name/>
                        <client:identifier>CG</client:identifier>
                        <client:isActive/>
                     </client:children>
                  </client:children>
               </client:children>
            </client:children>
         </client:children>
      </client:children>
   </client:children>
</client:LMSDetails>

Upvotes: 1

Views: 247

Answers (1)

Pablo Pozo
Pablo Pozo

Reputation: 1920

XSLT 1.0 (and XSLT 2.0) solution

It is based on use the <xsl:key> to arrange the parents and children while boosting the performance of the solution.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:client="http://client.org">

    <xsl:output method="xml" indent="yes" />

    <!-- Use keys to boost performance -->
    <xsl:key name="entity-key" match="EntityDim" use="pkEntity" />
    <xsl:key name="parent-key" match="EntityDim" use="entityParent" />

    <!-- Process root element -->
    <xsl:template match="EntityDimCollection">
        <client:LMSDetails>
            <!-- Apply template to root entities, i.e. entities with no parent nodes -->
            <xsl:apply-templates select="EntityDim[not(key('entity-key', entityParent))]" />
        </client:LMSDetails>
    </xsl:template>

    <!-- First one to use for the real parent -->
    <xsl:template match="EntityDim">
        <!-- Obtain the relevant information -->
        <client:ParentID><xsl:value-of select="pkEntity" /></client:ParentID>
        <client:ChildID><xsl:value-of select="entityParent" /></client:ChildID>
        <client:name/>
        <client:identifier><xsl:value-of select="entityType" /></client:identifier>
        <client:isActive/>
        <client:costCenter>
            <!-- Obtain the expression after the last dot in essEntityCode -->
            <xsl:call-template name="get-suffix">
                <xsl:with-param name="text" select="essEntityCode" />
            </xsl:call-template>
        </client:costCenter>
        <!-- Outputs the children for this node : we just search which entities have the
             current pkEntity as their entityParent-->
        <xsl:for-each select="key('parent-key', pkEntity)">
            <client:children>
                <xsl:apply-templates select="." />
            </client:children>
        </xsl:for-each>
    </xsl:template>

    <!-- Recursive template to obtain the suffix from essEntityCode (the part after
         the last dot ) -->
    <xsl:template name="get-suffix">
        <xsl:param name="text" />

        <!-- Check whether the current text contains a dot -->
        <xsl:choose>
            <!-- Case CONTAINS_DOT: recurse until there is not more dots in the string -->
            <xsl:when test="contains($text, '.')">
                <xsl:call-template name="get-suffix">
                    <xsl:with-param name="text" select="substring-after($text, '.')" />
                </xsl:call-template>            
            </xsl:when>
            <!-- Case WITHOUT_DOTS : output suffix -->
            <xsl:otherwise>
                <xsl:value-of select="$text" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

XSLT 2.0 only solution

The way of building the hierarchy is the same, the only difference is the way of obtaining the suffix from essEntityCode. In XSLT 1.0 we had to build a recursive template to detect the last dot and extract the suffix. However in XSLT 2.0 we can use the function tokenize which splits the given string using the given regular expresion.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:client="http://client.org">

    <xsl:output method="xml" indent="yes" />

    <xsl:key name="entity-key" match="EntityDim" use="pkEntity" />
    <xsl:key name="parent-key" match="EntityDim" use="entityParent" />

    <xsl:template match="EntityDimCollection">
        <client:LMSDetails>
            <xsl:apply-templates select="EntityDim[not(key('entity-key', entityParent))]" />
        </client:LMSDetails>
    </xsl:template>

    <xsl:template match="EntityDim">
        <client:ParentID><xsl:value-of select="pkEntity" /></client:ParentID>
        <client:ChildID><xsl:value-of select="entityParent" /></client:ChildID>
        <client:name/>
        <client:identifier><xsl:value-of select="entityType" /></client:identifier>
        <client:isActive/>
        <client:costCenter>
            <!-- This is the only difference from the XSLT 1.0 solution, instead
                 of calling the recursive template we tokenize the expression and
                 obtain the last element (which is the suffix) -->
            <xsl:value-of select="tokenize(essEntityCode, '\.')[last()]" />
        </client:costCenter>
        <xsl:for-each select="key('parent-key', pkEntity)">
            <client:children>
                <xsl:apply-templates select="." />
            </client:children>
        </xsl:for-each>
    </xsl:template>

</xsl:stelesheet>

EDIT: I have assumed that for each children a new <client:children> is created, that behavior can be changed by adjusting the code in the <for-each> loop.

Upvotes: 2

Related Questions