Search code examples
xmlperlxml-simple

Perl, Remove XML node


Data.xml

<people>
  <person name="John">
     <param name="age" value="21" />
  </person>
  <person name="Jane">
     <param name="age" value="25" />
  </person>
</people>

I have this piece of XML. I'm working on a script to append <person> nodes to the <people> node. I'm using XML::Simple

(Please refrain from suggesting that i use another library, i'm aware of its difficulties).

my $remove_person = "Jane";

my $xml = XMLin('data.xml', ForceArray => 1, KeepRoot => 1, KeyAttr => []);
if(exists $xml->{people}[0]{person}){
        my $var = $xml->{people}[0]{person};
        my $count = @$var;
        my $person_index = 0;
        for(my $i = 0; $i < $count; $i++){
                if($xml->{people}[0]{person}[$i]->{name} eq $remove_person){
                        print "Person found at " . $person_index . " index";
                        $person_index = $i;
                        $person_to_remove = $xml->{people}[0]{person}[$i];
                }
        }
} else {
        print "Person not found in data.xml\r";
}

The above piece of code will give me the index of the node i wish to remove. Its from this point that i'm having my trouble. I cannot figure a correct way to remove this index from the data.
So far i've tried a method of using splice, which returned the section of XML i want to remove, then i used XMLout()to convert the array back to XML. Using =~ s///g, i was able to edit node changes (<person> became <opt>). Once i'd XMLout()'ed the original data.xml structure, i attempted to string replace the variable of the removable section of XML, with empty string of the original structure.

Obviously, this didn't work.

my $new_xml    = XMLout($xml, KeepRoot => 1);
my $remove_xml = XMLout($person_to_remove, KeepRoot => 1);

$remove_xml =~ s/opt/person/g;
$new_xml =~ s/($remove_xml)//g; # facepalm, i know

How would i remove this section of XML, either by array data removal, or plain file text removal so as to write back to the original data.xml file the new structure?


Solution

  • As you have been already told, the point of XML::Simple is to use Perl data structures instead of string manipulation. So, forget s/// and try

    my $xml = XMLin($data, ForceArray => 1, KeepRoot => 1);
    my $remove = 'Jane';
    delete $xml->{people}[0]{person}{$remove};
    print XMLout($xml, KeepRoot => 1);
    

    or, with empty KeyAttr

    my $xml = XMLin($data, ForceArray => 1, KeepRoot => 1, KeyAttr => []);
    @{ $xml->{people}[0]{person} } = grep $_->{name} ne $remove,
                                     @{ $xml->{people}[0]{person} };
    print XMLout($xml, KeepRoot => 1);
    

    For comparison, the same task in XML::XSH2:

     open data.xml ;
     my $remove = 'Jane' ;
     delete /people/person[@name=$remove] ;
     save :b ;