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フィールドを使用して、DOM の正しい領域にいることを確認し、タグの属性を<ADI>変更する必要があります。Value<App_Data>NameChangeMe

これは私が思いついたコードのスニペットです...そして惨めに失敗しました。

#!/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";
    }
}

方法はありませんfindvaluesfindnodesXPath 式に一致するノードのリストを返します。それができたら、リストを繰り返し処理して、必要なデータを抽出できますMetadata

また、XML ファイルにはルートレベルの要素が 1 つあると想定しています。以下の修正版を使用して、上記のコードをテストしました。

<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"]
    
  • 複数の Metadata 要素がありますが、最後の要素のみに基づいて変数を設定します。

  • 1 つの情報を格納するために 3 つの異なる変数を使用するべきではありません。


#!/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 に答える