2

I want to move around elements in an xml document with xmlstarlet

Specifically I want to make the following changes

<parent>
    <before/>
    <span><a>blah</a></span>
    <after/>  
<parent>
<otherparent>
    <span><a>blah</a></span>
<otherparent>

becomes ..

<parent>
    <before/>
    <a>blah</a>
    <after/>
</parent>
<otherparent>
    <a>blah</a>
</otherparent>

I.e delete span but keep the children.

xmlstarlet has a move command: xmlstart ed -m source target but I don't really understand how it works.

It takes a source and a target xpath but I don't really understand how these get matched up because they can result in sets of different sizes. Is the target relative to the source?

  • add more context/parent structure - it should be shown where the resulting <a>blah</a> is located/inserted. Post the structure with parent nodes – RomanPerekhrest Oct 31 '17 at 17:02
  • 1
    I don't feel that this is particularly relevant. I want delete all spans while keeping their content. This is really just an example to motivate the question. – Att Righ Oct 31 '17 at 17:06
  • you don't understand, the crucial child nodes should be moved in right places in case if spans are in arbitrary order within xml document. – RomanPerekhrest Oct 31 '17 at 17:08
  • Ah I understand... the problem isn't completely specified without other children. I guess I want it to "stay correct location?" – Att Righ Oct 31 '17 at 17:09
  • For ex. <div><span id="1"><span><a>blah</a></span></span></div><div></div><div><span id="2"><span><a>blah</a></span></span></div> – RomanPerekhrest Oct 31 '17 at 17:11
1

Complex xmlstarlet solution:

Input xml file test.xml:

<div>
  <parent>
    <before/>
    <span>
      <a>value 1</a>
    </span>
    <after/>
  </parent>
  <otherparent>
    <span>
      <a>value 2</a>
    </span>
  </otherparent>
</div>

The job:

count=$(xmlstarlet sel -t -v 'count(//span[a])' test.xml)
for ((i=1; i<=$count; i++)); do 
    xmlstarlet ed -L -a '(//span[a])[1]' -t elem -n "a" -v "$(xmlstarlet sel -t -v "(//span/a)[1]" 1.xml)" -d '(//span[a])[1]' test.xml
done

  • count - variable containing the number of span nodes which have child a node

  • ed - edit mode

  • -L - modify the file inplace

  • -a - append action

  • -d - delete action


The final test.xml (after processing):

<?xml version="1.0"?>
<div>
  <parent>
    <before/>
    <a>value 1</a>
    <after/>
  </parent>
  <otherparent>
    <a>value 2</a>
  </otherparent>
</div>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.