我目前正在使用带有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之前运行它,但这可能会产生相当大的开销并且不是很好溶液
我觉得我忽略了一些重要的东西,因为这看起来像是一项非常基本的任务,但我似乎无法找到任何东西。也许我只是在寻找错误的方向,并且有一种完全不同的方法。任何帮助表示赞赏。
答案 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;