当标签(节点)包含前缀时,使用 perl XML::Twig 处理程序从 Excel XML 文件中提取数据
Extracting data from Excel XML files using perl XML::Twig handlers when tags (nodes) contain prefix
我使用 XML::Twig handlers/roots 从大型 XML 文件中提取信息,因为将整个文件加载到内存中的成本太高。这些 XML 文件是 Excel .xlsx 文件的内部 sheet 文件。
到目前为止,这种方法一直运行良好。下面是从内部 XML 文件 sheet1.xml.
中提取所有单元格引用的示例
use strict;
use warnings;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
use XML::Twig;
use Data::Dumper;
my $zipName='TestFile.xlsx';
my $zip = Archive::Zip->new();
my $zipread;
$zipread=$zip->read($zipName);
my $tw1=new XML::Twig();
my $fileToAnalyse='xl/worksheets/sheet1.xml';
my $sheetFile = $zip->contents($fileToAnalyse);
my @Results;
my $t= XML::Twig->new(twig_roots => {'worksheet/sheetData/row/c' =>
sub { Get_Sheet_Data_TEST_1(@_,\@Results);}})->parse($sheetFile);
print Dumper \@Results;
sub Get_Sheet_Data_TEST_1{
my($t,$elt,$Results)= @_;
my @attrib_NAMES=$elt->att_names();
for my $attrib_loop (0 .. scalar @attrib_NAMES-1){
if($attrib_NAMES[$attrib_loop] eq 'r'){
push @$Results,$elt->att($attrib_NAMES[$attrib_loop]);
}
}
$t->purge; # frees the memory
}
有时这些文件有我要查找的标签的前缀
所以
'worksheet/sheetData/row/c'
变成
'x:worksheet/x:sheetData/x:row/x:c'
现在我的处理程序永远不会触发,因为它找不到所需的标签。
有什么方法可以修改我的处理程序,而不用硬编码所有可能的前缀可能性,以便可以匹配这些前缀以及没有前缀的“普通”标签?
也许有一种方法可以提前找到任何给定文件使用了哪些前缀,并将这些值设置为一个变量,然后我可以将该变量传递给我的处理程序。
好的,我找到了解决方案。结果 XML::Twig 有一个可选参数
map_xmlns
我可以用它来解决我的问题。所以,我的原始代码
my $t= XML::Twig->new(twig_roots => {'worksheet/sheetData/row/c' =>
sub { Get_Sheet_Data_TEST_1(@_,\@Results);}})->parse($sheetFile);
变成
my $t= XML::Twig->new(
map_xmlns => {
'http://schemas.openxmlformats.org/spreadsheetml/2006/main' => 's'},
twig_roots => {'s:worksheet/s:sheetData/s:row/s:c' =>
sub { Get_Sheet_Data_TEST_1(@_,\@Results);}})->parse($sheetFile);
现在我的处理程序适用于所有前缀(甚至是空前缀!)。
如 XML::Twig 文档中所写:
map_xmlns
向此选项传递一个将 uri 映射到前缀的 hashref。文档中的前缀将替换为地图中的前缀。映射的前缀可以(实际上必须)用于触发处理程序、导航或查询文档。
我使用 XML::Twig handlers/roots 从大型 XML 文件中提取信息,因为将整个文件加载到内存中的成本太高。这些 XML 文件是 Excel .xlsx 文件的内部 sheet 文件。
到目前为止,这种方法一直运行良好。下面是从内部 XML 文件 sheet1.xml.
中提取所有单元格引用的示例use strict;
use warnings;
use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
use XML::Twig;
use Data::Dumper;
my $zipName='TestFile.xlsx';
my $zip = Archive::Zip->new();
my $zipread;
$zipread=$zip->read($zipName);
my $tw1=new XML::Twig();
my $fileToAnalyse='xl/worksheets/sheet1.xml';
my $sheetFile = $zip->contents($fileToAnalyse);
my @Results;
my $t= XML::Twig->new(twig_roots => {'worksheet/sheetData/row/c' =>
sub { Get_Sheet_Data_TEST_1(@_,\@Results);}})->parse($sheetFile);
print Dumper \@Results;
sub Get_Sheet_Data_TEST_1{
my($t,$elt,$Results)= @_;
my @attrib_NAMES=$elt->att_names();
for my $attrib_loop (0 .. scalar @attrib_NAMES-1){
if($attrib_NAMES[$attrib_loop] eq 'r'){
push @$Results,$elt->att($attrib_NAMES[$attrib_loop]);
}
}
$t->purge; # frees the memory
}
有时这些文件有我要查找的标签的前缀
所以
'worksheet/sheetData/row/c'
变成
'x:worksheet/x:sheetData/x:row/x:c'
现在我的处理程序永远不会触发,因为它找不到所需的标签。
有什么方法可以修改我的处理程序,而不用硬编码所有可能的前缀可能性,以便可以匹配这些前缀以及没有前缀的“普通”标签?
也许有一种方法可以提前找到任何给定文件使用了哪些前缀,并将这些值设置为一个变量,然后我可以将该变量传递给我的处理程序。
好的,我找到了解决方案。结果 XML::Twig 有一个可选参数
map_xmlns
我可以用它来解决我的问题。所以,我的原始代码
my $t= XML::Twig->new(twig_roots => {'worksheet/sheetData/row/c' =>
sub { Get_Sheet_Data_TEST_1(@_,\@Results);}})->parse($sheetFile);
变成
my $t= XML::Twig->new(
map_xmlns => {
'http://schemas.openxmlformats.org/spreadsheetml/2006/main' => 's'},
twig_roots => {'s:worksheet/s:sheetData/s:row/s:c' =>
sub { Get_Sheet_Data_TEST_1(@_,\@Results);}})->parse($sheetFile);
现在我的处理程序适用于所有前缀(甚至是空前缀!)。
如 XML::Twig 文档中所写:
map_xmlns
向此选项传递一个将 uri 映射到前缀的 hashref。文档中的前缀将替换为地图中的前缀。映射的前缀可以(实际上必须)用于触发处理程序、导航或查询文档。