XSLT存档  

不及格的程序员-八神

 查看分类:  ASP.NET XML/XSLT JavaScripT   我的MSN空间Blog

多条件查询

string.Format("/root/deviceList//item/guid[{0}]", strBuilder.ToString())
"/root/deviceList//item/guid[text()=\"h\" or text()=\"a\" or text()=\"c\"]"
谓词嵌套
var nodes = xmlDoc.SelectNodes(string.Format("/root/deviceList//item[guid[{0}]]", strBuilder.ToString()));

 

Check if a string is null or empty in XSLT

Asked 10 years, 10 months ago
Viewed 506k times
323

How can I check if a value is null or empty with XSL?

For example, if categoryName is empty? 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>
  •  
    Can you expand the code example? – Nick Allen May 5 '09 at 16:48
  •  
    Depending on your use-case, you probably don't want to use xsl:when for node-tests. Consider <xsl:template match="Category[categoryName[not(node())]]">... together with a <xsl:template match="Category">.... The processor will then make the correct decisions for you and you do not need to write out the business logic in nested xsl:choose anymore. In many cases, using matching templates makes writing stylesheets easier. – Abel Jul 21 '14 at 2:01

14 Answers

318
 
test="categoryName != ''"

Edit: This covers the most likely interpretation, in my opinion, of "[not] null or empty" as inferred from the question, including it's pseudo-code and my own early experience with XSLT. I.e., "What is the equivalent of the following Java?":

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

For more details e.g., distinctly identifying null vs. empty, see johnvey's answer below and/or the XSLT 'fiddle' I've adapted from that answer, which includes the option in Michael Kay's comment as well as the sixth possible interpretation.

  • 14
    The detailed semantics of this test is: return true if there is at least one categoryName element whose string value is an empty string. – jelovirt May 11 '09 at 6:08
  • 14
    @jelovirt did you mean to say if there is at least one categoryName that's NOT an empty string? (I'm an xsl newbie, so forgive any potential stupidity to my question.) – joedevon Nov 4 '11 at 23:39
  • 10
    This answer, while accepted and highly voted, is also very misleading. It really depends what you mean by "null or empty". If you want a test that succeeds if categoryName is either absent, or present with a zero-length value, you should use test="not(categoryName = '')". The supplied answer will return false if the categoryName element is absent, which in my interpretation of the question makes it a wrong answer. – Michael Kay Aug 12 '15 at 14:55
  • 2
    @MichaelKay: I've updated the answer to provide more details. Thanks for the comment and for the Saxon XSLT processor! – steamer25 Aug 26 '15 at 22:53
  •  
    How can I translate <xsl:for-each select="root/*[matches(name(.), 'grp')]"> so it can be used in VS2010? – Si8 Oct 28 '15 at 17:21
269

Absent of any other information, I'll assume the following 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>

A sample use case would look like:

<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>
  •  
    How do you test for instances of </CategoryName> ? , empty string tests don't work for this – raffian Feb 14 '12 at 17:22 
  • 3
    It's appreciated that you included multiple examples to show how each expression results. – doubleJ Nov 7 '13 at 20:13
  • 1
    @raffian: in XSLT, or related technologies (XQuery, DOM, XDM, Schema etc), end-tags are not considered as separate entities. Instead, you only deal with nodes, or elements in this case, which is the whole between start-tag and end-tag. In short, there is no way to test for </CategoryName>, nor is there any need for. – Abel Jul 21 '14 at 1:51
  • 4
    I starred the question specifically for this answer, and while the question is pretty old, this one seems much more deserving of being the selected answer – Patrick Aug 1 '14 at 13:28
67

From Empty Element:

To test if the value of a certain node is empty

