Search code examples
xmlperl

How to delete a node from XML using XML::Simple?


I need to delete a node from XML using XML::Simple.

My XML looks like:

<Install>
<version >
<number>6.0</number>
<build>1014445</build>
<path>path</path>
<kind>native</kind>
</version>
<version >
<number>6.1</number>
<build>1025654</build>
<path>path</path>
<kind>native</kind>
</version>
</Install>

I need to delete a node matching a particular number under version. For example, I need to delete the node with number=6.0.

Updated XML will look like:

<Install>
<version >
<number>6.1</number>
<build>1025654</build>
<path>path</path>
<kind>native</kind>
</version>
</Install>

Solution

  • This is a solution using XML::Twig. As I said in my comment, the XML::Simple module is not a good choice unless you have no choice

    XML::Twig uses a subset of XPath, so the expression used to find the required version element is different from the one in the XML::LibXML solution

    use strict;
    use warnings;
    
    use XML::Twig;
    
    my $twig = XML::Twig->new;
    $twig->parsefile('install.xml');
    
    for my $number ( $twig->findnodes('/Install/version/number[string()="6.0"]') ) {
        $number->parent->delete;
    }
    
    $twig->set_pretty_print('indented_c');
    $twig->print;
    

    output

    <Install>
      <version>
        <number>6.1</number>
        <build>1025654</build>
        <path>path</path>
        <kind>native</kind>
      </version>
    </Install>
    

    Update

    If you want to make the program write directly to the new XML file instead of using the command line to redirect the output, then you just need to open the new file and pass the file handle to the print method call

    Like this

    open my $xml_fh, '>', 'install_new.xml' or die $!;
    $twig->set_pretty_print('indented_c');
    $twig->print($xml_fh);
    

    Update 2

    To specify the version number to be deleted as a variable you could interpolate the string's value into the XPath expression

    my $filter = '6.0';
    
    for my $number ( $twig->findnodes(qq{/Install/version/number[string()="$filter"]} } {
        ...
    }
    

    but it is best to iterate over all of the number elements and write an explicit comparison, like this

    use strict;
    use warnings;
    
    use XML::Twig;
    
    my $twig = XML::Twig->new;
    $twig->parsefile('install.xml');
    
    my $filter = '6.0';
    
    for my $number ( $twig->findnodes('/Install/version/number') ) {
      $number->parent->delete if $number->trimmed_text eq $filter;
    }
    
    $twig->set_pretty_print('indented_c');
    $twig->print;