XML:Simple and arrays of hashes

R

Richard Lawrence

Hi there,

Consider the following XML in a file called "data.xml":

<user>
<name>Frank</name>
<age>27</age>
<friends>
<friend>
<name>Sue</name>
<location>London</location>
</friend>
<friend>
<name>Jane</name>
<location>Paris</location>
</friend>
</friends>
</user>

If I use the following:

#!/usr/bin/perl -w
use strict;
use XML::Simple;
my $user = XMLin("data.xml");
print "Hi, my name is " . $user->{name};
print " and I am " . $user->{age} . " years old. ";
print "My friends are ";
# Begin horrible code
my ($key, $value, $k, $v);
while(($key, $value) = each %{$user->{friends}})
{
while(($k, $v) = each %{$value})
{
print "$k (who lives in " . $v->{location} . ") and ";
}
}
# End horrible code
print "thats it!\n";

then I get the intended:

Hi, my name is Frank and I am 27 years old. My friends are Sue (who
lives in London) and Jane (who lives in Paris) and thats it!

which is great.

However as you have probably noticed from the code, the whole $key,
$value, $k, $v and nested while's looks really horrible to me and I'm
sure there is a far better way to do this.

Can anyone advise on the best way to re-write this section whilst still
maintaining readability?

Many thanks in advance,

Richard.
 
A

Anno Siegel

Richard Lawrence said:
Hi there,

Consider the following XML in a file called "data.xml":

<user>
<name>Frank</name>
<age>27</age>
<friends>
<friend>
<name>Sue</name>
<location>London</location>
</friend>
<friend>
<name>Jane</name>
<location>Paris</location>
</friend>
</friends>
</user>

If I use the following:

#!/usr/bin/perl -w
use strict;
use XML::Simple;
my $user = XMLin("data.xml");
print "Hi, my name is " . $user->{name};
print " and I am " . $user->{age} . " years old. ";
print "My friends are ";
# Begin horrible code
my ($key, $value, $k, $v);
while(($key, $value) = each %{$user->{friends}})
{
while(($k, $v) = each %{$value})
{
print "$k (who lives in " . $v->{location} . ") and ";
}
}
# End horrible code
print "thats it!\n";

then I get the intended:

Hi, my name is Frank and I am 27 years old. My friends are Sue (who
lives in London) and Jane (who lives in Paris) and thats it!

which is great.

However as you have probably noticed from the code, the whole $key,
$value, $k, $v and nested while's looks really horrible to me and I'm
sure there is a far better way to do this.

First off, I don't think your xml structure represents very well the
structure of the data. There is a superfluous level in your structure.
But I'm not touching that.

You're looping over keys that function as names of subfields. They
should be addressed through fixed strings, like you do with "name" and
"age". You know what they are, use your knowledge.

Further, don't declare variables ahead of time, declare them when they
are first used. And use reasonable variable names. $k and $v when
$key and $value are already there is indeed horrible.

So, replace you "horrible code" with something like this:

my $friends = $user->{ friends}->{ friend}; # see the extra level?
for my $name ( keys %$friends ) {
my $location = $friends->{ $name}->{ location};
print "$name (who lives in $location) and ";
}


The rest of your code isn't beyond critique either, but I'll let that
go.

Anno
 
R

Richard Lawrence

Anno said:
First off, I don't think your xml structure represents very well the
structure of the data. There is a superfluous level in your structure.
But I'm not touching that.

Thanks for this. I did wonder about the extra field myself but wasn't
sure whether it was better form to place groups of data (eg. the
friends) in a field of their own or not. I chose to do so and it seems
like that probably wasn't the best - easy to fix though :)
The rest of your code isn't beyond critique either, but I'll let that
go.

If you had the time then I would appreciated it if you did critique it.
Otherwise I'll only continue making the same mistakes over and over
again until someone else points them out.

Having said that, I'm assuming its my usage of . to include the
variables because I have no idea how to write:

print "My name is $user->{name}\n";

and actually get it to print correctly? Unfortunately my abilities to
find something on Google or perldoc on how to handle this has failed me
:(

Thanks for your comments,

Richard.
 
A

Anno Siegel

Richard Lawrence said:
Anno Siegel wrote:

If you had the time then I would appreciated it if you did critique it.
Otherwise I'll only continue making the same mistakes over and over
again until someone else points them out.

Having said that, I'm assuming its my usage of . to include the

There's that, yes.
variables because I have no idea how to write:

print "My name is $user->{name}\n";

Exactly like that.

Further, "use warnings" is preferable over "perl -w". With "use warnings"
you can switch off individual warnings later.

A third point concerns the piecewise printing of a larger chunk of
output. I'd use a variable to collect it and print it in one go.
Apart from efficiency consideration, the approach is more flexible
if you want to re-arrange things or modify the final string.

Anno
 
R

Richard Lawrence

Anno said:
Further, "use warnings" is preferable over "perl -w". With "use warnings"
you can switch off individual warnings later.

Thanks for this one!
A third point concerns the piecewise printing of a larger chunk of
output. I'd use a variable to collect it and print it in one go.
Apart from efficiency consideration, the approach is more flexible
if you want to re-arrange things or modify the final string.

Agreed.

To be honest, my whole use of poor variable names and multiple prints
was to get a 10 line bit of code quickly working to understand the
fundimentals on how to extract information from an XML file. Thankfully
none of this would be used in a proper application.

As for $user->{name}, I remembered that I'd been trying to use
$user->{"name"} and of course this won't work if I simply plop that
into a print. Something to remember.

Many thanks for your advice, I have learnt some useful things :)

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top