Search code examples
xmlxmlstarlet

Is it possible to insert a computed attribute (i.e. non-constant) into an XML file with xmlstarlet?


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?


Solution

  • 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.