Question

I have an XML structure like the following:

<Root>
  <!-- ...other stuff -->
  <Events>
    <Event date="0000-00-00">Event Description etc...</Event>
    <Event date="0000-00-00">Event Description etc...</Event>
    <Event date="0000-00-00">Event Description etc...</Event>
  </Events>
  <!-- ...other stuff -->
</Root>

Then I have XSLT in the Stylesheet like so:

<xsl:variable name="Events" select="/Root/Events/Event" />

<xsl:template match="/">
  <!-- Stuff -->    
  <xsl:apply-templates select="$Events" />
  <!-- Stuff -->    
</xsl:template>

<xsl:template match="Event">
  <!-- Regular Event Template Transformation here -->
</xsl:template>    

<!-- ERROR HAPPENS HERE -->
<xsl:template match="not(node())">
  <p class="message">There are currently no upcoming events</p>
</xsl:template>

What I WANT to do is have two templates, one which only shows when there are no events. I KNOW i can use XSLT with <xsl:choose> and <xsl:when> tests to do a count of elements and just call the right template like I would do in procedural languages, but I'm trying to learn how to do this with template processing.

The error I'm getting is : Expected end of the expression, found '('. not -->(<-- node())

Was it helpful?

Solution

not(node()) is not a valid XSLT pattern, try this:

<xsl:template match="Event">
  <!-- Regular Event Template Transformation here -->
</xsl:template>    

<xsl:template match="Events[not(Event)]">
  <p class="message">There are currently no upcoming events</p>
</xsl:template>

OTHER TIPS

This might work better

<xsl:template match="*[not(node())]">
  <!-- ... -->

See also this answer here: XSLT To remove empty nodes and nodes with -1

New answer

Reading your question thoroughly, I realize that my last solutions leads you nowhere. My new answer follows:

Since you need to know whether are any elements inside Events you need to change the / matching template:

<xsl:template match="/">
   <!-- Stuff -->    
   <xsl:apply-templates select="/Root/Events" />
   <!-- Stuff -->    
</xsl:template>

Now a template that only matches a populate Events along with the required Event template:

<xsl:template="Events[count(child::Event) &gt; 0]">
   <xsl:apply-template select='./Event'/>
</xsl:template>

<xsl:template="Event">
    <!-- some stuff -->
</xsl:template>

Now the template that matches an empty Events:

<xsl:template="Events[count(child::Event) = 0]">
   <p class="message">There are currently no upcoming events</p>
</xsl:template>

Old answer

Try with:

<!-- This matches every node with an empty string repr. Maybe you want
     to replace "self::*" to avoid attributes, etc." -->
<xsl:template match="*[string-length(self::*) = 0]">
</xsl:template>

Details:

If I recall correctly, in XPath/XSL '*' matches any type of node, i.e., text nodes, element nodes, comment nodes, attribute nodes, etc... So in a given context *[string-length(self::*) = 0] may match with a attribute; for instance, you may have a select='@*' somewhere else. So this template may be applied to attributes as well as elements.

If you're sure it won't match any attribute, you may leave it as it is. However, I like that my code expresses the right ideas. So, if this template your be applied to Event elements only, I would change the match for something like:

<xsl:template match="Event[string-length(self::*) = 0]">
</xsl:template>

Taking a peek at the XSLT spec at http://www.w3.org/TR/xslt, it seems that * matches only elements. However, I would test it.

Your processor throws an error because your matching pattern results in a boolean value and not in a node.


Try this transform out (which keeps the required variable):

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

    <xsl:variable name="Events" select="/Root/Events/Event"/>

    <xsl:template match="Root">
        <xsl:apply-templates select="$Events
            | Events[not(Event)]"/>  
    </xsl:template>

    <xsl:template match="Event">
        <p class="message">There is an event</p>
    </xsl:template>    

    <xsl:template match="Events">
        <p class="message">There are currently no upcoming events</p>
    </xsl:template>

</xsl:stylesheet>

When applied on your input, produces:

<p class="message">There is an event</p>
<p class="message">There is an event</p>
<p class="message">There is an event</p>

When applied on input without events, such as:

<Root>
  <Events/>
</Root>

produces:

<p class="message">There are currently no upcoming events</p>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top