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
Re: Is there any XML reader like this?
by toolic (Bishop) 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:
| [reply] [d/l] |
|
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 | [reply] [d/l] [select] |
|
| [reply] |
Re: Is there any XML reader like this?
by BrowserUk (Patriarch) on Jan 13, 2012 at 22:10 UTC
|
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".
| [reply] [d/l] [select] |
Re: Is there any XML reader like this?
by tobyink (Canon) 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>
| [reply] [d/l] [select] |
|
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".
| [reply] [d/l] [select] |
|
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.)
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
|
|
|
|
<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. | [reply] |
|
|
|
|
|
|
|
|
Re: Is there any XML reader like this?
by choroba (Cardinal) 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
| [reply] [d/l] |
|
|