I have an XML file that I'd like to flatten.
input.xml:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item id="item1">
<oldproperty oldname="mykey" oldvalue="keyname1"/>
<oldproperty oldname="myval" oldvalue="value1"/>
</item>
<item id="item2">
<oldproperty oldname="mykey" oldvalue="keyname2"/>
<oldproperty oldname="myval" oldvalue="value2"/>
</item>
<item id="item3">
<oldproperty oldname="mykey" oldvalue="keyname3"/>
<oldproperty oldname="myval" oldvalue="value3"/>
</item>
</items>
Desired output:
<?xml version="1.0" encoding="UTF-8"?>
<items>
<item id="item1" newkey="keyname1" newvalue="value1"/>
<item id="item2" newkey="keyname2" newvalue="value2"/>
<item id="item3" newkey="keyname3" newvalue="value3"/>
</items>
QUESTION: How can I do that with xmlstarlet
?
CodePudding user response:
The desired output can be produced by xmlstarlet edit
:
xmlstarlet edit \
-s '*/*' -t attr -n newkey -v '' \
-u '$prev' -x 'string(../oldproperty[@oldname="mykey"]/@oldvalue)' \
-s '*/*' -t attr -n newvalue -v '' \
-u '$prev' -x 'string(../oldproperty[@oldname="myval"]/@oldvalue)' \
-d '*/*/oldproperty' \
file.xml
- unlike
-s (--subnode)
's-v (--value)
the-x (--expr)
clause of the-u (--update)
option takes an XPath argument, hence the two-step approach - the
$prev
variable refers to the node(s) created by the most recent-s
,-i
, or-a
option which all define or redefine it (seexmlstarlet.txt
for examples of$prev
) */*
may be replaced withitems/item
or xmlstarlet select
:
xmlstarlet select --xml-decl -E 'UTF-8' --indent -t \
-e '{name(*)}' \
-m '*/*' \
-e '{name()}' \
-a 'id' -v '@id' -b \
-a 'newkey' -v '*[@oldname="mykey"]/@oldvalue' -b \
-a 'newvalue' -v '*[@oldname="myval"]/@oldvalue' \
file.xml
-e (--elem)
emits an element (here using an XSLT attribute value template)-a (--attr)
emits an attribute,-v (--value-of)
takes an XPath argument*[@oldname="…"]
may be replaced witholdproperty[@oldname="…"]
*/*
may be replaced withitems/item
(Assuming POSIX shell syntax.)