I am using XML::Simple
and I wish to convert this data to XML:
@rooms = (
{
id => 4,
is_key => 0,
name => B507,
capacity => 35
},
{
id => 5,
is_key => 1,
name => B502,
capacity => 24
}
);
I want to output this:
<rooms>
<room id=4 is_key=0>
<name>B507</name>
<capacity>35</capacity>
</room>
<room id=5 is_key=1>
<name>B502</name>
<capacity>24</capacity>
</room>
</rooms>
I don't see a way to do this with XML::Simple::XMLout
. Am I missing something?
I find XML::Simple
unintuitive and very awkward to use. It is easy to end up just throwing random options at it to try to get it to work.
However if you are stuck with it there is a way. First of all the ForceArray
option is very useful and, as the documentation says,
Check out "ForceArray" because you'll almost certainly want to turn it on
So you need to adjust your data so that it looks like ForceArray
was in effect when the original XML was parsed. This just involves putting in an anonymous array all data that should be the contents of an element instead of an attribute value.
This code does what you need. The KeepRoot
option just tells XMLout
that the top-level hash is the root element and it doesn't have to wrap the whole thing in another element.
use strict;
use warnings;
use XML::Simple;
my @rooms = (
{
id => 4,
is_key => 0,
name => 'B507',
capacity => 35
},
{
id => 5,
is_key => 1,
name => 'B502',
capacity => 24
}
);
for my $room (@rooms) {
for my $k (keys %$room) {
$room->{$k} = [ $room->{$k} ] unless grep $k eq $_, qw/ is_key id /;
}
}
my $xml = {rooms => {room => \@rooms} };
print XMLout($xml, KeepRoot => 1);
output
<rooms>
<room id="4" is_key="0">
<name>B507</name>
<capacity>35</capacity>
</room>
<room id="5" is_key="1">
<name>B502</name>
<capacity>24</capacity>
</room>
</rooms>
Update
You may prefer a solution using XML::Smart
, which allows you to specify which nodes are elements and which are tags. This allows you to leave the original data in @rooms
untouched.
This program accepts a similar hash reference to the XML::Simple
solution, and them loops through all /rooms/room
elements, setting all name
and capacity
child nodes to elements using set_tag
.
Note that the XML is output using scalar $smart->data()
because when called in list context
the data
method will return a second value: a boolean flag indicating whether the XML is Unicode-encoded. This doesn't appear to be documented in the POD.
You may omit the call to $smart->set_order
if you're not bothered about the order in which the attributes and elements appear in the XML.
use strict;
use warnings;
use XML::Smart;
my @rooms = (
{
id => 4,
is_key => 0,
name => 'B507',
capacity => 35
},
{
id => 5,
is_key => 1,
name => 'B502',
capacity => 24
}
);
my $smart = XML::Smart->new;
$smart->{rooms} = { room => \@rooms };
for my $room (@{$smart->{rooms}{room}}) {
$room->set_order(qw/ id is_key name capacity /);
$room->{name}->set_tag;
$room->{capacity}->set_tag;
}
print scalar $smart->data(noheader => 1, nometagen => 1);
output
<rooms>
<room id="4" is_key="0">
<name>B507</name>
<capacity>35</capacity>
</room>
<room id="5" is_key="1">
<name>B502</name>
<capacity>24</capacity>
</room>
</rooms>
Hash value arrayrefs become XML element content, simple hash values become XML attribute values.
use strictures;
use XML::Simple qw(:strict);
print XMLout(
{
room => [
{
id => 4,
is_key => 0,
name => ['B507'],
capacity => [35],
},
{
id => 5,
is_key => 1,
name => ['B502'],
capacity => [24],
}
]
},
KeyAttr => [],
RootName => 'rooms'
);
<rooms>
<room id="4" is_key="0">
<capacity>35</capacity>
<name>B507</name>
</room>
<room id="5" is_key="1">
<capacity>24</capacity>
<name>B502</name>
</room>
</rooms>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With