4

一部の子ノードのテキスト値が提供されると、XML で特定のノードを削除するサブルーチンを Perl で作成しようとしています。

次のような XML が与えられた場合:

<Path>
  <To>
    <My>
      <Node>
        <ChildA>ValA</ChildA>
        <ChildB>ValB</ChildB>
        <ChildC>ValC</ChildC>
      </Node>
    </My>
  </To>
</Path>
<!-- A lot of siblings follow... -->

私が使用している XPath 式は基本的に次のとおりです。

/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"]

スクリプトを実行しようとすると、次のようなエラーが発生します。

Error in XPath expression
/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"] at 
ChildA="ValA" and ChildB="ValB" and ChildC="ValC" at Twig.pm line 3353

私はこれに途方に暮れており、提案を探しています。グーグルで検索してみましたが、 でこのような述語を使用しようとする実際の例が見つかりませんXML::Twig。問題が XPath 構文にあるのか、それとも の使用方法にあるのかわかりませんXML::Twig

適切な測定のために、私も試しました:

/Path/To/My/Node[ChildA/text()="ValA" and ChildB/text()="ValB" and ChildC/text()="ValC"]

それも運が悪い。解決策は何ですか?

4

2 に答える 2

3

これを行うには2つの方法があります。XML全体をロードして不要なノードを削除してから小枝を出力するか、少し複雑ですがメモリの使用量が少ないフィルタリングを行います。

最初の方法(XML :: XPathEngineの最新バージョンが必要な場合があります。古いバージョンや、XPathエンジンとしても機能するXML :: XPathではテストしていません)

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig::XPath;

my $t= XML::Twig::XPath->new( pretty_print => 'indented')
                       ->parse( \*DATA);
$_->delete for ($t->findnodes( '/Path/To/My/Node[./ChildA="ValA" and ./ChildB="ValB" and ./ChildC="ValC"]'));

$t->print;

__DATA__
<Path>
  <To>
    <My>
      <Node>
        <ChildA>ValA</ChildA>
        <ChildB>ValB</ChildB>
        <ChildC>ValC</ChildC>
      </Node>
      <Node>
        <ChildA>ValD</ChildA>
        <ChildB>ValB</ChildB>
        <ChildC>ValC</ChildC>
      </Node>
    </My>
  </To>
</Path>

そして「フィルター」の方法:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new( twig_roots => { '/Path/To/My/Node' => \&filter },
                twig_print_outside_roots => 1,
                keep_spaces => 1,
              )
         ->parse( \*DATA);
exit;

# the handler expressions cannot lookahead, so we need to look at each node
# once it's completely parsed
sub filter
  { my( $t, $node)= @_;
    if(    ($node->field( 'ChildA') eq 'ValA')
        && ($node->field( 'ChildB') eq 'ValB')
        && ($node->field( 'ChildC') eq 'ValC')
      )
      { $node->delete; }
    else
      { $t->flush; }
  }

__DATA__
<Path>
  <To>
    <My>
      <Node>
        <ChildA>ValA</ChildA>
        <ChildB>ValB</ChildB>
        <ChildC>ValC</ChildC>
      </Node>
      <Node>
        <ChildA>ValD</ChildA>
        <ChildB>ValB</ChildB>
        <ChildC>ValC</ChildC>
      </Node>
    </My>
  </To>
</Path>
于 2011-07-08T13:09:00.010 に答える
3

テスト内にNodeはコンテキスト ノードがあるため、次のように言う必要があります。

/Path/To/My/Node[./ChildA="ValA" and ./ChildB="ValB" and ./ChildC="ValC"]

これは、を使用する短いテスト プログラムで機能しますXML::XPath

編集: 申し訳ありませんが、私は XML::Twig にあまり詳しくなく、その XPath 機能について誤った仮定をしました。ドキュメントによると、例の複雑さのレベルに達しない「XPath のような」構文のみをサポートしています。ただし、XML::Twig::XPath代わりにを使用するとXML::Twig、完全な XPath エンジンが得られます。

my $twig = XML::Twig::XPath->new;
$twig->parse('your string');
my $nodes = $twig->findnodes('/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"]');
print $nodes;

これにより、「ValAValBValC」が出力されます。

于 2011-07-07T22:11:41.483 に答える