Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Is there any XML reader like this?

by ashok.g (Beadle)
on Jan 13, 2012 at 21:24 UTC ( #947833=perlquestion: print w/ replies, xml ) Need Help??
ashok.g has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I have tried Config::Simple, XML::Simple modules but I am unable to find them that server my purpose.

My XML file looks like this:
<servers> <station18> <ip>10.0.0.101</ip> <ip>10.0.1.101</ip> <ip>10.0.0.102</ip> <ip>10.0.0.103</ip> <ip>10.0.1.103</ip> </station18> <station19> <ip>10.0.0.111</ip> <ip>10.0.1.111</ip> <ip>10.0.0.112</ip> <ip>10.0.0.113</ip> <ip>10.0.1.113</ip> </station19> <station17> <ip>10.0.0.121</ip> <ip>10.0.1.121</ip> <ip>10.0.0.122</ip> <ip>10.0.0.123</ip> <ip>10.0.1.123</ip> </station17> </servers>
My expectations:
1. Need to get the data from this file dynamically expect inner tags like "ip".
2. Should return tags under <servers> tag which are station18, station19 and station17
3. Should return ip values under each station tag which are 10.0.0.101, 10.0.1.101, 10.0.0.102, 10.0.0.103 and 10.0.1.103 for station18 etc..,

I explained this in detail and you answers may look like spoon feeding to me. But please let me know if there are any such modules defined already in perl or do I need to modify my XML file that can use an XML file to most....

Really appreciate your thoughts.

Thanks,
Ashok

Comment on Is there any XML reader like this?
Download Code
Re: Is there any XML reader like this?
by toolic (Chancellor) on Jan 13, 2012 at 22:04 UTC
    I'm not sure why you don't think you can use XML::Simple to parse your XML file, but here is one way to print all ip's for station17, for example:
    use warnings; use strict; use XML::Simple; my $str = ' <servers> <station18> <ip>10.0.0.101</ip> <ip>10.0.1.101</ip> <ip>10.0.0.102</ip> <ip>10.0.0.103</ip> <ip>10.0.1.103</ip> </station18> <station19> <ip>10.0.0.111</ip> <ip>10.0.1.111</ip> <ip>10.0.0.112</ip> <ip>10.0.0.113</ip> <ip>10.0.1.113</ip> </station19> <station17> <ip>10.0.0.121</ip> <ip>10.0.1.121</ip> <ip>10.0.0.122</ip> <ip>10.0.0.123</ip> <ip>10.0.1.123</ip> </station17> </servers> '; my $xml = XMLin($str); print "$_\n" for @{ $xml->{station17}{ip} }; __END__ 10.0.0.121 10.0.1.121 10.0.0.122 10.0.0.123 10.0.1.123
    See also:
      toolic,

      I have been using your code and which uses Simple::XML but now I am unable to retrieve the "ip" with the same code:
      print "CMD:",@{ $xml->{$server}{ip} },"\n";
      This is printing nothing.

      And I have my XML file with server like:
      <station19> <ip>10.0.1.113</ip> </station19>
      Anything missing here?

      Thanks,
      Ashok
Re: Is there any XML reader like this?
by BrowserUk (Pope) on Jan 13, 2012 at 22:10 UTC

    How does this differ from what you want?:

    C:\test>junk44 { station17 => { ip => [ "10.0.0.121", "10.0.1.121", "10.0.0.122", "10.0.0.123", "10.0.1.123", ], }, station18 => { ip => [ "10.0.0.101", "10.0.1.101", "10.0.0.102", "10.0.0.103", "10.0.1.103", ], }, station19 => { ip => [ "10.0.0.111", "10.0.1.111", "10.0.0.112", "10.0.0.113", "10.0.1.113", ], }, }

    The program that produced the above from your sample data in the __DATA__:

    #! perl -slw use strict; use Data::Dump qw[ pp ]; use XML::Simple; my $xml = XMLin( \*DATA ); pp $xml; __DATA__ ...

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re: Is there any XML reader like this?
by tobyink (Abbot) on Jan 13, 2012 at 22:32 UTC

    As always, I strongly recommend against XML::Simple. XML::Simple might seem simple until you end up in a situation where one of your stations has only a single IP address, and you end up with:

     {
       servers => {
         station19 => {ip=>['10.10.10.1','10.10.10.2']},
         station20 => {ip=>['10.10.10.3','10.10.10.4']},
         station21 => {ip=>'10.10.10.5'}, # D'oh!
       }
     }
    

    Notice that $hash->{servers}{station21}{ip} is not an arrayref, whereas the IP list is an arrayref for every other station.

    OK, so you can configure XML::Simple to force the IP addresses to always be arrayrefs, but by the time you've thought through every possible permutation of your data, XML::Simple becomes not so simple any more.

    Better to use a more powerful XML module, like XML::LibXML, which might seem more complicated to begin with, but is at least consistent.

    use XML::LibXML; my $xml = XML::LibXML->new->parse_fh(\*DATA); foreach my $station ($xml->findnodes('/servers/*')) { printf("Station: %s\n", $station->tagName); foreach my $ip ($station->findnodes('./ip')) { printf("\tIP: %s\n", $ip->textContent); } } __DATA__ <servers> <station18> <ip>10.0.0.101</ip> <ip>10.0.1.101</ip> <ip>10.0.0.102</ip> <ip>10.0.0.103</ip> <ip>10.0.1.103</ip> </station18> <station19> <ip>10.0.0.111</ip> <ip>10.0.1.111</ip> <ip>10.0.0.112</ip> <ip>10.0.0.113</ip> <ip>10.0.1.113</ip> </station19> <station17> <ip>10.0.0.121</ip> <ip>10.0.1.121</ip> <ip>10.0.0.122</ip> <ip>10.0.0.123</ip> <ip>10.0.1.123</ip> </station17> <station20> <!-- no IP addresses --> </station20> <station21> <!-- just one IP address --> <ip>10.2.1.123</ip> </station21> </servers>

      Sorry, but that is just so much BS. You simply need to add one simple option:

      C:\test>junk44 #! perl -slw use strict; use Data::Dump qw[ pp ]; use XML::Simple; my $xml = XMLin( \*DATA, ForceArray => 1 ); pp $xml; __DATA__ <servers> <station18> <ip>10.0.0.101</ip> <ip>10.0.1.101</ip> <ip>10.0.0.102</ip> <ip>10.0.0.103</ip> <ip>10.0.1.103</ip> </station18> <station19> <ip>10.0.0.111</ip> <ip>10.0.1.111</ip> <ip>10.0.0.112</ip> <ip>10.0.0.113</ip> <ip>10.0.1.113</ip> </station19> <station17> <ip>10.0.0.121</ip> </station17> </servers>

      Produces:

      { station17 => [{ ip => ["10.0.0.121"] }], station18 => [ { ip => [ "10.0.0.101", "10.0.1.101", "10.0.0.102", "10.0.0.103", "10.0.1.103", ], }, ], station19 => [ { ip => [ "10.0.0.111", "10.0.1.111", "10.0.0.112", "10.0.0.113", "10.0.1.113", ], }, ], }

      Which is still far simpler than wasting your time trying to figure out how use those complex monsters.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      The start of some sanity?

        I'm have no idea why you call XML::LibXML a monster compared to XML::Simple.

        use XML::Simple qw( :strict XMLin ); local $XML::Simple::PREFERRED_PARSER = 'XML::Parser'; my $stations = XMLin( \*DATA, ForceArray => 1, KeyAttr => [] ); for my $station_name (keys %$stations) { say $station_name; my $station = $stations->{$station_name}[0]; for my $ip (@{ $station->{ips} // [] }) { say " $ip"; } }
        use XML::LibXML qw( ); my $root = XML::LibXML->load_xml( IO => \*DATA )->documentElement; for my $station ($root->findnodes('*')) { say $station->getName; for my $ip ($station->findnodes('ip')) { say " ".$ip->textContent; } }

        And that's not even mentioning the fact that XML::LibXML is 20x faster* and able to handle so much more stuff than XML::Simple (including every day stuff).

        * — That assumes XML::Parser is used as XML::Simple's backend. XML::LibXML is 10,000x faster than XML::Simple's common default of XML::SAX::PurePerl (which handles encodings really badly).

        Update: Fixed an error in XML::Simple code.
        Update: Fixed an error in XML::LibXML code. ("IO" was mispelled, and the XPath was wrong.)

        You simply need to add one simple option

        And that helps you for precisely five minutes until someone adds this to the file:

          <ip assignment="temporary">10.0.0.101</ip>
        

        And then all your code which assumes stations have IP addresses which are arrayrefs of strings breaks again.

Re: Is there any XML reader like this?
by choroba (Abbot) on Jan 16, 2012 at 02:36 UTC
    I usually use XML::XSH2 for XML manipualtion:
    open 947833.xml ; for /servers/* echo name() ; # list stations for /servers/station18/ip echo text(); # list ip's for statio +n18 $station = 'station17' ; for /servers/*[name()=$station]/ip echo text(); # list ip's for a stat +ion given by a string var

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://947833]
Approved by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (14)
As of 2014-09-03 08:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (35 votes), past polls