web-dev-qa-db-ja.com

XML :: Simpleが推奨されないのはなぜですか?

XML::Simple のドキュメントから:

新しいコードでこのモジュールを使用することはお勧めしません。より簡単で一貫したインターフェースを提供する他のモジュールが利用可能です。特に、XML :: LibXMLを強くお勧めします。

このモジュールの主な問題は、多数のオプションと、これらのオプションが相互に作用するarbitrary意的な方法です。多くの場合、予期しない結果が生じます。

誰かが私のためにこれの主な理由を明確にすることはできますか?

54
Sobrique

本当の問題は、XML::Simpleが主にしようとすることはXMLを取得し、それをPerlデータ構造として表すことです。

間違いなく perldata に気づくでしょう。利用できる2つの主要なデータ構造はhasharrayです。

  • 配列は順序付けられたスカラーです。
  • ハッシュは順序付けられていないキーと値のペアです。

また、XMLも実際には機能しません。次の要素があります。

  • 一意でない名前(ハッシュは「適合しない」ことを意味します)。
  • ....しかし、ファイル内で「順序付けられています」。
  • 属性を持っている可能性があります(ハッシュに挿入できます)
  • コンテンツが含まれている場合があります(ただし、含まれていない場合がありますが、単項タグである可能性があります)
  • 子供がいる可能性があります(任意の深さ)

そして、これらは利用可能なPerlデータ構造に直接マッピングされません-単純化されたレベルでは、ハッシュのネストされたハッシュが適合するかもしれませんが、重複した名前を持つ要素には対処できません。また、属性と子ノードを簡単に区別することもできません。

したがって、XML::Simpleは、XMLコンテンツに基づいて推測を試み、さまざまなオプション設定から「ヒント」を取得します。その後、コンテンツを試してoutput、同じプロセスを逆に適用(試行)します。

その結果、ほとんどのsimpleXML以外の場合、せいぜい扱いにくくなるか、最悪の場合データを失います。

考慮してください:

<xml>
   <parent>
       <child att="some_att">content</child>
   </parent>
   <another_node>
       <another_child some_att="a value" />
       <another_child different_att="different_value">more content</another_child>
   </another_node>
</xml>

これ-XML::Simpleを介して解析すると、次の結果が得られます。

$VAR1 = {
          'parent' => {
                      'child' => {
                                 'att' => 'some_att',
                                 'content' => 'content'
                               }
                    },
          'another_node' => {
                            'another_child' => [
                                               {
                                                 'some_att' => 'a value'
                                               },
                                               {
                                                 'different_att' => 'different_value',
                                                 'content' => 'more content'
                                               }
                                             ]
                          }
        };

注-これでparentの下に-ただの匿名ハッシュがありますが、another_nodeの下には匿名ハッシュの配列があります。

したがって、childのコンテンツにアクセスするには:

my $child = $xml -> {parent} -> {child} -> {content};

「子」ノードがあり、その下に「コンテンツ」ノードがあることに注意してください。これはコンテンツであるためではありません。

しかし、最初のanother_child要素の下のコンテンツにアクセスするには:

 my $another_child = $xml -> {another_node} -> {another_child} -> [0] -> {content};

注意してください-複数の<another_node>要素があるため、XMLは1つの配列ではなく、配列に解析されています。 (その下にcontentと呼ばれる要素があった場合は、まだ別のものになります)。これはForceArrayを使用して変更できますが、配列のハッシュの配列のハッシュの配列のハッシュになります。ただし、子要素の処理は少なくとも一貫しています。編集:注、次の議論-これはXML :: Simpleの欠陥ではなく、悪いデフォルトです。

以下を設定する必要があります。

ForceArray => 1, KeyAttr => [], ForceContent => 1

これを上記のようにXMLに適用すると、代わりに次のようになります。

$VAR1 = {
          'another_node' => [
                            {
                              'another_child' => [
                                                 {
                                                   'some_att' => 'a value'
                                                 },
                                                 {
                                                   'different_att' => 'different_value',
                                                   'content' => 'more content'
                                                 }
                                               ]
                            }
                          ],
          'parent' => [
                      {
                        'child' => [
                                   {
                                     'att' => 'some_att',
                                     'content' => 'content'
                                   }
                                 ]
                      }
                    ]
        };

これにより、マルチノードとは異なる単一ノード要素の処理がなくなるため、一貫性が得られます。

しかし、あなたはまだ:

  • 値を取得するために、5つの参照の深いツリーがあります。

例えば。:

print $xml -> {parent} -> [0] -> {child} -> [0] -> {content};

contentおよびchildハッシュ要素は属性であるかのように扱われ、ハッシュは順序付けられていないため、単に入力を再構築することはできません。したがって、基本的には、それを解析し、Dumperを介して実行する必要があります。

しかし、xpathクエリでは、次のようにしてそのノードに到達します。

findnodes("/xml/parent/child"); 

