2011-07-20 147 views
1

我試圖研究一個簡單的問題,但我做不到。我試圖從XML獲取數據,並使用Perl解析它。現在,我知道如何循環重複元素。但是,當它不重複時(我知道這可能很愚蠢),我被卡住了。如果元素重複,我把它放在數組中並獲取數據。但是,當只有一個元素時,它會拋出錯誤,並說'不是數組引用'。我希望我的代碼能夠同時解析(單個和多個元素)。我使用的代碼如下:使用perl的XML解析

use LWP::Simple; 
use XML::Simple; 
use Data::Dumper; 

open (FH, ">:utf8","xmlparsed1.txt"); 

my $db1 = "pubmed"; 
my $query = "13054692"; 
my $q = 16354118;   #for multiple MeSH terms 
my $xml = new XML::Simple; 

$urlxml = "http://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=$db1&id=$query&retmode=xml&rettype=abstract"; 
$dataxml = get($urlxml); 
$data = $xml->XMLin("$dataxml"); 
#print FH Dumper($data); 
foreach $e(@{$data->{PubmedArticle}->{MedlineCitation}->{MeshHeadingList}->{MeshHeading}}) 
    { 
     print FH $e->{DescriptorName}{content}, ' $$ '; 
    } 

另外,我可以做一些事情使得分離$$不會得到最後一個元素後打印? 我也嘗試下面的代碼:

$mesh = $data->{PubmedArticle}->{MedlineCitation}->{MeshHeadingList}->{MeshHeading}; 
while (my ($key, $value) = each(%$mesh)){ 
    print FH "$value"; 
} 

但是,這種打印所有的childNodes,我只是想要的內容節點。

回答

5

Perl的XML::Simple將採用單個項目並將其作爲標量返回,如果該值重複,則將其作爲數組引用返回。因此,爲了使你的代碼的工作,你就必須強制MeshHeading總是返回數組引用:

$data = $xml->XMLin("$dataxml", ForceArray => [qw(MeshHeading)]); 
1

由於$data->{PubmedArticle}-> ... ->{MeshHeading}可以取決於有多少<MeshHeading>標籤存在於文檔中的字符串或數組引用,你需要ref檢查值的類型,並有條件地取消對它的引用。由於我不知道任何簡潔Perl的成語這樣做的,最好的辦法是寫一個函數:

 
sub toArray { 
my $meshes = shift; 
if (!defined $meshes) { return() } 
elsif (ref $meshes eq 'ARRAY') { return @$meshes } 
else { return ($meshes) } 
} 

,然後用它像這樣:

 
foreach my $e (toArray($data->{PubmedArticle}->{MedlineCitation}->{MeshHeadingList}->{MeshHeading})) { ... } 

爲了防止' $$ '從後正在打印最後一個元素,而不是循環通過列表,join串接所有元素結合在一起:

 
print FH join ' $$ ', map { $_->{DescriptionName}{content} } 
toArray($data->{PubmedArticle}->{MedlineCitation}->{MeshHeadingList}->{MeshHeading}); 
2

我想你錯過的部分「的perldoc XML ::簡單」談到有關的ForceArray選項:

check out ForceArray because you'll almost certainly want to turn it on 

然後,你總是會得到一個數組,即使該數組僅包含一個元素。

1

這是XML ::簡單的正在...簡單的地方。它通過是否有多次出現來推斷是否有數組。閱讀doc並尋找ForceArray選項來解決這個問題。

要想僅包括元素之間的' $$ ',與

print FH join ' $$ ', map $_->{DescriptorName}{content}, @{$data->{PubmedArticle}->{MedlineCitation}->{MeshHeadingList}->{MeshHeading}}; 
2

取代你的循環正如其他人所指出的那樣,ForceArray選項將解決這方面的問題。但是,由於XML :: Simple的假設與您的不匹配,您無疑會很快遇到另一個問題。作爲XML :: Simple的作者,我強烈建議您閱讀Stepping up from XML::Simple to XML::LibXML - 如果沒有其他內容,它會教你更多關於XML :: Simple的內容。