1

I have simple XML that I want to read in Perl and make hash containing all read keys.

Consider this code:

my $content = $xml->XMLin("filesmap.xml")->{Item};
my %files = map { $_->{Path} => 1 } @$content;

This snippet works great when XML file contains many Item tags. Then $content is a reference to array. But when there is only one Item I get an error when dereferencing as array. My assumption is that $content is a reference to the scalar, not array.

What is the practice to make sure I get array of values read from XML?

2
  • 2
    First: Don't use XML::Simple and then it's all nice and easy. Give us an XML Sample, and we'll be able to give you a precise answer. Commented Dec 8, 2015 at 15:09
  • Appeared posts already answer my question. In general there is a practice to use ref $content eq 'ARRAY' where I need to make sure I get arrays, which is ugly by the way, but in my XML case I can use ForceArray parameter or use another xml lib. Thanks! Commented Dec 8, 2015 at 15:30

3 Answers 3

8

What you need is to not use XML::Simple and then it's really trivial. My favourite for fairly straightforward XML samples is XML::Twig

use XML::Twig;
my $twig = XML::Twig -> new -> parsefile ( 'filesmap.xml' ); 

my @files = map { $_ -> trimmed_text } $twig -> get_xpath ( '//Path' ); 

With a more detailed XML sample (and desired result) I'll be able to give you a better answer.

Part of the problem with XML::Simple is it tries to turn an XML data structure into perl data structures, and - because hashes are key-values and unordered but arrays are ordered, it has to guess. And sometimes it does it wrong, and other times it's inconsistent.

If you want it to be consistent, you can set:

my $xml = XMLin( "filesmap.xml", ForceArray => 1, KeyAttr => [], ForceContent => 1 );

But really - XML::Simple is just a route to pain. Don't use it. If you don't like XML::Twig, try XML::LibXML instead.

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

Comments

1

What I would say you need is a flatten-ing step.

my %files 
    = map { $_->{Path} => 1 } 
      # flatten...
      map { ref() eq 'ARRAY' ? @$_ : $_ } 
      $xml->XMLin("filesmap.xml")->{Item}
    ;

Comments

1

You can do a check and force the return into an array reference if necessary:

my $content = $xml->XMLin("filesmap.xml")->{Item};

$content = ref $content eq 'ARRAY'
    ? $content
    : [$content];

2 Comments

personally, I'd reverse the logic: ref $content eq 'ARRAY' ? $content : [ $content ]
Or y'know, turn on ForceArray (or just not use XML::Simple in the first place).

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.