使用LibXML

时间:2015-07-28 13:26:58

标签: xml perl xml-libxml

我目前正在使用带有LibXML的perl脚本来处理给定的XML文件。这样做得不错,但如果我有一个包含子节点和自由文本的节点,我就开始挣扎了。 输入的示例如下:

<Errors>
    <Error>
        this node works fine
    </Error>
    <Error>
        some text <testTag>with a node</testTag> in between
    </Error>
</Errors>

预期产出:

<Errors>
    <Error>
        this node works fine
    </Error>
    <Error>
        some text HELLOwith a nodeHELLO in between
    </Error>
</Errors>

我尝试过replaceChild(&#34; HELLO&#34;,$ testTagNode);用一个字符串替换节点,然后我可以(如果需要的话)通过简单的搜索替换进一步处理,但我只是遇到了#34;而不是一个有福的引用&#34;错误。 (我觉得如果实际上这样做的话就会很脏。)

如果我尝试直接在父节点上运行简单的搜索替换,就像这样

$error=~s/\</HELLO/g;

它将永远不会触发(无论我是否逃避&lt; or not),因为LibXML似乎忽略了我并不特别要求的每个标签;如果我尝试打印出第二个错误它也会给我

some text with a node in between

这对于文件的其余部分实际上是一个非常好的功能,但在这个实例中不是。

但我可以

$error->removeChild($testTagNode);

告诉我它确实找到了,但对我没有帮助。理论上我可以删除节点,保存内容,然后将内容重新插入父节点;问题在于它需要位于之前的确切位置。我唯一能做的就是将整个文件作为一个字符串读取,让基本的搜索替换在将它提供给LibXML之前运行它,但这可能会产生相当大的开销并且不是很好溶液

我觉得我忽略了一些重要的东西,因为这看起来像是一项非常基本的任务,但我似乎无法找到任何东西。也许我只是在寻找错误的方向,并且有一种完全不同的方法。任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:1)

XML::XSH2只是XML::LibXML的包装中,以下内容似乎有效:

for //testTag/text() {
    insert text 'HELLO' prepend . ;
    insert text 'HELLO' append . ;
    move . replace .. ;
}

翻译回XML :: LibXML留给读者练习。

答案 1 :(得分:1)

首先 - 我不认为你想要做的事情必然特别有用。但是,我会注意到 - 当你处理你的节点时 - 如果你有第二个例子中的嵌套节点,你实际上得到3个'节点',但其中两个被指定为#PCDATA

所以你可以这样做:

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
use Data::Dumper;

my $twig = XML::Twig->new( pretty_print => 'indented_a' )->parse( \*DATA );
foreach my $error ( $twig->get_xpath('//Error') ) {
    my $replace_text;
    foreach my $child ( $error->children ) {
        my $tag = $child->tag;
        print "Child: $tag ", $child->trimmed_text, "\n";
        $tag = '' if $tag eq "#PCDATA";
        $replace_text .= $tag . $child->trimmed_text . $tag;
    }

    $error->set_text($replace_text);
    print $error ->trimmed_text, "\n";
}
print $twig->sprint;

__DATA__
<Errors>
    <Error>
        this node works fine
    </Error>
    <Error>
        some text <testTag>with a node</testTag> in between
    </Error>
</Errors>

这会把它变成:

<Errors>
  <Error>this node works fine</Error>
  <Error>some texttestTagwith a nodetestTagin between</Error>
</Errors>

显然,您可以将testTag重命名为您喜欢的任何内容。

(跟我一起 - 我会看看如何在LibXML中做到这一点 - 遗憾的是它不能轻易地安装在我的Windows机器上)。

好的,XML::LibXML

#!/usr/bin/env perl
use strict;
use warnings;
use XML::LibXML;


my $xml = XML::LibXML->load_xml( IO => \*DATA );
foreach my $error ( $xml -> findnodes ( '//Error' ) ) {
   my $replace_text; 
   foreach my $child ( $error -> childNodes ) {
      my $tag = $child -> nodeName;
      $tag = '' if $tag eq '#text';
      $replace_text .= $tag . $child -> textContent . $tag; 
      $err -> removeChild($child);
   } 
   $err -> appendTextNode($replace); 
}

print $xml -> toString;

__DATA__
<Errors>
    <Error>
        this node works fine
    </Error>
    <Error>
        some text <testTag>with a node</testTag> in between
    </Error>
</Errors>

答案 2 :(得分:1)

删除testTag元素也会删除其所有子元素,因此我们必须将每个testTag元素的子元素移动到testTag元素的父元素中,然后才能删除{{1}元素。在XML :: LibXML中,这可以通过以下方式完成:(已测试)

testTag

注意:

  • 处理具有任意数量的文本和元素子元素的for my $node ($doc->findnodes('/Errors/Error//testTag')) { my $parent = $node->parentNode(); for my $child_node ( XML::LibXML::Text->new("HELLO"), $node->childNodes(), XML::LibXML::Text->new("HELLO"), ) { $parent->insertBefore($child_node, $node); } $node->unbindNode(); } 元素。
  • 处理不testTag元素的子元素的testTag个元素。甚至处理嵌套的Error元素。 (如果您只想处理testTag元素的直接子女,请使用/Errors/Error/testTag代替/Errors/Error//testTag。)

答案 3 :(得分:-1)

这应该有效

$error='<Errors>
<Error>
    this node works fine
</Error>
<Error>
    some text <testTag>with a node</testTag> in between
</Error>
</Errors>';

$error=~ s/<testTag>/HELLO/gs;
$error=~ s/<\/testTag>/HELLO/gs;