如何正确使用XSLT的if语句?

3
我创建了一个XSLT样式表,用于查找并删除一个节点。这个功能非常好用。现在我想检查是否存在某个特定的节点,如果存在就删除它。因此,我尝试添加一个if语句,但是遇到了以下错误:
"编译错误:文件dt.xls第10行元素模板,元素模板只允许作为样式表的子元素"
我认为我理解了错误的原因,但不确定如何解决它。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="Ad">
    <xsl:template match="node()|@*">

      <xsl:if test="name-ad-size">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
      </xsl:if>

    </xsl:template>
  </xsl:template>


  <xsl:template match="phy-ad-width"/>
  <xsl:strip-space elements="*"/>
  <xsl:preserve-space elements="codeListing sampleOutput"/>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

我已经修复了你的格式,但是你的XSL无效,因为你不能在模板中放置<template... - Jim Garrison
@Jim:啊哈,我只看到了xsl:if,所以我猜测问题的其余部分。因为它不在代码块中,所以其余部分消失了。无论如何,答案应该保持大致相同;-) - Abel
1个回答

5
通常人们初次尝试XSLT时,会认为它是像C#、Java、PHP一样的语言。这些语言都用于告诉计算机要做什么。但是在XSLT中,情况相反,您需要根据规则告诉处理器您期望的输出。
有时使用xsl:if是很好的选择。但更多时候,它是一个错误的迹象。删除节点、元素或文本的技巧是创建一个匹配模板,其输出为空。例如:
<!-- starting point -->
<xsl:template match="/">
    <xsl:apply-templates select="root/something" />
</xsl:template>

<xsl:template match="name-ad-size">
   <!-- don't do anything, continue processing the rest of the document -->
   <xsl:apply-templates select="node() | @*" />
</xsl:template>

<!-- copy anything else -->
<xsl:template match="node() | @*">
   <xsl:copy>
      <xsl:apply-templates select="node() | @*" />
   </xsl:copy>
</xsl:template>

为什么这个方法有效?因为处理器会遍历每个元素和节点,并首先查找最佳匹配的模板。对于节点<name-ad-size>,最佳匹配是不输出任何内容的匹配,因此它有效地将其删除。其他节点没有匹配,因此最终落入“catch all”模板中。
注意1:您收到的错误很可能是因为您错误地将<xsl:template>添加到另一个元素中。它只能放置在根<xsl:stylesheet>下,而不能放在其他任何位置。
注意2: <xsl:template>语句的顺序无关紧要。处理器将使用所有模板来查找最佳匹配,无论它们放在哪里(只要它们直接放在根下)。
编辑:有人神奇地检索到了您的代码。上述故事已应用于您的完整样式表。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:preserve-space elements="codeListing sampleOutput"/>

  <!-- NOTE: it is better to have a starting point explicitly -->
  <xsl:template match="/">
    <xsl:apply-templates select="root/something" />
  </xsl:template>

  <!-- I assume now that you meant to delete the <Ad> elements -->
  <xsl:template match="Ad">
     <xsl:apply-templates select="node()|@*"/>
  </xsl:template>

  <!-- NOTE: here you were already deleting <phy-ad-width> and everything underneath it -->
  <xsl:template match="phy-ad-width"/>

  <!-- NOTE: copies everything that has no matching rule elsewhere -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

学习XSL的关键在于它不是一种命令式编程语言。 XSL处理器不使用样式表来驱动处理器;相反,它是输入XML文档(您正在处理的文档,而不是样式表)控制过程。当读取每个标记时,处理器通过搜索XSL以查找最佳匹配模板来决定要执行的操作。这有点过于简化了,但是学会将文档视为控制对象而不是样式表是必需的直觉飞跃。 - Jim Garrison
@Jim:我想这就是我想表达的。我知道这种颠倒思维是完全正确的,但我注意到学生很难将XML(或其他输入)作为主导角色。这就是我用 "规则" 进行比较的原因。当我使用XSLT时,我编写规则,当我编写规则时,我会问“如果处理器在输入XML中找到与我的规则匹配的节点,我们应该输出什么?” - Abel
哇,谢谢,我明白你的意思了,你是对的。我是一名C#程序员,我之前就是这样看待它的。然而,在我删除<xsl:template match="phy-ad-width"/>之前,我想要检查一下是否存在节点"name-ad-size"。我需要进行这个检查的原因是有时候我需要phy-ad-width,有时候我不需要,我的关键在于"name-ad size",如果它存在,那么我就想要删除phy-ad-width,这就是为什么我认为在这里可能需要一个if语句。再次感谢您提供这个好答案。 - Mike
@kc2scy:忘掉那个,不要考虑“是否存在或不存在”的问题。为存在的情况创建一个规则。当不存在时,它将被忽略。如果必须为不存在的情况创建规则,请使用<xsl:template match="parent-name[not(phy-ad-width)]">,它将应用于没有<phy-ad-width><parent-name>。当然,许多其他规则或组合也是可能的。 - Abel

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