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?

6
  • add more context/parent structure - it should be shown where the resulting <a>blah</a> is located/inserted. Post the structure with parent nodes Commented Oct 31, 2017 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
    Commented Oct 31, 2017 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. Commented Oct 31, 2017 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
    Commented Oct 31, 2017 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> Commented Oct 31, 2017 at 17:11

1 Answer 1

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>

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .