BradV has asked for the wisdom of the Perl Monks concerning the following question:

I wrote a script using XML::LibXML to remove some elements from an xml file. That is working. Now, I need to remove some attributes. Here is an example of the xml file:
<RIBCL VERSION="2.1"> <LOGIN USER_LOGIN="Administrator"> <USER_INFO MODE="write"> <ADD_USER USER_NAME="John Smith" USER_LOGIN="jsmith" PASSWORD="%user +_password%"> </ADD_USER> </USER_INFO> </LOGIN> </RIBCL>
I need to remove the USER_NAME and PASSWORD attributes only leaving the USER_LOGIN attribute. I was looking at XML::LibXML::Attr, but I don't see any delete/remove functions. I've been searching on the Internet, but can only find xml remove attribute code in c# or visual basic. Any suggestions?

Replies are listed 'Best First'.
Re: xml remove attribute
by hippo (Archbishop) on Aug 19, 2016 at 13:25 UTC
      OK, got a result, but not easily. I seem to be missing how to descend an xml tree and pick out the element I want to operate on. What I have is:
      #!/usr/bin/perl use strict; use warnings; use XML::LibXML; die "Usage: $0 filename \n"; unless ( @ARGV > 0 ); my $xml_file = shift; my $xml = XML::LibXML->new; my $dom = $xml->parse_file( $xml_file ); my $root = $dom->documentElement; my @logins = grep { $_->nodeType == XML_ELEMENT_NODE } $root->childNod +es; foreach my $login ( @logins ) { my @user_infos = grep { $_->nodeType == XML_ELEMENT_NODE } $login-> +childNodes; foreach my $user_info ( @user_infos ) { my @add_users = grep { $_->nodeType == XML_ELEMENT_NODE } $user_ +info->childNodes; foreach my $add_user ( @add_users ) { $add-user->removeAttribute( "USER_NAME" ); $add-user->removeAttribute( "PASSWORD" ); } } } my $output = $dom->toString(0); $output =~ s/(?<=\n)\s*\n//g; open ( my $FH, '>', 'newilo2' ) or die "Could not open file newilo2 $! +"; print $FH $output; close $FH;
      I tried using findnode, but I get an error about perl not being able to find that method. So, thanks for the pointer! It at least is getting me the results I want. :)

        No need to do a nested descent through the tree when you know in advance which nodes you want to alter. This revised version of your code does the trick for your sample data:

        #!/usr/bin/perl use strict; use warnings; use XML::LibXML; die "Usage: $0 filename \n" unless ( @ARGV > 0 ); my $xml_file = shift; my $xml = XML::LibXML->new; my $dom = $xml->parse_file( $xml_file ); foreach my $add_user ( $dom->getElementsByTagName('ADD_USER') ) { $add_user->removeAttribute( "USER_NAME" ); $add_user->removeAttribute( "PASSWORD" ); } my $output = $dom->toString(0); $output =~ s/(?<=\n)\s*\n//g; open ( my $FH, '>', 'newilo2' ) or die "Could not open file newilo2 $! +"; print $FH $output; close $FH;

        XML::LibXML is pretty powerful, but it's a steep old learning curve. If you do a lot of XML work it would pay to immerse yourself in the documentation for a while to let it all sink in. Good luck.

        Edit: better choice of verb

Re: xml remove attribute
by Jenda (Abbot) on Aug 23, 2016 at 12:34 UTC
    use strict; use XML::Rules; my $filter = XML::Rules->new(style => 'filter', rules => { ADD_USER => sub { my ($tag, $attrs) = @_; delete $attrs->{USER_NAME}; delete $attrs->{PASSWORD}; return $tag => $attrs; } }); $filter->filter(\*DATA); __DATA__ <RIBCL VERSION="2.1"> <LOGIN USER_LOGIN="Administrator"> <USER_INFO MODE="write"> <ADD_USER USER_NAME="John Smith" USER_LOGIN="jsmith" PASSWORD="%user +_password%"> </ADD_USER> </USER_INFO> </LOGIN> </RIBCL>

    You can of course read from and write to other places than DATA and STDOUT and the good thing is that even if the file is huuuuge, the memory footprint is constant.

    If you want to change the tag name as well, just change the return $tag => $attrs; to return 'DEL_USER' => $attrs;

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.