我有一个package.xml
文件,其结构如下: -
<package name="com/avinash/foo1">
<sourcefile name="bar1.java">
<line no="1" mi="3"/>
<line no="3" mi="2"/>
</sourcefile>
<sourcefile name="bar2.java">
<line no="1" mi="5"/>
<line no="6" mi="8"/>
<line no="7" mi="3"/>
</sourcefile>
</package>
<package name="com/avinash/foo2">
.
.
.
.
</package>
使用Perl
,我必须删除line
的所有no="1"
个节点。我发现splice
可用于删除xml中的节点。我写了以下代码来做到这一点: -
my $xmlFilePath = 'package.xml';
use XML::Simple;
my $xs = XML::Simple->new (ForceArray => 1);
my $ref = $xs->XMLin($xmlFilePath);
foreach(@{$ref->{'package'}}) {
my %packageTag = %{$_};
foreach(@{$packageTag{'sourcefile'}}){
my %sourcefileTag = %{$_};
my $lineCtr = 0;
foreach(@{$sourcefileTag{'line'}}){
my %lineTag = %{$_};
if($lineTag{'no'}==1){
#splice : something like "splice @{$ref{$packageTag{$sourcefileTag->{'line'}}}}, $lineCtr, 1;"
}
$lineCtr = $lineCtr + 1;
}
}
}
我是新手,对Perl中的@,%,$转换非常困惑。我不知道如何编写splice函数的数组部分(第一个参数)。任何人都可以告诉我什么是剪切函数,它将删除行节点?
提前致谢。
答案 0 :(得分:1)
我会建议不要使用XML::Simple
,但如果你继续进行下面的建议,我认为还有其他问题需要讨论。
你不能在splice
内for/foreach
,你正在修改你正在循环的阵列,这会导致各种各样的问题。
要过滤列表,您应该从其外部使用grep
。
此外,您的示例文件对我不起作用。我需要向XML文件添加更多标签(XML声明节点和包含根节点)或XML::Simple
抱怨。
最后,name
属性是特殊的(另一个不使用XML::Simple
的原因)。您需要提供KeyAttr
设置才能停止折叠数据。
尝试以下内容。
use XML::Simple;
my $xs = XML::Simple->new (ForceArray => 1, KeyAttr => []);
my $packages = $xs->XMLin('package.xml');
for my $package (@{$packages->{'package'}}) {
for my $sourcefile ( @{$package->{'sourcefile'}} ) {
my $lines = $sourcefile->{'line'};
my @filtered = grep { $_->{'no'} != 1 } @{$lines};
$sourcefile->{'line'} = \@filtered;
}
}
答案 1 :(得分:1)
作为XML :: Simple的替代方案,这里使用XML::Twig的解决方案,其优点是不将整个文档加载到内存中(如果输入文件很大,则很有用),同时保持相当简单。
use XML::Twig;
my $twig = XML::Twig->new(
twig_roots => {
'package/sourcefile/line' => \&handle_line,
},
twig_print_outside_roots => 1,
);
sub handle_line {
my ($twig, $line) = @_;
$line->print unless $line->att('no') == 1;
}
$twig->parsefile('package.xml');
是的,这很容易。 twig_print_outside_roots
表示line
内的sourcefile
内package
元素内的任何内容都应该打印到输出而不进行任何处理,而line
handle_line
应将元素传递给handle_line
子进行处理。 no
只是检查元素的twig_print_outside_roots
属性是否为1,并且仅在元素不存在时才打印该元素。
从package.xml读取并打印到标准输出,您可以将其重定向到新文件。或者您可以修改它以直接打印到文件,方法是自己打开文件,然后将文件句柄传递给print
和def spider_opened(self, spider):
#open a static/dynamic file to read and write to
file = open('%s_items.json' % spider.name, 'w+b')
self.files[spider] = file
file.write('''{
"product": [''')
self.exporter = JsonLinesItemExporter(file)
self.exporter.start_exporting()
def spider_closed(self, spider):
self.exporter.finish_exporting()
file = self.files.pop(spider)
file.write("]}")
file.close()
方法。
答案 2 :(得分:0)
使用XML :: Twig删除节点:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new ( 'pretty_print' => 'indented',
'twig_handlers' => {
'line[@no="1"]' => sub { $_ -> delete } } );
$twig -> parsefile ( 'your_file');
$twig -> print;
您可以parsefile_inplace
使用XML::Twig
来执行此操作:
my $twig = XML::Twig -> new ( 'pretty_print' => 'indented',
'twig_handlers' => { 'line[@no="1"]' => sub { $_ -> delete } } );
$twig -> parsefile_inplace ( 'your_file');
或者您可以简单地操作已解析的XML:
my $twig = XML::Twig->new( 'pretty_print' => 'indented' );
$twig->parsefile ('your_file');
foreach my $line ( $twig->get_xpath('//line') ) {
if ( $line->att("no") eq "1" ) {
$line->delete;
}
}
$twig->print;