2

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?

balin
  • 1,554
  • 1
  • 12
  • 26

2 Answers2

3

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>
Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
  • Thank you, @daniel-haley and @michael-kay for your explanation. Classical case of plundering google user trying to force an incorrect tool ... I'm trying that in my toolkit (`R`, for which I already found the corresponding `xslt`package) ASAP and report back. Many thanks. – balin Jul 03 '23 at 16:16
  • @balin - Most likely your R xslt package doesn't support XSLT 3.0. I updated my answer with an XSLT 1.0 version of the stylesheet. – Daniel Haley Jul 03 '23 at 17:18
  • After a hiatus, I have returned to this and made (the XSLT 1.0 version) work in conjunction with `R`s `xslt` package. Accordingly marked this answered. Many thanks once more. – balin Aug 08 '23 at 10:05
3

XPath is designed to select a set of nodes from your input; it can't be used to create a modified tree structure containing some of the original nodes and not others. For that you need XSLT or XQuery.

In XSLT 3.0 you would achieve your task with a set of template rules something like this:

<xsl:mode on-no-match="shallow-copy"/>

<xsl:template match="dependent[@polarity!='positive']"/>

The xsl:mode declaration says that by default you want to copy element nodes and apply-templates to their children; the xsl:template rule says that if you find an element named dependent with a polarity value other than positive then you want to produce no output in respect of that node.

kjhughes
  • 106,133
  • 27
  • 181
  • 240
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Thank you for your explanation. Classical case of plundering google user trying to force an incorrect tool ... I'm trying that in my toolkit (R, for which I already found the corresponding xsltpackage) ASAP and report back. Many thanks. – balin Jul 03 '23 at 16:17
  • Be aware that I gave you XSLT 3.0 code, the out-of-the-box XSLT processors from Microsoft only support 1.0. This one is easy enough to do in XSLT 1.0, it just needs 10 lines of code instead of 2. – Michael Kay Jul 04 '23 at 17:53