使用XML :: XPath解析问题

时间:2014-09-04 00:42:17

标签: xml perl xpath

我正在尝试解析包含以下格式的服务器列表的xml文件(serverslist2.xml):

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

我可能会收到&#34;别名&#34;变量中的值(我知道它是唯一的),需要检索&#34; conname&#34;值。

到目前为止,我的代码(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"; 

奇怪的是,使用foreach分支的Test1可以工作,但没有它,即使使用基本相同的命令也会失败。

输出结果为:

$ ./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处理lib,那将是值得赞赏的。 (P.S。:我可能不会简单地使用一些正则表达式,因为xml将始终符合此标准,但不一定是&#34;打印良好&#34;)

此致 雷纳托

1 个答案:

答案 0 :(得分:0)

->get_nodelist会返回一个列表。

因此,您必须将您的变量放在括号中,否则只会为其分配列表计数而不是第一个值:

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