2014-02-27 39 views
1

我的XML看起来是这样的:如何导航XML所有标签包括属性

<Servers type="Container"> 
<Server type="Category"> 
    <HostName type="Property">dmitri-spB</HostName> 
    <HostIPAddress type="Property">14.341.516.564</HostIPAddress> 

在这个数据中案件,我不需要的属性 - 或者至少我只见过在我可能需要的数据中有一个案例。在任何情况下...如果XML是这样的:

<Servers> 
<Server> 
    <HostName>dmitri-spB</HostName> 
    <HostIPAddress>14.341.516.564</HostIPAddress> 

...我会做这样的事情:

my $parser = XML::LibXML -> new(); 
    my $tree = $parser -> parse_file ($source_dir."\\".$xmlfiles); 
    for my $Servers ($tree->findnodes ('/Servers')) { 
     foreach my $Server ($Servers->findnodes('./Server')) { 
      $hostname = $Server->findvalue('HostName'); 
      $hostIP = $Server->findvalue('HostIPAddress'); 
     } 
    } 

我试图在字面上使用整个标签使用同样的方法代码,并得到了“XPath错误:无效的表达式”。

因此,确实存在两个问题 - 当我不关心属性时如何浏览和提取,以及如何在属性显着时执行此操作?也许这是一个XPath问题,但我根本无法将XPath文档与我正在尝试做的事情联系起来。建议?

+0

一个type属性是什么XPath表达式给你“的XPath错误:无效的表达式”所有Server节点? –

+0

你是否尝试运行上面显示的示例代码?它可能工作得很好。 – mirod

回答

2

您可以完全忽略这些属性。它们不是您想象的“整个标签”的一部分,而是每个元素的附加信息。

因此,使用你的代码,因为它是,它应该工作。

要按其属性的值选择元素,可以在谓词中使用XPath @表示法。因此,例如,要查找所有Server元素和type属性Category,您可以编写Server[type = "Category"]

您可以使用my $document = XML::LibXML->load_data(location => 'myfile')创建解析器对象并一次处理源数据。 location允许您传递文件名或URL,还可以指定string并传递包含XML或IO的简单Perl字符串,并传递打开的文件句柄以供读取。

也不需要在单独的循环中迭代路径的每一步。例如,您可以加工的Category这样

use strict; 
use warnings; 

use XML::LibXML; 

my $doc = XML::LibXML->load_xml(location => "$source_dir/$xmlfiles"); 

for my $server ($doc->findnodes('/Servers/Server[@type = "Category"]')) { 
    print ref $server, "\n"; 
} 
+0

是的。如果我在XPath路径的每个元素中都包含'@'符号,那么效果很好。没有它我没有成功,但没关系。我已经使用了关于取回所有节点并明确循环它们的提示。由于我的能力有限,显式循环对我来说更加清晰,但是你的方法迫使我更多地思考实际返回的内容以及我如何访问内容 - 这对我的大脑来说是一个很好的练习。您已经删除了我的包版,并且正在按照我的需要运作。 –

+0

上面,我的意思是写'*而不是*显式循环',而不是*和*显式循环。我必须有一些我已经纠正的小错误,因为正如你们所建议的那样,现在代码运行得很好,无论是否使用'@'谓词。一切都很好。 –

+0

@KirkFleming:如你所做的那样,编写两个循环的问题是外层循环只循环一个*'Servers'元素,因此只执行一次。循环的存在不正确意味着有多个节点需要迭代。 – Borodin