检查XSLT中的字符串是否为空或为空


325

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

例如,如果categoryName为空?我在选择构造使用了a 。

例如:

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

您可以扩展代码示例吗?
尼克·艾伦

根据您的用例,您可能不希望xsl:when用于节点测试。<xsl:template match="Category[categoryName[not(node())]]">...与一起考虑<xsl:template match="Category">...。然后,处理器将为您做出正确的决定,并且您不再需要写出嵌套的业务逻辑xsl:choose。在许多情况下,使用匹配的模板可以更轻松地编写样式表。
Abel 2014年

Answers:


322
test="categoryName != ''"

编辑:我认为,这涵盖了从问题中得出的对[[not] null or empty“的最可能的解释,包括其伪代码和我自己对XSLT的早期经验。即,“以下Java等价于什么?”:

!(categoryName == null || categoryName.equals(""))

有关更多详细信息,例如,明确标识空值还是空值,请参阅下面的约翰维答案和/或我根据该答案改编的XSLT“小提琴”,其中包括迈克尔·凯的评论中的选项以及第六种可能的解释。


14
此测试的详细语义是:如果至少一个categoryName元素的字符串值为空字符串,则返回true。
jelovirt

14
@jelovirt的意思是说是否至少有一个categoryName不是空字符串?(我是xsl新手,所以请原谅我的任何潜在愚蠢。)
joedevon 2011年

10
这个答案虽然被接受并获得了高度评价,但也很容易引起误解。这实际上取决于您所说的“空或空”。如果您希望类别不存在或长度值为零的情况下通过的测试成功,则应使用test="not(categoryName = '')"。如果没有categoryName元素,则提供的答案将返回false,这在我对问题的解释中使其成为错误的答案。
Michael Kay

2
@MichaelKay:我已经更新了答案以提供更多详细信息。感谢您的评论和Saxon XSLT处理器!
steamer25年

如何翻译<xsl:for-each select="root/*[matches(name(.), 'grp')]">以便可以在VS2010中使用?
Si8

276

缺少任何其他信息,我将假定以下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 2012年

3
值得赞赏的是,您包括了多个示例来展示每个表达式的结果。
doubleJ

1
@raffian:在XSLT或相关技术(XQuery,DOM,XDM,Schema等)中,结束标记不被视为单独的实体。相反,在这种情况下,您仅处理节点或元素,这是开始标记和结束标记之间的整体。简而言之,没有办法进行测试</CategoryName>,也没有任何需要。
Abel

4
我为这个答案专门加注了这个问题,虽然这个问题已经很老了,但这个问题似乎更值得我们选择-Patrick
Patrick

68

空元素

测试某个节点的值是否为空

这取决于您的意思是空的。

  • 不包含任何子节点: not(node())
  • 不包含文字内容: not(string(.))
  • 除空格外,不包含其他任何文本: not(normalize-space(.))
  • 除评论外不包含任何内容: not(node()[not(self::comment())])

2
+1。一些注意事项。第一个项目符号还测试文本内容,它也是一个节点。第二个项目符号要在任意深度测试任何文本节点,如果您想知道当前节点是否不包含文本,但可以包含其他节点,则可以使用not(text())。您的第二个项目符号的替代方法也是not(.//text())。正如您的最后一颗子弹所示:有很多方法可以考虑“虚无”;)。
Abel 2014年

非常实用:要测试字符串是否不为空,可以只测试字符串本身!if ($mystring) then ... else ...
Felix Dombek

22

关于什么?

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

1
这很好。即使内部有评论<categoryName> <!-- some comment --> </categoryName>,否则就没有有意义的文本,这仍将评估为true
rustyx

9

前两个处理空值,第二个处理空字符串。

<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

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>

对于null元素将返回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-style构造来处理所有这些情况,换句话说,势在必行,很快就会变得混乱(如果元素可以在不同的级别上,则更是如此)。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-templatesin他们)。

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

注意:nullXML中没有这样的东西。有xsi:nil,但是很少使用,尤其是在没有某种类型的架构的无类型脚本中很少使用。


1
祝贺您提到“ 不使用if分支进行编程 ”。有些人不明白这一点的重要性。对于所有这些人来说,这里都有一个非常不错的Pluralsight课程的链接,该课程与此主题有关:Zoran Horvat 撰写的.NET中的战术设计模式:控制流 ”:app.pluralsight.com/library/courses/… 必须阅读!
Dimitre Novatchev 2016年

5

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

例如,如果categoryName为空?

这可能是最简单的XPath表达式(接受的答案中的一个提供了相反的测试,如果被否定,则更长):

not(string(categoryName))

说明

not()上面函数的参数false()恰好在categoryName上下文项没有子代(“ null”)或(单个)categoryName子代有字符串值(即空字符串)的情况下。

在选择构造使用了a 。

例如:

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

嗨,@ Dimitre,我在这里发布了一个问题stackoverflow.com/questions/38150093/…–
zyberjock

4

如果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
空节点集的字符串值(categoryNamecategoryName当前上下文中没有子元素时,XPath表达式为您提供的字符串值)被定义为空字符串,因此这是多余的- string-length(categoryName)如果没有categoryName元素,则为零。
伊恩·罗伯茨

3

如果节点在输入xml中没有可用值,例如xpath下方,

<node>
    <ErrorCode/>
</node>

string()函数将转换为空值。所以这很好用:

string(/Node/ErrorCode) =''

2

这样的事情对我有用:

<xsl:choose>
  <xsl:when test="string(number(categoryName)) = 'NaN'"> - </xsl:when> 
  <xsl:otherwise> 
    <xsl:number value="categoryName" />
  </xsl:otherwise>
</xsl:choose>

或反过来:

<xsl:choose>
  <xsl:when test="string(number(categoryName)) != 'NaN'">
    <xsl:number value="categoryName" />
  </xsl:when> 
  <xsl:otherwise> - </xsl:otherwise>
</xsl:choose>

注意:如果您不检查空值或处理空值,则IE7返回-2147483648而不是NaN。


1

实际上,我发现仅测试字符串长度会更好,因为很多时候该字段都不为空,而是为空

<xsl:when test =“字符串长度(您要测试的字段)<1”>


0

以我的经验,最好的方法是:

<xsl:when test="not(string(categoryName))">
    <xsl:value-of select="other" />
</xsl:when>
<otherwise>
    <xsl:value-of select="categoryName" />
</otherwise>

0

使用简单的categoryName / text()这样的测试可以正常工作 <categoryName/>和上也<categoryName></categoryName>

<xsl:choose>
    <xsl:when test="categoryName/text()">
        <xsl:value-of select="categoryName" />
    </xsl:when>
    <xsl:otherwise>
        <xsl:value-of select="other" />
    </xsl:otherwise>
</xsl:choose>
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.