簡単な問題ではありません。属性の順序を維持したい場合、おそらくそれを指定せず、残りのフォーマットを指定することで、XML を実際には XML として扱っていません。ほとんどの XML パーサーは、目的を達成するために必要なデータに関する詳細を提供しません。
XML を処理するソフトウェアは、属性の順序や意味のない空白を気にするべきではありません。したがって、属性をXML::Twigやその他の方法で追加するのは簡単です。
しかし、まったく同じ属性の順序を維持したいということは、コードに制約を課して、コードを大幅に変更することになります。XML のドメインを離れて、データを純粋なテキストとして扱っています。これは問題なく、それほど大したことではありません。元のフォーマットにアクセスできる単純なパーサーを作成する必要があるだけかもしれません。入力がおそらく「XML」として指定されていることを除いて、XMLパーサーではなく、コードを壊すような方法で将来変更される可能性があります。
OK、これは邪魔にならないので、XML::Twigは実際に属性の順序を保持できます ;--)、keep_atts_order
小枝を作成するときにオプションを使用します。それは簡単です。
ただし、フォーマットを維持するのは少し難しいです。あなたの場合、あなたが与えたデータの限られたサンプルについては、要素の開始タグを返すメソッドをサブクラス化することで機能させることができます。ただし、一般的に機能させるのははるかに複雑です。
だからここにあなたが使うことができるフレームワークがあります
#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use XML::Twig;
# get the input and the expected result
my( $in, $expected)= do { $/="\n\n"; <DATA>};
chomp $in; chomp $expected;
my $xna= 'false'; # represents the user inpput
my $t= XML::Twig->new( twig_handlers => { Host => sub { $_->set_att( xmlNamespaceAware => $xna); }
},
keep_atts_order => 1, # the bit you were looking for
elt_class => 'XML::Twig::MyElt', # to use the element sub-class
)
->parse( $in);
is( $t->sprint, $expected, 'one test for now');
done_testing();
package XML::Twig::MyElt;
use XML::Twig;
use base 'XML::Twig::Elt';
sub start_tag
{ my( $elt)= @_;
if( $elt->tag ne 'Host')
{ return $elt->SUPER::start_tag }
else
{ return '<' . $elt->tag . ' '
. join( "\n ",
map { qq{$_="} . $elt->att( $_) . qq{"} }
keys %{$elt->atts} # the keys are in the right order
)
. '>';
}
}
package main;
__DATA__
<Host appBase="webapps"
unpackWARs="true"
autoDeploy="true"
deployOnStartup="true"
deployXML="true"
name="localhost"
xmlValidation="false">
**<Alias>HOST.com</Alias>**
</Host>
<Host appBase="webapps"
unpackWARs="true"
autoDeploy="true"
deployOnStartup="true"
deployXML="true"
name="localhost"
xmlValidation="false"
xmlNamespaceAware="false">
**<Alias>HOST.com</Alias>**
</Host>
しかし、実際には、フォーマットをそのまま維持するのは狂気です。または、この種の挑戦が好きなら楽しいです;--)