1
votes

In an XML document such as:

<a>
  <b>
    <c>Text1</c>
    ...
  </b>
  <b>
    <c>Text2</c>
    ...
  </b>
  ...
</a>

What is a single XPath 1.0-compatible, local-namespace()-compatible, expression to return a Null set if any element contains the inner text of 'Text1'.

I have tried numerous different expressions and cannot get elements that would return a null set.

e.g.

//*[local-name()='c' and .='Text1']

- or -   

/*[local-name()='a']/*[local-name()='b']/*not(local-name()='c' and .='Text1'])

The stringent requirements are due to a specific implementation of the .NET function call XmlNode.SelectSingleNode Method (String)

Final exact solution Courtesy Dimitre

/*[not(//*[local-name()='c' and . = 'Text1'])]
2

2 Answers

1
votes

What is a single XPath 1.0-compatible, local-namespace()-compatible, expression to return a Null set if any element contains the inner text of 'Text1'.

You haven't specified what should be selected if there is no such c element, so below I select in this case the top element.

Use:

/*[not(//c[. = 'Text1'])]

This would select nothing in case there is a c element in the XML document, whose string value is "Text1".

In case of using default namespace, this would be written as:

/*[not(//*[name()='c' and . = 'Text1'])]

XSLT - based verification:

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

 <xsl:template match="/">
     <xsl:copy-of select="/*[not(//c[. = 'Text1'])]"/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<a>
  <b>
    <c>Text1</c>
    ...
  </b>
  <b>
    <c>Text2</c>
    ...
  </b>
  ...
</a>

the XPath expression is evaluated and the result of this evaluation is copied to the output -- in this case nothing is produced -- as expected.

If we change the above XML document to:

<a>
  <b>
    <c>Text3</c>
    ...
  </b>
  <b>
    <c>Text2</c>
    ...
  </b>
  ...
</a>

then the condition is true() and the result of the evaluation is the subtree rooted by the top element:

<a>
    <b>
        <c>Text3</c>
    ...
    </b>
    <b>
        <c>Text2</c>
    ...
    </b>
  ...
</a>
0
votes

Just negate your expression

not(//*[local-name()='c' and .='Text1'])

It will return true if there is no <c>Text1</c>, otherwise, it does not return anything.