我使用XML::LibXML
来解析XML文件。在访问节点元素时使用已注册的命名空间似乎存在一些问题。我打算将这个xml数据转换为CSV文件。我试图访问这里的每个元素。首先,我尝试提取<country>
和<state>
标记的属性值。以下是我附带的代码。但我收到错误XPath error : Undefined namespace prefix
。
use strict;
use warnings;
use Data::Dumper;
use XML::LibXML;
my $XML=<<EOF;
<DataSet xmlns="http://www.w3schools.com" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com note.xsd">
<exec>
<survey_region ver="1.1" type="x789" date="20160312"/>
<survey_loc ver="1.1" type="x789" date="20160312"/>
<note>Population survey</note>
</exec>
<country name="ABC" type="MALE">
<state name="ABC_state1" result="PASS">
<info>
<type>literacy rate comparison</type>
</info>
<comment><![CDATA[
Some random text
contained here
]]></comment>
</state>
</country>
<country name="XYZ" type="MALE">
<state name="XYZ_state2" result="FAIL">
<info>
<type>literacy rate comparison</type>
</info>
<comment><![CDATA[
any random text data
]]></comment>
</state>
</country>
</DataSet>
EOF
my $parser = XML::LibXML->new();
my $doc = $parser->parse_string($XML);
my $xc = XML::LibXML::XPathContext->new($doc);
$xc->registerNs('x','http://www.w3schools.com');
foreach my $camelid ($xc->findnodes('//x:DataSet')) {
my $country_name = $camelid->findvalue('./x:country/@name');
my $country_type = $camelid->findvalue('./x:country/@type');
my $state_name = $camelid->findvalue('./x:state/@name');
my $state_result = $camelid->findvalue('./x:state/@result');
print "state_name ($state_name)\n";
print "state_result ($state_result)\n";
print "country_name ($country_name)\n";
print "country_type ($country_type)\n";
}
更新 如果我从XML中删除名称空间并稍微改变我的XPath似乎工作。有人可以帮助我理解差异。
foreach my $camelid ($xc->findnodes('//DataSet')) {
my $country_name = $camelid->findvalue('./country/@name');
my $country_type = $camelid->findvalue('./country/@type');
my $state_name = $camelid->findvalue('./country/state/@name');
my $state_result = $camelid->findvalue('./country/state/@result');
print "state_name ($state_name)\n";
print "state_result ($state_result)\n";
print "country_name ($country_name)\n";
print "country_type ($country_type)\n";
}
答案 0 :(得分:1)
这将是我的方法
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;
my $XML=<<EOF;
<DataSet xmlns="http://www.w3schools.com" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.w3schools.com note.xsd">
<exec>
<survey_region ver="1.1" type="x789" date="20160312"/>
<survey_loc ver="1.1" type="x789" date="20160312"/>
<note>Population survey</note>
</exec>
<country name="ABC" type="MALE">
<state name="ABC_state1" result="PASS">
<info>
<type>literacy rate comparison</type>
</info>
<comment><![CDATA[
Some random text
contained here
]]></comment>
</state>
</country>
<country name="XYZ" type="MALE">
<state name="XYZ_state2" result="FAIL">
<info>
<type>literacy rate comparison</type>
</info>
<comment><![CDATA[
any random text data
]]></comment>
</state>
</country>
</DataSet>
EOF
my $parser = XML::LibXML->new();
my $tree = $parser->parse_string($XML);
my $root = $tree->getDocumentElement;
my @country = $root->getElementsByTagName('country');
foreach my $citem(@country){
my $country_name = $citem->getAttribute('name');
my $country_type = $citem->getAttribute('type');
print "Country Name -- $country_name\nCountry Type -- $country_type\n";
my @state = $citem->getElementsByTagName('state');
foreach my $sitem(@state){
my @info = $sitem->getElementsByTagName('info');
my $state_name = $sitem->getAttribute('name');
my $state_result = $sitem->getAttribute('result');
print "State Name -- $state_name\nState Result -- $state_result\n";
foreach my $i (@info){
my $text = $i->getElementsByTagName('type');
print "Info --- $text\n";
}
}
print "\n";
}
当然,无论如何你都可以操纵数据。如果要从文件解析,请将 parse_string 更改为 parse_file 。
对于xml中的各个元素,使用 getElementsByTagName 来获取标记中的元素。这应该足以让你前进
答案 1 :(得分:1)
这里似乎有两个小错误 1.以上下文节点作为参数调用XPathContext文档的findvalue 2. name是国家/地区中没有节点的属性。
因此尝试:
my $country_name = $xc->findvalue('./x:country/@name', $camelid );
更新以更新问题如果我从XML中删除名称空间并稍微更改我的XPath似乎可以正常工作。有人可以帮助我理解其中的差异。
要了解此处发生的事情,请查看NOTE ON NAMESPACES AND XPATH
在您的情况下$camelid->findvalue('./x:state/@name');
调用为节点调用findvalue。
但是:推荐的方法是使用XML :: LibXML :: XPathContext模块为XPath评估定义显式上下文,其中可以定义文档无关的前缀到命名空间映射。我上面做了什么。
<强>结论:强>
在节点上调用find只能起作用:如果根元素没有名称空间
(或者如果你使用与xml doucment相同的前缀,如果有的话)