あなたが XML::Simple で行うXML::Twigで得られないもの(そしてXML::LibXMLと仮定しますが、私はそれをあまりよく知りません):

  • xpathサポート。 xpathは、ノードへのパスを表現するXMLの方法です。したがって、get_xpath('//child')を使用して上記のノードを「見つける」ことができます。 xpathで属性を使用することもできます-get_xpath('//another_child[@different_att]')のように、必要な属性を正確に選択します。 (マッチについても繰り返すことができます)。
  • cutおよびpasteは要素を移動します
  • parsefile_inplaceを使用して、インプレース編集でXMLを変更できます。
  • XMLをフォーマットするpretty_printオプション。
  • twig_handlersおよびpurge-これにより、メモリにすべてをロードすることなく、非常に大きなXMLを処理できます。
  • XML::Simpleとの後方互換性が本当に必要な場合は、simplify
  • 一般に、コードはハッシュと配列への参照のデイジーチェーンをたどろうとするよりもはるかに簡単です。構造の根本的な違いのために一貫して実行することはできません。

また、CPANから簡単にダウンロードでき、多くのオペレーティングシステムにインストール可能なパッケージとして配布されています。 (残念ながら、デフォルトのインストールではありません。しかし)

参照: XML :: Twigクイックリファレンス

比較のために:

my $xml = XMLin( \*DATA, ForceArray => 1, KeyAttr => [], ForceContent => 1 );

print Dumper $xml;
print $xml ->{parent}->[0]->{child}->[0]->{content};

Vs.

my $twig = XML::Twig->parse( \*DATA );
print $twig ->get_xpath( '/xml/parent/child', 0 )->text;
print $twig ->root->first_child('parent')->first_child_text('child');
53
Sobrique

XML :: Simpleは利用可能な最も複雑なXMLパーサーです

XML :: Simpleの主な問題は、結果の構造が正しくナビゲートするのが非常に難しいことです。 _$ele->{ele_name}_は、次のいずれかを返すことができます(同じ仕様に従う要素の場合でも):

_[ { att => 'val', ..., content => 'content' }, ... ]
[ { att => 'val', ..., }, ... ]
[ 'content', ... ]
{ 'id' => { att => 'val', ..., content => 'content' }, ... }
{ 'id' => { att => 'val', ... }, ... }
{ 'id' => { content => 'content' }, ... }
{ att => 'val', ..., content => 'content' }
{ att => 'val', ..., }
'content'
_

これは、実際に得たものを確認するためにあらゆる種類のチェックを実行する必要があることを意味します。しかし、これは非常に複雑であるため、開発者は代わりに非常に悪い推測をするようになります。これにより、あらゆる種類の問題が実稼働環境に滑り込み、コーナーケースが発生するとライブコードが失敗します。

より規則的なツリーを作成するためのオプションが不足しています

次のオプションを使用して、より規則的なツリーを作成できます。

_ForceArray => 1, KeyAttr => [], ForceContent => 1
_

ただし、これらのオプションを使用しても、ツリーから情報を抽出するには多くのチェックが必要です。たとえば、ドキュメントから_/root/eles/ele_ノードを取得することは、実行するのが簡単な一般的な操作ですが、XML :: Simpleを使用する場合は以下が必要です。

_# Requires: ForceArray => 1, KeyAttr => [], ForceContent => 1, KeepRoot => 0
# Assumes the format doesn't allow for more than one /root/eles.
# The format wouldn't be supported if it allowed /root to have an attr named eles.
# The format wouldn't be supported if it allowed /root/eles to have an attr named ele.
my @eles;
if ($doc->{eles} && $doc->{eles}[0]{ele}) {
    @eles = @{ $doc->{eles}[0]{ele} };
}
_

別のパーサーでは、次のものを使用します。

_my @eles = $doc->findnodes('/root/eles/ele');
_

XML :: Simpleはnumerous制限を課し、共通の機能を欠いています

  • XMLの作成にはまったく役に立ちません。 _ForceArray => 1, ForceContent => 1, KeyAttr => [], KeepRoot => 1_を使用しても、制御できない詳細が多すぎます。

  • 名前の異なる子の相対的な順序は保持されません。

  • 名前空間と名前空間プレフィックスのサポートは(XML :: SAXバックエンドを使用して)制限されているか(XML :: Parserバックエンドを使用して)サポートされていません。

  • テキストと要素の両方を持つ要素を子として処理できません(つまり、XHTMLを処理できないことを意味します)。

  • 一部のバックエンド(XML :: Parserなど)は、ASCII(UTF-16le)など)に基づいていないエンコーディングを処理できません。

  • 要素に同じ名前の子要素と属性を含めることはできません。

  • コメント付きのXMLドキュメントを作成することはできません。

前述の主要な問題を無視すると、XML :: Simpleはこれらの制限で引き続き使用できます。しかし、XML :: Simpleがドキュメント形式を処理でき、後で別のパーサーに切り替える必要があるかどうかを確認する手間がかかるのはなぜですか?最初からすべてのドキュメントに対して、より優れたパーサーを使用できます。

