1

I have an xml file as below. In perl, I am trying to use use XML::Simple; utility where PluginsName should be the hash key and the rest of Plugin details should be values.

So, I want to fetch the below values : PluginsName, PluginsStatus, PluginspatchLogName, PluginsLogFileName and PluginsLogFileErrors which is in tag PluginsLogFiles.

Basically I want all information for a given PluginsName

<installer>
  <Plugins>
      <PluginsRunningSeq>1</PluginsRunningSeq>
      <PluginspatchLogName>abc_patch.log</PluginspatchLogName>
      <PluginsName>ConfigValue</PluginsName>
      <PluginsAttemptNo>1</PluginsAttemptNo>
      <PluginsStatus>Success</PluginsStatus>
      <PluginsStartTime>2015-05-10 20:02:46.651 +0000</PluginsStartTime>
      <PluginsEndTime>2015-05-10 20:03:14.002 +0000</PluginsEndTime>
      <PluginsDuration>0 min, 27 sec, 351 millis</PluginsDuration>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsErrors>No Errors</PluginsErrors>
    </Plugins>
    <Plugins>
      <PluginsRunningSeq>2</PluginsRunningSeq>
      <PluginspatchLogName>abc_patch_patch.log</PluginspatchLogName>
      <PluginsName>Patching Manager</PluginsName>
      <PluginsAttemptNo>1</PluginsAttemptNo>
      <PluginsStatus>Success</PluginsStatus>
      <PluginsStartTime>2015-05-10 20:03:14.002 +0000</PluginsStartTime>
      <PluginsEndTime>2015-05-10 20:03:16.573 +0000</PluginsEndTime>
      <PluginsDuration>0 min, 2 sec, 571 millis</PluginsDuration>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.lo</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsErrors>No Errors</PluginsErrors>
    </Plugins>
    <Plugins>
      <PluginsRunningSeq>3</PluginsRunningSeq>
      <PluginspatchLogName>abc_patch.log</PluginspatchLogName>
      <PluginsName>Download Patching</PluginsName>
      <PluginsAttemptNo>1</PluginsAttemptNo>
      <PluginsStatus>Success</PluginsStatus>
      <PluginsStartTime>2015-05-10 20:03:18.863 +0000</PluginsStartTime>
      <PluginsEndTime>2015-05-10 20:03:29.983 +0000</PluginsEndTime>
      <PluginsDuration>0 min, 11 sec, 120 millis</PluginsDuration>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsErrors>No Errors</PluginsErrors>
    </Plugins>
    <Plugins>
    <installerStartTime>2015-05-10 20:02:46.651 +0000</installerStartTime>
    <installerEndTime>2015-05-10 21:37:47.428 +0000</installerEndTime>
  </installer>

I was able to create an hash map based on PluginsName using the below code. But, I am unable to proceed further

my $pluginDetails = XMLin('/tmp/Installer.xml', KeyAttr => {Plugins => 'PluginsName'});

Could someone please help?

bumble_bee_tuna
  • 3,533
  • 7
  • 43
  • 83
Shankar Guru
  • 1,071
  • 2
  • 26
  • 46
  • 2
    Please *don't use* [`XML::Simple`](https://metacpan.org/pod/XML::Simple). It can be very difficult to get the result you require and it it based on an inherently faulty design. Its own documentation says *"The use of this module in new code is discouraged. Other modules are available which provide more straightforward and consistent interfaces. In particular, [`XML::LibXML`](https://metacpan.org/pod/XML::LibXML) is highly recommended"* – Borodin May 11 '15 at 15:08
  • Your XML data isn't well-formed so nothing can process it. Please show the real data – Borodin May 11 '15 at 15:11
  • And also - please include what you want out specifically. – Sobrique May 11 '15 at 15:12

2 Answers2

2

Don't use XML::Simple. It lies - it's for simple XML. Even it's own page says: "The use of this module in new code is discouraged. Other modules are available which provide more straightforward and consistent interfaces"

Also note - the XML you posted is malformed. By deleting a couple of lines off the end, I've created this example, but you should make sure you post valid XML to any XML problems.

So with that in mind - I like XML::Twig personally.

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;
use Data::Dumper;

my @desired = qw ( PluginsStatus PluginspatchLogName  );

my %plugin_data; 

