使用 LibXML 在 Perl 中将 XML 节点替换为字符串
Replace XML node with String in Perl using 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("HELLO", $testTagNode);用字符串替换节点,然后我可以(如果需要)用简单的搜索替换进一步处理,但我只 运行 进入 "not a blessed reference" 错误。 (我觉得如果它真的那样工作会很脏。)
如果我尝试 运行 像这样直接在父节点上进行简单的搜索替换
$error=~s/\</HELLO/g;
它永远不会触发(无论我是否转义 < 或不转义),因为 LibXML 似乎忽略了我没有特别要求的每个标签;如果我尝试打印出第二个错误,它也会给我
some text with a node in between
对于文件的其余部分,这实际上是一个非常好的功能,但在本例中不是。
不过我可以
$error->removeChild($testTagNode);
这表明它确实被发现了,但对我没有进一步的帮助。理论上我可以删除节点,保存内容,然后将内容插入父节点;问题是它需要在它之前的确切位置。我唯一可能做的就是将整个文件作为字符串读取,让基本搜索替换 运行 在将其输入 LibXML 之前对其进行替换,但这可能会产生相当大的开销这并不是一个很好的解决方案。
我觉得我忽略了一些重要的事情,因为这看起来像是一项非常基本的任务,但我似乎找不到任何东西。也许我只是看错了方向,并且有一种完全不同的方法可用。感谢任何帮助。
在 XML::XSH2 which is just a wrapper around XML::LibXML 中,以下似乎有效:
for //testTag/text() {
insert text 'HELLO' prepend . ;
insert text 'HELLO' append . ;
move . replace .. ;
}
翻译回 XML::LibXML 留作 reader 的练习。
首先 - 我认为您尝试做的事情不一定特别有用。但是,我会注意到 - 当您处理节点时 - 如果您有第二个示例中的嵌套节点,您实际上会得到 3 'nodes',但其中两个指定为 #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>
这应该有效
$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;
删除 testTag
元素也会删除它的所有子元素,因此在删除 testTag
元素。在 XML::LibXML 中,这是按如下方式完成的:(已测试)
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
元素。 (如果您只想处理 Error
元素的直接子元素,请使用 /Errors/Error/testTag
而不是 /Errors/Error//testTag
。)
我目前正在使用带有 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("HELLO", $testTagNode);用字符串替换节点,然后我可以(如果需要)用简单的搜索替换进一步处理,但我只 运行 进入 "not a blessed reference" 错误。 (我觉得如果它真的那样工作会很脏。)
如果我尝试 运行 像这样直接在父节点上进行简单的搜索替换
$error=~s/\</HELLO/g;
它永远不会触发(无论我是否转义 < 或不转义),因为 LibXML 似乎忽略了我没有特别要求的每个标签;如果我尝试打印出第二个错误,它也会给我
some text with a node in between
对于文件的其余部分,这实际上是一个非常好的功能,但在本例中不是。
不过我可以
$error->removeChild($testTagNode);
这表明它确实被发现了,但对我没有进一步的帮助。理论上我可以删除节点,保存内容,然后将内容插入父节点;问题是它需要在它之前的确切位置。我唯一可能做的就是将整个文件作为字符串读取,让基本搜索替换 运行 在将其输入 LibXML 之前对其进行替换,但这可能会产生相当大的开销这并不是一个很好的解决方案。
我觉得我忽略了一些重要的事情,因为这看起来像是一项非常基本的任务,但我似乎找不到任何东西。也许我只是看错了方向,并且有一种完全不同的方法可用。感谢任何帮助。
在 XML::XSH2 which is just a wrapper around XML::LibXML 中,以下似乎有效:
for //testTag/text() {
insert text 'HELLO' prepend . ;
insert text 'HELLO' append . ;
move . replace .. ;
}
翻译回 XML::LibXML 留作 reader 的练习。
首先 - 我认为您尝试做的事情不一定特别有用。但是,我会注意到 - 当您处理节点时 - 如果您有第二个示例中的嵌套节点,您实际上会得到 3 'nodes',但其中两个指定为 #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>
这应该有效
$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;
删除 testTag
元素也会删除它的所有子元素,因此在删除 testTag
元素。在 XML::LibXML 中,这是按如下方式完成的:(已测试)
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
元素。 (如果您只想处理Error
元素的直接子元素,请使用/Errors/Error/testTag
而不是/Errors/Error//testTag
。)