在 XSLT 中检查字符串是不是为空或空
Posted
技术标签:
【中文标题】在 XSLT 中检查字符串是不是为空或空【英文标题】:Check if a string is null or empty in XSLT在 XSLT 中检查字符串是否为空或空 【发布时间】:2010-10-23 23:22:26 【问题描述】:如何使用XSL 检查值是否为空或空?
例如,如果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>
【问题讨论】:
可以扩展代码示例吗? 根据您的用例,您可能不想使用xsl:when
进行节点测试。考虑<xsl:template match="Category[categoryName[not(node())]]">...
和<xsl:template match="Category">...
。然后处理器将为您做出正确的决定,您不再需要在嵌套的xsl:choose
中写出业务逻辑。在许多情况下,使用匹配的模板可以更轻松地编写样式表。
【参考方案1】:
test="categoryName != ''"
编辑:在我看来,这涵盖了从问题中推断出的“[not] null or empty”最可能的解释,包括它的伪代码和我自己早期使用 XSLT 的经验.即,“以下 Java 的等价物是什么?”:
// Equivalent Java, NOT XSLT
!(categoryName == null || categoryName.equals(""))
有关更多详细信息,例如,明确识别 null 与空,请参阅 johnvey's answer below 和/或 the XSLT 'fiddle' 我已改编自该答案,其中包括 Michael Kay 评论中的选项以及第六种可能的解释。
【讨论】:
这个测试的详细语义是:如果至少有一个categoryName元素的字符串值为空字符串,则返回true。 @jelovirt 你的意思是说是否至少有一个 categoryName 不是空字符串? (我是 xsl 新手,请原谅我的问题可能存在的愚蠢问题。) 这个答案虽然被接受并被高度评价,但也非常具有误导性。这真的取决于你所说的“空或空”是什么意思。如果您希望在 categoryName 不存在或以零长度值存在时成功的测试,您应该使用test="not(categoryName = '')"
。如果 categoryName 元素不存在,则提供的答案将返回 false,在我对问题的解释中,这使其成为错误答案。
@MichaelKay:我已经更新了答案以提供更多详细信息。感谢您的评论和 Saxon XSLT 处理器!
如何翻译<xsl:for-each select="root/*[matches(name(.), 'grp')]">
以便在VS2010中使用?【参考方案2】:
来自Empty Element:
测试某个节点的值是否为空
这取决于你所说的空是什么意思。
不包含子节点:not(node())
不包含文本内容:not(string(.))
除空格外不包含任何文本:not(normalize-space(.))
除了 cmets 不包含任何内容:not(node()[not(self::comment())])
【讨论】:
+1。一些笔记。第一个要点还测试文本内容,它也是一个节点。第二个要点测试任意深度的任意文本节点,如果想知道当前节点是否不包含文本,但可以包含其他节点,可以使用not(text())
。你的第二个子弹的替代品也是not(.//text())
。正如您的最后一个项目符号所示:有很多方法可以考虑“虚无”;)。
非常实用:要测试一个字符串是否非为空,你可以只测试字符串本身! if ($mystring) then ... else ...
【参考方案3】:
如果没有任何其他信息,我将假设以下 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:在 XSLT 或相关技术(XQuery、DOM、XDM、Schema 等)中,结束标签不被视为单独的实体。相反,在这种情况下,您只处理节点或元素,即开始标记和结束标记之间的整体。总之,没有办法测试</CategoryName>
,也没有必要。
我专门为这个答案加了星标,虽然这个问题已经很老了,但这个问题似乎更值得成为选择的答案【参考方案4】:
在某些情况下,您可能想知道该值何时具体为 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。
【讨论】:
【参考方案5】:这样的东西对我有用:
<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>
注意:如果不检查 null 值或不处理 null 值,IE7 将返回 -2147483648 而不是 NaN。
【讨论】:
【参考方案6】:怎么样?
test="not(normalize-space(categoryName)='')"
【讨论】:
这很好用。即使<categoryName> <!-- some comment --> </categoryName>
内有评论并且没有有意义的文本,这仍然评估为true
【参考方案7】:
前两个处理空值,后两个处理空字符串。
<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>
【讨论】:
可怕。如果有多个用户或多个名字怎么办?使用xsl:apply-templates
和匹配的模板来获得你想要的,更容易。【参考方案8】:
如果 XML 中可能不存在该元素,我将测试该元素是否存在以及字符串长度是否大于零:
<xsl:choose>
<xsl:when test="categoryName and string-length(categoryName) > 0">
<xsl:value-of select="categoryName " />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="other" />
</xsl:otherwise>
</xsl:choose>
【讨论】:
空节点集的字符串值(当当前上下文中没有categoryName
子元素时,XPath 表达式categoryName
为您提供)被定义为空字符串,所以这是多余的 - 如果没有 categoryName
元素,string-length(categoryName)
为零。【参考方案9】:
根据我的经验,最好的方法是:
<xsl:when test="not(string(categoryName))">
<xsl:value-of select="other" />
</xsl:when>
<otherwise>
<xsl:value-of select="categoryName" />
</otherwise>
【讨论】:
【参考方案10】:我知道这个问题很老,但在所有答案中,我错过了一个在 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
值。
请注意,在这种情况下,无需专门匹配categories
或category
,因为处理器将自动处理所有孩子,除非我们另有说明(在此示例中,第二个和第三个模板不会进一步处理孩子,因为他们没有xsl:apply-templates
)。
这种方法比命令式方法更容易扩展,因为它自动处理多个类别,并且可以通过添加另一个匹配模板来扩展其他元素或异常。 没有 if 分支的编程。
注意:在 XML 中没有 null
这样的东西。有xsi:nil,但很少使用,尤其是在没有某种模式的无类型场景中。
【讨论】:
恭喜您提到“没有 if 分支的编程”。有些人不明白这一点的重要性。对于他们所有人来说,这里有一个关于这个主题的非常好的 Pluralsight 课程的链接:Zoran Horvat 的“Tactical Design Patterns in .NET: Control Flow”:app.pluralsight.com/library/courses/… 必读!跨度> 【参考方案11】:如果一个节点在输入 xml 中没有可用的值,如下面的 xpath,
<node>
<ErrorCode/>
</node>
string() 函数转换为空值。所以这很好用:
string(/Node/ErrorCode) =''
【讨论】:
【参考方案12】:使用简单的 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>
【讨论】:
【参考方案13】:如何使用 XSL 检查值是 null 还是空?
例如,如果
categoryName
为空?
这可能是最简单的 XPath 表达式(接受答案中的表达式提供了相反的测试,如果否定,会更长):
not(string(categoryName))
解释:
上面not()
函数的参数是false()
,恰好当上下文项没有categoryName
子(“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>
在 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 课程中详细了解避免条件结构的重要性:
"Tactical Design Patterns in .NET: Control Flow"
【讨论】:
嗨 Dimitre,我需要你的 1.0 解决方案,所以我需要在我拥有的每个标签中对其进行编码,还是有更简单的方法来为整个 XML 实现它? @zyberjock,不清楚你在问什么。请发表一个问题,并给我一个带有链接的评论。遵循如何提出好问题的指南。 嗨@Dimitre,我在这里发布了一个问题***.com/questions/38150093/…【参考方案14】:实际上我发现只测试字符串长度更好,因为很多时候该字段不是空的,只是空的
【讨论】:
以上是关于在 XSLT 中检查字符串是不是为空或空的主要内容,如果未能解决你的问题,请参考以下文章
Python Django 模板和测试变量是不是为空或空字符串