Extracting and modifying XML with deep structure from Linux command line

Viewed 20

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?

1 Answers

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>
Related