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

Good day Monks,

I am really having a hard time understanding the XML::Twig module and how to actually use it. I am hoping someone could give me a jumpstart on how to extract attributes and values from any level of the xml tree. I have fumbled and fumbled with different trival examples found but I can't seem to understand the real logic of what I am reading. I would with all sincerity appreciate any help that could be provided. I think if I could get a working example of code against the small snippet of xml defined below I might actually catch on and understand how to walk down any twig and extract the attrib/values regardless of how deep they go. Please pardon my short-comings with regard to OOP.

Many thanks,
Danny

# XML <AGENT hostname="viper3"> <LADDER> <ACL> <ACCOUNT id="4cf031986c"> <USERNAME>emcon</USERNAME> <HOST>*sppcon*</HOST> <PERMISSION>CDOPS</PERMISSION> </ACCOUNT> <ACCOUNT id="b92794bbd7"> <USERNAME>cpemcon</USERNAME> <HOST>*</HOST> <PERMISSION>COPS</PERMISSION> </ACCOUNT> <ACCOUNT id="8ff0478641"> <USERNAME>dbemcon</USERNAME> <HOST>*</HOST> <PERMISSION>COPS</PERMISSION> </ACCOUNT> <ACCOUNT id="22d2647740"> <USERNAME>tuxemcon</USERNAME> <HOST>*</HOST> <PERMISSION>COPS</PERMISSION> </ACCOUNT> </ACL> </LADDER> </AGENT>


# script use strict; use XML::Twig; my $file = $ARGV[0]; my $twig = XML::Twig->new(); $twig->parsefile($file); my $root = $twig->root; foreach my $acl ($root->children('ACL')){ print $acl->att('id'); print "\n"; print $acl->first_child('USERNAME'); print "\n"; print $acl->first_child('HOST'); print "\n"; print $acl->first_child('PERMISSION'); print "\n"; }

Replies are listed 'Best First'.
Re: XML::Twig Help
by toolic (Bishop) on Jan 07, 2011 at 14:13 UTC
    One approach is to use Twig handlers. For example:
    use warnings; use strict; use XML::Twig; my $xmlstr = <<EOF; <AGENT hostname="viper3"> <LADDER> <ACL> <ACCOUNT id="4cf031986c"> <USERNAME>emcon</USERNAME> <HOST>*sppcon*</HOST> <PERMISSION>CDOPS</PERMISSION> </ACCOUNT> <ACCOUNT id="b92794bbd7"> <USERNAME>cpemcon</USERNAME> <HOST>*</HOST> <PERMISSION>COPS</PERMISSION> </ACCOUNT> <ACCOUNT id="8ff0478641"> <USERNAME>dbemcon</USERNAME> <HOST>*</HOST> <PERMISSION>COPS</PERMISSION> </ACCOUNT> <ACCOUNT id="22d2647740"> <USERNAME>tuxemcon</USERNAME> <HOST>*</HOST> <PERMISSION>COPS</PERMISSION> </ACCOUNT> </ACL> </LADDER> </AGENT> EOF my $twig = XML::Twig->new( twig_handlers => {ACCOUNT => \&acct} ); $twig->parse($xmlstr); sub acct { my ($t, $elt) = @_; print $elt->att('id'), "\n"; print $elt->first_child('USERNAME' )->text(), "\n"; print $elt->first_child('HOST' )->text(), "\n"; print $elt->first_child('PERMISSION')->text(), "\n"; print "\n"; } __END__ 4cf031986c emcon *sppcon* CDOPS b92794bbd7 cpemcon * COPS 8ff0478641 dbemcon * COPS 22d2647740 tuxemcon * COPS

    Update: There is a great tutorial at http://web.archive.org/web/20080702002018/http://xmltwig.com (unfortunately, the tutorial link from XML::Twig seems to be down).

    Update: using mirod's suggestion, and refactoring a little...

    sub acct { my ($t, $elt) = @_; print $elt->att('id'), "\n"; for my $tag (qw(USERNAME HOST PERMISSION)) { print $elt->field($tag), "\n"; } print "\n"; }

      Usually, instead of writing $elt->first_child('USERNAME'  )->text(), I write $elt->field( 'USERNAME'), it is easier to read. It also doesn't die if there is no USERNAME child, which may be convenient.

      Oh, and xmltwig.com is down at the moment, but it's mirrored at mirod.org.

        ++mirod, I was unaware of field.

        On another topic, you probably didn't see my update regarding access to your excellent website. Were you aware that it has been tough to get to for some time now?

Re: XML::Twig Help
by Gulliver (Monk) on Jan 07, 2011 at 14:33 UTC

    Take a look at the module LibXML. XPath is what I use for grabbing or changing nodes in XML. The LibXML module comes with example scripts using XPath and there are XPath tutorials out there.

    #************************************************** # # pass this the xml file name. # uses LibXML and XPath to find the username. # sub connect_check { my ($xml_file) = @_; use XML::LibXML; my $parser = XML::LibXML->new(); my $doc = $parser->parse_file($xml_file); foreach my $element ($doc->findnodes("//USERNAME")) { my $user = $element->to_literal; print "$xml_file,$user,-\n"; } }