1

我已经尝试过XML Simple,但是由于它只是将 XML 读入哈希,因此在针对 DTD 运行时输出是无用的。艰难地学会了它。

所以我采用了XML::LibXML,有趣的是我发现最难完成的要求是XML::Simple最简单的。然而,我发现一些更容易做的事情XML::Simple被证明是不可能的(由于我对 DOM 缺乏了解,以及一些令人困惑的行为XML::LibXML)。

所以这里是一个 XML 示例:

    <Metadata>
        <ADI Name="movie" />
        <App_Data Name="Something I don't care about" value="who cares" />
        <App_Data Name="Something I don't care about as well" value="who cares" />
        <App_Data Name="ChangeMe" Value="" />
    </Metadata>
    <Metadata>
        <ADI Name="photo" />
        <App_Data Name="Something I don't care about" value="who cares" />
        <App_Data Name="Something I don't care about as well" value="who cares" />
        <App_Data Name="ChangeMe" Value="" />
    </Metadata>
    <Metadata>
        <ADI Name="poster" />
        <App_Data Name="Something I don't care about" value="who cares" />
        <App_Data Name="Something I don't care about as well" value="who cares" />
        <App_Data Name="ChangeMe" Value="" />
    </Metadata>

注意:我已对此进行了简化,以便在本文中使用。

所以基本上我必须使用标签中的Name字段<ADI>来确认我在 DOM 的正确区域中以更改标签中的Value属性<App_Data>who's Nameis ChangeMe

这是我想出的代码片段......并且惨遭失败。

#!/usr/bin/perl

use strict;
use XML::LibXML;

my $xml2 = XML::LibXML->new();
my $data = $xml2->parse_file("adi.xml");
my $movie;
my $photo;
my $poster;

foreach my $test ($data->findnodes('//Metadata')) {
    if ($test->findvalues('./ADI/@Name[.="movie"]')){
        $movie = 1;
        undef $photo;
        undef $poster;
    }
    elsif ($test->findvalues('./ADI/@Name[.="photo"]')){
        undef $movie;
        $photo = 1;
        undef $poster;
    }
    elsif ($test->findvalues('./ADI/@Name[.="poster"]')){
        undef $movie;
        undef $photo;
        $poster = 1;
    }
}

我没有除此之外的任何东西,因为它不起作用。我得到一个错误的东西

Can't locate object method "findvalues" via package "XML::LibXML::Element"

作为对这个问题的补充,如果我想完全删除<Metadata>包含照片和/或海报的那些(和所有孩子)怎么办?

4

3 回答 3

3

试试这个作为初学者。

#!/usr/bin/perl

use strict;
use XML::LibXML;

my $xml2 = XML::LibXML->new();
my $data = $xml2->parse_file("adi.xml");

foreach my $test ($data->findnodes('//Metadata')) {
    if ($test->findnodes('./ADI/@Name[.="movie"]')){
        print "movie\n";
    }
    elsif ($test->findnodes('./ADI/@Name[.="photo"]')){
        print "photo\n";
    }
    elsif ($test->findnodes('./ADI/@Name[.="poster"]')){
        print "poster\n";
    }
}

没有findvalues方法。您想要做的是 use findnodes,它将向您返回与 XPath 表达式匹配的节点列表。一旦你有了它,你就可以遍历列表并提取你需要的任何数据,就像你已经为Metadata.

另外,我假设您的 XML 文件有一个根级元素。我使用下面的修改版本来测试上面的代码。

<root>
   <Metadata>
        <ADI Name="movie" />
        <App_Data Name="Something I don't care about" value="who cares" />
        <App_Data Name="Something I don't care about as well" value="who cares" />
        <App_Data Name="ChangeMe" Value="" />
    </Metadata>
    <Metadata>
        <ADI Name="photo" />
        <App_Data Name="Something I don't care about" value="who cares" />
        <App_Data Name="Something I don't care about as well" value="who cares" />
        <App_Data Name="ChangeMe" Value="" />
    </Metadata>
    <Metadata>
        <ADI Name="poster" />
        <App_Data Name="Something I don't care about" value="who cares" />
        <App_Data Name="Something I don't care about as well" value="who cares" />
        <App_Data Name="ChangeMe" Value="" />
    </Metadata>
</root>

我发现这个备忘单对 Perl 的 LibXML 库很有用。

于 2013-06-16T04:36:00.827 回答
2
  • 你在哪里找到的findvalues?文件:

    @nodes = $node->findnodes( $xpath_expression );
    $result = $node->find( $xpath );
    print $node->findvalue( $xpath );
    
  • 为什么这么多用途.

    ./ADI/@Name[.="movie"]
    

    应该是

    ADI[@Name="movie"]
    
  • 您有多个元数据元素,但您仅根据最后一个元素设置变量。

  • 您不应该使用三个不同的变量来存储一条信息。


#!/usr/bin/perl

use strict;
use warnings;

use XML::LibXML qw( );

my $parser = XML::LibXML->new();
my $doc = $parser->parse_file("adi.xml");

for my $metadata ($doc->findnodes('//Metadata')) {
    my ($adi_type) = $metadata->find('ADI/@Name')
       or next;

    my ($app_data) = $metadata->find('App_Data[@Name="ChangeMe"]');

    if ($adi_type eq 'movie') {
       ...
    }
    elsif ($adi_type eq 'photo') {
       ...
    }
    elsif ($adi_type eq 'poster') {
       ...
    }
}

或者你甚至可以使用:

my ($movie_adi) = $doc->findnodes('//Metadata[ADI/@Name="movie"]');
my ($movie_app_data) = $movie_adi->findnodes('App_Data[@Name="ChangeMe"]');
...

my ($photo_adi) = $doc->findnodes('//Metadata[ADI/@Name="photo"]');
my ($photo_app_data) = $photo_adi->findnodes('App_Data[@Name="ChangeMe"]');
...

my ($poster_adi) = $doc->findnodes('//Metadata[ADI/@Name="poster"]');
my ($poster_app_data) = $poster_adi->findnodes('App_Data[@Name="ChangeMe"]');
...
于 2013-06-16T04:42:30.217 回答
2

在 XPath 表达式中可以做很多事情来找到您感兴趣的节点。

该程序将按照您的要求进行。我已将根元素添加<root>到您的数据中,以使其成为格式良好的 XML 文档。

use strict;
use warnings;

use XML::LibXML;

my $doc = XML::LibXML->load_xml(location => 'adi.xml', no_blanks => 1);

for my $metadata ($doc->findnodes('//Metadata')) {
  if ( $metadata->findnodes('ADI[@Name = "movie" or @Name = "photo"]') ) {
    $metadata->parentNode->removeChild($metadata);
  }
}

print $doc->toString(1);

输出

<?xml version="1.0"?>
<root>
  <Metadata>
    <ADI Name="poster"/>
    <App_Data Name="Something I don't care about" value="who cares"/>
    <App_Data Name="Something I don't care about as well" value="who cares"/>
    <App_Data Name="ChangeMe" Value=""/>
  </Metadata>
</root>
于 2013-06-16T08:17:40.687 回答