I have an XML file similar to:
<?xml version="1.0"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<Folder>
<name>Assets and Risks</name>
<Placemark>
<name>Asset_4000</name>
<description>Address: 2286</description>
<Point>
<coordinates>xxx.xxx,yyy.yyy,0</coordinates>
</Point>
</Placemark>
<Placemark>
<name>Risk_2000</name>
<description>Address: 32</description>
<Point>
<coordinates>xxx.xxx,yyy.yyy,0</coordinates>
</Point>
</Placemark>
</Folder>
<Folder>
<name>The second folder</name>
</Folder>
</Document>
</kml>
I want to use xmlstarlet (preferably all command line rather than XSLT) to move the Placemark for Risk_2000 to be the first Placemark within the same Folder (ie. before the Placemark for Asset_4000).
I know the first part is:
xmlstarlet edit --move "//_:kml/_:Document/_:Folder[_:name=\"Assets and Risks\"]/_:Placemark[_:name=\"Risk_2000\"]" **But What Goes Here**
Any guidance appreciated.
The destination for xmlstarlet edit
's --move
must be a single node,
so work around:
(For a non-GNU/Linux platform adjust quoting and line continuation characters in following command.)
xmlstarlet edit -N v='http://www.opengis.net/kml/2.2' \
--insert '//v:Folder[v:name="Assets and Risks"]/v:Placemark[1]' \
--type elem --name 'Placemark_TMP' --value '' \
--update '$xstar:prev' --expr '../v:Placemark[v:name="Risk_2000"]/node()' \
--delete '$xstar:prev/../v:Placemark[v:name="Risk_2000"]' \
--rename '$xstar:prev' --value 'Placemark' \
file.xml | xmlstarlet format --nsclean
xmlstarlet edit
code can use the convenience $xstar:prev
(aka
$prev
) node to refer to the node created by the most recent
-i / --insert
, -a / --append
, or -s / --subnode
option. Examples
of $xstar:prev
are given in
doc/xmlstarlet.txt and
the source code's examples/ed-backref*.
--expr '../v:Placemark[…]/node()'
makes a deep copy of element's
child nodes but not its attributes
(background).
The element in question has no attributes, otherwise use the XPath
--expr '../v:Placemark[…]/node() | ../v:Placemark[…]/@*'
.
The --nsclean
step removes redundant namespace declarations.
Since xmlstarlet
supports exslt there may be possibilities in the
set:leading or
set:trailing
functions but I didn't look into it.
For a stylesheet-based approach see this.
UPDATE 2021-09-29
The code above can be rewritten as follows using
--var <name> <xpath>
option (in
doc/xmlstarlet.txt
but not in the user's guide)_:
and adding | $src/@*
to include attributes:
xmlstarlet ed \
--var dir '//_:Folder[_:name="Assets and Risks"]' \
--var tgt '$dir/_:Placemark[1]' \
--var src '$dir/_:Placemark[_:name="Risk_2000"]' \
-i '$tgt' -t elem -n 'Placemark_TMP' -v '' \
-u '$xstar:prev' -x '$src/node() | $src/@*' \
-d '$src' \
-r '$xstar:prev' -v 'Placemark' \
file.xml | xmlstarlet fo -N