我有以下XML,如果正则表达式与另一个子元素匹配,我希望从同一个父元素获得子元素。问题是,XML到处都有重复的命名标签,因此很难完成Movie-> Year,因为电影元素很多。
例如
数据:
<movie>
<title>Titanic</title>
<year>1997</year>
<genre>Drama</genre>
</movie>
<movie>
<title>Moneyball</title>
<year>2011</year>
<genre>Sport/Drama</genre>
</movie>
<movie>
<title>Fight Club</title>
<year>1999</year>
<genre>Drama/Action</genre>
</movie>
Perl
my $simple = XML::Simple->new( );
my $tree = $simple->XMLin($_);
my $movie = $tree->{movie}{title};
if($movie =~ /Titanic/)
{
# $movie -> year ???
# desired output = 1997
}
使用XML :: Simple最简单的方法是什么?
答案 0 :(得分:4)
使用XML :: Simple没有简单的方法,因为it's the hardest XML parser to use。它自己的文档警告不要使用它。 (“强烈建议不要在新代码中使用此模块。”)
您所拥有的没有有效的XML,因此我们首先必须使其成为有效的XML
use XML::LibXML qw( );
my $parser = XML::LibXML->new();
my $doc = $parser->parse_string("<movies>$not_quite_xml</movies>");
my ($movie_node) = $doc->findnodes('/movies/movie[title/text()="Titanic"]')
or die("Titanic not found\n");
my $year = $movie_node->findvalue('year/text()');
...
答案 1 :(得分:3)
我希望已经表明,不应该使用XML::Simple
,因为它早就被更好的模块所取代,而"strongly discouraged"也被它自己的作者反对,也就在几年前。
此示例显示了一种使用父节点来查询兄弟姐妹的方法,这是专门要求的。 (我用<document>
根节点填充了示例,以便获得格式正确的XML。)ikegami的回答显示了如何更直接地执行您似乎需要的操作。
如果您有理由扫描<title>
节点(也许正在寻找各种标题),则可以通过以下方式找到它们的同级<year>
节点:
use strict;
use warnings;
use feature 'say';
use XML::LibXML;
my $file = shift || die "Usage: $0 filename\n";
my $doc = XML::LibXML->load_xml(location => $file, no_blanks => 1);
my $xpath = '/document/movie/title';
foreach my $node ($doc->findnodes($xpath)) {
if ($node->to_literal =~ /(Titanic)/) {
say "Title: $1";
foreach my $yr ($node->parentNode->findnodes('./year')) {
say "\tyear: ", $yr->to_literal;
}
}
}
如果在一个<year>
节点下始终只有一个<movie>
节点,则可以通过快捷方式findvalue
来简化,将{1}}上的循环替换为>
$node->parentNode->findnodes
这里我们直接获取文本,因此也不需要foreach my $node ($doc->findnodes($xpath)) {
if ($node->to_literal =~ /(Titanic)/) {
say "Title: $1";
say "\tyear: ", $node->parentNode->findvalue('./year');
}
}
。
XML::LibXML::Node中还有许多方法,这是用于派生其他特定类的节点的基类。这里有趣的一个可能是->to_literal
,作为一种在nextSibling
中细读有关标题的其他信息的方法。
请注意,这个完整且功能齐全的库提供了更多使用XML的工具。首先,将详细信息(如属性)添加到源文件中将允许利用库的其他优势。
文档分布在许多页面上。有关相关文档的链接摘要,请参见this post。 <movie>
的作者还有tutorial for XML::LibXML。
答案 2 :(得分:2)
还有另一种方法,这次用Mojo::DOM。除了其他解决方案(除了XML :: Simple之外),没有什么可推荐的。
这将添加一个根元素,然后使用CSS选择器来获取标题:
use utf8;
use strict;
use warnings;
my $xml = <<'HERE';
<movies>
<movie>
<title>Titanic</title>
<year>1997</year>
<genre>Drama</genre>
</movie>
<movie>
<title>Moneyball</title>
<year>2011</year>
<genre>Sport/Drama</genre>
</movie>
<movie>
<title>Fight Club</title>
<year>1999</year>
<genre>Drama/Action</genre>
</movie>
</movies>
HERE
use Mojo::DOM;
my @movies = Mojo::DOM
->new( $xml )
->find( 'movies title' )
->map( 'text' )
->each;
say join "\n", @movies;
答案 3 :(得分:1)
您还可以从Perl中调用xmlstarlet
之类的命令行工具来快速提取所需信息。
例如,如果您的XML文档的片段存储在/tmp/foo.xml
,则以下shell脚本会将其转换为表格形式,通过一次读取一行即可在Perl中更容易处理。 / p>
{ echo '<movies>' ; cat /tmp/foo.xml ; echo '</movies>'; } \
| xmlstarlet sel -T -t -m '//movie' -v "concat(title, '|', year)" -n
打印
Titanic|1997
Moneyball|2011
Fight Club|1999
这种将xml文档转换为更方便的格式的特殊方法对于电影标题中的换行符或|
来说并不可靠,并且需要外部工具,但这很容易。