2012-07-20 49 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',);