2014-09-04 110 views
1

我試圖解析XML文件(serverslist2.xml)與包含在格式的服務器列表:解析問題,使用XML :: XPath的

<?xml version="1.0"?> 
<servers> 
    <aplication name="App1"> 
    <env name="Test"> 
     <appliance alias="app1t1" conname="10.0.0.10:80" /> 
     <appliance alias="app1st1" conname="10.0.0.10:443" /> 
    </env> 
    <env name="Prod"> 
     <appliance alias="app1p1" conname="10.0.1.10:80" /> 
     <appliance alias="app1sp1" conname="10.0.1.10:443" /> 
     <appliance alias="app1p2" conname="10.0.1.11:80" /> 
     <appliance alias="app1sp2" conname="10.0.1.11:443" /> 
    </env> 
</aplication> 
    <aplication name="App2"> 
    <env name="Test"> 
     <appliance alias="app2t1" conname="10.0.0.20:80" /> 
     <appliance alias="app2st1" conname="10.0.0.20:443" /> 
    </env> 
    <env name="Prod"> 
     <appliance alias="app2p1" conname="10.0.1.20:80" /> 
     <appliance alias="app2sp1" conname="10.0.1.20:443" /> 
     <appliance alias="app2p2" conname="10.0.1.21:80" /> 
     <appliance alias="app2sp2" conname="10.0.1.21:443" /> 
    </env> 
</aplication> 
</servers> 

我可能會收到一個「別名」值(我知道它是唯一的)在一個變量中,並需要檢索「conname」值。

到目前爲止,我的代碼(testxpath.pl)看起來像:

#!/usr/bin/perl 
use XML::XPath; 

my $file = 'serverslist2.xml'; 
my $xp = XML::XPath->new(filename => $file); 

my $server = app2st1; 

print "Starting Test1:\n"; 
foreach my $test1 ($xp->find('//appliance[@alias="' . $server . '"]')->get_nodelist) { 
    print "Test1 output: " , $test1->find('@conname') , "\n"; 
    } ; 

print "Starting Test2:\n"; 
my $test2 = $xp->find('//appliance[@alias="' . $server . '"]')->get_nodelist ; 
print "Test2 output: " , $test2->find('@conname') , "\n"; 

好奇的是,測試1,使用foreach科,工,但是沒有它,它失敗了,即使使用基本相同命令。

輸出是:

$ ./testxpath.pl 
Starting Test1: 
Test1 output: 10.0.0.20:443 
Starting Test2: 
Can't call method "find" without a package or object reference at ./testxpath.pl line 16. 

有人能澄清什麼是錯的,如何解決? 此外,如果有更好的方法使用這個或其他Perl的XML處理庫,它將不勝感激。 (P.S:我可能不是簡單的「grep」可以使用一些正則表達式,因爲XML將始終符合這個標準,但不一定會被「印刷精美」)

問候 雷納託

回答

0

->get_nodelist返回一個列表。

因此你必須把你的變量在括號或它只會被分配名單count,而不是第一個值:

my ($test2) = $xp->find(qq{//appliance[\@alias="$server"]})->get_nodelist; 

或者,因爲你只是在尋找一個值,你可以使用該findvalue方法:

print "Starting Test3:\n"; 
my $conname = $xp->findvalue(qq{//appliance[\@alias="$server"]/\@conname}); 
print "Test3 output: $conname\n"; 

這是一個完整的工作的工作腳本了:

#!/usr/bin/perl 
use strict; 
use warnings; 

use XML::XPath; 

my $file = 'serverslist2.xml'; 
my $xp = XML::XPath->new(xml => do { local $/; <DATA> }); 

my $server = 'app2st1'; 

print "Starting Test1:\n"; 
foreach my $test1 ($xp->find(qq{//appliance[\@alias="$server"]})->get_nodelist) { 
    print "Test1 output: ", $test1->find('@conname'), "\n"; 
} 

print "Starting Test2:\n"; 
my ($test2) = $xp->find(qq{//appliance[\@alias="$server"]})->get_nodelist; 
print "Test2 output: ", $test2->find('@conname'), "\n"; 

print "Starting Test3:\n"; 
my $conname = $xp->findvalue(qq{//appliance[\@alias="$server"]/\@conname}); 
print "Test3 output: $conname\n"; 

__DATA__ 
<?xml version="1.0"?> 
<servers> 
    <aplication name="App1"> 
    <env name="Test"> 
     <appliance alias="app1t1" conname="10.0.0.10:80" /> 
     <appliance alias="app1st1" conname="10.0.0.10:443" /> 
    </env> 
    <env name="Prod"> 
     <appliance alias="app1p1" conname="10.0.1.10:80" /> 
     <appliance alias="app1sp1" conname="10.0.1.10:443" /> 
     <appliance alias="app1p2" conname="10.0.1.11:80" /> 
     <appliance alias="app1sp2" conname="10.0.1.11:443" /> 
    </env> 
</aplication> 
    <aplication name="App2"> 
    <env name="Test"> 
     <appliance alias="app2t1" conname="10.0.0.20:80" /> 
     <appliance alias="app2st1" conname="10.0.0.20:443" /> 
    </env> 
    <env name="Prod"> 
     <appliance alias="app2p1" conname="10.0.1.20:80" /> 
     <appliance alias="app2sp1" conname="10.0.1.20:443" /> 
     <appliance alias="app2p2" conname="10.0.1.21:80" /> 
     <appliance alias="app2sp2" conname="10.0.1.21:443" /> 
    </env> 
</aplication> 
</servers> 

輸出:

Starting Test1: 
Test1 output: 10.0.0.20:443 
Starting Test2: 
Test2 output: 10.0.0.20:443 
Starting Test3: 
Test3 output: 10.0.0.20:443 
+0

findvalue()的工作原理與我在找的一樣。這是一個更優雅的解決方案。 Tkx Miller。 – Renato 2014-09-04 02:47:53

+0

關於'findvalue'的另外一個發現是: 對於打印來說,工作很好,但對於任務我發現這樣做效果更好: 'my $ conname = $ xp-> findvalue(qq {// appliance [\ @ alias = 「$服務器」]/\ @ CONNAME}) - >值(); ' 這是因爲'findvalue'返回'XML :: XPath :: Literal'對象而不是字符串,因爲'value()'會做。 – Renato 2014-09-04 20:31:10

+0

是的,['findvalue'](https://metacpan.org/pod/XML::XPath#findvalue-path-context)返回['XML :: XPath :: Literal'](https:// metacpan。組織/ POD/XML :: XPath的::文字)。您可以使用[' - > value()'](https://metacpan.org/pod/XML::XPath::Literal#value)來訪問字符串表示,但也可以通過quote運算符重載。 ' 「$ CONNAME」' – Miller 2014-09-04 20:41:24