1

I have an XML data in the following form :

<table>
 <col>
  <name>Addresses</name>
 </col>
 <col>
  <name>Addresses/Address1</name>
 </col>
 <col>
  <name>Addresses/Address2</name>
 </col>
 <col>
  <name>Addresses/Address1/Flat Number</name>
 </col>
 <col>
  <name>Employee Name</name>
 </col>
 <col>
  <name>Phone Number</name>
 </col>
 <col>
  <name>Profession</name>
 </col>
 <col>
  <name>Employee Name/First Name</name>
 </col>
 <col>
  <name>Employee Name/Last Name</name>
 </col>
 <col>
  <name>Employee Name/First_Name</name>
 </col>
 <col>
  <name>Accounts/Account Name/First/Saving</name>
 </col>
 <col>
  <name>Accounts/Account_Name/Second</name>
 </col>
</table> 

Now I want to create a treeview like structure in HTML using XSLT on this XML. The structure of the Treeview would be similar to following structure :

  • Addresses
    • Address1
    • Address2
  • Employee Name
    • First Name
    • Last Name
  • Accounts
    • Account Name

Please note that substring before first occurrence of "/" is the First Level Node of treeview and the substring after the first occurrence of "/" is the Second Level Node of the matching First Level Node, and this is only a 2 level treeview.

Also, the values displayed are unique ones. Even if the values are repeating in the XML, we need to pick only unique one value. One more condition to consider is that values with "" are considered same as without "" as given in example : "First Name" and "First_Name", so we need to display the value after replace "-" with " ".

<xsl:variable name="currentNode" select="//table/col" />
<xsl:key name="uniqueCategoryKey" match="record" use="name"/>
<xsl:key name="uniqueSubCategoryKey" match="record" use="substring-before(substring-after(concat(name,'/'),'/'),'/')"/>
<xsl:template match="/">
<xsl:call-template name="treeTemplate" />
</xsl:template>

<xsl:template name="treeTemplate">


  <div id ="newtreeview">
    <ul>
      <xsl:for-each select="$currentNode[generate-id() = generate-id(key('uniqueCategoryKey', name))]">

        <xsl:variable name="category" select="name"/>

        <xsl:if test="string-length($category) > 0 and not(contains($category,'/'))">
          <li>
            <a href="#">
              <xsl:value-of select="$category"/>
            </a>
            <xsl:if test="//record[contains(name, concat($category,'/'))]">
              <ul>

                <xsl:for-each select="$currentNode[generate-id() = generate-id(key('uniqueSubCategoryKey', substring-before(substring-after(concat(name,'/'),'/'),'/')))]">

                  <li>
                    <a href="#">
                      <xsl:variable name="subcat">
                        <xsl:call-template name="string-replace-all">
                          <xsl:with-param name="text" select="string(substring-before(substring-after(concat(name,'/'),'/'),'/'))" />
                          <xsl:with-param name="replace" select="'_'" />
                          <xsl:with-param name="by" select="' '" />
                        </xsl:call-template>
                      </xsl:variable>
                      <xsl:value-of select="$subcat"/>


                    </a>
                  </li>
                </xsl:for-each>
              </ul>
            </xsl:if>
          </li>
        </xsl:if>
       </xsl:for-each>
    </ul>
  </div>

</xsl:template>
<xsl:template name="string-replace-all">
<xsl:param name="text" />
<xsl:param name="replace" />
<xsl:param name="by" />
<xsl:choose>
  <xsl:when test="contains($text, $replace)">
    <xsl:value-of select="substring-before($text,$replace)" />
    <xsl:value-of select="$by" />
    <xsl:call-template name="string-replace-all">
      <xsl:with-param name="text"
      select="substring-after($text,$replace)" />
      <xsl:with-param name="replace" select="$replace" />
      <xsl:with-param name="by" select="$by" />
    </xsl:call-template>
  </xsl:when>
  <xsl:otherwise>
    <xsl:value-of select="$text" />
  </xsl:otherwise>
</xsl:choose>
</xsl:template>

The problem is that, this code is giving me an output like

  • Addresses
      • -
    • Address1
    • Address2
  • Employee Name
      • -
    • First Name
    • First Name
    • Last Name
  • Accounts
    • Account Name
    • Account Name

Please help me solve this issue.

Thanks in Advance.

1
  • could you provide sample xml that might work! Commented Jan 6, 2012 at 14:49

1 Answer 1

