2015-08-26 2 views
1

Я пытаюсь получить атрибут @id1 из <Incoming> в приведенном ниже XML:Использование LibXML и XPath, чтобы найти узел с Colon (локальное пространство имен)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<Incomings xmlns:ns2="http://testme.org/foo/schema"> 
    <Incoming id1="6bbaec22" id2="928c2081"> 
     <ns2:Address>[email protected]</ns2:Address> 
    </Incoming> 
</Incomings> 

Единственная информация, которую я могу передать это электронная почта адрес [email protected]

Я использую XML::LibXML и XML::LibbXML::XPathContext, как показано ниже:

my $dom = XML::LibXML->new->parse_file($xml_file); # XML contains as above 
my $xpc = XML::LibXML::XPathContext->new($dom->documentElement); 
$xpc->registerNs('x', 'http://testme.org/foo/schema'); 

my $email = '[email protected]'; 
my $xpath = "/x:Incomings/x:Incoming/x:ns2:Address[text()='$email']/../\@id1"; 
my @nodes = $xpc->findnodes($xpath); 

Но всегда дают s me недопустимое выражение в $xpath вокруг ns2: Адрес.

Какая ошибка я сделал выше? Если имя узла только <Address>, то удаление ns2: из моего $xpath заявление, в котором указаны правильные значения в @nodes.

Спасибо!

ответ

1

Я думаю, что здесь есть две проблемы - во-первых, xpath выражения находят узлы. Вы можете искать на основе наличия и содержимого атрибута, но findnodes предоставит вам элемент, а не контент.

Во-вторых - вы не можете вложить пространства имен в XML. x:ns2:Address недействителен. Вам действительно нужно зарегистрировать ваше пространство имен x? Вам может и не понадобиться. (например, на основе небольшого фрагмента XML).

Могу ли я предложить альтернативный вариант? Поскольку вы работаете с perl, вам действительно не нужно делать все с помощью выражения xpath.

я бы, возможно, думал findnodes следуют grep:

NB: Использование XML :: Twig для иллюстрации - довольно уверен, что что-то очень похожие работы в XML :: LibXML.

#!/usr/bin/env perl 
use strict; 
use warnings; 
use XML::Twig; 

my $twig = XML::Twig->new('pretty_print' => 'indented_a')->parse(\*DATA); 

my @elt_list = grep { $_->trimmed_text =~ m{fubar\@test.com} } 
    ($twig->findnodes('//ns2:Address')); 

foreach my $elt (@elt_list) { 
    print $elt -> parent -> att('id1'); 
} 


__DATA__ 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<Incomings xmlns:ns2="http://testme.org/foo/schema"> 
    <Incoming id1="6bbaec22" id2="928c2081"> 
     <ns2:Address>[email protected]</ns2:Address> 
    </Incoming>  
</Incomings> 

Я бы также отметить - ваш XPath позволяет найти элемент - а не атрибут - так что вы можете выбрать на «элементы с id1 атрибута, как это:

my @elt_list = ($twig->findnodes("//ns2:Address[string()='$email']/../.[\@id1]")); 

foreach my $elt (@elt_list) { 
    print $elt -> att('id1'); 
} 

зависит скорее от как конкретно вы хотите быть в поиске findnodes.Основываясь на том, что вы предоставили в этом фрагменте кода, вы ушли слишком сложно, и может просто сделать:

use XML::Twig; 

my $twig = XML::Twig->parsefile('your_file.xml'); 
print $twig -> findnodes('//Incoming',0)->att('id1'),"\n"; 

Или:

#!/usr/bin/env perl 
use strict; 
use warnings; 
use XML::LibXML; 

my $xml = XML::LibXML->new->parse_file('sample2.xml'); 
foreach my $node ( $xml -> findnodes('//Incoming')) { 
    print $node ->getAttribute('id1'), "\n"; 
} 

Или с немного оглавлению:

#!/usr/bin/env perl 
use strict; 
use warnings; 
use XML::LibXML; 

my $email = '[email protected]'; 
my $xml = XML::LibXML->new->parse_file('sample2.xml'); 
foreach my $node (grep { $_ -> textContent =~ m{$email} } $xml -> findnodes('//Incoming')) { 
    print $node ->getAttribute('id1'), "\n"; 
} 

Если вы особенно хотите, чтобы с помощью этого x пространства имен, хотя - это работает:

#!/usr/bin/env perl 
use strict; 
use warnings; 
use XML::LibXML; 

my $xml = XML::LibXML->new->parse_file('sample2.xml'); 
my $xpc = XML::LibXML::XPathContext->new($xml->documentElement); 
$xpc->registerNs('x', 'http://testme.org/foo/schema'); 

my $email = '[email protected]'; 
my ($id1) = map { $_ -> getAttribute('id1') //() } $xpc->findnodes("/Incomings/Incoming/x:Address[text()='$email']/.."); 
print $id1,"\n"; 

(также работает, если я макете некоторые XML с несколькими узлами «Входящие», чтобы выбрать первое с правильным адресом электронной почты. Примечание // имеет значение perl 5.10 и является условным значением «определено». Возможно, вы могли бы заменить его || на более старые версии, что является «истинным/ложным» - единственными местами, где существуют различия, являются пустые строки и нули)

+0

Благодарим вас за подробные ответы и варианты. – est

1

Попробуйте маршрут "/Incomings/Incoming[x:Address = '$email']/@id1". Если строковый литерал Perl нуждается в побеге \@id1, тогда сохраните его, т. Е. "/Incomings/Incoming[x:Address = '$email']/\@id1".

1

В двух случаях вы ищете элемент в неправильном пространстве имен , и в одном случае. вы используете два префикса, которые не имеют смысла. Исправлено:

my $email = '[email protected]'; 
my $xpath = "/Incomings/Incoming/x:Address[text()='$email']/../\@id1"; 
my @nodes = $xpc->findnodes($xpath); 

Я предпочитаю избегать использования ... Я бы использовал следующее:

my $email = '[email protected]'; 
my $xpath = "/Incomings/Incoming[x:Address/text()='$email']/\@id1"; 
my @nodes = $xpc->findnodes($xpath); 
+0

Спасибо @ikegami - если бы я мог принять два решения, я бы сделал так, так как ваш ответ здесь решает мою оригинальную проблему. – est

Смежные вопросы