sub process_plugin {
    my ( $twig, $plugin ) = @_;
    my $name = $plugin -> first_child_text('PluginsName');
    foreach my $tag ( @desired ) { 
       if ( $plugin -> first_child_text($tag) ) {
          $plugin_data{$name}{$tag} = $plugin -> first_child_text($tag);
       }
    }
    my $logfile = $plugin -> first_child ('PluginsLogFiles') -> first_child_text('PluginsLogFileName');
    $plugin_data{$name}{'PluginLogFileName'} = $logfile;
    my $errors =  $plugin -> first_child ('PluginsLogFiles') -> first_child_text('PluginsLogFileErrors'); 
    $plugin_data{$name}{'PluginsLogFileErrors'} = $errors; 
}

my $twig = XML::Twig -> new ( twig_handlers => { 'Plugins' => \&process_plugin } ) -> parse ( \*DATA );
print Dumper \%plugin_data;


__DATA__
<installer>
    <Plugins>
      <PluginsRunningSeq>1</PluginsRunningSeq>
      <PluginspatchLogName>abc_patch.log</PluginspatchLogName>
      <PluginsName>ConfigValue</PluginsName>
      <PluginsAttemptNo>1</PluginsAttemptNo>
      <PluginsStatus>Success</PluginsStatus>
      <PluginsStartTime>2015-05-10 20:02:46.651 +0000</PluginsStartTime>
      <PluginsEndTime>2015-05-10 20:03:14.002 +0000</PluginsEndTime>
      <PluginsDuration>0 min, 27 sec, 351 millis</PluginsDuration>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsErrors>No Errors</PluginsErrors>
    </Plugins>
    <Plugins>
      <PluginsRunningSeq>2</PluginsRunningSeq>
      <PluginspatchLogName>abc_patch_patch.log</PluginspatchLogName>
      <PluginsName>Patching Manager</PluginsName>
      <PluginsAttemptNo>1</PluginsAttemptNo>
      <PluginsStatus>Success</PluginsStatus>
      <PluginsStartTime>2015-05-10 20:03:14.002 +0000</PluginsStartTime>
      <PluginsEndTime>2015-05-10 20:03:16.573 +0000</PluginsEndTime>
      <PluginsDuration>0 min, 2 sec, 571 millis</PluginsDuration>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.lo</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsErrors>No Errors</PluginsErrors>
    </Plugins>
    <Plugins>
      <PluginsRunningSeq>3</PluginsRunningSeq>
      <PluginspatchLogName>abc_patch.log</PluginspatchLogName>
      <PluginsName>Download Patching</PluginsName>
      <PluginsAttemptNo>1</PluginsAttemptNo>
      <PluginsStatus>Success</PluginsStatus>
      <PluginsStartTime>2015-05-10 20:03:18.863 +0000</PluginsStartTime>
      <PluginsEndTime>2015-05-10 20:03:29.983 +0000</PluginsEndTime>
      <PluginsDuration>0 min, 11 sec, 120 millis</PluginsDuration>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsLogFiles>
        <PluginsLogFileName>abc.log</PluginsLogFileName>
        <PluginsLogFileLink>/tmp/abc.log</PluginsLogFileLink>
        <PluginsLogFileErrors>No Errors</PluginsLogFileErrors>
      </PluginsLogFiles>
      <PluginsErrors>No Errors</PluginsErrors>
    </Plugins>

  </installer>

Which will print:

$VAR1 = {
          'Patching Manager' => {
                                'PluginsLogFileErrors' => 'No Errors',
                                'PluginsStatus' => 'Success',
                                'PluginspatchLogName' => 'abc_patch_patch.log',
                                'PluginLogFileName' => 'abc.log'
                              },
          'Download Patching' => {
                                 'PluginspatchLogName' => 'abc_patch.log',
                                 'PluginLogFileName' => 'abc.log',
                                 'PluginsLogFileErrors' => 'No Errors',
                                 'PluginsStatus' => 'Success'
                               },
          'ConfigValue' => {
                           'PluginLogFileName' => 'abc.log',
                           'PluginspatchLogName' => 'abc_patch.log',
                           'PluginsStatus' => 'Success',
                           'PluginsLogFileErrors' => 'No Errors'
                         }
        };

