使用 XML::Twig 更新包含命名空间前缀的 xml 文件

Update xml file containing namespace prefix with XML::Twig

我需要修改的 xml 文件中有以下信息:

<?xml version="1.0" encoding="utf-8"?>
<x:workbook xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" 
            xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
    <x:workbookPr codeName="ThisWorkbook"/>
    <x:bookViews>
        <x:workbookView firstSheet="0" activeTab="0"/>
    </x:bookViews>
    <x:sheets>
        <x:sheet name="Sheet1" sheetId="2" r:id="rId2"/>
    </x:sheets>
    <x:definedNames/>
    <x:calcPr calcId="125725"/>
</x:workbook>

一般来说,前缀“x:”可以是任何内容,包括空前缀。

我现在需要将子元素“x:sheet”添加到文件中的“x:sheets”元素。 但是,如果文件内容没有前缀,那么我需要将子元素“sheet”添加到文件中的“sheets”元素。

因为前缀,一般来说,可以是任何东西我用下面的perl代码来扫描和修改文件。

use strict;
use warnings;
use XML::Twig;

my $fileName1='/folder1/file1.xml';
my $fh1;
my $fh1_filename='/folder1/file1_NEW.xml';
my $tw1=new XML::Twig(
map_xmlns => {
    'http://schemas.openxmlformats.org/officeDocument/2006/relationships' => 'r',
    'http://schemas.openxmlformats.org/spreadsheetml/2006/main' => 's'      
},
keep_original_prefix => 1,
pretty_print => 'indented',
twig_handlers => {'/s:workbook/s:sheets' => sub{Add_Sheet_1(@_)}}
);
$tw1->parsefile($fileName1);
open($fh1, '>:encoding(UTF-8)', $fh1_filename);
$tw1->flush($fh1);
close $fh1;
}   
#
#
#
sub Add_Sheet_1{
    my ( $twig, $tag) = @_;
    my $var1='x:sheet';
    $tag->insert_new_elt( 'last_child', => $var1=>{name=>'Sheet_N1',sheetId=>'200'});
}

对于前缀为“x:”的情况,此代码生成所需的结果。

但是,一般来说,前缀是未知的。我的处理程序将未知前缀映射到前缀“s:”,因此找到正确的元素没有问题。

但是,我找不到任何方法来添加具有正确前缀的新子元素。

有什么方法可以从文件中获取原始前缀并在添加新子元素时使用它?

如果您知道您感兴趣的特定命名空间的 URI(在下面的变量 $schema_uri 中),您可以查找文档中使用的前缀(如果有)通过遍历所有分配的前缀寻找匹配项:

sub Add_Sheet_1{
    my ($twig, $tag) = @_;
    my $ns = "";
    foreach my $prefix ($twig->parser->current_ns_prefixes) {        
        if ($twig->original_uri($prefix) eq $schema_uri) {
            $ns = "$prefix:";
            last;
        }
    }
    my $name = $ns . "sheet";
    $tag->insert_new_elt('last_child', $name, {name => 'Sheet_N1', sheetId => '200'});
}

这适用于前缀 (x:sheet),并且在我的测试中它是默认命名空间 (sheet)。