Search code examples
xmlstarlet

updating xml, using xmlstarlet, with a sequence of letters


When I am having input.xml, and want to update it to get output.xml (see below) XMLSTARLET fails.

First I tried to find the correct XSLT function to get the needed values, which led to this:

xmlstarlet sel -t -m //field -v . -o "=" -v 'substring("ABCDEFGHIJK",position(),1)' -n input.xml

output:

 5 =A
 3 =B
 2 =C
 4 =D
 55 =E
 42 =F

This made me believe that I should be able to update this XML with the following command:

xmlstarlet ed -u //field -x 'substring("ABCDEFGHIJK",position(),1)' input.xmlxmlstarlet ed -u //field -x 'substring("ABCDEFGHIJK",position(),1)' input.xml

But I did get:

Invalid context position
Segmentation fault

I tried using XmlStarlet on Windows 11, and on Ubuntu 20.04, both did a core dump.

I am interested in another solution using XmlStarlet.

FILES

input.xml

<root>
   <field> 5 </field>
   <field> 3 </field>
   <field> 2 </field>
   <field> 4 </field>
   <field> 55 </field>
   <field> 42 </field>
</root>

(desired) output.xml

<root>
   <field>A</field>
   <field>B</field>
   <field>C</field>
   <field>D</field>
   <field>E</field>
   <field>F</field>
</root>

Solution

  • position() works with xmlstarlet select's -m (--match) option (i.e. xsl:for-each) which determines the context position.

    xmlstarlet select --indent -t \
      -e '{name(*)}' \
        -m '//field' -e '{name()}' -v 'substring("ABCDEFGHIJK",position(),1)' \
    file.xml
    

    With xmlstarlet edit's -u (--update) you can use a sibling node count, e.g.

    xmlstarlet edit -O \
      -u '//field' -x 'substring("ABCDEFGHIJK",1+count(preceding-sibling::field),1)' \
    file.xml
    

    or

    xmlstarlet edit -O \
      -u '//field' -x 'substring("ABCDEFGHIJK",count(preceding-sibling::* | self::*),1)' \
    file.xml
    

    Each of these commands produces the desired output. Line continuation chars added for readability.