title: How to extract and transform XML into a Perl data structure
body: <p>I have xml files in a folder, I need to extract some information from xml files and store in a hash.My xml files look like this </p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;Servicemodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  &lt;Service Id="478" Name="Pump Motor"&gt;
  &lt;Description&gt;It delivers actual pump speed&lt;/Description&gt;
  &lt;ServiceCustomers&gt;
     &lt;SW Service="SKRM" Path="/work/hr_service.xml"/&gt;
  &lt;/ServiceCustomers&gt;
  &lt;ServiceSuppliers&gt;
     &lt;HW Type="s" Nr="12" Service="1" Path="/work/hardware.xml"/&gt;
     &lt;HW Type="v" Nr="2" Service="1" Path="/work/hardware.xml"/&gt; 
     &lt;HW Type="mt" Nr="1" Service="1" Path="/work/hardware.xml"/&gt;
   &lt;/ServiceSuppliers&gt;
   &lt;/Service&gt;
   &lt;/Servicemodule&gt;
  </code></pre>
  
  <p>I want to save this information in a hash like Service Id as the key and reaming information as the array of hash values of that key. SW and HW attributes in both service  customers and suppliers elements are the array of values for the hash key(service Id). Its easy task for experts but I am new learner so this problem suffers me.
  I tried like this</p>
  
  <pre><code>use strict;
  use warnings;
  use feature ':5.10';
  use XML::Twig;
  use File::Find;
  
  my $num=0;
  my %combeh;
  my $dir="V:/Main/work";
  find(\&amp;wanted, $dir);
   sub wanted() {
      if ( -f and /(_service\.xml)$/) {# find all the files with a suffix of .xml                                          
   my $tweak_server =sub{
                  my @bhi;                                                                                       
      my ($twig, $root) =@_;                                                                                     
      my $code=$root-&gt;first_child_text('Service Id');                                                                                 
      my $ser=$root-&gt;first_child('ServiceCustomers');                                                                                      
      my $ser_cnt=$root-&gt;first_child_text('SW');
       my $ser1=$root-&gt;first_child('ServiceSuppliers');                                                                                      
      my $ser1_cnt=$root-&gt;first_child_text('HW');                                                                                      
      if ($ser){                                                                                                                      
      push (@bhi, $ser-&gt;toString,$File::Find::name);                                                                                                                     
         $combeh{$code}=[@bhi];
           }
         if ($ser1){                                                                                                                     
      push (@bhi, $ser1-&gt;toString,$File::Find::name);                                                                                                                    
         $combeh{$code}=[@bhi];           
              };
          my $roots = { Service =&gt; 1 };
    my $handlers = { 'Servicemodule/Service' =&gt; $tweak_server,                                                                                                                                                                             
                         };
         my $twig = new XML::Twig(TwigRoots =&gt; $roots,                                                                                                                                                                                                           
                               TwigHandlers =&gt; $handlers,                                                                                                                                                                                                          
                                 pretty_print  =&gt; 'indented'                                                                                                                                                                            
                                 );
                 $twig-&gt;parsefile($_);                                                                                      
                               }                       
                         }
                 return (%combeh) ;
                  }
  </code></pre>
  
  <p>I am not able to create hash with as I want using above script. please help me with script how to get attribute values and store in a hash like this
  out put need like this</p>
  
  <pre><code> '478'=&gt;[
            {
            Description='It delivers actual pump speed'
            }
           {
             Service='SKRM',
             Path='/work/hr_service.xml'
            }
            {
               Type='s'.
               Nr='12',
               Service='s',
               path='/work/hardware.xml'
             }
  
            {
               Type='v'.
               Nr='2',
               Service='s',
               path='/work/hardware.xml'
             }
            {
               Type='mt'.
               Nr='1',
               Service='1',
               path='/work/hardware.xml'
             }
           ...
            ...
            ....
  </code></pre>
  
  <p>Please help me with this problem. </p>
  
  <p>Thanks in advance.</p>
  
  <p>I tried like this After your suggestion</p>
  
  <pre><code>#!/usr/bin/perl
   use warnings;
   use strict;
   use XML::Simple;
   use Carp;
   use File::Find;
   use File::Spec::Functions qw( canonpath );     
   use Data::Dumper;
  
   my @ARGV ="C:/Main/work";die "Need directories\n" unless @ARGV;
   find(
    sub {
       return unless ( /(_service\.xml)$/ and -f );
       extract_information();
      return;
    },
   @ARGV
    );
  
  sub extract_information {
           my $path= $_;
  
  my $xml=XMLin($path);
     my $xml_services = $xml-&gt;{Service};  
     my %services;
     for my $xml_service (@$xml_services) {
  
      my %service = (
          description     =&gt; $xml_service-&gt;{Description},
          name            =&gt; $xml_service-&gt;{Name},
          id              =&gt; $xml_service-&gt;{Id},
      );
  
       $service{sw} = _maybe_list( $xml_service-&gt;{ServiceCustomers}{SW} );
      $service{hw} = _maybe_list( $xml_service-&gt;{ServiceSuppliers}{HW} );
      $service{sw} = _maybe_list( $xml_service-&gt;{ServiceSuppliers}{SW} );
       $services{ $service{id} } = \%service;
   }
  
   print Dumper \%services;
  
    }
   sub _maybe_list {
   my $maybe = shift;
   return ref $maybe eq 'ARRAY' ? $maybe : [$maybe];
   }
  </code></pre>
  
  <p>Thanks for your reply,I am new to XML::Simple and I studied that module and I understand your script. But when I run your code I am getting error like "Not an array Reference" at for loop line.I tried in different ways to overcome this but still same error. And some times I have SW and HW attribute in ServiceSuppliers. So I added one more line same as your format. I have one question You said "If there's a single element in the XML it won't be wrapped in" but some times In ServiceCustomers I have only one element with some attributes like as I shown in my xml file.Is it OK ? or what should I do? Can you help me with these problems.</p>
  
  <p>please any one help me with this error.</p>