2012-07-20 43 views
3

我使用XML::Simple我想这个数据转换成XML:使用XML ::简单从一些键哈希阵列构建XML和属性

@rooms = (
    { 
     id => 4, 
     is_key => 0, 
     name => B507, 
     capacity => 35 
    }, 
    { 
     id => 5, 
     is_key => 1, 
     name => B502, 
     capacity => 24 
    } 
); 

我想这个输出:

<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> 

我没有看到与XML::Simple::XMLout这样做的方法。我错过了什么吗?

回答

5

我发现XML::Simple直观,非常难以使用。很容易最终抛弃随机选项来尝试使其工作。

但是,如果你坚持下去,有一种方法。首先所有的ForceArray选项是非常有用的,因为文档中说,

退房“ForceArray”,因为你几乎肯定会想打开它

所以,你需要调整数据,以便它看起来像ForceArray在解析原始XML时有效。这只需要将所有应该是元素内容而不是属性值的数据放入匿名数组中。

此代码可以满足您的需求。 KeepRoot选项只是告诉XMLout顶级散列是根元素,它不必将整个事物包装在另一个元素中。

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); 

输出

<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> 

更新

你可能更喜欢使用XML::Smart的解决方案,它允许你指定哪个节点是元素和它们的标签。这样可以保留原始数据@rooms不变。

此程序接受一个类似的散列参考XML::Simple溶液,并将它们循环通过所有/rooms/room元素,所有namecapacity子节点设置为使用set_tag元件。

注意,XML是使用scalar $smart->data()因为在列表上下文 调用时data方法将返回的第二值输出:一个布尔标志指示XML是否是Unicode编码。这似乎没有记录在POD中。

如果您不关心属性和元素在XML中出现的顺序,您可以省略对$smart->set_order的调用。

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); 

输出

<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> 
+0

我希望避免数据的额外处理,但是没有写一个新的XML Out过程,这似乎是最好的方法。谢谢! – hayesk 2012-07-20 17:40:13

+0

@hayesk:我已经使用'XML :: Smart'添加了一个解决方案,它允许您明确区分XML属性和元素。这避免了对源数据的额外处理。 – Borodin 2012-07-21 11:31:16

2

哈希值arrayrefs变为XML元素内容,简单的哈希值成为XML属性值。

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> 
0

Daxim的答案已经是正确的。既然我已经输入了,我会发布这个。这里有一段代码将把你的数据结构转换成你需要的(Daxim已经指出的)。

my $stuff = { 
    'room' => [ 
    map { { 
     'id' => $_->{'id'}, 
     'is_key' => $_->{'is_key'}, 
     'name' => [ $_->{'name'} ], 
     'capacity' => [ $_->{'capacity'} ] 
    } } @rooms 
    ] 
}; 

print XMLout($stuff, RootName=> 'rooms',);