2

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:key name="kCatFromName" match="name"
      use="substring-before(concat(.,'/'), '/')"/>

     <xsl:key name="kValFromName" match="name[contains(., '/')]"
      use="concat(substring-before(.,'/'),
           '+',
           translate(
               substring-before
                 (concat(substring-after(.,'/'), '/'),
                  '/'
                 ),
               '_',
               ' '
                       )
                )
          "/>

     <xsl:template match="/*">
      <ul>
         <xsl:apply-templates mode="cat" select=
          "*/name
             [generate-id()
             =
              generate-id(key('kCatFromName',
                               substring-before(concat(.,'/'), '/')
                               )[1]
                        )
             ]
          "/>
        </ul>
     </xsl:template>

     <xsl:template match="name" mode="cat">
      <xsl:variable name="vCat" select=
      "substring-before(concat(.,'/'), '/')"/>
      <li><xsl:value-of select="$vCat"/></li>

       <xsl:variable name="vInThisCat" select=
       "key('kCatFromName', $vCat)
         [generate-id()
         =
          generate-id(key('kValFromName',
                          concat(substring-before(.,'/'),
                                 '+',
                                 translate(
                                           substring-before
                                            (concat(substring-after(.,'/'), '/'),
                                             '/'
                                            ),
                                            '_',
                                            ' '
                                            )
                                )
                          )[1]
                      )
         ]"/>

         <xsl:if test="$vInThisCat">
        <ul>
          <xsl:apply-templates mode="val" select="$vInThisCat"/>
         </ul>
       </xsl:if>
     </xsl:template>

      <xsl:template match="name" mode="val">
        <li>
          <xsl:value-of select=
          "translate(substring-before
                (concat(substring-after(.,'/'), '/'),
                 '/'
                ),
               '_',
               ' '
               )
          "/>
        </li>
      </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<table>
    <col>
        <name>Addresses</name>
    </col>
    <col>
        <name>Addresses/Address1</name>
    </col>
    <col>
        <name>Addresses/Address2</name>
    </col>
    <col>
        <name>Addresses/Address1/Flat Number</name>
    </col>
    <col>
        <name>Employee Name</name>
    </col>
    <col>
        <name>Phone Number</name>
    </col>
    <col>
        <name>Profession</name>
    </col>
    <col>
        <name>Employee Name/First Name</name>
    </col>
    <col>
        <name>Employee Name/Last Name</name>
    </col>
    <col>
        <name>Employee Name/First_Name</name>
    </col>
    <col>
        <name>Accounts/Account Name/First/Saving</name>
    </col>
    <col>
        <name>Accounts/Account_Name/Second</name>
    </col>
</table>

produces the wanted, correct result:

<ul>
   <li>Addresses</li>
   <ul>
      <li>Address1</li>
      <li>Address2</li>
   </ul>
   <li>Employee Name</li>
   <ul>
      <li>First Name</li>
      <li>Last Name</li>
   </ul>
   <li>Phone Number</li>
   <li>Profession</li>
   <li>Accounts</li>
   <ul>
      <li>Account Name</li>
   </ul>
</ul>

and it is displayed by the browser as:

  • Addresses
    • Address1
    • Address2
  • Employee Name
    • First Name
    • Last Name
  • Phone Number
  • Profession
  • Accounts
    • Account Name
Sign up to request clarification or add additional context in comments.

8 Comments

Navatchev, Thanks a lot :) ... Your xslt code worked well but it displayed the result as follows :<ul><li>Addresses</li><ul><li>Address1</li><li>Address2</li></ul></ul>. And after the first list item it stopped, displayed nothing. Also I had planned my ordered list as <ul><li>Addresses<ul><li>Address1</li><li>Address2</li></ul></li></ul>... Could you please help me display it new this format, also all the list items not just the first one.
Sorry, my bad, I got the problem why your code didn't worked for me, I am updating the XML, as we have not thought through one more scenario and then we can try again to get the desired results. Hope you will help me this time too. :)
@manishekhawat: This code is tested with several XSLT 1.0 processors (MSXML3/4, .NET XslCompledTransform, Saxon 6.5.4 and AltovaXML (XML-SPY) to work correctly and produce the same complete result. In case you got incomplete result, either you changed the XML document, or the XSLT code, or both, or you are using a buggy, incompliant XSLT processor.
@manishekhawat: It would be better to ask a new question rather than update this, already very complicated question. This question has been fully answered, therefore, please, consider accepting the answer. I would be glad to address your new question.
I have updated the XML, I have added two new col tags without "/" and these will also be the first level node without second level nodes in treeview <col> <name>Phone Number</name> </col> <col> <name>Profession</name> </col> . I hope you will get better idea of the problem after reading updates in comments and new XML, thanks for helping me so far, expect the same again :)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.