Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

How do I get a list in a perl hash generated from an XML?

by PV66 (Novice)
on Apr 19, 2021 at 08:48 UTC ( [id://11131461]=perlquestion: print w/replies, xml ) Need Help??

PV66 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Community, Really hoping to get some perl-wisdom on this one, its my second day working on Perl today, and I'm just stuck with an issue that I can't seem to figure out.

Basically I have an XML

<root> <ref name="abc_sia_%version1.ref%"> <func envname = "test01" objectdir = "/home/pv66" base="default_771"/> </ref> </root>
I want to get the ref name's value, that is "abc_sia_%version1.ref%" as my hash key and the corresponding elements of the tag in a list. So I used Simple XML's function
my $xml = new XML::Simple; my $data = $xml->XMLin("/home/pv66", ForceArray => 1, KeyAttr => { ref + => "name"}); print Dumper ($data);
But this gives me
$VAR1 = { 'ref' => { 'abc_sia_%version1.ref' => { 'func' => [ { 'envname' => 'test01', 'objectdir' => '/home/pv66', 'base' => 'default_771' } ] } };
And this extra level of hierarchy (after ref) is undesired. I've read at some places that XML::Simple is not the preferred module, and XMLLib should be used, but I couldn't find any resources that drew a parallel to the ForceArray attribute of the XML in method. The desired hash should be:
$VAR1 = { 'abc_sia_%version1.ref' => { 'func' => [ { 'envname' => 'test01', 'objectdir' => '/home/pv66', 'base' => 'default_771' } ] } };
Any help pertaining to this would be appreciated.

Replies are listed 'Best First'.
Re: How do I get a list in a perl hash generated from an XML?
by choroba (Cardinal) on Apr 19, 2021 at 09:53 UTC
    Here's how to get the requested structure using XML::LibXML.
    #! /usr/bin/perl use warnings; use strict; use XML::LibXML; my $dom = 'XML::LibXML'->load_xml(location => shift); my %struct; for my $ref ($dom->findnodes('/root/ref')) { for my $child ($ref->findnodes('*')) { push @{ $struct{ $ref->{name} }{ $child->nodeName } }, { map { $_->nodeName => $_->value } $child->findnodes('@* +') }; } } use Data::Dumper; print Dumper \%struct;

    But converting from XML::Simple to XML::LibXML usually changes the code much more. You can extract only the information you need from the XML, so you don't need to build the whole structure at all.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      This does make sense @choroba. Out of curiosity, what if my XML had more tags, for example if it looked like:
      <root> <config> <build host="https://cpzdomain.local"/> </config> <ref name="abc_sia_%version1.ref%"> <func envname = "test01" objectdir = "/home/pv66" base="default_771"/> </ref> </root>
      And I wanted to just make a hash as in my question for only the elements in the <ref> tag, is there anyway to do it with the XML::Simple module?
        I can't help you with XML::Simple (and I don't want to). If you need advice to migrate the whole logic to XML::LibXML , though, I can be your consultant.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: How do I get a list in a perl hash generated from an XML?
by AnomalousMonk (Archbishop) on Apr 19, 2021 at 09:15 UTC

    If you're determined to disregard the sound advice against the use of XML::Simple, you can do

    Win8 Strawberry 5.8.9.5 (32) Mon 04/19/2021 5:08:21 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings use Data::Dumper; my $got_data = { 'ref' => { 'abc_sia_%version1.ref' => { 'func' => [ { 'envname' => 'test01', 'objectdir' => '/home/pv66', 'base' => 'default_771' } ] } } }; my $want_data = $got_data->{'ref'}; print Dumper $want_data; ^Z $VAR1 = { 'abc_sia_%version1.ref' => { 'func' => [ { 'base' => 'defaul +t_771', 'objectdir' => '/ +home/pv66', 'envname' => 'tes +t01' } ] } };
    It looks like the hash reference you want is just the value of the 'ref' key. See also perldsc.


    Give a man a fish:  <%-{-{-{-<

      Thanks a lot!!!! This did resolve the issue for which I've been scratching my head for so long and it did turn out to be a very efficient, and yeah I'm okay with using XML::Simple in this case :P
Re: How do I get a list in a perl hash generated from an XML?
by haukex (Archbishop) on Apr 20, 2021 at 05:56 UTC

    I very strongly recommend against XML::Simple! A good replacement for reading XML is XML::Rules, its output is much more customizable. Update: If you want func to be an arrayref, just replace 'as is' by 'as array'.

    use warnings; use strict; use XML::Rules; use Data::Dump; my $parser = XML::Rules->new( stripspaces => 3|4, rules => [ root=>'pass', config=>'', build=>'', ref=>'by name', func=>'as is', _default => sub { die "Unknown tag $_[0]" } ] ); my $data = $parser->parse(<<'END_XML'); <root> <config> <build host="https://cpzdomain.local"/> </config> <ref name="abc_sia_%version1.ref%"> <func envname = "test01" objectdir = "/home/pv66" base="default_771"/> </ref> </root> END_XML dd $data; __END__ { "abc_sia_%version1.ref%" => { func => { base => "default_771", envname => "test01", objectdir => "/home/pv66" }, }, }
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11131461]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (2)
As of 2024-04-20 04:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found