keys function in perl

257 views Asked by At

I have a perl script which evaluates an xml script using the keys() function. My script is written in such a way, attributes/tags in xml are not coming in order. Does the keys function in perl, evaluate randomly??

Ex:

if( (keys %{$data})[0] eq 'fileFooter' and
    (keys %{$data->{fileFooter}})[0] eq 'measCollec' and
    (keys %{$data->{fileFooter}->{measCollec}})[0] eq 'endTime' and
    (keys %{$data})[1] eq 'fileHeader' and
    (keys %{$data->{fileHeader}})[0] eq 'measCollec' and
    (keys %{$data->{fileHeader}->{measCollec}})[0] eq 'beginTime'

Here the fileFooter attribute in XML file is coming at the end and the fileHEader is coming at the beginning. In this case perl will work fine??

Please find the script in below link : https://docs.zoho.com/writer/published.do?rid=x6jdb8effa7ba9b0140258c9b3b1fb9617386

Please find the XML file in below link :

https://docs.zoho.com/writer/ropen.do?rid=x6jdbcd99dd2df097455f99fa2907a84620ee

6

There are 6 answers

1
G. Cito On

One way to think about this is to remember that you access the "values" in an array by the ordered elements in the data structure (which is why you use $array[2], @array[1,2,3] or $array_ref->[3]). With a hash you access "values" by their respective keys which could be in any order because you are accessing them in an "unordered" way.

See Why do hash keys have different order when printing? and other posts linked to under the "Related" sidebar for more detailed discussion.

5
ysth On

It looks to me like you are trying to see if keys exist; if so, try:

if( exists $data->{fileFooter} &&
    exists $data->{fileFooter}{measCollec} &&
    exists $data->{fileFooter}{measCollec}{endTime} &&
    exists $data->{fileHeader} &&
    exists $data->{fileHeader}{measCollec} &&
    exists $data->{fileHeader}{measCollec}{beginTime}
) {

or, if you have no autovivification; set,

if( exists $data->{fileFooter}{measCollec}{endTime} &&
    exists $data->{fileHeader}{measCollec}{beginTime}
) {

or do explicitly what no autovivification; does for you:

if ( exists ${ ${ $data->{fileFooter} || {} }{measCollec} || {} }{endTime} &&
    exists ${ ${ $data->{fileHeader} || {} }{measCollec} || {} }{beginTime}
) {
1
Dondi Michael Stroma On

The keys will be stored in an apparently random (but not truly random) order. The keys and values functions promise to return elements in the same order each time, but only when called on the same hash. A different hash–even if the keys are the same–may return keys in a completely different order. Modifying a hash may also change the order of the keys.

0
Tim De Lange On

Yes. Since hashes are by definition unordered, the keys function will seem to "evaluate randomly".

It would be better to parse your xml into array refs.

You cannot count on a hash to be rendered in the same order time after time.

0
Neil H Watson On

Hashes do not store keys in a reliable order. This is the nature of hashes, either use the sort function or use an array. See: http://perldoc.perl.org/functions/keys.html

0
Sobrique On

OK, sorry for taking a while to come back on this - it's easy to miss question updates.

Anyway - Given your script seems to be 'gather XML, check for certain keys' - I honestly think we may have an XY problem here. Why are you trying to validate your XML when you could instead just decompose it and do a 'by key' search?

use strict;
use warnings;
use XML::Twig;

my %pos_lookup;

sub extract_measType {
    my ( $twig, $meastype ) = @_;
    my $pos = $meastype->att('pos');
    $pos_lookup{$pos} = $meastype->text;
}

my $twig = XML::Twig->new(
    'pretty_print'  => 'indented_a',
    'twig_handlers' => { 'measType' => \&extract_measType }
);
$twig->parse( \*DATA );

foreach my $element ( $twig->root->get_xpath('measData/measInfo/measValue') )
{
    my $ldn = $element->att('measObjLdn');
    print "Data for: $ldn\n";
    foreach my $reading ( $element->children('r') ) {
        my $pos = $reading->att('pos');
        print "\t", $pos_lookup{$pos}, ":", $reading->text, "\n";
    }
}


__DATA__
<?xml version="1.0" encoding="UTF-8"?> 
 <measCollecFile> 
   <fileHeader fileFormatVersion="32.435 V10.0" dnPrefix="DC=ericsson.se,g3SubNetwork=Sweden"> 
     <fileSender localDn="ManagedElement=1,Chassis=1"/> 
     <measCollec beginTime="2015-06-08T05:06:58Z"/> 
   </fileHeader> 
   <measData> 
     <managedElement localDn="ManagedElement=1,Chassis=1"/> 
     <measInfo measInfoId="schema_profile_1"> 
       <granPeriod duration="PT60S" endTime="2015-06-08T05:06:58Z"/> 
       <repPeriod duration="PT60S"/> 
       <measType pos="1">inOctets</measType> 
       <measType pos="2">inPackets</measType> 
       <measType pos="3">mcastInOctets</measType> 
       <measType pos="4">mcastInPackets</measType> 
       <measType pos="5">mcastOutOctets</measType> 
       <measType pos="6">mcastOutPackets</measType> 
       <measType pos="7">meteringClassCounter</measType> 
       <measType pos="8">meteringPolicyName</measType> 
       <measType pos="9">outOctets</measType> 
       <measType pos="10">outPackets</measType> 
       <measType pos="11">policingClassCounter</measType> 
       <measValue 
measObjLdn="ManagedElement=1,Chassis=1,Slot=1,Eth1GbCard=1,Ethernet1GBPort=1"> 
         <r pos="1">337060</r> 
         <r pos="2">5616</r> 
         <r pos="3">0</r> 
         <r pos="4">0</r> 
         <r pos="5">0</r> 
         <r pos="6">0</r> 
         <r pos="7">(N/A)</r> 
         <r pos="8">(N/A)</r> 
         <r pos="9">1176</r> 
         <r pos="10">28</r> 
         <r pos="11">(N/A)</r> 
       </measValue> 
       <measValue 
measObjLdn="ManagedElement=1,Chassis=1,Slot=1,Eth1GbCard=1,Ethernet1GBPort=2"> 
         <r pos="1">1300</r> 
         <r pos="2">20</r> 
         <r pos="3">0</r> 
         <r pos="4">0</r> 
         <r pos="5">0</r> 
         <r pos="6">0</r> 
         <r pos="7">(N/A)</r> 
         <r pos="8">(N/A)</r> 
         <r pos="9">336936</r> 
         <r pos="10">5624</r> 
         <r pos="11">(N/A)</r> 
       </measValue> 
     </measInfo> 
   </measData> 
   <fileFooter> 
     <measCollec endTime="2015-06-08T05:06:58Z"/> 
   </fileFooter> 
 </measCollecFile> 

Now, if you really want to check for the presence of particular things, I'd suggest a 'get_xpath' search:

my %to_check = (
    '/measCollecFile/measData/measInfo/measType[@pos="1"]' => 'inOctets',
    '/measCollecFile/measData/measInfo/granPeriod'         => '',
    '/measCollecFile/fileFooter/measCollec'                => '',
    '/some/bogus/value'                                    => "value",
);
foreach my $xpath ( keys %to_check ) {
    my $node = $twig->root->get_xpath( $xpath, 0 );
    my $value = "";
    if ($node) { $value = $node->text; }
    print $xpath, " => ", $value;
    if   ( $node and $value eq $to_check{$xpath} ) { print " OK\n"; }
    else                                           { print " ERROR\n"; }
}

Sorry, I haven't reproduced your whole thing, but hopefully this illustrates the idea? I don't think you need to do quite as exhaustive a validation though.