3

So I'm working within an xml file that basically has a nested array of elements with each element having a lot of nested attributes:

<Export land_name="tx">
<Area>
    <Location name="foo"
              square_feet="10,000"
              highway="I-35"/>
    <Location name="dog"
              square_feet="20,000"
              highway="I-45"/>
</Area>
</Export>

My goal is to parse out the attributes (square_feet, highway, name) and output them to a .csv file. I'm using XML::Simple but the more I've worked with it I think I'm using the wrong package. My main question is what is the best control structure to parse out the nested attributes? I keep getting caught up on referring to non-existent has references or array references. Here is the code I have so far:

use warnings;
use XML::Simple;
use Data::Dumper;
my $in = "XML_in.xml";
my $xml = XML::Simple->new(KeyAttr => [], ForceArray => [Device], KeepRoot => 1 );
my $Inv = $xml->XMLin($in);
print $Inv->{Export}->{Area}->{Location}->[0]->{name};

If my code is in error, is it the incorrect usage of the KeyAttr, ForceArray, or keeproot?

3
  • 2
    Yes, you are. Even the documentation says not to use it. :) Commented Aug 11, 2015 at 15:55
  • Also, welcome to Stack Overflow! This is a very good first question. :) Commented Aug 11, 2015 at 16:12
  • No such thing as nested attributes?!? Commented Aug 11, 2015 at 16:40

2 Answers 2

5

The XML::Simple docs say not to use it in new code. This is a good example why. Instead, you can use XML::Twig, which has extensive documentation.

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

my $twig = XML::Twig->new(
  twig_handlers => {
    'Location' => sub {
      my ( $twig, $elem ) = @_;
      say join ';', map { $elem->att($_) } qw( name square_feet highway );
    },
});

$twig->parse( \*DATA );

__DATA__
<Export land_name="tx">
<Area>
    <Location name="foo"
              square_feet="10,000"
              highway="I-35"/>
    <Location name="dog"
              square_feet="20,000"
              highway="I-45"/>
</Area>
</Export>

This defines a handler for the <Location> tag, and grabs the three attributes out of the tag. You can then do what you want with them.

Since you want to write them to a CSV file you can use Text::CSV. Set it up outside and call it to write stuff in the handler.

See also:

Sign up to request clarification or add additional context in comments.

7 Comments

To make sure i can understand the code, you're making an object with XML::Twig->new and setting the parameters to grab anything within the location tag, what is qw, and if I have a lot more attributes to work with do I have to list them all?
qw( ... ) is the same as split(' ', q( ... ))
And yes, you will have to list them. How else are you going to make sure they're in the order you want?
I'm not sure how helpful that explanation is @ikegami. qw constructs a list from quoted words. It's identical to ('name', 'square_feet', 'highway').
You provided an explanation that works in one case. I provided an explanation that works in all cases. (Except I should have put the whole thing in parens.)
|
4

XML::Simple:

# ForceArray => [qw( Location )],
# KeyAttr    => [],
# KeepRoot   => 1,

for my $node (@{ $doc->{Export}{Area}{Location} }) {
   say join ';',
      map { $node->{$_} }
         qw( name square_feet highway );
}

XML::Simple requires that you do all that configuration, and there are many gotchas. The module's own documentation advises you to avoid XML::Simple as a result.

XML::LibXML:

for my $node ($doc->findnodes('/Export/Area/Location')) {
   say join ';',
      map { $node->getAttribute($_) }
         qw( name square_feet highway );
}

XML::Twig:

my $twig = XML::Twig->new(
   twig_handlers => {
      '/Export/Area/Location' => sub {
         my ($twig, $elem) = @_;
         say join ';',
            map { $elem->att($_) }
               qw( name square_feet highway );
      },
   },
);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.