在Perl中使用LibXML将XML节点替换为字符串
我目前正在使用一个带有LibXML的perl脚本来处理给定的XML文件。这进展得不错,但如果我有一个同时包含子节点和自由文本的节点,我就会开始挣扎。 输入的一个例子是:在Perl中使用LibXML将XML节点替换为字符串,xml,perl,xml-libxml,Xml,Perl,Xml Libxml,我目前正在使用一个带有LibXML的perl脚本来处理给定的XML文件。这进展得不错,但如果我有一个同时包含子节点和自由文本的节点,我就会开始挣扎。 输入的一个例子是: <Errors> <Error> this node works fine </Error> <Error> some text <testTag>with a node</testTag> in be
<Errors>
<Error>
this node works fine
</Error>
<Error>
some text <testTag>with a node</testTag> in between
</Error>
</Errors>
对于文件的其余部分,这实际上是一个非常好的功能,但在本例中不是
但我能做到
$error->removeChild($testTagNode);
这表明它确实被找到了,但对我没有帮助。理论上,我可以删除节点,保存内容,然后将内容插入到父节点中;问题是它需要在它之前所在的确切位置。我可能唯一能做的就是将整个文件作为字符串读取,让基本搜索替换在将其输入LibXML之前运行,但这可能会产生相当大的开销,并不是一个好的解决方案
我觉得我忽略了一些实质性的事情,因为这看起来是一项相当基本的任务,但我似乎找不到任何东西。也许我只是看错了方向,有一种完全不同的方法可用。非常感谢您的帮助。在这封信中,以下内容似乎起到了作用:
for //testTag/text() {
insert text 'HELLO' prepend . ;
insert text 'HELLO' append . ;
move . replace .. ;
}
将其翻译回XML::LibXML是留给读者的一个练习。首先,我不认为您要做的一定特别有用。但是,我要注意的是,在处理节点时,如果像第二个示例中那样有一个嵌套节点,那么实际上会得到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>
这应该行得通
$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;
$error=
这个节点工作正常
某些文本之间有一个节点
';
$error=~s//HELLO/gs;
$error=~s//HELLO/gs;
删除testTag
元素也会删除它的所有子元素,因此在删除testTag
元素之前,我们必须将每个testTag
元素的子元素移动到testTag
元素的父元素中。在XML::LibXML中,这是按如下方式完成的:(已测试)
注:
- 处理具有任意数量文本和元素子元素的
元素testTag
- 处理不是
元素的直接子元素的Error
元素。偶数处理嵌套的testTag
元素。(如果只想处理testTag
元素的直接子元素,请使用错误
而不是/Errors/Error/testTag
。)/Errors/Error//testTag
testTag
的childnres只会是文本节点是可以接受的。感谢您提供了不同的方法,但我会选择那些不需要比我已经运行的包更多的包的。嗯,那么有效地创建一个新的#text
元素来包装这个子节点?比我的方法更简洁。@Sobrique,不。我没有用文本节点包装孩子们。这甚至没有任何意义,因为文本节点不能包含其他节点。OK。我还得再盯着它看一会儿,才能弄清楚到底发生了什么。@Sobrique,删除testTag
元素也会删除它的所有子元素,所以我们必须先将子元素移出。在数组术语中,我们正在进行拼接(@$parent,$idx_of_node,1,“HELLO”,@$node,“HELLO”)
。代码将testTag
元素的子元素移动到testTag
元素的父元素,将它们定位在testTag
元素的前面。除了子节点之外,还将在那里创建两个请求的文本节点。最后,现在空的testTag
被删除了。LibXML::Text和insertBefore的组合正是我想要的,它现在就像一个魔咒。漂亮的小代码。我不确定假定testTag
的子节点只是文本节点是可以接受的。LibXML解决方案确实有效,但代码中有3点需要更正:$err(两次)必须是$error,在最后一行,$replace必须是$replace\u text。(把这个留给未来的旁观者。)除此之外,它还可以工作。我会接受@ikegami的回答,因为他可以处理嵌套标签。目前,您对testTag节点中只有文本的假设是正确的,但这可能会改变,并且未来安全始终是一件好事。非常感谢您的帮助。是的,转录错误-LibXML不能很好地安装在我的Windows机器上。
for //testTag/text() {
insert text 'HELLO' prepend . ;
insert text 'HELLO' append . ;
move . replace .. ;
}
#!/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>
#!/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>
$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;
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();
}