Trying to use xmlstarlet to delete element but doesn't seem to be doing anything

Viewed 149

I am running the following command

xmlstarlet ed -d "/Package/types[name='ApexEmailNotifications']" test.xml

against the below XML

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
  <types>
    <members>1</members>
    <name>ApexEmailNotifications</name>
  </types>
  <types>
    <members>2</members>
    <name>ApexTrigger</name>
  </types>
  <types>
    <members>3</members>
    <members>4</members>
    <name>AppMenu</name>
  </types>
  <version>50.0</version>
</Package>

expecting it to change to the following

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
  <types>
    <members>2</members>
    <name>ApexTrigger</name>
  </types>
  <types>
    <members>3</members>
    <members>4</members>
    <name>AppMenu</name>
  </types>
  <version>50.0</version>
</Package>

however nothing happens I just feel like I am missing one stupid character here or there or have misunderstood the man pages.

any help would be much appreciated by what's left of my hair

2 Answers

XML namespaces make things generally difficult. This is what you have to do in xmlstarlet: declare the namespace (there's nothing magic about _ below, it's just a name) and then use the prefix for each tag

xmlstarlet ed \
    -N _=http://soap.sforce.com/2006/04/metadata \
    -d "/_:Package/_:types[_:name = 'ApexEmailNotifications']" \
    test.xml

result

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
  <types>
    <members>2</members>
    <name>ApexTrigger</name>
  </types>
  <types>
    <members>3</members>
    <members>4</members>
    <name>AppMenu</name>
  </types>
  <version>50.0</version>
</Package>

As Daniel Haley comments, since the namespace applies to the root element, we can use the _ prefix without -N, for xmlstarlet version >= 1.2.1

xmlstarlet ed -d "/_:Package/_:types[_:name = 'ApexEmailNotifications']"  test.xml

And version 1.5.0 provided DEFAULT for the same purpose

xmlstarlet ed -d "/DEFAULT:Package/DEFAULT:types[DEFAULT:name = 'ApexEmailNotifications']"  test.xml

Another way, use local-name():

xml ed -d  '//*[local-name()="Package"]//*[local-name()="types"][.//*[local-name()="name"][./text()="ApexEmailNotifications"]]' test.xml
Related