I have an XML file like this:
sources.xml
<?xml version="1.0" encoding="UTF-8"?>
<sources>
<source>
<identity id="abc"/>
<somestuff/>
</source>
<source>
<identity id="def"/>
<someotherstuff/>
</source>
</sources>
I want to get rid of the tag identity
and transfer its attribute into the parent node. With other words, I need to change my input file into:
<?xml version="1.0" encoding="UTF-8"?>
<sources>
<source id="abc">
<somestuff/>
</source>
<source id="def">
<someotherstuff/>
</source>
</sources>
I tried:
xmlstarlet edit \
--insert "//source" --type attr --name "id" --value "identity/@id" \
--delete "//identity" \
sources.xml
which is not working because --value
understands its argument as a constant string.
My question is: is it possible for xmlstarlet to compute the value of the attribute id
as an XPATH expression like in the select
operations?
Edit nr. 1
I found a way to get something done in that direction:
xmlstarlet edit \
--insert "//source" --type attr --name "id" --value "{PLACE HOLDER}" \
--update "//source/@id" --expr "../identity/@id" \
--delete "//identity" \
sources.xml
but I get empty values for attribute id
:
<?xml version="1.0" encoding="UTF-8"?>
<sources>
<source id="">
<somestuff>Hello</somestuff>
</source>
<source id="">
<someotherstuff/>
</source>
</sources>
What is wrong in the expression ../identity/@id
?
Yes, it is possible, and this is the solution:
xmlstarlet edit \
--insert "//source" --type attr --name "id" \
--update "//source/@id" --expr "string(../identity/@id)" \
--delete "//identity" \
sources.xml
The --insert
operation doesn't admit --expr
parameters, only --value
, but --update
does. So we do it in two different steps: first create an empty attribute and second, change its value with --expr
.
We also need to convert the text node given as argument to the option --expr
into a string with the XPATH function string()
. This is unsufficiently documented in the references or tutorials.