2010-11-03 15 views
6

Bir XPath sorgusu kullanarak bir düğüm seçmeye çalışıyorum ve neden XML :: LibXML düğümünü bulamadığını anlamıyorum. bir xmlns atribute vardır. İşte sorunu göstermek için bir komut dosyası:Neden XML :: LibXML bir ad alanı kullanırken bu xpath sorgusu için düğüm bulamadı

#!/usr/bin/perl 

use XML::LibXML; # 1.70 on libxml2 from libxml2-dev 2.6.16-7sarge1 (don't ask) 
use XML::XPath; # 1.13 
use strict; 
use warnings; 

use v5.8.4; # don't ask 

my ($xpath, $libxml, $use_namespace) = @ARGV; 

my $xml = sprintf(<<'END_XML', ($use_namespace ? 'xmlns="http://www.w3.org/2000/xmlns/"' : q{})); 
<?xml version="1.0" encoding="iso-8859-1"?> 
<RootElement> 
    <MyContainer %s> 
    <MyField> 
     <Name>ID</Name> 
     <Value>12345</Value> 
    </MyField> 
    <MyField> 
     <Name>Name</Name> 
     <Value>Ben</Value> 
    </MyField> 
    </MyContainer> 
</RootElement> 
END_XML 

my $xml_parser 
    = $libxml ? XML::LibXML->load_xml(string => $xml, keep_blanks => 1) 
    :   XML::XPath->new(xml => $xml); 

my $nodecount = 0; 
foreach my $node ($xml_parser->findnodes($xpath)) { 
    $nodecount ++; 
    print "--NODE $nodecount--\n"; #would use say on newer perl 
    print $node->toString($libxml && 1), "\n"; 
} 

unless ($nodecount) { 
    print "NO NODES FOUND\n"; 
} 

Bu komut XML :: Libxml ayrıştırıcı ve XML :: XPath çözümleyici arasında seçim sağlar. Ayrıca MyContainer öğesinde bir xmlns özniteliği tanımlamanıza veya geçirilen bağımsız değişkenlere bağlı olarak onu bırakmanıza izin verir.

Kullanmakta olduğum xpath ifadesi "RootElement/MyContainer". Ancak

[email protected]:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' libxml 
--NODE 1-- 
<MyContainer> 
    <MyField> 
     <Name>ID</Name> 
     <Value>12345</Value> 
    </MyField> 
    <MyField> 
     <Name>Name</Name> 
     <Value>Ben</Value> 
    </MyField> 
    </MyContainer> 

Ben yerinde ad ile çalıştırdığınızda hiçbir düğümleri bulur: Ben ad olmadan XML :: Libxml ayrıştırıcı kullanarak sorguyu çalıştırdığınızda hiçbir sorunla düğümünü bulur

[email protected]:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' libxml use_namespace 
NO NODES FOUND 
[email protected]:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' 0 # no namespace 
--NODE 1-- 
<MyContainer> 
    <MyField> 
     <Name>ID</Name> 
     <Value>12345</Value> 
    </MyField> 
    <MyField> 
     <Name>Name</Name> 
     <Value>Ben</Value> 
    </MyField> 
    </MyContainer> 
[email protected]:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' 0 1 # with namespace 
--NODE 1-- 
<MyContainer xmlns="http://www.w3.org/2000/xmlns/"> 
    <MyField> 
     <Name>ID</Name> 
     <Value>12345</Value> 
    </MyField> 
    <MyField> 
     <Name>Name</Name> 
     <Value>Ben</Value> 
    </MyField> 
    </MyContainer> 

bu ayrıştırıcı uygulamalarının onu "doğru" yapıyor: XMLL kullanarak :: XPath çözümleyici çıkışı ile

Kontrast bu? XML :: LibXML neden bir ad alanı kullandığımda farklı davranıyor? Ad alanı bulunduğunda düğümü almak için ne yapabilirim?

+0

İyi soru, +1. Açıklama ve iki olası çözüm için cevabımı görün. –

+0

@ikegami, Hem gelişmiş * hem de yeni kullanıcılar için yararlı olmalı. Sorular sormaktan vazgeçilmemelidirler. –

cevap

14

Bu bir SSS'dur. XPath, bir ifadede herhangi bir unfix olmayan ismi "isimsiz" olarak adlandırır. Sonra

, ifadesi:

RootElement/MyContainer 

"hayır ad" aittir ve (mevcut bağlamda çocukları "hayır ad" ait olup tüm RootElement elemanların çocukları olan tüm MyContainer elemanlarını seçer düğüm). Ancak, tüm belgede "isimsiz" olan hiçbir öğe yoktur - tüm öğeler varsayılan ad alanına aittir.

Bu, aldığınız sonucu açıklar. XML :: LibXML , haklı.