It depends on what you mean by empty.

  • Contains no child nodes: not(node())
  • Contains no text content: not(string(.))
  • Contains no text other than whitespace: not(normalize-space(.))
  • Contains nothing except comments: not(node()[not(self::comment())])
  • 2
    +1. Some notes. The first bulletpoint also tests for text-content, which is also a node. The second bulletpoint tests for any text node at any depth, if you want to know if the current node does not contain text, but can contain other nodes, you can use not(text()). An alternative to your 2nd bullet is also not(.//text()). As your last bullet shows: there are many ways to consider "nothingness" ;). – Abel Jul 21 '14 at 1:56 
  •  
    Very practical: To test if a string is not empty, you can just test the string itself! if ($mystring) then ... else ... – Felix Dombek Feb 14 '17 at 2:33
22

What about?

test="not(normalize-space(categoryName)='')"
  • 1
    This works great. Even when there is a comment inside <categoryName> <!-- some comment --> </categoryName> and otherwise no meaningful text, this still evaluates to true – rustyx Sep 15 '15 at 10:33 
9

First two deal with null value and second two deal with empty string.

<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
    Scary. What if there are multiple users or multiple firstnames? Use xsl:apply-templates and matching templates to get what you want, much easier. – Abel Jul 21 '14 at 2:06
7

In some cases, you might want to know when the value is specifically null, which is particularly necessary when using XML which has been serialized from .NET objects. While the accepted answer works for this, it also returns the same result when the string is blank or empty, i.e. '', so you can't differentiate.

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

So you can simply test the attribute.

<xsl:if test="CategoryName/@xsi:nil='true'">
   Hello World.
</xsl:if>

Sometimes it's necessary to know the exact state and you can't simply check if CategoryName is instantiated, because unlike say Javascript

<xsl:if test="CategoryName">
   Hello World.
</xsl:if>

Will return true for a null element.

6

I know this question is old, but between all the answers, I miss one that is a common approach for this use-case in XSLT development.

I am imagining that the missing code from the OP looks something like this:

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

And that the input looks something like this:

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

I.e., I assume there can be zero, empty, single or multiple categoryName elements. To deal with all these cases using xsl:choose-style constructs, or in other words, imperatively, is quickly getting messy (even more so if elements can be at different levels!). A typical programming idiom in XSLT is using templates (hence the T in XSLT), which is declarative programming, not imperative (you don't tell the processor what to do, you just tell what you want output if certain conditions are met). For this use-case, that can look something like the following:

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

This works (with any XSLT version), because the first one above has a higher precedence (it has a predicate). The "fall-through" matching template, the second one, catches anything that is not valid. The third one then takes care of outputting the categoryName value in a proper way.

Note that in this scenario there is no need to specifially match categories or category, because the processor will automatically process all children, unless we tell it otherwise (in this example, the second and third template do not further process the children, because there is no xsl:apply-templates in them).

This approach is more easily extendible then the imperative approach, because it automically deals with multiple categories and it can be expanded for other elements or exceptions by just adding another matching template. Programming without if-branches.

Note: there is no such thing as null in XML. There is xsi:nil, but that is rarely used, especially rarely in untyped scenarios without a schema of some sort.

  • 1
    Congrats on mentioning "Programming without if-branches". There are some people who fail to understand the importance of this. For all of them here is a link to a very nice Pluralsight course on this topic: "Tactical Design Patterns in .NET: Control Flow" by Zoran Horvat: app.pluralsight.com/library/courses/… A must-read! – Dimitre Novatchev Jan 21 '16 at 3:00
5

How can I check if a value is null or empty with XSL?

For example, if categoryName is empty?

This is probably the simplest XPath expression (the one in accepted answer provides a test for the opposite, and would be longer, if negated):

not(string(categoryName))

Explanation:

The argument to the not() function above is false() exactly when there is no categoryName child ("null") of the context item, or the (single such) categoryName child has string value -- the empty string.

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>

In XSLT 2.0 use:

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

Here is a complete example:

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

When this transformation is applied on the following XML document:

<categoryName>X</categoryName>

the wanted, correct result is produced:

X

When applied on this XML document:

<categoryName></categoryName>

or on this:

<categoryName/>

or on this

<somethingElse>Y</somethingElse>

the correct result is produced:

Other

Similarly, use this XSLT 1.0 transformation:

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

Do note: No conditionals are used at all. Learn more about the importance of avoiding conditional constructs in this nice Pluralsight course:

"Tactical Design Patterns in .NET: Control Flow"

4

If there is a possibility that the element does not exist in the XML I would test both that the element is present and that the string-length is greater than zero:

<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
    The string value of an empty node set (which is what the XPath expression categoryName gives you when there are no categoryName child elements in the current context) is defined to be the empty string, so this is redundant - string-length(categoryName) is zero if there are no categoryName elements. – Ian Roberts Jun 14 '13 at 16:25
3

If a node has no value available in the input xml like below xpath,

<node>
    <ErrorCode/>
</node>

string() function converts into empty value. So this works fine:

string(/Node/ErrorCode) =''
2

Something like this works for me:

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

Or the other way around:

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

Note: If you don't check null values or handle null values, IE7 returns -2147483648 instead of NaN.

1

I actually found it better just testing for string length since many times the field is not null, just empty

<xsl:when test="string-length(field-you-want-to-test)<1">

0

By my experience the best way is:

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

Use simple categoryName/text() Such test works fine on <categoryName/> and also <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>
 
Highly active question. Earn 10 reputation in order to answer this question. The reputation requirement helps protect this question from spam and non-answer activity.

Not the answer you're looking for? Browse other questions tagged     or ask your own question.

posted on 2022-09-06 13:50  不及格的程序员-八神  阅读(167)  评论(0编辑  收藏  举报