Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

XMLout and Elements vs Attributes

by atreyu (Sexton)
on Feb 20, 2014 at 16:20 UTC ( [id://1075603]=perlquestion: print w/replies, xml ) Need Help??

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

Hi,

I'm having a problem formatting the output of XMLout, and my XML file must have a specific format.

Here is my code that generates the data hash ref and then attempts to create an XML file:

#!/usr/bin/perl use strict; use warnings FATAL => 'uninitialized'; use XML::Simple qw(:strict); use Data::Dumper; $| = 1; my %ref; # create 1st node push(@{$ref{'node'}},{ 'id' => 'net_1.2.3.0', 'desc' => 'network', }); # create 2nd node push(@{$ref{'node'}},{ 'id' => 'ip_1.2.3.4', 'desc' => 'ipaddr', }); my $xmlParser = XML::Simple->new(); my $fh;open $fh,'>','/tmp/testfile.xml' or die "$!"; $xmlParser->XMLout(\%ref,( 'XMLDecl' => '<?xml version="1.0" encoding="utf-8"?>', 'NoAttr' => 0, 'KeepRoot' => 0, 'RootName' => 'test', 'OutputFile' => $fh, 'AttrIndent' => 1, 'KeyAttr' => { 'node' => 'id' }, )); print Dumper \%ref;
which yields:
$VAR1 = { 'node' => [ { 'desc' => 'network', 'id' => 'net_1.2.3.0' }, { 'desc' => 'ipaddr', 'id' => 'ip_1.2.3.4' } ] };
The output file, /tmp/testfile.xml, looks like this:
<?xml version="1.0" encoding="utf-8"?> <test> <node id="net_1.2.3.0" desc="network" /> <node id="ip_1.2.3.4" desc="ipaddr" /> </test>
So it properly makes id an attribute of the node, but it does the same for desc. I want desc to be a nested element of the node instead. So I need it to look like this:
<?xml version="1.0" encoding="utf-8"?> <test> <node id="net_1.2.3.0"> <desc>network</desc> </node> <node id="ip_1.2.3.4"> <desc>ipaddr</desc> </node> </test>

Note that there will be keys other than desc that will potentially exist in the real hash, and those should be nested elements of the node, too.

Can anyone spot my error(s) or see what XML option I am missing/misusing? Or do I have to reconstruct my hash ref (which is fine, too)?

Btw, I make liberal use of the XML::Simple module elsewhere so I want to stay with that method.

thx!

EDIT

After reading this thread (particularly grantm's post at the end), I got my code to do what I wanted by defining the desc (and other) key values as anonymous arrays:

# create 1st node push(@{$ref{'node'}},{ 'id' => 'net_1.2.3.0', 'desc' => ['network'], 'data' => ['testdata'], }); # create 2nd node push(@{$ref{'node'}},{ 'id' => 'ip_1.2.3.4', 'desc' => ['ipaddr'], 'data' => ['testdata2'], });
Is this the right way to do this? I know, timtowtdi...

Replies are listed 'Best First'.
Re: XMLout and Elements vs Attributes
by dasgar (Priest) on Feb 20, 2014 at 16:56 UTC

    The documentation of XML::Simple shows you exactly how to do this. (see the Quick Start section)

    Here's how I changed your code:

    #!/usr/bin/perl use strict; use warnings FATAL => 'uninitialized'; use XML::Simple qw(:strict); use Data::Dumper; $| = 1; my %ref; # create 1st node push(@{$ref{'node'}},{ 'id' => 'net_1.2.3.0', 'desc' => ['network'], }); # create 2nd node push(@{$ref{'node'}},{ 'id' => 'ip_1.2.3.4', 'desc' => ['ipaddr'], }); my $xmlParser = XML::Simple->new(); my $fh;open $fh,'>','testfile.xml' or die "$!"; $xmlParser->XMLout(\%ref,( 'XMLDecl' => '<?xml version="1.0" encoding="utf-8"?>', 'NoAttr' => 0, 'KeepRoot' => 0, 'RootName' => 'test', 'OutputFile' => $fh, 'AttrIndent' => 1, 'KeyAttr' => { 'node' => 'id' }, )); print Dumper \%ref;

    Here's the script output:

    $VAR1 = { 'node' => [ { 'desc' => [ 'network' ], 'id' => 'net_1.2.3.0' }, { 'desc' => [ 'ipaddr' ], 'id' => 'ip_1.2.3.4' } ] };

    Here's the XML file that was created:

    <?xml version="1.0" encoding="utf-8"?> <test> <node id="net_1.2.3.0"> <desc>network</desc> </node> <node id="ip_1.2.3.4"> <desc>ipaddr</desc> </node> </test>

    Basically, I just changed the 'desc' element from being a hash to be a hash of an array instead.

      Thanks, dasgar (and jellisii2), that's what I came up with too. So I guess that is *the way* to do it.

      cheers!

      EDIT

      yep, you're right, dasgar. i (thought i) had read the entire documentation, but my eyes glossed right over the example in the Quick Start area. d'oh.

Re: XMLout and Elements vs Attributes
by kcott (Archbishop) on Feb 20, 2014 at 17:13 UTC

    G'day atreyu,

    If you pass the structure you want to XML::Simple::XMLin():

    my $ref = $xmlParser->XMLin(<<'EOX', ForceArray => 1, KeyAttr => { nod +e => 'id' }); <?xml version="1.0" encoding="utf-8"?> <test> <node id="net_1.2.3.0"> <desc>network</desc> </node> <node id="ip_1.2.3.4"> <desc>ipaddr</desc> </node> </test> EOX

    You can then look at $ref to see how %ref should have been constructed. Here's print Dumper $ref; output:

    $VAR1 = { 'node' => { 'ip_1.2.3.4' => { 'desc' => [ 'ipaddr' ] }, 'net_1.2.3.0' => { 'desc' => [ 'network' ] } } };

    So, your

    push(@{$ref{'node'}},{ 'id' => 'net_1.2.3.0', 'desc' => 'network', });

    needs to be

    push @{$ref{node}}, { 'net_1.2.3.0' => { desc => ['network'] } };

    and similarly for 'ip_1.2.3.4'. With those changes, my /tmp/testfile.xml looks like this:

    <?xml version="1.0" encoding="utf-8"?> <test> <node id="net_1.2.3.0"> <desc>network</desc> </node> <node id="ip_1.2.3.4"> <desc>ipaddr</desc> </node> </test>

    -- Ken

      That is a great trick, Ken! I'll be filing that one away for the next obtuse XML file i need to generate. Thanks for the code example and the suggestion.
Re: XMLout and Elements vs Attributes
by jellisii2 (Hermit) on Feb 20, 2014 at 16:58 UTC

    Shooting from the hip, you need to change your data structure from

    push(@{$ref{'node'}},{ 'id' => 'net_1.2.3.0', 'desc' => 'network', });

    to

    push(@{$ref{'node'}},{ 'id' => 'net_1.2.3.0', 'desc' => ['network'] , });

    Or something similar.

    An interesting note from the XML::Simple documentation: "The use of this module in new code is discouraged. Other modules are available which provide more straightforward and consistent interfaces. In particular, XML::LibXML is highly recommended." While I don't necessarily agree with the recommendation for XML::LibXML myself, I would take this under advisement and look for something else. I can highly recommend XML::Twig for ease of use and power.

      "The use of this module in new code is discouraged. Other modules are available which provide more straightforward and consistent interfaces. In particular, XML::LibXML is highly recommended."

      Heck. I missed that part, too. I need glasses. and coffee.

      Thanks for the tips, jellisii2.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (3)
As of 2024-04-25 07:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found