Sık karşılaşılan çözüm, barındırma dilinin API'sinin, bir ad alanını "kaydederek" belirli bir önekin ad alanına bağlı olmasına izin vermesidir. Sonra bir benzeri bir ifade kullanabilirsiniz: x ad tescil edildiği ile önek olan

x:RootElement/x:MyContainer 

. barındırma dil kayıt ad alanları sunmuyor Çok ender olarak

aşağıdaki ifadeyi kullanın:

*[name()='RootElement']/*[name()='MyContainer'] 
+0

XML :: LibXML ile, XML :: LibXML :: XPathContext kullanarak ad alanlarını kaydedersiniz. Bu, "findnodes" ile belgelenmiştir. – ikegami

+0

@ikegami, Muhtemelen bütün olası XPath ana bilgisayarlarının ad alanı öneklerini kaydetmeyi nasıl uygulayacağını bilmemeli. Bu genel ve tekrarlayan soruya verilen doğru cevap (eğer cevabın sadece belirli bir XPath uygulamasının kullanıcılarına hizmet etmesini istemiyorsak), neler olduğunu açıklamalı ve kullanıcıların uygulamaya yönelik tanımlanmış detaylar için kendi belgelerine bakmalarına izin vermelidir. –

+0

Bu olabilir, ancak OP, XML :: LibXML'de nasıl yapılacağını sordu, o zaman neden bana cevabınızdan kaçırdığınızı söylerken bana suçluyorsunuz? – ikegami

7

@Dmitre haklı. Ad alanını bildirmenizi sağlayacak XML::LibXML::XPathContext'a bir göz atmanız ve daha sonra ad alanı farkında XPath ifadelerini kullanmanız gerekir. Bunu bir süre önce stackoverflow üzerinde kullanma örneği verdim - Why should I use XPathContext with Perl's XML::LibXML

+0

+1 ayrıntılı bilgi için. –

+0

XPathContext sorusunun işaretçisi için teşekkürler. Bana yardım edeceğinden ve ne yaptığımı bilmeden, onu kullanmaya çalışacağından şüpheleniyorum. Orada örneklerin yardımcı olup olmayacağını göreceğim. – benrifkah

1

XML :: LibXML 1.69'u kullanın.

Belki bu bir XML :: LibXML 1.69 olayı ama garip kısmı normal XPath ve findnodes() kullanabildiğim ve aşağıdaki kod düğümleri basıyor. Sonra XML kullanarak "http://www.w3.org/2000/xmlns/" dışındaki bir şey için ad alanını değiştirirseniz

use strict; 
use XML::LibXML; 

my $xml = <<END_XML; 
<?xml version="1.0" encoding="iso-8859-1"?> 
<RootElement> 
    <MyContainer xmlns="http://www.w3.org/2000/xmlns/"> 
    <MyField> 
     <Name>ID</Name> 
     <Value>12345</Value> 
    </MyField> 
    <MyField> 
     <Name>Name</Name> 
     <Value>Ben</Value> 
    </MyField> 
    </MyContainer> 
</RootElement> 
END_XML 

my $parser = XML::LibXML->new(); 

$parser->recover_silently(1); 

my $doc = $parser->parse_string($xml); 

my $root = $doc->documentElement(); 

foreach my $node ($root->findnodes('MyContainer/MyField')) { 
    print $node->toString(); 
} 

Ama :: Libxml :: XPathContext aynı düğümleri almak için gereklidir yazdırmak.

use strict; 
use XML::LibXML; 

my $xml = <<END_XML; 
<?xml version="1.0" encoding="iso-8859-1"?> 
<RootElement> 
    <MyContainer xmlns="http://something.org/2000/something/"> 
    <MyField> 
     <Name>ID</Name> 
     <Value>12345</Value> 
    </MyField> 
    <MyField> 
     <Name>Name</Name> 
     <Value>Ben</Value> 
    </MyField> 
    </MyContainer> 
</RootElement> 
END_XML 

my $parser = XML::LibXML->new(); 

$parser->recover_silently(1); 

my $doc = $parser->parse_string($xml); 

my $root = $doc->documentElement(); 

my $xpc = XML::LibXML::XPathContext->new($root); 

$xpc->registerNs("x", "http://something.org/2000/something/"); 

foreach my $node ($xpc->findnodes('x:MyContainer/x:MyField')) { 
    print $node->toString(); 
} 
+0

İlk örnekte $ parser-> recover_silently (1); satırını kaldırın ve hata mesajını alırsınız 'namepace error: xmlns ad alanı adının yeniden kullanılması yasak'. "Kurtar" seçeneğini kullanırsanız, isim alanı bildirimi göz ardı edilir. Eğer 'recover_silently' kullanırsanız bir hata mesajı bile yazdırılmaz. Bu yüzden genellikle kötü bir fikir. – nwellnhof