【问题标题】:How to change namespace如何更改命名空间
【发布时间】:2011-07-14 14:32:07
【问题描述】:

如何使用 perl 将命名空间和 uri 更改为新的?文件很大(20 MB),包含一行,结构复杂。 示例:

<?xml version="1.0" encoding="utf-8"?>
<m:sr xmlns:m="http://www.example.com/mmm" xml:lang="et">
<m:A m:AS="EX" m:KF="sss1">
<m:m m:u="uus" m:O="ggg">ggg</m:m>
</m:A>

</m:sr>

收件人:

<?xml version="1.0" encoding="utf-8"?>
<a:sr xmlns:a="http://www.example.com/aaa" xml:lang="et">
<a:A a:AS="EX" a:KF="sss1">
<a:m a:u="uus" a:O="ggg">ggg</a:m>
</a:A>

</a:sr>

【问题讨论】:

    标签: xml perl namespaces


    【解决方案1】:

    你可以用 XML::Twig 做到这一点。

    下面的代码非常简洁,因为它对输入的假设很少,特别是它依赖于输入中命名空间的 URI,而不是前缀。每次解析完任何元素时都会刷新树枝,因此它在内存中保留的很少。

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use XML::Twig;
    
    # parameters, may be changed
    my $SR     = 'sr';                          # local name of the root of the fragment
    my $OUT    = 'a';                           # prefix in the output
    my $IN_NS  = 'http://www.example.com/mmm';  # namespace URIs
    my $OUT_NS = 'http://www.example.com/aaa';
    
    my $t= XML::Twig->new( 
              map_xmlns => { $IN_NS => $OUT, $OUT_NS => $OUT, },
              start_tag_handlers => { "$OUT:$SR"  => \&change_ns_decl, },
              twig_handlers => { _all_ => sub { $_->flush; }, },
              keep_spaces => 1,
                           )
                      ->parse( \*DATA); # replace by parsefile( "my.xml");
    
    exit;
    
    sub change_ns_decl
      { my( $t, $sr)= @_;
        $sr->set_att( "xmlns:$OUT" => $OUT_NS);
      }
    
    __DATA__
    <?xml version="1.0" encoding="utf-8"?>
    <m:sr  xml:lang="et" xmlns:m="http://www.example.com/mmm">
    <m:A m:AS="EX" m:KF="sss1">
    <m:m m:u="uus" m:O="ggg">ggg</m:m>
    </m:A>
    </m:sr>
    

    【讨论】:

      【解决方案2】:

      您可以通过此 XSLT 运行 XML 以转换为所需的输出:

      <?xml version="1.0"?>
      <xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:m="http://www.example.com/mmm"
      xmlns:a="http://www.example.com/aaa"
      exclude-result-prefixes="m">
      
      <xsl:output indent="yes"/>
      
          <!--identity template to copy content forward by default-->
          <xsl:template match="@* | node()">
              <xsl:copy>
                  <xsl:apply-templates select="@* | node()" />
              </xsl:copy>
          </xsl:template>
      
          <!--Change any elements bound to the "m" namespace, to be in the "a" namespace-->
          <xsl:template match="m:*">
              <xsl:element name="a:{local-name()}" >
                  <xsl:apply-templates select="@*|node()"/>
              </xsl:element>
          </xsl:template>
      
          <!--Change any attributes bound to the "m" namespace, to be in the "a" namespace-->
          <xsl:template match="@m:*">
              <xsl:attribute name="a:{local-name()}">
                  <xsl:value-of select="."/>
              </xsl:attribute>
          </xsl:template>
      
      </xsl:stylesheet>
      

      【讨论】:

        【解决方案3】:

        进行全局替换很容易。假设您已将文件加载到一个长字符串中,以下将执行此替换:

        my $current_namespace = "m";
        my $new_namespace = "a";
        
        $xml =~ s/\<$current_namespace:/\<$new_namespace:/g;
        

        正如您所说,文件相对较大,因此您可能必须实现流式传输方法。例如,您可以逐行读取文件。读取文件时,使用上述方法转换每一行,然后写入临时文件。完成后,删除磁盘上的文件并重命名临时文件以替换它。

        【讨论】:

        • 因为所有的 xml 都在一行上。如果我逐行阅读,那么这种方法会很快用完所有内存。
        • 我的建议是在处理下一个输入行之前将每一行写入输出文件。这不会很快耗尽内存。
        猜你喜欢
        • 2010-12-04
        • 1970-01-01
        • 1970-01-01
        • 2010-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-25
        • 2015-09-21
        相关资源
        最近更新 更多