Search code examples
xmlxpathxml-parsing

xpath: All nodes with exceptions?


This is my first foray into xpath and I'm failing miserably.

Consider something like this:

<node1>
  <node11>
    <dependent>Retain</dependent>
    <dependent polarity="positive">Retain</dependent>
    <dependent polarity="negative">Discard</dependent>
  </node11>
  <dependent>Retain</dependent>
  <dependent polarity="positive">Retain</dependent>
  <dependent polarity="negative">Discard</dependent>
  <somethingelse>Retain</somethingelse>
</node1>

I'm looking for an expression that will (while maintaining tree structure)

  1. return all nodes not named dependent,
  2. return nodes named dependent that do not have an attribute named polarity
  3. return nodes named dependent with attribute polarityset to positive.

Above example would thus result in:

<node1>
  <node11>
    <dependent>Retain</dependent>
    <dependent polarity="positive">Retain</dependent>
  </node11>
  <dependent>Retain</dependent>
  <dependent polarity="positive">Retain</dependent>
  <somethingelse>Retain</somethingelse>
</node1>

Many attempts at googleing and ChatGPLing have not produced what I want. Below is what I believe should work, but doesn't - polarity='negative' nodes are retained!?

//*[not(self::dependent) or (descendant-or-self::dependent[not(@polarity)]) or (descendant-or-self::dependent[@polarity='positive'])]

Where do I err?


Solution

  • You could use something like this:

    //*[not(self::dependent[@polarity!='positive'])]
    

    But since you're selecting each element, it's going to select the root element (node1) and that's going to include everything anyway. If you're trying to modify the tree, you'll probably want to use xquery or xslt.

    Example of XSLT that results in the desired output:

    <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>
        
        <xsl:mode on-no-match="shallow-copy"/>
        
        <xsl:template match="dependent[@polarity!='positive']"/>
        
    </xsl:stylesheet>
    

    XSLT 1.0 version (replaced xsl:mode with identity template)...

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>
        
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
        
        <xsl:template match="dependent[@polarity!='positive']"/>
        
    </xsl:stylesheet>