Search code examples
.netxmlxpathxmlnodexpath-1.0

XPath - Return Null if single instance of element contains specific text


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'])]

Solution

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