2016-03-16 114 views
0

我正在使用XML::LibXML解析XML文件。在訪問節點元素時使用註冊名稱空間似乎存在一些問題。我打算將這個XML數據轉換爲CSV文件。我試圖訪問這裏的每一個元素。首先,我嘗試提取<country><state>標籤的屬性值。以下是我帶的代碼。但我得到錯誤說XPath error : Undefined namespace prefix無法使用註冊命名空間解析xml文件

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"; 
} 

回答

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_stringparse_file

爲XML的單個元素使用的getElementsByTagName 獲取標籤內的元素。這應該足以讓你去

1

這裏似乎有兩個小錯誤。
1.以上下文節點爲參數調用XPathContext文檔的findvalue。
2.名稱是國家中沒有節點的屬性。

爲此嘗試:

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評估定義顯式上下文,其中可以定義與文檔無關的前綴到名稱空間的映射。我在上面做過。

結論: 調用找到一個節點上才起作用:如果根元素沒有命名空間
(或者,如果您使用相同的前綴,如XML doucment如存在任何)

+0

如果我刪除註冊的命名空間,並嘗試使用我的相同的代碼,它似乎工作。你能幫我理解差異嗎?已經更新了我的問題中的代碼。 – chidori

+0

@chidori請看看更新。 –