使用 LibXML 和 XPath 查找带冒号的节点(本地命名空间)
Using LibXML and XPath To Find Node With Colon (Local Namespace)
我正在尝试从下面的 <Incoming>
中获取属性 @id1
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Incomings xmlns:ns2="http://testme.org/foo/schema">
<Incoming id1="6bbaec22" id2="928c2081">
<ns2:Address>fubar@test.com</ns2:Address>
</Incoming>
</Incomings>
我唯一可以传递的信息是电子邮件地址fubar@test.com
我正在使用 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 = 'fubar@test.com';
my $xpath = "/x:Incomings/x:Incoming/x:ns2:Address[text()='$email']/../\@id1";
my @nodes = $xpc->findnodes( $xpath );
但它总是在 ns2:Address.
周围的 $xpath
中给我一个无效的表达式
上面我犯了什么错误?如果节点名称仅为 <Address>
,则从我的 $xpath
语句中删除 ns2:,从而在 @nodes
.[=21= 中提供正确的值]
谢谢!
尝试路径 "/Incomings/Incoming[x:Address = '$email']/@id1"
。如果 Perl 字符串文字需要转义 \@id1
,则保留它,即 "/Incomings/Incoming[x:Address = '$email']/\@id1"
.
我认为这里有两个问题 - 首先,xpath
表达式查找节点。您可以根据属性的存在和内容进行搜索,但 findnodes
会为您提供元素,而不是内容。
其次 - 您不能在 XML 中嵌套命名空间。 x:ns2:Address
无效。你真的需要在那里注册你的 x
命名空间吗?你可能根本不需要。 (例如,基于您的 XML 小片段)。
我可以提供替代方案吗?因为您正在使用 perl
,所以您实际上不一定需要通过 xpath 表达式完成所有操作。
我可能会想 findnodes
然后是 grep
:
注意:使用 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>fubar@test.com</ns2:Address>
</Incoming>
</Incomings>
我还要注意 - 你的 xpath 让你找到一个元素 - 而不是一个属性 - 所以你可以 select 在具有 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";
}
或者用一点 grepping:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $email = 'fubar@test.com';
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 = 'fubar@test.com';
my ( $id1 ) = map { $_ -> getAttribute('id1') // () } $xpc->findnodes("/Incomings/Incoming/x:Address[text()='$email']/..");
print $id1,"\n";
(如果我模拟一些具有多个 'Incoming' 节点的 XML 到第一个具有正确电子邮件地址的 select 也可以工作。注意 //
是 perl 5.10 以上, 并且是 'defined' 的条件。您可以在旧版本上用 ||
替换它,即 'true/false' - 唯一不同的地方是空字符串和零)
在两种情况下,您在错误的命名空间中搜索元素,在一种情况下。您使用的是两个没有意义的前缀。固定:
my $email = 'fubar@test.com';
my $xpath = "/Incomings/Incoming/x:Address[text()='$email']/../\@id1";
my @nodes = $xpc->findnodes($xpath);
我更愿意避免使用 ..
。我会使用以下内容:
my $email = 'fubar@test.com';
my $xpath = "/Incomings/Incoming[x:Address/text()='$email']/\@id1";
my @nodes = $xpc->findnodes($xpath);
我正在尝试从下面的 <Incoming>
中获取属性 @id1
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Incomings xmlns:ns2="http://testme.org/foo/schema">
<Incoming id1="6bbaec22" id2="928c2081">
<ns2:Address>fubar@test.com</ns2:Address>
</Incoming>
</Incomings>
我唯一可以传递的信息是电子邮件地址fubar@test.com
我正在使用 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 = 'fubar@test.com';
my $xpath = "/x:Incomings/x:Incoming/x:ns2:Address[text()='$email']/../\@id1";
my @nodes = $xpc->findnodes( $xpath );
但它总是在 ns2:Address.
周围的$xpath
中给我一个无效的表达式
上面我犯了什么错误?如果节点名称仅为 <Address>
,则从我的 $xpath
语句中删除 ns2:,从而在 @nodes
.[=21= 中提供正确的值]
谢谢!
尝试路径 "/Incomings/Incoming[x:Address = '$email']/@id1"
。如果 Perl 字符串文字需要转义 \@id1
,则保留它,即 "/Incomings/Incoming[x:Address = '$email']/\@id1"
.
我认为这里有两个问题 - 首先,xpath
表达式查找节点。您可以根据属性的存在和内容进行搜索,但 findnodes
会为您提供元素,而不是内容。
其次 - 您不能在 XML 中嵌套命名空间。 x:ns2:Address
无效。你真的需要在那里注册你的 x
命名空间吗?你可能根本不需要。 (例如,基于您的 XML 小片段)。
我可以提供替代方案吗?因为您正在使用 perl
,所以您实际上不一定需要通过 xpath 表达式完成所有操作。
我可能会想 findnodes
然后是 grep
:
注意:使用 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>fubar@test.com</ns2:Address>
</Incoming>
</Incomings>
我还要注意 - 你的 xpath 让你找到一个元素 - 而不是一个属性 - 所以你可以 select 在具有 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";
}
或者用一点 grepping:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;
my $email = 'fubar@test.com';
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 = 'fubar@test.com';
my ( $id1 ) = map { $_ -> getAttribute('id1') // () } $xpc->findnodes("/Incomings/Incoming/x:Address[text()='$email']/..");
print $id1,"\n";
(如果我模拟一些具有多个 'Incoming' 节点的 XML 到第一个具有正确电子邮件地址的 select 也可以工作。注意 //
是 perl 5.10 以上, 并且是 'defined' 的条件。您可以在旧版本上用 ||
替换它,即 'true/false' - 唯一不同的地方是空字符串和零)
在两种情况下,您在错误的命名空间中搜索元素,在一种情况下。您使用的是两个没有意义的前缀。固定:
my $email = 'fubar@test.com';
my $xpath = "/Incomings/Incoming/x:Address[text()='$email']/../\@id1";
my @nodes = $xpc->findnodes($xpath);
我更愿意避免使用 ..
。我会使用以下内容:
my $email = 'fubar@test.com';
my $xpath = "/Incomings/Incoming[x:Address/text()='$email']/\@id1";
my @nodes = $xpc->findnodes($xpath);