1

I'm trying to remove all elements from an XML file that contain a certain other element. Here's a very simplified version of what I'm trying to do. Suppose I have the XML file

<RootEl>
   <A>
      <B/>
   </A>
   <A>
      <C/>
   </A>
 </RootEl>

If I wanted to keep all and only the A's that DO contain B's, I could use this command line:

xml_grep -root A -cond B < TheFile.xml

But if I want to do the reverse--keep only the A's that do NOT contain B's, I'm at a loss. A command similar to the above, namely

xml_grep -root A -exclude B < TheFile.xml

gives me

<RootEl>
   <A>

   </A>
   <A>
      <C/>
   </A>
 </RootEl>

whereas what I want is

<RootEl>
   <A>
      <C/>
   </A>
 </RootEl>

I get the same undesired answer if I use

xml_grep -root A -exclude A/B < TheFile.xml

or

xml_grep -exclude A/B < TheFile.xml

I could figure out how to do this in Python, and I suppose it's possible in xslt. But I was hoping for a way to do this in xml_grep.

BTW, I'm sure someone will ask why I don't just tell it I want the As that contain Cs. The problem is that there are 20 or so things an A can contain besides B or C, so I'd have to specify an A that contains a C or a D or...or a Z. Which is a whole lot more work than specifying the one kind of A I don't want.

The question Delete XML node containing certain element is asking basically the same question, but there are no answers there using xml_grep. I'm hoping someone can suggest such an answer, since xml_grep seems reasonably popular and built for purposes like this...almost.

4
  • I think I can do this with xmlstarlet. Do you need to use xml_grep? Commented Feb 14, 2019 at 14:04
  • I hadn't heard of xmlstarlet before; from the documentation, it looks like it might work. I'm installing it now, I'll let you know how it works. Thanks for the pointer! Commented Feb 15, 2019 at 18:33
  • Yeap, it works! For the record, this is the command line: xmlstarlet sel -t -m "RootEl" -c "A[not(./B)]" I am now sold on xmlstarlet! Commented Feb 15, 2019 at 20:36
  • I chose Kusalananda's offered answer, although roaima actually suggested that approach in above (but as a comment, not an "answer"). Thanks to both! Commented Feb 23, 2019 at 4:21

1 Answer 1

2

Using xmlstarlet:

$ xmlstarlet ed -d '//A[not(B)]' file.xml
<?xml version="1.0"?>
<RootEl>
  <A>
    <B/>
  </A>
</RootEl>

The XPATH expression //A[not(B)] would select all A nodes in the document that does not contain a B node as a child node. These selected nodes are deleted.

The expression could also be written as //A[not(child::B)] which would be a bit more explicit.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .