Link to home
Start Free TrialLog in
Avatar of gregoftheweb
gregoftheweb

asked on

XSLT: open tag on first condition, close tag on second. Breaks XML'ishness of XSLT.

So this has been a problem I have been kicking around for awhile.  I have actually given up on doing it in XSLT and just ripped it out with the DOM in code and then cache the resulting html string at the application scope.

Nevertheless:

Assume an xml structure like so.

<root>
  <item num="1" />
  <item num="2" />
  <item num="3" >
      <item num="1" />
      <item num="2" />
      <item num="3" />
  </item>
  <item num="4" />
  <item num="5" />
</root>

And then assume that the output that I would like should look like this:
<ul>
    <li>1</li>
    <li>2</li>
    <li>3
        <ul>
            <li>1</li>
            <li>2</li>
            <li>3</li>
        </ul>
    </li>
    <li>4</li>
    <li>5</li>
</ul>

There is of course explicit ways of having the output be displayed in that manner.  But of course it isn't that simple.

1. You don't know the depth of the node structure ahead of time.  It can conceivably be as deep as it wants to be, meaning the output would have to create new unordered list elements for each node depth.

2. Well there really isn't a number 2.

Now me being the sharp guy that I am found that we can locate the beginning of a new node depth with the directive: count(preceding-sibling::*)=0

and we can know we are at the end of a node depth group with the directive: count(following-sibling::*)=0

So we can simply make a nice <xsl:choose> group with three statements, if we are at the start of a node depth area begin the template with a "<ul>".  Then if we are at the end of a node depth area then end the template with a "</ul>".  And the xsl:otherwise would just output a simple <li>.  Easy.

Well, easy in theory (at least in the theory of how I would LIKE it to work)..

Of course it does'nt work because it has mismatched tags that then break the xml'ishness (technical term) of the xslt.

This is a puzzler.

Does anyone have a solution?

Below is the broken xslt template.  note the open <ul> in the first <xsl:when> and the closing <ul> in the second when statement.  Removing those will make it operational.

<xsl:template match="item">                  
      <xsl:choose>
            <xsl:when test="count(preceding-sibling::*)=0">
                  <ul>
                        <li>
                        <xsl:value-of select="@num " />
                        </li>
                  <xsl:apply-templates select="item" />      
                                    
            </xsl:when>
                  
            <xsl:when test="count(following-sibling::*)=0">
                        <li>
                        <xsl:value-of select="@num " />
                        </li>
                        
                  </ul>
                  <xsl:apply-templates select="item" />      
                              
            </xsl:when>
                  
            <xsl:otherwise>
                  <li>
                        <xsl:value-of select="@num " />
                  </li>
                  <xsl:apply-templates select="item" />      
            </xsl:otherwise>            
                  
      </xsl:choose>      
</xsl:template>
ASKER CERTIFIED SOLUTION
Avatar of jurn
jurn

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial