XSLT匹配空节点。

4

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>

然后我在样式表中使用XSLT,如下所示:

<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>

我希望做的是有两个模板,一个仅在没有事件时显示。我知道可以使用XSLT和和测试来计算元素数量并调用正确的模板,就像在过程式语言中一样,但我正在尝试学习如何使用模板处理来完成此操作。 我得到的错误是: 预期表达式结束,找到'('. not -->(<-- node())
4个回答

8

not(node()) 不是有效的XSLT模式,尝试使用以下方式:

<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>

第一个模板将匹配具有(任何)子节点的“Event”节点。第二个模板则相反。我不认为它解决了OP的问题。 - Emiliano Poggi
@Dimitre 不认为这是最精确和正确的。如果 OP 不改变应用模板的方式(如我的答案中所示),第二个模板将永远不会被执行。 - Emiliano Poggi
@_empo:@Max_Toro已经提供了完整的解决方案--原帖作者应该直接使用它(例如复制和粘贴)。 - Dimitre Novatchev
为了清晰起见,对于任何试图做同样事情的人,这只在我使用$Events变量中的/Root/Events而不是我的问题所述的/Root/Events/Event时才有效。所以,我进行了更改。顺便说一下,在我的情况下,我的“Events”节点实际上是<in:result name="GetEvents">,因此我需要将第一个模板更改为:<xsl:template match="in:result[@name='GetEvents' and not(Event)]">使其正常工作。它有效了。^_^谢谢。 - Armstrongest
这很酷!我正在使用一个稍微不同的版本作为未被识别节点的备用方案,其中任何具有子元素(select =“”)的内容都会呈现为 div 块,而任何叶节点(select =“ [not (*)]”)都将呈现为键=值... - Alex

3

这个可以工作,但是我有点好奇...按照我的理解,星号代表了一组“事件”,对吗?那么这是否意味着匹配任何没有节点的“事件”或者如果它没有节点则匹配“该集合”? - Armstrongest
1
嗯,重新想一下,我很惊讶它确实起作用了... 我只是想纠正你的语法,而不是语义。 - Lukas Eder
这是任何元素(可以是RootEventsEvent)的匹配,它没有子节点(元素或文本)。它部分地解决了OP的问题并且很随意。 - Emiliano Poggi
1
我明白了。所以,它是任何没有节点的东西。它可能会起作用...但我认为在我的特定情况下不行...因为“Events”节点将具有属性节点。 - Armstrongest

2

新回答

仔细阅读您的问题后,我意识到我的上一个解决方案没有帮到您。以下是我的新答案:

由于您需要知道是否有任何元素在 Events 中,因此您需要更改匹配模板中的 /

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

现在有一个模板,只匹配填充了所需Event模板的Events
<xsl:template="Events[count(child::Event) &gt; 0]">
   <xsl:apply-template select='./Event'/>
</xsl:template>

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

现在,匹配空的Events的模板如下:
<xsl:template="Events[count(child::Event) = 0]">
   <p class="message">There are currently no upcoming events</p>
</xsl:template>

旧答案

尝试使用:

<!-- 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>

细节:
如果我没记错的话,在XPath / XSL中,“*”匹配任何类型的节点,即文本节点、元素节点、注释节点、属性节点等等...因此,在给定的上下文中,“*[string-length(self::*) = 0]”可能会与属性匹配;例如,您可能在其他地方有一个“select ='@ *'”。因此,此模板也可以应用于属性和元素。
如果您确定它不会匹配任何属性,则可以将其保留原样。但是,我希望我的代码表达正确的想法。因此,如果这个模板只适用于事件元素,我会更改匹配条件,例如:
<xsl:template match="Event[string-length(self::*) = 0]">
</xsl:template>

从查看XSLT规范http://www.w3.org/TR/xslt来看,*仅匹配元素。但是,我会进行测试。

1
你能帮忙解释一下避免使用属性的含义吗?这是否意味着如果一个Event里面没有内容,它也会被匹配到? - Armstrongest
更新答案,附加更多细节。 - manu

1

你的处理器抛出了一个错误,因为你的匹配模式导致了一个布尔值而不是一个节点。


尝试使用以下变换(保留所需的变量):
<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>

当应用于您的输入时,会产生:

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

当应用于没有事件的输入时,例如:

<Root>
  <Events/>
</Root>

产生:

<p class="message">There are currently no upcoming events</p>

1
我尝试过这个...发现无法使用$Events变量,例如 match="$Events[not(Event)]"。不幸的是,我的结构需要使用一个变量...我给出的示例是简化后反映了我得到的结构。 - Armstrongest
现在请检查我的答案。我已经编辑了模板,以便使用变量。希望这就是你要寻找的,否则我认为我需要更清晰的关于你的需求的信息。 - Emiliano Poggi
我认为这是我对“match”参数工作方式的误解。我试图做这个:<xsl:template match="$Events[not(Event)]">但它不喜欢$ - Armstrongest
是的,我意识到了...我改变了变量的值。一开始我希望能匹配"null",但是当我想明白后发现这样做没有多大意义。 - Armstrongest
仅在XSLT 2.0中允许在模板匹配中使用变量。 - Emiliano Poggi
你的解决方案还不是很清楚。如果这里没有人回答,你可以自己添加一个答案(一段时间后接受它)。我很好奇... - Emiliano Poggi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接