Brendon C.
Brendon C.

Reputation: 25

Accessing Sibling Elements in XML DOM using Perl?

For my problem I am having a user enter a string to search for a provider, then the provider as well as some select plan information appears in the console. However, I cannot find out how to print out a select provider + the other information needed all in one.

For example:

Vodafone || 12 months || 120gb

Vodafone || 24 months || 200gb

I have made a start and do not mind too much about the sting input as I can figure that out later, but I cannot find how to firstly search for a select provider and then to print the information. Every provider and all the information is within a < plan > node (see XML below).

XML FILE

<plans>
    <internet>
        <plan>
            <technology>ADSL2+</technology>
            <length>12</length>
            <provider>Vodafone</provider>
            <price>60</price>
            <mincost>600</mincost>
            <data>120</data>
            <shaping>128</shaping>
            <homebundle>true</homebundle>
        </plan>
        <plan>
            <technology>ADSL2+</technology>
            <length>12</length>
            <provider>Telstra</provider>
            <price>80</price>
            <mincost>800</mincost>
            <data>200</data>
            <shaping>256</shaping>
            <homebundle>true</homebundle>
        </plan> 
        (etc.)

And the current Perl file

use XML::DOM;

my $dom_obj;
my $xml_file= shift;

my $parser = new XML::DOM::Parser;

die "Unable to parse XML document\n" unless $dom_obj = $parser->parsefile($xml_file);

my @nodes = $dom_obj->getElementsByTagName("plan"); #return list of objects of 'Plan'

foreach $elem (@nodes) #for each 'Plan' object
{
    foreach $child ($elem->getChildElements) #should return each element under plan?
    {
        print $child->getNodeValue; #should print elements
        print " ";
    }
    print "\n";
} 

I tried an if-statement thinking I was close but only received errors.

foreach $elem (@nodes)
{
    if($elem->getElementByTagName("provider")->getFirstChild->getNodeValue =~ /Vodafone/){
        print "$unit->getElementByTagName("provider")->getFirstChild->getNodeValue";
        print " || ";
        print "$unit->getElementByTagName("length")->getFirstChild->getNodeValue";
        print " || ";

Any help would be appreciated :)

Upvotes: 1

Views: 148

Answers (2)

Sobrique
Sobrique

Reputation: 53498

I'm not familiar with XML::DOM so would tend to pick up XML::Twig instead. Something like this:

#!/usr/bin/perl

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

my $provider = 'Vodafone';

sub print_plan_summary {
   my ( $twig, $plan ) = @_; 
   return unless $plan -> first_child_text('provider') eq $provider; 

   print join ( "||",  $plan -> first_child_text('provider'), 
                       $plan -> first_child_text('length')." months", 
                       $plan -> first_child_text('data'). " GB" );
}

my $twig = XML::Twig -> new ( twig_handlers => { 'plan' => \&print_plan_summary } ) -> parsefile('yourXML.xml');

XML::Twig allows you to set handlers, which take subsets of the xml (in this case plan elements) and process them individually. Because we can do this, we can manipulate as we go, or just 'simply' extract data from them.

Upvotes: 2

simbabque
simbabque

Reputation: 54373

If you can use something else, it's pretty easy with XML::Simple. Usually I don't like XML::Simple, but with this kind of straight-forward XML structure it actually works.

Just read the file (I put it in the __DATA__ section) into a data strucutre, and then iterate over the plans. Those are in an arrayref. Now all you need to do is join the hashref values.

use strict;
use warnings;
use feature 'say';
use XML::Simple;

my $xs   = XML::Simple->new;
my $data = $xs->XMLin( \*DATA );


foreach my $plan ( @{ $data->{internet}->{plan} } ) {
    next if $plan->{provider} ne 'Vodafone';
    say join q{ || }, $plan->{provider}, $plan->{length} . ' months', $plan->{data} . 'gb';
}

Upvotes: 1

Related Questions