他のパーサーはこれらの制限を受けないだけでなく、他の便利な機能も追加で提供します。以下に、XML :: Simpleにはない機能がいくつかあります。

  • 速度。 XML :: Parser以外のバックエンドを使用する場合は特に、XML :: Simpleは非常に遅くなります。私は他のパーサーよりも桁違いに遅い話をしています。

  • XPathセレクターなど。

  • 非常に大きなドキュメントのサポート。

  • プリティ印刷のサポート。

XML :: Simpleは便利ですか?

XML :: Simpleが最も単純な形式は、オプションの要素がない形式のみです。私は無数のXMLフォーマットの経験があり、そのようなフォーマットに出会ったことはありません。

この脆弱性と複雑さだけでも、XML :: Simpleから離れることを保証するのに十分な理由ですが、他にもあります。

代替案

XML :: LibXMLを使用します。非常に高速でフル機能のパーサーです。メモリに収まらないドキュメントを処理する必要がある場合、XML :: LibXML :: Reader(およびそのcopyCurrentNode(1))またはXML :: Twig(_twig_roots_を使用)を使用します。

32
ikegami

私はドキュメントに同意しません

私は異議を唱え、XML::Simpleはまさにそれだと言います。そして、私にとってはいつも使いやすくて楽しいものでした。受け取った入力でテストします。入力が変更されない限り、あなたは大丈夫です。 XML::Simpleの使用について文句を言う同じ人々は、JSON::Syckを使用してMooseをシリアル化することについて文句を言います。ドキュメントは、効率よりも正確さを考慮しているため、間違っています。次のことだけを気にするなら、あなたは大丈夫です:

  • データを捨てない
  • 抽象スキーマではなく、提供された形式への構築

アプリケーションではなく仕様で定義されている抽象パーサーを作成している場合は、別のものを使用します。ある会社で働いたことがありますが、仕様のないXMLスキーマを300種類受け入れなければなりませんでした。 XML::Simpleは簡単に仕事をしました。他のオプションでは、仕事を成し遂げるために実際に誰かを雇う必要がありました。 XMLは、パーサーを1つ記述するだけでよいように、すべてが仕様に準拠した厳密な形式で送信されるものだと考えています。その場合は、XML::Simpleを使用しないでください。 JSONは、JSONの前に、ある言語から別の言語への「これをダンプして歩く」形式でした。人々は実際にXML::Dumperのようなものを使用しました。何が出力されたのか実際には誰も知りませんでした。そのシナリオに対処するXML::Simpleは素晴らしいです!同じことを達成するために、健全な人々はまだ仕様なしでJSONにダンプします。それはまさに世界の仕組みです。

データを読み込みたいのですが、フォーマットについて心配しませんか? XMLの可能性ではなく、Perl構造をトラバースしたいですか? XML::Simpleに移動します。

拡張によって...

同様に、mostアプリケーションJSON::Syckで十分ですこれをダンプして歩いてください。多くの人に送信する場合、私は非常に潅水ノズルではなく、エクスポート先の仕様を作成することを提案します。しかし、あなたは何を知っていますか。いつかは、通常はエクスポートしないデータを望んでいる人と話をしたくない人から電話を受けるでしょう。そして、あなたはJSON::Syckのブードゥーを通してそれをパイプして、彼らにそれを心配させます。 XMLが必要ですか?彼らにさらに500ドルを請求し、あなたがたのオレXML::Dumperを起動します。

取り除く

完全ではないかもしれませんが、XML::Simpleは非常に効率的です。このアリーナで節約された1時間ごとに、より有用なアリーナで潜在的に過ごすことができます。それは現実世界の考慮事項です。

他の答え

XPathにはいくつかの利点があります。ここでのすべての答えは、PerlよりもXPathを好むことに要約されます。それはいいです。標準化されたXMLドメイン固有の言語を使用してXMLにアクセスする場合は、ぜひお試しください。

Perlは、深くネストされたオプション構造にアクセスする簡単なメカニズムを提供していません。

var $xml = [ { foo => 1 } ];  ## Always w/ ForceArray.

var $xml = { foo => 1 };

これら2つのコンテキストでfooの値を取得するのは難しい場合があります。 XML::Simpleはこれを知っているため、前者を強制することができます。しかし、ForceArrayを使用しても、要素が存在しない場合はエラーがスローされます。

var $xml = { bar => [ { foo => 1 } ] };

barがオプションの場合、$xml->{bar}[0]{foo}にアクセスしたままになり、@{$xml->{bar}}[0]はエラーをスローします。とにかく、それは単なるPerlです。これは、XML::Simple imhoと関係がありません。そして、XML::Simpleは仕様に合わせて構築するのには向いていないことを認めました。データを表示すると、XML :: Simpleでアクセスできます。

4
Evan Carroll