Search code examples
xmlstarlet

Extracting and modifying XML with deep structure from Linux command line


I would like to select and change a value in an XML file. I'm trying to use xmlstarlet for this.

I have this file

<?xml version='1.0' encoding='UTF-8'?>
<DeviceDescription xmlns="http://www.3s-software.com/schemas/DeviceDescription-1.0.xsd">  
    <House>
        <Id>
            <Number>1</Number>
        </Id>   
    </House>   
    <Car>
        <Id>
            <Number>2</Number>
        </Id>
   </Car> 
</DeviceDescription>

My problem is the xmlns= field which xmlstarlet is picky about. Without this field I can use

xmlstarlet sel -t -v '/Description/House/Id/Number' /tmp/x.xml

I found that I can use a default namespace like this, but that returns both Id's

xmlstarlet sel -t -m "//_:Id" -v '_:Number' /tmp/x.xml

How do I specify a full path?


Solution

  • To only match the House id, add it to the -m argument:

    xml sel -t -m '//_:House/_:Id' -v '_:Number'
    

    If you want to use the namespace, specify it with -N, e.g.:

    xml sel -N ns="http://www.3s-software.com/schemas/DeviceDescription-1.0.xsd" \
            -t -v 'ns:DeviceDescription/ns:House/ns:Id/ns:Number'
    

    So to update the value:

    xml ed -N ns="http://www.3s-software.com/schemas/DeviceDescription-1.0.xsd" \
           -u 'ns:DeviceDescription/ns:House/ns:Id/ns:Number' -v 3
    

    Output:

    <?xml version="1.0" encoding="UTF-8"?>
    <DeviceDescription xmlns="http://www.3s-software.com/schemas/DeviceDescription-1.0.xsd">
      <House>
        <Id>
          <Number>3</Number>
        </Id>
      </House>
      <Car>
        <Id>
          <Number>2</Number>
        </Id>
      </Car>
    </DeviceDescription>