Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

how to get attribute values and store in a hash.

by veerubiji (Sexton)
on Dec 09, 2011 at 16:23 UTC ( [id://942690]=perlquestion: print w/replies, xml ) Need Help??

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

Hi monks, I have Xml files in a folder, I need to extract some attribute values form xml files and store in a hash. My xml file look like this.

<?xml version="1.0" encoding="UTF-8"?> <Servicelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi +:noNamespaceSchemaLocation="file:///files/service.xsd"> <Service Num="B7a" Name="temperature sensor"> <Des>It delivers actual temperature in the form ov Volts</Des> <Customermodules> <Softwaremodule Service="ADC" Path="/main/ADCservice.xml"/> </Customermodules> <Suppliermodules> <Softwaremodule Service="input" Path="/main/inputservice.xml"/> <Softwaremodule Service="signal" Path="/main/signalservice.xml"/> <Hardwaremodule Type="engine" Nr="18" Servicenum="1" Path="/main/e +ngineservice.xml"/> <Hardwaremodule Type="motor" Nr="7" Servicenum="1" Path="/main/mo +torservice.xml"/> <Hardwaremodule Type="supply" Nr="1" Servicenum="1" Path="/main/sup +plyservice.xml"/> </Suppliermodules> </Service> </Servicelist>

In hash service num attribute is the key and Customermodules ,Suppliermodules attributes are the values. for example In the xml file B7a is the key and its Customermodules ,Suppliermodules attribute values are the values to the key, like that.

I tried like this but Its not working

#!/usr/bin/perl use warnings; use strict; use XML::LibXML; use Carp; use File::Find; use File::Spec::Functions qw( canonpath ); use XML::LibXML::Reader; my %hash; my @ARGV ="C:/Main"; die "Need directories\n" unless @ARGV; find( sub { return unless ( /(_service\.xml)$/ and -f ); extract_information(); return; }, @ARGV ); sub extract_information { my ($path $hash) = $_; if( my $reader = XML::LibXML::Reader->new( location => $path )){ while ( $reader->nextElement( 'Service' )) { my $elem = $reader->getAttribute( 'Id'); $reader->nextElement( 'Customermodules' ); my $elem1 = $reader->getAttribute( 'Service'); $reader->nextElement( 'Suppliermodules' ); my $elem2 = $reader->getAttribute('Service'); $hash->{$elem} = $elem1; push @{$hash{$elem}}, '$elem2'; } } return; } print my $num=keys%hash;

Can any one help me with the script, that extract attributes from xml file and store in a hash, I need to get all attributes in Suppliermodules( type, nr), no need of path attribute at any where. Please help me with this problem,i am learning perl scripting language.

Thanks in advance

Replies are listed 'Best First'.
Re: how to get attribute values and store in a hash.
by kennethk (Abbot) on Dec 09, 2011 at 19:05 UTC
    Please read How do I post a question effectively?. In particular, the code has significant complications that are unnecessary for the aspect which is providing you issues (finding files which are unlike to exist on a monk's drive, importing several unused modules) and fails to pass a basic compilation check (perl -c, see -c). Some other issues:
    1. Line 22 needs to be corrected - you have a syntax error in omitting a comma from your list assignment, and you likely meant to copy the contents of the argument array (@_) and not the default input variable ($_). That should read my ($path, $hash) = @_;. Of course, you call that function with no arguments, so that is likely not going to work very well for you.

    2. Your posted XML is not well-formed. You have an opening Servicelist tag but no closing one.

    3. You are interacting with your references incorrectly on lines 35 & 36. Read perlreftut for an intro to interacting with references.

    4. Single quotes do not interpolate whereas double quotes do, so on line 36 you likely mean "$elem2"; this is for your purposes a no-op since it should already be a string, so it would be better written as $elem2.

    5. Your Service element does not contain the attribute Id, and your Customermodules and Suppliermodules elements do not have Service attributes. It might do some good to review the difference between an element and an attribute -- see XML#Key_terminology.

    This is an example script that takes the XML you have provided (modified to be well-formed) and extracts a list of type and nr attributes from the Hardwaremodule elements in Suppliermodules from the Service tags. It should hopefully give you an idea how to proceed, particularly when read with the documentation of XML::LibXML::Reader.

    #!/usr/bin/perl use warnings; use strict; use XML::LibXML; use XML::LibXML::Reader; use Data::Dumper; my $hash_ref; my $reader = XML::LibXML::Reader->new( IO => *DATA ); while ( $reader->nextElement( 'Service' )) { my $number = $reader->getAttribute( 'Num'); $reader->nextElement( 'Suppliermodules' ); while ( $reader->nextElement( 'Hardwaremodule' )) { my $module = {}; $module->{type} = $reader->getAttribute('Type'); $module->{nr} = $reader->getAttribute('Nr'); push @{$hash_ref->{$number}}, $module; } } print Dumper $hash_ref; __DATA__ <?xml version="1.0" encoding="UTF-8"?> <Servicelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi +:noNamespaceSchemaLocation="file:///files/service.xsd"> <Service Num="B7a" Name="temperature sensor"> <Des>It delivers actual temperature in the form ov Volts</Des> <Customermodules> <Softwaremodule Service="ADC" Path="/main/ADCservice.xml"/> </Customermodules> <Suppliermodules> <Softwaremodule Service="input" Path="/main/inputservice.xml"/> <Softwaremodule Service="signal" Path="/main/signalservice.xml"/> <Hardwaremodule Type="engine" Nr="18" Servicenum="1" Path="/main/e +ngineservice.xml"/> <Hardwaremodule Type="motor" Nr="7" Servicenum="1" Path="/main/mo +torservice.xml"/> <Hardwaremodule Type="supply" Nr="1" Servicenum="1" Path="/main/sup +plyservice.xml"/> </Suppliermodules> </Service> </Servicelist>

      Hi, I am sorry for my mistakes. Thanks for your suggestion. I tried as you said but still problem is there, I am showing what I tried as below

      #!/usr/bin/perl use warnings; use strict; use Carp; use File::Find; use File::Spec::Functions qw( canonpath ); use XML::LibXML::Reader; use Data::Dumper; my @ARGV ="c:/main/folder"; die "Need directories\n" unless @ARGV; my $Number; my %hash; find( sub { my $file = $_; # my $path = canonpath $File::Find::name; my $path =$_; return unless -f $path; return unless $file =~ /(_service\.xml)$/; extract_information($path, \%hash); return; }, @ARGV ); sub extract_information { my( $path, $hash)=@_; my $ret = open my $xmlin, '<', $path; unless ($ret) { carp "Cannot open '$path': $!"; return; } my $reader = XML::LibXML::Reader->new(IO => $xmlin); unless ($reader) { carp "Cannot create reader using '$path'"; return; } while ( $reader->nextElement( 'Service' )) { my $Number = $reader->getAttribute( 'num'); $reader->nextElement( 'Suppliermodules' ); while ( $reader->nextElement( 'Hardwaremodule' )) { my $module = {}; $module->{type} = $reader->getAttribute('Type'); $module->{nr} = $reader->getAttribute('Nr'); push @{$hash->{$Number}}, $module; } while ( $reader->nextElement( 'softwaremodule' )) { my $module1 = {}; $module1->{service} = $reader->getAttribute('Service'); push @{$hash->{$Number}}, $module1; } $reader->nextElement( 'customermodules' ); while ( $reader->nextElement( 'softwaremodule' )) { my $module2 = {}; $module2->{cusservice} = $reader->getAttribute('Service'); push @{$hash->{$Number}}, $module2; } } close $xmlin or carp "Cannot close '$path': $!"; return; } print Dumper \%hash;

      I tried with find(\&wanted, @directories); to search directives But I have some difficulties with that so I fallowed my procedure.What I want to do is I need to create hash with service num as the key and and all information of software module and hardware module attributes in customer and supplier module elements are array of values for that key. here I have two elements customer and supplier modules so I think array of values in a hash value.So finally after running this script I need to look my hash like (Service num attribute as the key and remaining information as the array of values for that key. so I have all service nums as the keys and its information is array of values.) I tried like that in the above script but it doesn't give any errors.I am not getting exact output, it giving some element attributes and some service num's only.I am new to perl, I am not able to produce what I want, please help me with that. If any typing errors is there please excuse me.

      My output like this

      'B7a'=>[ { Service='ADC', path='....' } { Type='engine', Nr='18', Servicenum='1', path='....' } { Type='motor', Nr='7', Servicenum='1', path='....' } { Type='supply', Nr='1', Servicenum='1', path='....' } { Service='input', path='----' } { Service='signal', path='----' } ].....................

      Thanks in advance.

        If you read How do I post a question effectively?, why are you still posting code that uses File::Find? I do not get your result because I do not have a c:/main/folder directory on my computer. Your issue is with parsing XML, so all your example should include is code to parse XML. You have almost 100 posts - seriously, you should know better by now. I've already given you a template for just working on your parsing logic.

        You have now provided an expected output, so I can modify my previous code to replicate your specified output, except of course that your quoted path's do not correspond to the paths in your XML and the order is different because your spec does not respect the order of the source document.

        #!/usr/bin/perl use warnings; use strict; use XML::LibXML::Reader; use Data::Dumper; my $hash_ref; my $reader = XML::LibXML::Reader->new( IO => *DATA ); while ( $reader->nextElement( 'Service' )) { my $number = $reader->getAttribute( 'Num'); $hash_ref->{$number} = []; $reader->read; while (1) { if ( $reader->localName eq 'Customermodules' or $reader->localName eq 'Suppliermodules') { $reader->read; while (1) { if ($reader->localName eq 'Softwaremodule') { my $module = {}; $module->{Service} = $reader->getAttribute('Servic +e'); $module->{path} = $reader->getAttribute('Path'); push @{$hash_ref->{$number}}, $module; } elsif ($reader->localName eq 'Hardwaremodule') { my $module = {}; $module->{Type} = $reader->getAttribute('Type'); $module->{Nr} = $reader->getAttribute('Nr'); $module->{Servicenum} = $reader->getAttribute('Ser +vicenum'); $module->{path} = $reader->getAttribute('Path'); push @{$hash_ref->{$number}}, $module; } $reader->nextSibling() > 0 or last; } } $reader->nextSibling() > 0 or last; } } $reader->finish; print Dumper $hash_ref; __DATA__ <?xml version="1.0" encoding="UTF-8"?> <Servicelist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi +:noNamespaceSchemaLocation="file:///files/service.xsd"> <Service Num="B7a" Name="temperature sensor"> <Des>It delivers actual temperature in the form ov Volts</Des> <Customermodules> <Softwaremodule Service="ADC" Path="/main/ADCservice.xml"/> </Customermodules> <Suppliermodules> <Softwaremodule Service="input" Path="/main/inputservice.xml"/> <Softwaremodule Service="signal" Path="/main/signalservice.xml"/> <Hardwaremodule Type="engine" Nr="18" Servicenum="1" Path="/main/e +ngineservice.xml"/> <Hardwaremodule Type="motor" Nr="7" Servicenum="1" Path="/main/mo +torservice.xml"/> <Hardwaremodule Type="supply" Nr="1" Servicenum="1" Path="/main/sup +plyservice.xml"/> </Suppliermodules> </Service> </Servicelist>

        outputs

        $VAR1 = { 'B7a' => [ { 'path' => '/main/ADCservice.xml', 'Service' => 'ADC' }, { 'path' => '/main/inputservice.xml', 'Service' => 'input' }, { 'path' => '/main/signalservice.xml', 'Service' => 'signal' }, { 'Type' => 'engine', 'Servicenum' => '1', 'path' => '/main/engineservice.xml', 'Nr' => '18' }, { 'Type' => 'motor', 'Servicenum' => '1', 'path' => '/main/motorservice.xml', 'Nr' => '7' }, { 'Type' => 'supply', 'Servicenum' => '1', 'path' => '/main/supplyservice.xml', 'Nr' => '1' } ] };
Re: how to get attribute values and store in a hash.
by choroba (Cardinal) on Dec 10, 2011 at 00:28 UTC
    As pointed out by kennethk, your specification is unclear and contradictory. Guessing wildly and using XML::XSH2 which I prefer for XML manipulation, this could give you a clue:
    #!/usr/bin/perl use warnings; use strict; use XML::XSH2; use Data::Dumper; xsh << '}'; open 942690.xml ; for my $num in /Servicelist/Service/@Num { for my $s in $num/..//@Service perl { push @{ $hash->{$num->value} }, $s->value } ; } } print Dumper $XML::XSH2::Map::hash;
Re: how to get attribute values and store in a hash.
by Jenda (Abbot) on Dec 15, 2011 at 09:01 UTC

    Have a look at XML::Rules, this is the kind of task that suits the module perfectly. There are example of its use here on PerlMonks, see Super search.

    Jenda
    Enoch was right!
    Enjoy the last years of Rome.

Re: how to get attribute values and store in a hash.
by sundialsvc4 (Abbot) on Dec 10, 2011 at 11:40 UTC
    It also might be practical to parse the XML file using one of the many available CPAN power-tools, and then use that file directly as your data source instead of munching things into hashes.   In any case, devise a Perl object (or objects ...) that will “give you the answers you’re looking for, never mind just how.”   Otherwise such code can get really messy really quick, especially if it winds up retaining large amounts of data “in memory” and thus taking a trip to Virtual Memory Thrashing Hell.
Re: how to get attribute values and store in a hash.
by choroba (Cardinal) on Dec 14, 2011 at 23:58 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (6)
As of 2024-04-26 08:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found