Reputation: 5347
I'm attempting to create an xquery expression that will return selected nodes but will not return their children. This is probably best illustrated with an example. I have the following myNode node:
<myNode>
<myElements id="1">
<myElement key="one">aaa</myElement>
<myElement key="two" >bbb</myElement>
<myElement key="three">ccc</myElement>
</myElements>
<myElements id="2">
<myElement key="one">ddd</myElement>
<myElement key="two" >eee</myElement>
<myElement key="three">fff</myElement>
</myElements>
</myNode>
I am interested in returning the <myElements > nodes, but nothing lower. My desired return would look like the following:
<myNode>
<myElements id="1" />
<myElements id="2" />
</myNode>
or
<myNode>
<myElements id="1"></myElements>
<myElements id="2"></myElements>
</myNode>
I currently have a xpath expression that would look something like the following (simplified for this illustration), which as expected, is returning the myElement children:
$results/myNode/MyElements
Am I barking up the wrong tree? Is this even possible in XPath/XQuery?
Upvotes: 2
Views: 2587
Reputation: 1432
try this is the shortest way
let $a :=
<myNode>
<myElements id="1">
<myElement key="one">aaa</myElement>
<myElement key="two" >bbb</myElement>
<myElement key="three">ccc</myElement>
</myElements>
<myElements id="2">
<myElement key="one">ddd</myElement>
<myElement key="two" >eee</myElement>
<myElement key="three">fff</myElement>
</myElements>
</myNode>
for $e1 in $a
return element {name($e1)}
{
for $e2 in $e1/*
return element{name($e2)}{$e2/@*}
}
Upvotes: 1
Reputation: 163595
XQuery is only returning the top level nodes, it is not returning their children. But the returned nodes are still connected to their children, and whatever code it is that is displaying the results is choosing to display the full subtree under the nodes that you actually selected.
The first step to understanding your problem is therefore to realise that you don't want you query to select nodes from the source document, you want it to construct new nodes that differ from the original by having different children.
This means it's one of those queries where you want to produce a result document that is the same as the original but with a few small changes. As Dimitre says, XSLT is much better at this class of problem than XQuery. But it can certainly be done with XQuery, as Scott shows.
Upvotes: 2
Reputation: 243579
XQuery is quite inferior to XSLT for such tasks:
<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="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="myElements/node()"/>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<myNode>
<myElements id="1">
<myElement key="one">aaa</myElement>
<myElement key="two" >bbb</myElement>
<myElement key="three">ccc</myElement>
</myElements>
<myElements id="2">
<myElement key="one">ddd</myElement>
<myElement key="two" >eee</myElement>
<myElement key="three">fff</myElement>
</myElements>
</myNode>
the wanted, correct result is produced:
<myNode>
<myElements id="1" />
<myElements id="2" />
</myNode>
Explanation: The identity rule, overriden for child nodes of myElements
Upvotes: 2
Reputation: 106
Try this recursive algorithm..
xquery version "1.0";
declare function local:passthru($x as node()) as node()* { for $z in $x/node() return local:recurseReplace($z) };
declare function local:recurseReplace($x as node()) {
typeswitch ($x)
(: Changes based on a condition :)
case element(myElements) return <myElements id="{$x/@id}" />
(: IGNORE ANY CHANGES :)
case text() return $x
case comment() return comment {"an altered comment"}
case element() return element {fn:node-name($x)} {for $a in $x/attribute()
return $a, local:passthru($x)}
default return ()
};
let $doc :=
<myNode>
<myElements id="1">
<myElement key="one">aaa</myElement>
<myElement key="two" >bbb</myElement>
<myElement key="three">ccc</myElement>
</myElements>
<myElements id="2">
<myElement key="one">ddd</myElement>
<myElement key="two" >eee</myElement>
<myElement key="three">fff</myElement>
</myElements>
</myNode>
return local:recurseReplace($doc)
Upvotes: 4