Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSL / XPath expression to check if a node contains at least one non-empty child

Tags:

xml

xslt

xpath

I need to check if an XML node has at least one non-empty child. Applied to this XML the expression should return true

<xml>
    <node>
       <node1/>
       <node2/>
       <node3>value</node3>
    </node>
</xml>

I tried to use this expression: <xsl:if test="not(/xml/node/child::* = '')"> but it seems to check if all children are not empty.

How can I write an expression which returns true if at least one element is not empty? Is there a way to do this without creating another template to iterate over node chldren?

UPD: I'm thinking of counting non-empty nodes like
test="count(not(/xml/node/child::* = '')) &gt; '0'"
but somehow just can't make it work right. This expression is not a well-formed one.

like image 299
svz Avatar asked Dec 04 '12 12:12

svz


3 Answers

More accurate, simpler and more efficient (no need to use the count() function):

  /*/node/*[text()]

If you want to exclude any element that has only whitespace-only text children, use:

  /*/node/*[normalize-space()]
like image 197
Dimitre Novatchev Avatar answered Nov 20 '22 21:11

Dimitre Novatchev


You just need <xsl:if test="/xml/node/* != ''" />.

In XPath an = or != comparison where one side is a node set and the other side is a string succeeds if any of the nodes in the set passes the comparison. Thus

not(x = '')

means "it is not the case that any x child element of the current node has an empty string value", which is fundamentally different from

x != ''

which means "at least one x child element of the current node has a string value that is not empty". In particular, if you want to check that all x children are empty, you need to use a "double-negative" test

not(x != '')
like image 21
Ian Roberts Avatar answered Nov 20 '22 19:11

Ian Roberts


Here's one XPath that should accomplish the job:

count(/*/node/*[text()]) &gt; 0

When used in a sample XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/">
     <xsl:value-of select="count(/*/node/*[text()]) &gt; 0" />
  </xsl:template>

</xsl:stylesheet>

...which is, in turn, applied to the provided example XML:

<xml>
  <node>
    <node1/>
    <node2/>
    <node3>value</node3>
  </node>
</xml>

...the expected result is produced:

true

If we apply the same XSLT against a simply modified XML:

<xml>
  <node>
    <node1/>
    <node2/>
    <node3/>
  </node>
</xml>

...again, the expected result is produced:

false

Explanation:

The XPath used searches for all children of a <node> element (which are, in turn, children of the root element) that have a non-empty text value (as specified by text()); should the count of such <node> children be greater than 0, then the XPath resolves to true.

like image 4
ABach Avatar answered Nov 20 '22 21:11

ABach