1

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

Amar
  • 11
  • 2
  • 2
    Perl hashes are unordered. You can not rely on the hash to return keys in any particular order. – friedo Jun 15 '15 at 18:18
  • See [Why do the same hash keys have different order when printing?](http://stackoverflow.com/questions/30340027/why-do-the-same-hash-keys-have-different-order-when-printing/30340150#30340150) – G. Cito Jun 15 '15 at 18:22
  • 2
    This also looks like XML as parsed by `XML::Simple`. I would suggest using something else, and just using `xpath` expressions instead. Quote a sample of the XML and the desired outcome, and I'm sure someone can give you an example that works. – Sobrique Jun 15 '15 at 19:40
  • OK, with that information @Amar there's a different question to be had. I have posted an answer of what I think is a better way of solving the problem. But it might be worth opening a new question entirely anyway. – Sobrique Jun 25 '15 at 16:23

6 Answers6

2

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

Neil H Watson
  • 1,002
  • 1
  • 14
  • 31
2

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}
) {
ysth
  • 96,171
  • 6
  • 121
  • 214
  • Hi @ikegami, Ive attached my Script & xml file in the question now. Could you please check let me know, the above exists is enough to change in the script?? – Amar Jun 17 '15 at 14:21
  • @Amar, uh, why don't you tell us if you're still having a problem instead! – ikegami Jun 17 '15 at 15:21
  • @ ikegami: Sorry man. I aint that much good in perl to write and test it . :/Moreover I need few setup's like Linux box to run this script, which will be free by tomorrow. I just want to get my code ready, so that i can run and check it once I get the setup!! – Amar Jun 17 '15 at 16:54
  • There are some xml attributes like below : How can i write my filter for those attributes?? – Amar Jun 17 '15 at 16:56
  • that sounds like a completely different question. though if you do start a new question, you'll need to be a lot more informative than "How can i write my filter"? – ysth Jun 17 '15 at 17:09
1

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.

Tim De Lange
  • 739
  • 3
  • 6
0

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.

Community
  • 1
  • 1
G. Cito
  • 6,210
  • 3
  • 29
  • 42
0

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.

Dondi Michael Stroma
  • 4,668
  • 18
  • 21
  • Please find the xml file in below location: https://docs.zoho.com/writer/published.do?rid=x6jdbcd99dd2df097455f99fa2907a84620ee – Amar Jun 17 '15 at 13:51
0

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.

Sobrique
  • 52,974
  • 7
  • 60
  • 101