XSLT matching empty nodes
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())
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>
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) > 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>
精彩评论