Perl XML获取同一父级的子详细信息

问题描述 投票:2回答:4

我有以下XML,如果正则表达式与另一个子元素匹配,我希望从同一个父元素中获取子元素。问题是,XML在整个地方都有重复的命名标记,所以很难简单地做Movie-> Year,因为有很多电影元素。

EG

数据:

<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最简单的方法是什么?

xml perl
4个回答
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()');
...

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简化,替换$node->parentNode->findnodes上的循环,

foreach my $node ($doc->findnodes($xpath)) {
    if ($node->to_literal =~ /(Titanic)/) {
        say "Title: $1";
        say "\tyear: ", $node->parentNode->findvalue('./year');
    }   
}

这里我们直接得到文本,因此也不需要->to_literal

XML::LibXML::Node中有许多方法,用于派生其他特定类的节点的基类。这里感兴趣的可能是nextSibling,作为在一个<movie>内仔细阅读有关标题的其他信息的一种方式。

请注意,这个完整且功能齐全的库提供了许多用于处理XML的工具。首先,向源文件添加详细信息(如属性)将允许使用库的其他优势。

文档分布在许多页面上。请参阅this post以获取相关文档链接的摘要。 tutorial for XML::LibXML的作者还有一个XML::Simple


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;

1
投票

您还可以从Perl调用命令行工具(如xmlstarlet)来快速提取所需的信息。

例如,如果您的XML文档片段存储在/tmp/foo.xml中,那么以下shell脚本会将其转换为表格形式,通过一次读取一行更容易在Perl中处理。

{ 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文档转换为更方便的形式的这种特殊方式对于电影标题中的换行符或|s并不健壮,并且需要外部工具,但这很容易。

© www.soinside.com 2019 - 2024. All rights reserved.