我正在尝试使用XML将子元素添加到XML中的节点:Twig。 我的XML是
<Install>
<version >
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
<version >
<number>7.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
</Install>
我想要的输出是: -
<Install>
<version >
<number>6.0</number>
<build>1037124</build>
<path>path/path>
<kind>kind</kind>
<patch>patch</patch>
<patchv>patchv</patchv>
</version>
<version >
<number>7.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
</Install>
我想将两个新的子节点添加到父版本,其中编号为6.0
我试过这段代码
my $version = "6.0";
my $file_loc = "data.xml";
my $twig = XML::Twig->new( pretty_print => 'indented_a' )->parsefile( $file_loc );
for my $number ( $twig->findnodes('/Install/version/number') ) {
$number->parent->last_child->insert(
patch => { version =>'patch' },
patchversion => { version =>'patchv' },
) if $number->trimmed_text eq $version;
}
但我得到的输出不是我想要的。 输出是: -
<Install>
<version>
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>
<patch version="patch">
<patchversion version="patchv">kind</patchversion>
</patch>
</kind>
</version>
<version >
<number>7.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
</Install>
我用其他方式尝试了这个也使用粘贴如下: -
my $version = "6.0";
my $file_loc = "data.xml";
my $myXML = <<"XML";
<patch>
<build></build>
<version></version>
</patch>
XML
my $t = XML::Twig->new(
twig_handlers => {
Install => sub { add_version( $myXML, @_ ); }, }
)->parsefile( $file_loc ) or die $!;
$t->set_pretty_print('indented_c');
open my $xml_fh, '>', "$file_loc" or die $!;
$t->print($xml_fh);
sub add_version {
my ( $xml, $t, $install ) = @_;
my $new_elt = XML::Twig::Elt->parse($xml);
for my $number ( $t->findnodes('/Install/version/number') ) {
if($number->trimmed_text eq $version )
{
$new_elt->paste( $number->parent->last_child->paste => $install );
}
}
}
这里我收到了以下错误
cannot paste an element that belongs to a tree at
我可以使用insert来实现这个,或者我必须使用paste? 在两种情况下如何做到?
答案 0 :(得分:2)
您第一次尝试时走在正确的轨道上,但您需要使用insert_new_element
。 insert
替换元素的内容,并使用您选择的语法创建属性。
你可以把它全部放在一个非常简单的twig_handler
中。抓取所有<version>
元素,检查其<number>
的文本节点,然后在修补程序和 patchv中添加一个内置文本节点的新元素值。
last_child => ''
部分将新元素放在父元素的末尾。它是the $optional_position
that is documented with paste
。
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig->new(
pretty_print => 'indented_a',
twig_handlers => {
'version' => sub {
if ( $_->first_child('number')->text() eq '6.0' ) {
$_->insert_new_elt( last_child => 'patch' )->set_text('patch');
$_->insert_new_elt( last_child => 'patchv' )->set_text('patchv');
}
}
}
)->parse( \*DATA );
$twig->print;
<强>输出:强>
<Install>
<version>
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
<patch>patch</patch>
<patchv>patchv</patchv>
</version>
<version>
<number>7.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
</Install>
您的第二次尝试至少有两个原因无效:
# this elem get's pasted (1)
# | on this positon (2)
# | | in that elem (3)
$new_elt->paste( $number->parent->last_child->paste => $install );
在(2)中它应该有一个位置,这是一个字符串。这类似于first_child
,last_child
等等。 They are listed in the docs of paste
因此它会计算$number->parent->last_child->paste
的返回值并将其用作位置字符串。但是没有这样的返回值,所以这不起作用。
但它没有那么远。错误消息无法粘贴属于树的元素来自(2)中的paste
,因为您在paste()
上调用$number->parent->last_child
并且已经在树上了。这是不允许的。如果删除代码编译的那部分。
if ( $number->trimmed_text eq $version ) {
$new_elt->paste( $install );
}
输出是(剪断):
<Install>
<patch>
<build></build>
<version></version>
</patch>
<version>
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
这仍然不是我们想要的,因为<patch>
最终在<Install>
内,而不在<version>
内。但是我们已经从循环中获得了$number
。那是<number>
节点内的<version>
节点,所以我们可以得到它的父节点并在我们的(3)中使用它。
$new_elt->paste( $number->parent );
现在我们明白了:
<Install>
<version>
<patch>
<build></build>
<version></version>
</patch>
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
</version>
那更好。它是在正确的地方。如果您添加位置last_child
,它最终会结束。
$new_elt->paste( last_child => $number->parent );
现在就在这里:
<Install>
<version>
<number>6.0</number>
<build>1037124</build>
<path>path</path>
<kind>kind</kind>
<patch>
<build></build>
<version></version>
</patch>
</version>
现在它只是我们正在粘贴的错误内容,因为我们想要两个元素<patch>
和<patchv>
,两者都带有文本。那是因为你有$myXML
中的那些。
你不能有两个顶级元素。这是无效的XML语法。
my $myXML = <<"XML";
<patch>patch</patch>
<patchv>patchv</patchv>
XML
在第2行,第0列,第21行的文档元素之后的垃圾将会失败... 。所以你必须有两个变量并调用paste
两次,或者包装它们并获取新根元素的子节点。
但当然这很复杂。上面的第一种方法更清洁。