Search code examples
xmlxsltxpathxmlstarlet

XMLStarlet, XPath - How to remove attributes depending on node


I'm trying to use XMLStarlet to transform this (test.xml):

<?xml version="1.0"?>
<root xmlns:doc="http://www.test.com" doc:id="hello">
  <a doc:id="x"/>
  <a doc:id="x"/>
  <c doc:id="x">
    <a doc:id="x"/>
    <a doc:id="x"/>
    <c doc:id="x"/>
  </c>
</root>

into this (removing the doc:id attribute on all nodes except c):

<?xml version="1.0"?>
<root xmlns:doc="http://www.test.com" doc:id="hello">
  <a/>
  <b/>
  <c doc:id="hello">
    <a/>
    <b/>
    <c doc:id="hello"/>
  </c>
</root>

I've tried this, but it removes all of the doc:id attributes:

xml ed -N doc="http://www.test.com" -d "//@doc:id[not(self::c)]" test.xml

Solution

  • You can use this concatenation of two xmlstarlet commands:

    1. The first one deletes(-d) all doc:id attributes which are not part of a <c> element (as you already did)
    2. The second part of the command updates(-u) the values(-v) of all doc:id attributes of the <c> elements.

    This is the command:

    xml ed -N doc="http://www.test.com" -d "//*[not(self::c)]/@doc:id" -u "//c/@doc:id" -v "hello" test.xml
    

    The output is as desired.