为什么默认情况下XSLT输出所有文本?


73

嗨,我执行了一个转换,如果该标记为null,则会删除该标记。

我想检查我的转换是否工作正常,所以我不再编写代码,而是手动编写了一个XSLT代码,该代码仅检查OUTPUT XML中特定标记的存在,如果为空,则第二个XSLT应该输出一个文字“找到”。(我实际上不需要某种XML输出,但我只是使用XSLT进行搜索。)

当我尝试使用此XSL代码::

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/SiebelMessage//SuppressCalendar[.!='']">
      FOUND
  </xsl:template>
</xsl:stylesheet>

它输出XML文件中存在的所有TEXT DATA,

为了避免这种情况,我必须编写以下代码::

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/SiebelMessage//SuppressCalendar[.!='']">
      FOUND
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

为什么以前的代码输出TEXT,为什么我要坚持XSL忽略所有其他文本?是所有XML解析器的行为还是我自己的行为(我正在使用msxml解析器)。

Answers:


147

为什么以前的代码输出TEXT,为什么我要坚持XSL忽略所有其他文本?是所有XML解析器的行为还是我自己的行为

您将发现规范中指定的最基本的XSLT功能之一:XSLT的内置模板

规格

有一个内置的模板规则,允许样式表中的显式模板规则在没有成功的模式匹配的情况下继续进行递归处理。此模板规则适用于元素节点和根节点。下面显示了等效的内置模板规则:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

每个模式还有一个内置的模板规则,如果样式表中的显式模板规则没有成功的模式匹配,则允许在相同模式下继续递归处理。此模板规则适用于元素节点和根节点。下面显示了模式m的等效内置模板规则。

<xsl:template match="*|/" mode="m">
  <xsl:apply-templates mode="m"/>
</xsl:template>

还有一个用于文本和属性节点的内置模板规则,可通过以下方式复制文本:

<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

用于处理指令和注释的内置模板规则不执行任何操作。

<xsl:template match="processing-instruction()|comment()"/>

命名空间节点的内置模板规则也不执行任何操作。没有可以匹配名称空间节点的模式。因此,内置模板规则是应用于名称空间节点的唯一模板规则。

内置模板规则被视为好像是在样式表之前隐式导入的,因此其导入优先级低于所有其他模板规则。因此,作者可以通过包含显式模板规则来覆盖内置模板规则。

因此,报告的行为是应用内置模板的结果-所有三个模板中的第一个和第二个。

一个很好的XSLT设计模式是用您自己的模板覆盖内置模板该模板每次被调用时都会发出错误消息,以便程序员立即知道他的转换是“泄漏的”:

例如,如果有此XML文档:

<a>
  <b>
    <c>Don't want to see this</c>
  </b>
</a>

并通过以下转换进行处理

<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:template match="a|b">
   <xsl:copy>
      <xsl:attribute name="name">
        <xsl:value-of select="name()"/>
      </xsl:attribute>
      <xsl:apply-templates/>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

结果是

<a name="a">
   <b name="b">Don't want to see this</b>
</a>

程序员会很困惑不需要的文本是如何出现的。

但是,仅添加它catch-all template有助于避免任何此类混淆并立即捕获错误

 <xsl:template match="*">
  <xsl:message terminate="no">
   WARNING: Unmatched element: <xsl:value-of select="name()"/>
  </xsl:message>

  <xsl:apply-templates/>
 </xsl:template>

现在,除了令人困惑的输出外,程序员还会收到警告,可以立即解释问题

 WARNING: Unmatched element: c

后来由Michael Kay为XSLT 3.0添加

在XSLT 3.0中,您可以添加xsl:mode声明的后备行为,而不是添加一个包罗万象的模板规则。例如,<xsl:mode on-no-match="shallow-skip"/>使所有不匹配的节点(包括文本节点)被跳过,而<xsl:mode on-no-match="fail"/>将不匹配视为错误,并<xsl:mode warning-on-no-match="true"/>产生警告。


4
+1:很好的解释-比接受的答案要完整得多,尽管接受的答案也可以回答特定的问题。
唐·罗比

@donroby:谢谢。为什么我的答案不能解决问题并回答?任何有此问题并阅读我的答案的人都将理解其问题的原因,并将能够解决该问题。
Dimitre Novatchev

@donroby:我的答复提供了确切的答案:"So, the reported behavior is the result of the application of the built-in templates -- the 2nd of all three of them"
Dimitre Novatchev

1
是的,您的回复也可以回答特定的问题。并不打算暗示其他情况。目的是不专门弃用已接受的答案。实际上,我确实希望您的回答。
唐·罗比

@ZacharyYoung,对不起,我不明白这个问题。如果您要说明为什么不匹配源XML文档中的所有可能元素(以及文本节点),那么对于此示例而言,这是不匹配的元素。
Dimitre Novatchev 2014年

14

XSL中有一些内置的模板规则,其中之一是:

<xsl:template match="text()|@*">
  <xsl:value-of select="."/>
</xsl:template>

它输出文本。


谢谢,是特定于特定的解析器还是一般而言?
InfantPro'Aravind'2010年

我想,对于所有解析器来说,它的通用规则都是因为它的w3c规范。好吧,谢谢:)
InfantPro'Aravind'2010年

1
@婴儿程序员-正如您所指出的,它在规范中...所有XSL解析器都必须实现它们。
奥德

该特定的内置规则转义了特殊的XML字符,即它将&amp;原始文本重写&amp;amp;为输出。为了避免这种情况,请disable-output-escaping="yes"<xsl:value-of>元素中添加一个属性。
延斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.