在XSLT中检查一个字符串是否为空或null

353

如何使用XSL检查一个值是null还是空的?

比如,如果categoryName是空的怎么办?我正在使用一个选择时结构。

例如:

<xsl:choose>
    <xsl:when test="categoryName !=null">
        <xsl:value-of select="categoryName " />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>

你能扩展一下代码示例吗? - Nick Allen
根据您的用例,您可能不想使用xsl:when进行节点测试。考虑使用<xsl:template match="Category[categoryName[not(node())]]">...</xsl:template><xsl:template match="Category">...</xsl:template>。处理器将为您做出正确的决策,您无需再嵌套编写业务逻辑的xsl:choose。在许多情况下,使用匹配模板可以使样式表编写更加容易。 - Abel
14个回答

351
test="categoryName != ''"

编辑:根据我的理解,包括伪代码和我自己在XSLT方面的早期经验,这涵盖了“[非]null或空”的最有可能的解释。也就是说,“以下Java代码的等效代码是什么?”

// Equivalent Java, NOT XSLT
!(categoryName == null || categoryName.equals(""))

有关更多细节,例如明确定义null和empty的区别,请参见下面 johnvey的答案 和/或我从该答案中改编的XSLT 'fiddle',其中包括Michael Kay评论中的选项以及第六种可能的解释。


15
此测试的详细语义是:如果存在至少一个类别名称元素的字符串值为空字符串,则返回true。 - jelovirt
14
@jelovirt你是不是想说如果至少有一个categoryName不是空字符串?(我是XSL新手,所以请原谅我问题可能会有些愚蠢。) - joedevon
12
这个回答虽然被接受并且得到了很高的投票,但是它也非常误导。这取决于你对“null或为空”的理解。如果你想要一个测试,在categoryName不存在或存在零长度值时都能成功,那么你应该使用“test=‘not(categoryName = '')’”。提供的答案将会返回false,如果categoryName元素不存在,在我对问题的解释中,这就是一个错误的答案。 - Michael Kay
2
@MichaelKay:我已经更新了答案,提供了更多细节。感谢您的评论和Saxon XSLT处理器! - steamer25
如何将 <xsl:for-each select="root/*[matches(name(.), 'grp')]"> 翻译成可在 VS2010 中使用的代码? - Si8
显示剩余5条评论

301

如果没有其他信息,我会假设以下XML:

<group>
    <item>
        <id>item 1</id>
        <CategoryName>blue</CategoryName>
    </item>
    <item>
        <id>item 2</id>
        <CategoryName></CategoryName>
    </item>
    <item>
        <id>item 3</id>
    </item>
    ...
</group>

一个示例用例可能如下:

<xsl:for-each select="/group/item">
    <xsl:if test="CategoryName">
        <!-- will be instantiated for item #1 and item #2 -->
    </xsl:if>
    <xsl:if test="not(CategoryName)">
        <!-- will be instantiated for item #3 -->
    </xsl:if>
    <xsl:if test="CategoryName != ''">
        <!-- will be instantiated for item #1 -->
    </xsl:if>
    <xsl:if test="CategoryName = ''">
        <!-- will be instantiated for item #2 -->
    </xsl:if>
</xsl:for-each>

你如何测试 </CategoryName> 的实例?空字符串测试对此无效。 - raffian
3
感谢您提供了多个例子来展示每个表达式的效果。 - doubleJ
1
@raffian:在XSLT或相关技术(XQuery、DOM、XDM、Schema等)中,结束标签不被视为单独的实体。相反,您只处理节点或元素,在这种情况下是开始标记和结束标记之间的整个内容。简而言之,没有办法测试</CategoryName>,也没有任何必要这样做。 - Abel
4
我特意为这个答案点了赞,虽然这个问题很旧,但这个答案似乎更值得被选为最佳答案。 - Patrick

78

来自空元素:

要测试某个节点的值是否为空,取决于您所说的“空”是指什么。

  • 不包含任何子节点:not(node())
  • 不包含任何文本内容:not(string(.))
  • 除空格外没有任何文本:not(normalize-space(.))
  • 只包含注释内容:not(node()[not(self::comment())])

