Search code examples
xmlxsltdhtmlx

XSL tree construction from XML


I'm breaking my head since few days/week so now i think it's time to ask some new advices.

*It's my first project in XML/XSL, i'll try to be cleariest as possible

I got an output in XML from a SQL query, the output looks like this :

<row DEPARTMENT="SOCIETY" DEPARTMENT_LEVEL="0" CHILD_DEPARTMENT="1111" PARENT_DEPARTMENT="1111"/> 

<row DEPARTMENT="Boss" DEPARTMENT_LEVEL="1" CHILD_DEPARTMENT="2222" PARENT_DEPARTMENT="1111"/>

<row DEPARTMENT="Second Boss" DEPARTMENT_LEVEL="1" CHILD_DEPARTMENT="3333" PARENT_DEPARTMENT="1111"/>

<row DEPARTMENT="Secretary" DEPARTMENT_LEVEL="1" CHILD_DEPARTMENT="4444" PARENT_DEPARTMENT="1111"/>

<row DEPARTMENT="Desk" DEPARTMENT_LEVEL="2" CHILD_DEPARTMENT="5555" PARENT_DEPARTMENT="4444"/>

<row DEPARTMENT="Chief" DEPARTMENT_LEVEL="3" CHILD_DEPARTMENT="6666" PARENT_DEPARTMENT="5555"/>

<row DEPARTMENT="post1" DEPARTMENT_LEVEL="4" CHILD_DEPARTMENT="7777" PARENT_DEPARTMENT="6666"/>

<row DEPARTMENT="post2" DEPARTMENT_LEVEL="4" CHILD_DEPARTMENT="8888" PARENT_DEPARTMENT="6666"/>

<row DEPARTMENT="post3" DEPARTMENT_LEVEL="4" CHILD_DEPARTMENT="9999" PARENT_DEPARTMENT="6666"/>

Then i'm searching to construct a tree with XSL :

<xsl:template match='/'>
<html>
<body>
    <ul>
    <tree id='0'>
        <li><item>
        <xsl:attribute name ='id'>1111</xsl:attribute>
        <xsl:attribute name ='text'>Society</xsl:attribute>
        <xsl:apply-templates select='//row' />
        </item></li>
    </tree>
    </ul>
</body>
</html>
</xsl:template>
<xsl:template match='//row'>
<li><item>
    <xsl:attribute name ='id'>
    <xsl:value-of select='@CHILD_DEPARTMENT' />
    </xsl:attribute>
        <xsl:attribute name ='text'>
    <xsl:value-of select='@DEPARTMENT' />
    </xsl:attribute>
</item></li>
</xsl:template>

I need to get the data well formed to put them in a DHTMLX tree, it has to look like this :

<tree id="0">
<item id="1111" text="Society">
  <item id="2222" text="Boss"/>
  <item id="3333" text="Second Boss"/>
  <item id="4444" text="Secretary">
     <item id="5555" text="Desk">
        <item id="6666" text="Chief">
           <item id="7777" text="post1"/>
           <item id="8888" text="post2"/>
           <item id="9999" text="post3"/>
        </item>
     </item>
  </item>
</item>
</tree>

And here is what i get with my code :

<tree id="0">
<item id="1111" text="Society"/>
    <item id="2222" text="Boss"/>
    <item id="3333" text="Second Boss"/>
    <item id="4444" text="Secretary"/>
        <item id="5555" text="Desk"/>
        <item id="6666" text="Chief"/>
        <item id="7777" text="post1"/>
        <item id="8888" text="post2"/>
        <item id="9999" text="post3"/>
</item>
</tree>

I choosed to harcode the "tree" and first "item" elements to get the begining of structure well formed.

As you may noticied nothing in this code can test if it has to be a new element or if it's a child element.

I tried lot of things as :

  1. Conditional test on child/parent attribute => don't works because don't apply test on every "row" ?
  2. Using variable to stock the value of parent attribute => don't works because value of variable can't be changed after declaration

So i'm really lost on how can i find a solution for constructing this tree :(

If someone has some advices for me it'll be very helpfull !

Ps: sorry for some spelling errors, english is not my first language.

Regards


Solution

  • In your template matching / you should start off by selecting only row elements with a department level of 0

    <xsl:apply-templates select="//row[@DEPARTMENT_LEVEL='0']" />
    

    Then, in your template matching row you then need to select the relevant child items for the current row

    <xsl:apply-templates select="//row[@PARENT_DEPARTMENT = current()/@CHILD_DEPARTMENT and @DEPARTMENT_LEVEL = current()/@DEPARTMENT_LEVEL + 1]" />
    

    Try this XSLT

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
    <xsl:output method="xml" indent="yes" />
    
    <xsl:template match='/'>
        <tree id='0'>
            <xsl:apply-templates select="//row[@DEPARTMENT_LEVEL='0']" />
        </tree>
    </xsl:template>
    
    <xsl:template match='//row'>
        <item>
            <xsl:attribute name ='id'>
                <xsl:value-of select='@CHILD_DEPARTMENT' />
            </xsl:attribute>
            <xsl:attribute name ='text'>
                <xsl:value-of select='@DEPARTMENT' />
            </xsl:attribute>
            <xsl:apply-templates select="//row[@PARENT_DEPARTMENT = current()/@CHILD_DEPARTMENT and @DEPARTMENT_LEVEL = current()/@DEPARTMENT_LEVEL + 1]" />
        </item>
    </xsl:template>
    </xsl:stylesheet>
    

    Alternatively, read up on xsl:key which could be used to get child rows by parent

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
    <xsl:output method="xml" indent="yes" />
    
    <xsl:key name="rowsByParent" match="row" use="@PARENT_DEPARTMENT" />
    
    <xsl:template match='/'>
        <tree id='0'>
            <xsl:apply-templates select="//row[@DEPARTMENT_LEVEL='0']" />
        </tree>
    </xsl:template>
    
    <xsl:template match='//row'>
        <item id="{@CHILD_DEPARTMENT}" text="{@DEPARTMENT}">
            <xsl:apply-templates select="key('rowsByParent', @CHILD_DEPARTMENT)[@DEPARTMENT_LEVEL = current()/@DEPARTMENT_LEVEL + 1]" />
        </item>
    </xsl:template>
    </xsl:stylesheet>
    

    Also note the use of "Attribute Value Templates" to set the attributes of the item elements.