tags:

views:

70

answers:

3

Thse is my xml

<element1> <subel1/> </element1> <element2> <subel2/> </element2> <element3> <subel3/> </element3> <criteria> <subel3/> </criteria>

how i can select all node with xsl that not are in criteria subnodes? like these

 <subel1/> <subel2/>

How is this done?

If the xml is formated as:

<element1> 
<el> subel1 </el>
</element1>
 <element2> 
<el> subel2 
</el> 
</element2> 
<element3> 
<el> subel3 </el> 
</element3> 
<criteria> 
<subel3/> 
</criteria>
A: 

arent you missing the xmlcode? would be easier to help if you put in the code and in the editor mark it as sourcecode (the button with "101 010")

@elfaralfreds Thanx
Haroldis
A: 

The easiest way to do this is to construct an xpath expression which cross checks against the criteria nodes. For instance:

Step 1: The following will select all nodes that are not in the criteria area (So all nodes that do not have a parent named "criteria")

//*[name(..) != 'criteria'] 

Step 2: Filter out any nodes that also appear in critera section

//*[name(..) != 'criteria' and not(name(//criteria/*) = name(.))]

Now that last statement will select all the nodes that don't match a criteria node. But you just want sub nodes, so we can modify the selector to only grab elements that are "sub elements" or leaf elements that have no child nodes.

//*[name(..) != 'criteria' and not(name(//criteria/*) = name(.)) and not(*)]

So here is the breakdown of each of our conditionals one more time:

name(..) != 'criteria' -- Limits to nodes that are not in the criteria section

not(name(//criteria/*) = name(.)) -- Limits to nodes that do not have the same name as a node in the criteria section

and not(*) - Limits to nodes that do not have any child nodes (so the leaf nodes that you want.)

So if you were do something like:

<xsl:for-each select="//*[name(..) != 'criteria' and not(name(//criteria/*) = name(.)) and not(*)]">
 <xsl:value-of select="name(current())"/> :
</xsl:for-each>

For your above example this would print:

subel1 : subel2

Hope this helps.

Cheers,

Casey

Casey Jordan
This solution when applied on the XML document in my answer produces two element names, but the correct result is only one element name.
Dimitre Novatchev
Indeed, my solution does not work for the xml you presented. However that is not the same xml he supplied. My solution does work for his xml. That being said, your solution is more robust. Which is why I am scoring it up!
Casey Jordan
+3  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:for-each select=
  "/*/*[not(self::criteria)]/*">
   <xsl:copy-of select=
   "self::node()[not(/*/criteria/*[name()=name(current())])]"/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document (based on the provided XML fragment, but wrapped the XML fragmentwithin a top element and added one more child to criteria to make the problem less trivial):

<t>
    <element1>
        <subel1/>
    </element1>
    <element2>
        <subel2/>
    </element2>
    <element3>
        <subel3/>
    </element3>
    <criteria>
        <subel3/>
        <subel1/>
    </criteria>
</t>

produces the wanted result (no other published answer at the time of writing this produces a correct result):

<subel2/>

Step-by-step explanation:

  1. The XPath expression:

    /*/*[not(self::criteria)]/*

selects every element whose parent is an element not named "criteria" and which is a child of the top element of the document.

  1. Within the <xsl:for-each> instruction we check if the current node has a name that is not one of the names of any children of criteria and only copy such a node:

self::node()[not(/*/criteria/*[name()=name(current())])]

This XPath expression selects the current node ( self::node() ) only if there doesn't exist a child of /*/criteria, whose name is the same as the name of the current node ( not(/*/criteria/*[name()=name(current())]) ).

Here we use the fact that not(someNode-Set) is false() only if the nodeset someNode-Set is empty.

Dimitre Novatchev
you da man Dimitre
Mark Schultheiss