Perl:使用XML::Twig插入XML::Twig节点

Perl:使用XML::Twig插入XML::Twig节点,perl,xml-twig,Perl,Xml Twig,我正在比较两个XML文件。如果在其中一个文件中发现缺少节点,我希望将其插入到另一个文件中。以下是我一直在尝试的方式: my $out_file = 'fbCI_report.xml'; open my $fh_out, '>>', $out_file or die "Can't open $out_file for writing: $!"; my $currentReport = XML::Twig->new( pretty_print => 'indented' )

我正在比较两个XML文件。如果在其中一个文件中发现缺少节点,我希望将其插入到另一个文件中。以下是我一直在尝试的方式:

my $out_file = 'fbCI_report.xml';
open my $fh_out, '>>', $out_file or die "Can't open $out_file for writing: $!";

my $currentReport = XML::Twig->new( pretty_print => 'indented' );
$currentReport->parsefile($path_to_currentReport);
print "Loaded current report.\n";

my $newReport = XML::Twig->new( pretty_print => 'indented' );
$newReport->parsefile($path_to_newReport);
print "Loaded new report.\n";

my $currentRoot   = $currentReport->root;             # get the root
my $currentBuilds = $currentRoot->first_child();      # get the builds node
my $currentXCR    = $currentBuilds->first_child();    # get the xcr node

my $newRoot   = $newReport->root;                     # get the root
my $newBuilds = $newRoot->first_child();              # get the builds node
my $newXCR    = $newBuilds->first_child();            # get the xcr node

my @currentXCRarray = $currentBuilds->children('xcr');
my @newXCRarray     = $newBuilds->children('xcr');
my $numberOfxcr     = $newBuilds->children_count();

foreach my $currentXCRmod ( @currentXCRarray ) {

    my $currentID = $currentXCRmod->att("id");

    foreach my $newXCRmod (@newXCRarray) {

        my $newID = $newXCRmod->att("id");

        if ( $newID == $currentID ) {
            last;
        }
        elsif ( $count == $numberOfxcr && $newID != $currentID ) {
            my $insert = $currentBuilds->insert_new_elt($newXCRmod);
            print "XCR does not exist in current report, adding it..\n";
        }

        $count++;
    }
}

print $fh_out $currentReport->sprint();
close $fh_out;
但是,这不会插入包含相应子节点的节点,但我猜是对节点的引用:
。有没有办法正确插入节点?我还没有在CPAN网站上找到任何东西

示例数据,current.xml:

<project>
  <builds>
    <xcr id="13367" buildable="false">
        <artifact name="rb"/>
        <artifact name="syca"/>
    </xcr>
    <xcr id="13826" buildable="false">
        <artifact name="dcs"/>
    </xcr>
  <\builds>
<\project>

new.xml:

<project>
<builds>
    <xcr id="13367" buildable="false">
        <artifact name="rb"/>
        <artifact name="syca"/>
    </xcr>
    <xcr id="13826" buildable="false">
        <artifact name="dcs"/>
    </xcr>
    <xcr id="10867" buildable="true">
        <artifact name="smth"/>
        <artifact name="top"/>
        <artifact name="tree"/>
    </xcr>
<\builds>
<\project>

你说得对-这是
XML::Twig::Elt
的字符串化文本

问题是-
insert\u new\u elt
创建新元素。因此,您所做的就是有效地“打印”元素id(
XML::Twig::Elt=HASH(0x326efe0)
),并创建一个名为该元素的新节点

但你不想这么做——你想复制一个现有的

所以我建议你要做的是:

my $copied_elt = $currentXCRmod -> copy;
$copied_elt -> paste ( last_child => $currentBuilds );
这将转移元素(到“最后一个子”位置)

虽然我建议您的循环也可以改进,但我建议您查看一个twig_处理程序,以检查在解析时文件中存在哪个ID:

my %seen_id; 
sub collect_ids {
   my ( $twig, $element ) = @_;
   $seen_id { $element->att('id') } ++; 
} 
然后在解析时调用:

my $currentReport = XML::Twig->new(twig_handlers => { 'xcr' => \&collect_ids}, 
                                   pretty_print=>'indented');
$currentReport->parsefile($path_to_currentReport);
这将让你很容易地比较/复制哪些存在或不存在

或者(根据到目前为止的XML示例):


您可能应该移动节点(我不记得当您尝试插入已经是树的一部分的元素时会发生什么)。因此,编写
$newXCRmo->move(first_child($currentBuilds))
,看看这是否能改善情况


我没有太多的时间来查看您的代码,因此它可能还有其他问题。

您的比较循环“由内而外”

此外,测试
$count==$numberOfxcr
将永远不会成功,因为每个我的$newXCRmod(@newXCRarray)的循环
将在该测试为真之前终止

下面是代码的改进版本,它使用XPath表达式以及
List::Util
中的
any
,使循环更加简洁

使用严格;
使用“全部”警告;
使用XML::Twig;
使用列表::Util'any';
my($path_to_curr_report,$path_to_new_report)=qw/current.xml new.xml/;
我的$out_文件='fbCI_report.xml';
my$curr\u report=XML::Twig->new->parsefile($path\u to\u curr\u report);
我的$new_report=XML::Twig->new->parsefile($path_to_new_report);
my($curr_builds)=$curr_report->findnodes('/project/builds');
对于我的$new\u xcr\u mod($new\u report->findnodes('/project/builds/xcr')){
我的$new_id=$new_xcr_mod->att('id');
接下来如果有任何{$new\u id eq$\->att('id')}$curr\u report->findnodes('/project/builds/xcr');
打印ID为“$new_ID”的qq{XCR”在当前报表中不存在。正在添加。\n};
$new\u xcr\u mod->copy->paste(最后一个子项=>$curr\u构建);
}
{
$curr_report->set_pretty_print('indented');
打开我的$fh,“>”,$out_文件或die“无法打开$out_文件进行写入:$!”;
$curr_报告->打印($fh);
收盘价$fh;
}
输出 当前报告中不存在ID为“10867”的XCR。添加它。


另外:示例输入/输出XML在这里会有所帮助!我还建议不要只使用
first\u child
遍历您的树,而是使用
xpath
显式执行。因此,对于这些XML示例,您是否希望基于
xcr
id进行合并?@Sobrique是的,这正是我在这里想要做的。我想知道为什么我从来没有想过这个词。消息是“无法粘贴属于树的元素”
#!/usr/bin/env perl

use strict;
use warnings 'all';

use Data::Dumper;
use XML::Twig;

my $current = XML::Twig -> new ( ) -> parsefile ('test1.xml');
my $new = XML::Twig -> new (  ) -> parsefile ( 'test2.xml'); 

my $cur_builds = $current -> root -> get_xpath('./builds',0);

foreach my $xcr ( $new -> findnodes('//xcr') ) {
   my $id = $xcr -> att('id'); 
   if ( not $current -> findnodes("//xcr[\@id=\"$id\"]") ) {
      print "$id not in current, copying\n"; 
      my $copy = $xcr -> copy; 
      $copy -> paste ( last_child => $cur_builds ); 
   }
}

$current -> set_pretty_print('indented_a');
$current -> print;