使用 XML::Twig,有没有办法找到具有特定属性的 'first_child'?
With XML::Twig, is there a way of find a 'first_child' with a particular attribute?
我有一些 XML 看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
<Category>
<Name mode="source">somename</Name>
<Name mode="destination">someothername</Name>
<Content>Some text here</Content>
</Category>
</DataSet>
我想做的是处理 'Category',并根据上下文提取不同的名称。
我已经尝试用 children
对其进行迭代 - 这有效:
use strict;
use warnings;
use XML::Twig;
sub process_category {
my ( $twig, $category ) = @_;
my $cat_name;
foreach my $name ( $category->children('Name') ) {
if ( $name->att('mode') eq 'source' ) {
$cat_name = $name->text;
}
}
print "$cat_name ", $category->first_child_text('Content'), "\n";
}
my $twig =
XML::Twig->new( twig_handlers => { 'Category' => \&process_category } )
->parse( \*DATA );
__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
<Category>
<Name mode="source">somename</Name>
<Name mode="destination">someothername</Name>
<Content>Some Text</Content>
</Category>
</DataSet>
但是我想知道 - 是否有比迭代元素更好的方法?我不知道 first_child
是否支持属性搜索,或者是否有其他方法可以实现同样的功能。
您可以将 code_ref 传递给 first_child
。这个 sub 依次传递每个元素,如果它 returns 'true' 那么 first_child
方法匹配。 (然后不继续看)。
所以这应该可以解决问题:
use strict;
use warnings;
use XML::Twig;
sub is_name_source {
my ($element) = @_;
print $element ->tag, "\n";
if ( $element->tag eq 'Name'
and $element->att('mode') eq 'source' )
{
return 1;
}
}
sub process_category {
my ( $twig, $category ) = @_;
my $cat_name = $category->first_child( \&is_name_source )->text;
print "$cat_name ", $category->first_child_text('Content'), "\n";
}
my $twig =
XML::Twig->new( twig_handlers => { 'Category' => \&process_category } )
->parse( \*DATA );
__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
<Category>
<Name mode="source">somename</Name>
<Name mode="destination">someothername</Name>
<Content>Some Text</Content>
</Category>
</DataSet>
您当然可以将 is_name_source
与匿名子内联。这是一个品味问题。
使用XML::Twig的get_xpath
方法在属性中搜索匹配值。例如:
my $cat_name = $category->get_xpath('./Name[@mode="source"]', 0)->text;
默认情况下,get_xpath returns 一个数组。通过传递“0”,仅传递数组的第一个元素(这是您所需要的,而且很可能只有一个匹配项)。然后,用 ->text
拉出文本。使用它,您可以删除 for 循环。
我有一些 XML 看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
<Category>
<Name mode="source">somename</Name>
<Name mode="destination">someothername</Name>
<Content>Some text here</Content>
</Category>
</DataSet>
我想做的是处理 'Category',并根据上下文提取不同的名称。
我已经尝试用 children
对其进行迭代 - 这有效:
use strict;
use warnings;
use XML::Twig;
sub process_category {
my ( $twig, $category ) = @_;
my $cat_name;
foreach my $name ( $category->children('Name') ) {
if ( $name->att('mode') eq 'source' ) {
$cat_name = $name->text;
}
}
print "$cat_name ", $category->first_child_text('Content'), "\n";
}
my $twig =
XML::Twig->new( twig_handlers => { 'Category' => \&process_category } )
->parse( \*DATA );
__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
<Category>
<Name mode="source">somename</Name>
<Name mode="destination">someothername</Name>
<Content>Some Text</Content>
</Category>
</DataSet>
但是我想知道 - 是否有比迭代元素更好的方法?我不知道 first_child
是否支持属性搜索,或者是否有其他方法可以实现同样的功能。
您可以将 code_ref 传递给 first_child
。这个 sub 依次传递每个元素,如果它 returns 'true' 那么 first_child
方法匹配。 (然后不继续看)。
所以这应该可以解决问题:
use strict;
use warnings;
use XML::Twig;
sub is_name_source {
my ($element) = @_;
print $element ->tag, "\n";
if ( $element->tag eq 'Name'
and $element->att('mode') eq 'source' )
{
return 1;
}
}
sub process_category {
my ( $twig, $category ) = @_;
my $cat_name = $category->first_child( \&is_name_source )->text;
print "$cat_name ", $category->first_child_text('Content'), "\n";
}
my $twig =
XML::Twig->new( twig_handlers => { 'Category' => \&process_category } )
->parse( \*DATA );
__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<DataSet>
<Category>
<Name mode="source">somename</Name>
<Name mode="destination">someothername</Name>
<Content>Some Text</Content>
</Category>
</DataSet>
您当然可以将 is_name_source
与匿名子内联。这是一个品味问题。
使用XML::Twig的get_xpath
方法在属性中搜索匹配值。例如:
my $cat_name = $category->get_xpath('./Name[@mode="source"]', 0)->text;
默认情况下,get_xpath returns 一个数组。通过传递“0”,仅传递数组的第一个元素(这是您所需要的,而且很可能只有一个匹配项)。然后,用 ->text
拉出文本。使用它,您可以删除 for 循环。