Click here to Skip to main content
15,890,882 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I've got quite some challenge in here...

Is it possible to have an XSL code that will apply to whatever hierarchy of XML, to transform it into nested HTML list?

Example XML:

XML
<Base>
  <Parent>
    <Child />
    <Child />
    <Child>
      <ChildOne />
      <ChildOne />
      <ChildOne>
       <ChildTwo />
       <ChildTwo />
      </ChildOne>
    </Child>
  </Parent>
  <Parent>
    <Child>
      <ChildOne>
        <ChildTwo>
          <ChildThree>
            <ChildFour />
          </ChildThree>
          <ChildThree/>
        </ChildTwo>
      </ChildOne>
      <ChildOne/>
      <ChildOne/>
    </Child>
    <Child/>
    <Child/>
  </Parent>
</Base>

Desired result:

HTML
<ul>
  <li>Parent1
    <ul>
      <li>Child</li>
      <li>Child
        <ul>
        <li>ChildOne</li>
        <li>ChildOne>
           <ul>
              <li>ChildTwo</li>
              <li>ChildTwo</li>
           </ul>
        </li>
        </ul>
      </li>
    </ul>
  </li>
  <li>Parent2
    <ul>
      <li>Child
        <ul>
            <li>ChildOne
                <ul>
                    <li>ChildTwo
                  <ul>
                  <li>ChildThree
                     <ul>
                         <li>ChildFour</li>
                     </ul>
                  </li>
                  <li>ChildThree</li>
                  </ul>
                </li>
            </ul>
            </li>
            <li>ChildOne</li>
            <li>ChildOne</li>
         </ul>
       </li>
       <li>Child</li>
    </ul>
  </li>
</ul>

So the logic is that if there is other elements(suppressed el.) under an element(upper el.) - the suppressed el. get surround by <li>name of upper el.<ul>all suppressed el.</ul></li> and so on.

My point is that this XSL code works not by values or names of elements or attributes, but by condition checking, if there are other elements falling under a particular element.

F.e., if ChildThree didn't have ChildFour</li> under itself, it would just be <li>ChildThree> instead of

HTML
<li>ChildThree
   <ul>
      <li>ChildFour</li>
   </ul>
</li>

Hope I explained myself clear. :)
Posted

This gets you close without much effort. Not sure how/why you're filtering out some of your elements, and your use of an index in your desired output is inconsistent, so left that out:
XML
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="*">
      <ul>
        <xsl:for-each select="*">
          <li>
            <xsl:value-of select="name(.)"/>
            <xsl:if test="count(*) > 0">
              <xsl:apply-templates select="."></xsl:apply-templates>
            </xsl:if>
          </li>
        </xsl:for-each>
      </ul>
    </xsl:template>
</xsl:stylesheet>
 
Share this answer
 
v2
The above solution is working fine, but it doesn't leave place for manipulating the output by adding other HTML tags. Because for example I need to put, for the child elements, <a> and <img>.
Here is what I ended up with:
HTML
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

<xsl:template match="*[*]">
  <li>
    <xsl:value-of select="local-name()" />
    <ul>
      <xsl:apply-templates />
    </ul>
  </li>
</xsl:template>

<xsl:template match="*[not(*)]">
  <li>
    <xsl:value-of select="local-name()" />
  </li>
</xsl:template>

<xsl:template match="/*" priority="5">
  <ul>
    <xsl:apply-templates />
  </ul>
</xsl:template>

</xsl:stylesheet>
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900