'How to wrap child elements in a parent tag using xmlstarlet

I want to wrap "table" elements with a new parent anywhere in the xml file. Am new using xmlstarlet. Input:

<table>
</table>   
<oldTable>
    <table>
    </table>   
</oldTable>
    
Desired output:   
<newparent id="tab">
    <table>
    </table>    
</newparent>
     
<oldTable>
    <newparent id="tab">
        <table>
        </table>
    </newparent>    
<oldTable>


Solution 1:[1]

Assuming a POSIX shell:

xmlstarlet edit \
  --var T '//table[not(parent::newparent)]' \
  -i '$T' -t 'elem' -n 'newparent' \
  -a '$prev' -t 'attr' -n 'typ' -v 'wtab' \
  -u '//newparent[following-sibling::*[1][self::table]]' \
  -x 'following-sibling::*[1]' \
  -d '$T' \
file.xml > newfile.xml

where:

  • --var T … collect candidate table elements in variable T
  • -i insert a newparent element before each table
  • -a append a typ attribute to the element just created
  • -u update all newparent elements having an immediately following table sibling element
    with (-x XPath expression) a deep copy of that sibling
  • -d delete the copied siblings

xmlstarlet edit code can use the convenience $prev (aka $xstar:prev) node to refer to the node created by the most recent -i (--insert), -a (--append), or -s (--subnode) option. Examples of $prev are given in doc/xmlstarlet.txt.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1