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
$prevvariable refers to the node(s) created by the most recent-s,-i, or-aoption which all define or redefine it (seexmlstarlet.txtfor 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.)