2
+1. 一些注释。第一个项目点还测试文本内容,这也是一个节点。第二个项目点测试任何深度的任何文本节点,如果您想知道当前节点是否不包含文本,但可以包含其他节点,则可以使用 not(text())。你的第二个项目点的替代方案也是 not(.//text())。正如你最后一个项目点所示:有很多方法来考虑“无”。;) - Abel
非常实用:要测试一个字符串是否不为空,你只需要测试字符串本身!if ($mystring) then ... else ... - Felix Dombek

26

怎么样?

test="not(normalize-space(categoryName)='')"

2
这个很好用。即使在<categoryName> <!-- some comment --> </categoryName>中有注释而且没有其他有意义的文本,它仍然会被评估为“true”。 - rustyx

10

前两个处理 null 值,后两个处理空字符串。

<xsl:if test="USER/FIRSTNAME">
    USERNAME is not null
</xsl:if>
<xsl:if test="not(USER/FIRSTNAME)">
    USERNAME is null
 </xsl:if>
 <xsl:if test="USER/FIRSTNAME=''">
     USERNAME is empty string
 </xsl:if>
 <xsl:if test="USER/FIRSTNAME!=''">
     USERNAME is not empty string
 </xsl:if>

1
可怕。如果有多个用户或多个名字怎么办?使用 xsl:apply-templates 和匹配模板,可以更轻松地获取所需内容。 - Abel

8

如何使用XSL检查值是否为null或空?

例如,如果categoryName为空怎么办?

这可能是最简单的XPath表达式(接受答案中提供了相反的测试,并且如果取反,将更长):

not(string(categoryName))

解释:

上述not()函数的参数 false() 恰好在以下情况下成立: 上下文项中不存在categoryName子元素(即“null”),或者(唯一的)categoryName子元素具有空字符串的字符串值。

I'm using a when choosing construct.

For example:

<xsl:choose>
    <xsl:when test="categoryName !=null">
        <xsl:value-of select="categoryName " />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>

XSLT 2.0 中使用:

<xsl:copy-of select="concat(categoryName,  $vOther[not(string(current()/categoryName))])"/>

以下是完整示例

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vOther" select="'Other'"/>

 <xsl:template match="/">
  <xsl:copy-of select="concat(categoryName,$vOther[not(string(current()/categoryName))])"/>
 </xsl:template>
</xsl:stylesheet>

当这个转换应用于以下XML文档时:
<categoryName>X</categoryName>

期望的,正确的结果已生成:

X

当应用于此XML文档时:

<categoryName></categoryName>

或者这个:
<categoryName/>

或者在这里

<somethingElse>Y</somethingElse>

正确的结果将会生成:

Other

同样地,使用这个 XSLT 1.0 转换:
<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="vOther" select="'Other'"/>

  <xsl:template match="/">
    <xsl:copy-of select=
    "concat(categoryName,  substring($vOther, 1 div not(string(categoryName))))"/>
  </xsl:template>
</xsl:stylesheet>

请注意:此处没有使用任何条件语句。在这个很好的Pluralsight课程中了解避免条件结构的重要性:“.NET战术设计模式:控制流”

嗨,Dimitre,我需要你的1.0解决方案,那么我需要在我拥有的每个标签中编写代码吗?还是有一种更简单的方法来实现整个XML? - zyberjock
@zyberjock,您的问题不太清楚。请发布一个问题并发送带有链接的评论。遵循如何提出好问题的指南。 - Dimitre Novatchev
嗨@Dimitre,我在这里发布了一个问题http://stackoverflow.com/questions/38150093/set-a-default-value-for-each-empty-xml-tags-in-xslt-1-0 - zyberjock

7

在某些情况下,您可能想要知道值是否特定为null,这在使用已从.NET对象序列化的XML时尤其必要。虽然接受的答案适用于此,但当字符串为空或空白时(即''),它也会返回相同的结果,因此您无法区分。

<group>
    <item>
        <id>item 1</id>
        <CategoryName xsi:nil="true" />
    </item>
</group>

所以您可以简单地测试该属性。
<xsl:if test="CategoryName/@xsi:nil='true'">
   Hello World.
</xsl:if>

有时候需要知道确切的状态,而不能仅仅检查 CategoryName 是否被实例化,因为与 Javascript 不同。
<xsl:if test="CategoryName">
   Hello World.
</xsl:if>

对于空元素将返回 true。


6

我知道这个问题旧了,但在所有答案中,我错过了一个在XSLT开发中常见的方法。

我想象OP缺失的代码看起来像这样:

<xsl:template match="category">
    <xsl:choose>
        <xsl:when test="categoryName !=null">
            <xsl:value-of select="categoryName " />
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="other" />
        </xsl:otherwise>
    </xsl:choose>
</category>

而输入看起来像这样:
<categories>
    <category>
       <categoryName>Books</categoryName>
    </category>
    <category>
       <categoryName>Magazines</categoryName>
       <categoryName>Periodicals</categoryName>
       <categoryName>Journals</categoryName>
    </category>
    <category>
        <categoryName><!-- please fill in category --></categoryName>
    </category>
    <category>
        <categoryName />
    </category>
    <category />
</categories>

也就是说,我假设categoryName元素可以为零、空、单个或多个。使用xsl:choose风格的构造处理所有这些情况,或者换句话说,采用命令式方法,很快就会变得混乱(如果元素可以位于不同的层次结构中,则更加如此!)。在XSLT中,一个典型的编程惯用语是使用模板(因此在XSLT中有T),这是一种声明式编程,而不是命令式编程(您不告诉处理器要做什么,只是告诉它在满足某些条件时要输出什么内容)。对于这种用例,可以像以下示例一样:

<!-- positive test, any category with a valid categoryName -->
<xsl:template match="category[categoryName[text()]]">
    <xsl:apply-templates />
</xsl:template>

<!-- any other category (without categoryName, "null", with comments etc) -->
<xsl:template match="category">
    <xsl:text>Category: Other</xsl:text>
</xsl:template>

<!-- matching the categoryName itself for easy handling of multiple names -->
<xsl:template match="categoryName">
    <xsl:text>Category: </xsl:text>
    <xsl:value-of select="." />
</xsl:template>

这个例子适用于任何XSLT版本,因为上面的第一个模板具有更高的优先级(它具有谓词)。"落空"匹配模板,即第二个模板,捕获任何无效内容。第三个模板负责以正确的方式输出categoryName值。

请注意,在这种情况下,没有必要特别匹配categoriescategory,因为处理器会自动处理所有子元素,除非我们告诉它不这样做(在本例中,第二个和第三个模板不进一步处理子元素,因为它们中没有xsl:apply-templates)。

这种方法比命令式方法更易于扩展,因为它自动处理多个类别,并且可以通过添加另一个匹配模板来扩展其他元素或异常。 编程无需if分支

注意:XML中没有null这样的东西。有xsi:nil,但很少使用,尤其是在没有某种类型的模式的未分类场景中很少使用。


1
恭喜提到“无if语句编程”。有些人不理解这一点的重要性。对于所有这些人,这里有一个非常好的Pluralsight课程链接,介绍这个主题:“Tactical Design Patterns in .NET: Control Flow”,作者是Zoran Horvat:https://app.pluralsight.com/library/courses/tactical-design-patterns-dot-net-control-flow/table-of-contents 这是必读的! - Dimitre Novatchev

5

如果在XML中可能不存在该元素,则需要同时测试元素是否存在以及字符串长度大于零:

<xsl:choose>
    <xsl:when test="categoryName and string-length(categoryName) &gt; 0">
        <xsl:value-of select="categoryName " />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>

3
当当前上下文中没有categoryName子元素时,XPath表达式categoryName会返回一个空节点集的字符串值。这个字符串被定义为空字符串,因此string-length(categoryName)为零就显得多余了。 - Ian Roberts

3

如果一个节点在输入的xml中没有可用的值,就像下面的xpath一样:

<node>
    <ErrorCode/>
</node>

string()函数将值转换为空值。因此,以下代码可以正常工作:

string(/Node/ErrorCode) =''

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