Как изменить пространство имен - PullRequest
3 голосов
/ 14 июля 2011

Как изменить пространство имен и URI на новое с помощью Perl? Файлы большие (20 МБ) и содержат одну строку, а структуры сложные. Пример:

<?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>

Ответы [ 3 ]

4 голосов
/ 15 июля 2011

Вы можете сделать это с 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>
3 голосов
/ 15 июля 2011

Вы можете запустить XML через этот XSLT для преобразования в желаемый результат:

<?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>
1 голос
/ 14 июля 2011

Выполнение глобальной замены будет легко работать.Предполагая, что вы загрузили ваш файл в одну длинную строку, следующая подстановка сделает эту замену:

my $current_namespace = "m";
my $new_namespace = "a";

$xml =~ s/\<$current_namespace:/\<$new_namespace:/g;

Как вы сказали, файлы относительно велики, поэтому вам, возможно, придется реализовать потоковый метод.Например, вы можете прочитать файл построчно.Когда вы читаете файл, преобразуйте каждую строку, используя метод выше, а затем запишите во временный файл.Когда закончите, удалите файл на диске и переименуйте временный файл, чтобы заменить его.

...