Reputation: 2852
Following is the XML Structure -
<Docs>
<Doc>
<P>blah blah blah<pg>1</pg>blah blah</P>
<P>blah blah blah<pg>2</pg>blah blah</P>
</Doc>
<Doc>
<P>blah blah blah<pg>3</pg>blah blah</P>
<P>blah blah blah<pg>4</pg>blah blah</P>
</Doc>
</Docs>
I want to delete the pg
node within the P
node and insert it as a sibling of P
node.
Like this -
<Docs>
<Doc>
<P>blah blah blah</P>
<pg>1</pg>
<P>blah blah</P>
<P>blah blah blah</P>
<pg>2</pg>
<P>blah blah</P>
</Doc>
<Doc>
<P>blah blah blah</P>
<pg>3</pg>
<P>blah blah</P>
<P>blah blah blah</P>
<pg>4</pg>
<P>blah blah</P>
</Doc>
</Docs>
How to get it done ?
Upvotes: 0
Views: 717
Reputation: 20414
XQuery 3.0 on the other hand, provides the new tumbling window feature that helps to mimic the for-each-group behavior described in my other answer. Here is how it looks like:
xquery version "3.0";
let $xml :=
<Docs>
<Doc>
<P>blah blah blah<pg>1</pg>blah blah</P>
<P>blah blah blah<pg>2</pg>blah blah</P>
</Doc>
<Doc>
<P>blah blah blah<pg>3</pg>blah blah</P>
<P>blah blah blah<pg>4</pg>blah blah</P>
</Doc>
</Docs>
return
<Docs>{
for $doc in $xml/Doc
return
<Doc>{
for $P in $doc/P
return
for tumbling window $w in $P/node()
start when true()
end next $e
when $e instance of element(pg)
return (
$w[self::pg],
<P>{
$w[not(self::pg)]
}</P>
)
}</Doc>
}</Docs>
It does require an XQuery processor with 3.0 support, including these tumbling windows. Zorba is an excellent example, you can test this code online at http://try.zorba.io
HTH!
Upvotes: 1
Reputation: 20414
XSLT looks more suited for this task indeed. XSLT 2.0 provides the xsl:for-each-group
feature that can be very usefull here. It is more robust than matching just on text() nodes inside the P. That will certainly help if the P element contains other inline elements beside the pg markers.
Here the 2.0 solution, a slight alteration of the solution by Daniel Haley:
<xsl:stylesheet version="2.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="P">
<xsl:variable name="P" select="."/>
<xsl:for-each-group select="node()" group-starting-with="pg">
<xsl:apply-templates select="self::pg"/>
<xsl:element name="{node-name($P)}">
<xsl:apply-templates select="$P/@*|current-group()[not(self::pg)]"/>
</xsl:element>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
HTH!
Upvotes: 2
Reputation: 38682
This is an XQuery solution if you've got support for XQuery Update.
for $p in $c//P (: for each paragraph tag :)
return (
for $node in $p/(text(), pg) (: find all subnodes :)
return (
let $node :=
if ($node/self::text())
then element P { $node } (: wrap text nodes in new paragraph tags :)
else $node
return insert node $node after $p (: insert the node after the old paragraph tag :)
),
delete node $p (: drop the old paragraph tag :)
)
Just realized a version without XQuery Update (only returning the results) is even shorter:
element Docs {
element Doc {
for $node in //P/(text(), pg)
return
if ($node/self::text())
then element P { $node }
else $node
}
}
Upvotes: 2
Reputation: 52858
Here's an XSLT option...
XML Input
<Docs>
<Doc>
<P>blah blah blah<pg>1</pg>blah blah</P>
<P>blah blah blah<pg>2</pg>blah blah</P>
</Doc>
<Doc>
<P>blah blah blah<pg>3</pg>blah blah</P>
<P>blah blah blah<pg>4</pg>blah blah</P>
</Doc>
</Docs>
XSLT 1.0
<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="P">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="P/text()">
<P><xsl:value-of select="."/></P>
</xsl:template>
</xsl:stylesheet>
XML Output
<Docs>
<Doc>
<P>blah blah blah</P>
<pg>1</pg>
<P>blah blah</P>
<P>blah blah blah</P>
<pg>2</pg>
<P>blah blah</P>
</Doc>
<Doc>
<P>blah blah blah</P>
<pg>3</pg>
<P>blah blah</P>
<P>blah blah blah</P>
<pg>4</pg>
<P>blah blah</P>
</Doc>
</Docs>
Upvotes: 3