XSL select all node where not node of another node
Thse is my xml
<element1> <subel1/> </element1> <element2> <subel2/> </e开发者_如何学JAVAlement2> <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>
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<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:
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.
- 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 ofcriteria
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.
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
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")
精彩评论