Note though - it doesn't handle the duplicate PluginsLogFiles elements with any grace - it looks at just the first. You can do multiple easily enough, but then you need to figure out if that'll be a separate hash key (because you can't have dupes) or you're going to insert an array into the hash.

Sobrique
  • 52,974
  • 7
  • 60
  • 101
  • Thank you, this worked for me. This is really helpful. Could you please help me in handling the duplicate PluginsLogFiles element using separate hash keys (May be like PluginLogFileName1, PluginsLogFileErrors1 and PluginLogFileName2, PluginsLogFileErrors2) ?? But, need to note that few Plugin have multiple PluginsLogFiles sub elements and some have only one sub element. So its not hard coded. If you could help me here, it will be much appreciated. – Shankar Guru May 11 '15 at 18:48
  • 1
    Have a look at `children` in the `XML::Twig` documentation. This lets you iterate nodes matching a criterial - such as `$plugin -> children('PluginsLogFiles');`. Something like `foreach my $logfile ( $plugin -> children('PluginsLogFiles') {` - and then you could extract and add the elements you wanted, either by pushing them onto an array, or adding a new hash key. – Sobrique May 11 '15 at 20:33
  • This is exactly what I wanted. Thank you so much @Sobrique.. It was a much needed help. – Shankar Guru May 12 '15 at 07:19
1

I suggest that you make use of XML::LibXML or XML::Twig rather than the awkward XML::Simple.

This solution uses XML::LibXML, and uses Data::Dump to reveal the resulting hash

use strict;
use warnings;

use XML::LibXML;
use Data::Dump;

my $xml = XML::LibXML->load_xml(location => 'plugins.xml');

my %plugins;

for my $plugin ( $xml->findnodes('/root/installer/Plugins') ) {

  my $name = $plugin->findvalue('PluginsName');

  for my $info ( $plugin->findnodes('*') ) {

    my @elements = $info->findnodes('*');
    @elements = ($info) unless @elements;

    $plugins{$name}{$_->localname} = $_->textContent for @elements;
  }
}

use Data::Dump;
dd \%plugins;

output

{
  "" => {
    installerEndTime   => "2015-05-10 21:37:47.428 +0000",
    installerStartTime => "2015-05-10 20:02:46.651 +0000",
  },
  "ConfigValue" => {
    PluginsAttemptNo     => 1,
    PluginsDuration      => "0 min, 27 sec, 351 millis",
    PluginsEndTime       => "2015-05-10 20:03:14.002 +0000",
    PluginsErrors        => "No Errors",
    PluginsLogFileErrors => "No Errors",
    PluginsLogFileLink   => "/tmp/abc.log",
    PluginsLogFileName   => "abc.log",
    PluginsName          => "ConfigValue",
    PluginspatchLogName  => "abc_patch.log",
    PluginsRunningSeq    => 1,
    PluginsStartTime     => "2015-05-10 20:02:46.651 +0000",
    PluginsStatus        => "Success",
  },
  "Download Patching" => {
    PluginsAttemptNo     => 1,
    PluginsDuration      => "0 min, 11 sec, 120 millis",
    PluginsEndTime       => "2015-05-10 20:03:29.983 +0000",
    PluginsErrors        => "No Errors",
    PluginsLogFileErrors => "No Errors",
    PluginsLogFileLink   => "/tmp/abc.log",
    PluginsLogFileName   => "abc.log",
    PluginsName          => "Download Patching",
    PluginspatchLogName  => "abc_patch.log",
    PluginsRunningSeq    => 3,
    PluginsStartTime     => "2015-05-10 20:03:18.863 +0000",
    PluginsStatus        => "Success",
  },
  "Patching Manager" => {
    PluginsAttemptNo     => 1,
    PluginsDuration      => "0 min, 2 sec, 571 millis",
    PluginsEndTime       => "2015-05-10 20:03:16.573 +0000",
    PluginsErrors        => "No Errors",
    PluginsLogFileErrors => "No Errors",
    PluginsLogFileLink   => "/tmp/abc.log",
    PluginsLogFileName   => "abc.log",
    PluginsName          => "Patching Manager",
    PluginspatchLogName  => "abc_patch_patch.log",
    PluginsRunningSeq    => 2,
    PluginsStartTime     => "2015-05-10 20:03:14.002 +0000",
    PluginsStatus        => "Success",
  },
}
Borodin
  • 126,100
  • 9
  • 70
  • 144
  • Got below error: Can't locate auto/XML/LibXML/load_xml.al in @INC (@INC contains: /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi /usr/lib/perl5/site_perl/5.8.8 /usr/lib/perl5/site_perl /usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi /usr/lib/perl5/vendor_perl/5.8.8 /usr/lib/perl5/vendor_perl /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi /usr/lib/perl5/5.8.8 .) at readXML.pl line 8 Module use XML::LibXML; is not present – Shankar Guru May 11 '15 at 17:47
  • @ArunShankar: You haven't installed `XML::LibXML` properly. I would guess you've just copied it into place? You need to build it – Borodin May 11 '15 at 18:01
  • thank you for your quick response. The tool that am developing needs to be run irrespective of the machine. So, I cannot install a new library. Moreover I do not have privileges as well.. Please let me know if there is anything you can suggest – Shankar Guru May 11 '15 at 18:10