XMLStarlet: using xpath to move an element in front of another
Given:
<x>
<a />
<d />
<b />
<c />
<e />
<f />
</x>
I would like to use xmlstarlet to navigate <d />
to <e />
.
The closest I have is this:
echo .. | xml ed -m "//d" "//e"
What produces:
<e>
<d/>
</a>
This is, unfortunately, an example the guide gives.
echo .. | xml ed -m "//d" "//x"
Places <d />
at the end, which is not the correct place.
I tried to get it preceding-sibling
to work (if it really is), but while:
echo .. | xml sel -t -c "//e/preceding-sibling::*[1]"
Results in <c />
, this query does not work as a move destination (it complains that the destination is not one of a node) and it really is not, as the best case would be that it ends up inside <c />
.
I'm not sure what ed -m
is the wrong approach if there is an XPATH form that points to a location between elements, not with an element.
Edit: Interesting to insert is more like what I expected, inserting what you are passing in front of the element selected with xpath:
$ xml ed -i "//c" -t elem -n "foo" -v "bar" test.xml
<?xml version="1.0"?>
<x>
<a/>
<d/>
<b/>
<foo>bar</foo>
<c/>
<e/>
<f/>
</x>
Unfortunately, the value passed ( bar
above) cannot be XML, so I could select it somewhere with sel and then enter it using this command, I don't think.
source to share
It seems like it should be easy with ed
. If so, I don't see it. (I don't use xmlstarlet very often.)
You may need to use XSLT ...
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()[not(self::d)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="c">
<xsl:copy-of select="."/>
<xsl:copy-of select="../d"/>
</xsl:template>
</xsl:stylesheet>
Command line
$ xml tr test.xsl test.xml <?xml version="1.0"?> <x> <a/> <b/> <c/> <d/> <e/> <f/> </x>
source to share