简体   繁体   中英

How to delete an XML element according to value of one of its children?

I have an xml element looking something like this:

<Description>
    <ID>1234</ID>
    <SubDescription>
        <subID>4501</subID>
    </SubDescription>
    <SubDescription>
        <subID>4502</subID>
    </SubDescription>
</Description>

How can I delete the entire "Description" element according to the value of its "ID" child?

You can use the following xpath to select Description nodes that contain an ID node with value 1234:

//Description[./ID[text()='1234']]

So to remove the node, you can do:

doc.xpath("//Description[./ID[text()='1234']]").remove

Example:

require 'nokogiri'

str = %q{
<root>
    <Description>
        <ID>2222</ID>
        <SubDescription>
        <subID>4501</subID>
        </SubDescription>
        <SubDescription>
        <subID>4502</subID>
        </SubDescription>
    </Description>
    <Description>
        <ID>1234</ID>
        <SubDescription>
        <subID>4501</subID>
        </SubDescription>
        <SubDescription>
        <subID>4502</subID>
        </SubDescription>
    </Description>
</root>
}
doc = Nokogiri::XML(str)
doc.xpath("//Description[./ID[text()='1234']]").remove
puts doc
#=> <root>
#=> <Description>
#=>     <ID>2222</ID>
#=>     <SubDescription>
#=>     <subID>4501</subID>
#=>     </SubDescription>
#=>     <SubDescription>
#=>     <subID>4502</subID>
#=>     </SubDescription>
#=> </Description>
#=></root>

As you can see, the desired description node is removed.

I personally would use the solution by @JustinKo, albeit with the simpler XPath:

doc.xpath("//Description[ID='1234']").remove

However, if crafting XPath isn't your idea of fun, and writing Ruby is, you can lean on Ruby harder (if slightly less efficiently):

doc.css('ID').select{ |el| el.text=="1234" }.map(&:parent).each(&:remove)

That says:

  • Find all the elements named <ID>
  • But pare that down do just the ones whose text is "1234"
  • Map this to be the <Description> nodes (the result of calling .parent on each)
  • And then call .remove on each of those.

If you know that there's only ever going to be one match, you can make it simpler with:

doc.css('ID').find{ |el| el.text=="1234" }.parent.remove

To find the ID do:

id = doc.xpath("//ID").text

where doc is the Nokogiri object created from loading the xml document

To check if the element id is what you want try:

if id == "1234"

From your xml file this should return true

Finally to remove the entire Description use:

doc.xpath("//Description").remove

What you're looking for is this:

doc = Nokogiri::XML(File.open("test.xml"))    #create Nokogiri object from "test.xml"
id = doc.xpath("//ID").text    #this will be a string with the id
doc.xpath("//Description").remove if id == "1234"    #returns true with your xml document and remove the entire Description element."

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM