为啥 XSLT 默认输出所有文本?

Posted

技术标签:

【中文标题】为啥 XSLT 默认输出所有文本?【英文标题】:Why does XSLT output all text by default?为什么 XSLT 默认输出所有文本? 【发布时间】:2011-03-22 13:28:15 【问题描述】:

您好,我执行了一个转换,如果标签为空,则删除它。

我想检查我的转换是否工作正常,因此我没有手动检查,而是编写了另一个 XSLT 代码,它只检查 OUTPUT XML 中该特定标记的存在,如果它为空,则第二个 XSLT应该输出一个文本“FOUND”。 (我实际上并不需要某种 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 解析器)。

【问题讨论】:

【参考方案1】:

为什么之前的代码输出的是TEXT, 为什么我要坚持 XSL 忽略所有 其他文字?那是 所有的 XML 解析器或只有我自己的

您正在发现规范中指定的最基本的 XSLT 功能之一:built-in templates of XSLT

来自the Spec

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

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

内置模板规则 处理指令和 cmets 就是什么都不做。

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

内置模板规则 命名空间节点也是什么都不做。 没有模式可以匹配 命名空间节点;所以,内置 模板规则是唯一的模板 应用于命名空间的规则 节点。

内置的模板规则是 被视为进口的 隐式在样式表之前和 所以进口优先级低于 所有其他模板规则。就这样 作者可以覆盖内置 通过包含显式的模板规则 模板规则。

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

用您自己的模板覆盖内置模板是一种很好的 XSLT 设计模式,它会在调用时发出错误消息,以便程序员立即知道他的转换正在“泄漏”: p>

例如,如果有这个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 声明中指定回退行为,而不是添加包罗万象的模板规则。例如,&lt;xsl:mode on-no-match="shallow-skip"/&gt; 导致所有不匹配的节点(包括文本节点)被跳过,而&lt;xsl:mode on-no-match="fail"/&gt; 将不匹配视为错误,&lt;xsl:mode warning-on-no-match="true"/&gt; 导致警告。

【讨论】:

+1:很好的解释 - 比接受的答案更完整,尽管接受的答案也回答了具体问题。 @donroby:谢谢。为什么我的回答不解决问题并回答它?任何遇到此问题并阅读我的回答的人都会了解他们的问题的原因并能够解决它。 @donroby:我的回复提供了准确的答案:"So, the reported behavior is the result of the application of the built-in templates -- the 2nd of all three of them" 是的,您的回复也回答了具体问题。无意暗示其他。目的是不特别反对接受的答案。不过,我确实更喜欢你的回答。 @ZacharyYoung,对不起,我不明白这个问题。如果您的意思是为什么不是源 XML 文档中的所有可能元素都匹配(以及文本节点),这是因为此示例具有不匹配的元素。【参考方案2】:

XSL 中有几个built in template rules,其中之一是这样的:

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

它输出文本。

【讨论】:

谢谢,那么它是特定于特定解析器的,还是一般的? 我猜,它适用于所有解析器的一般规则'因为它的 w3c 规范。好的,谢谢:) @infant 程序员 - 正如您所指出的那样,它在规范中......所有 XSL 解析器都必须实现它们。 特定的内置规则转义special XML characters,即它将原始文本中的&amp;amp;amp; 重写为输出中的&amp;amp;amp;。为避免这种情况,请将disable-output-escaping="yes" 作为属性添加到&lt;xsl:value-of&gt; 元素。

以上是关于为啥 XSLT 默认输出所有文本?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Mallet 文本分类为所有测试文件输出相同的值 1.0?

为啥我的所有 UIAlertView 标签/文本在 iOS 8 中默认为粗体

为啥有些文本框默认不接受 Control + A 快捷方式全选

如何使用 XSLT 将 base64 编码的文本加载到 XML 文档中?

源文件中的长文本字符串,导致输出文件中出现多行

XSLT 表在输出中乱序