ある日 XML::Simple がいきなりこけて泣かないために

ちゃんと PREFERRED_PARSER を指定すること。

$XML::Simple::PREFERRED_PARSER = 'XML::Parser';

なにかこう、2005年ぐらいにみた感じの話題で恐縮なのですが。

XML::SAX をインストールしたら、いきなり XML::Simple を使ってたコードが

Cannot decode string with wide characters

って言ってコケ始めた。しかし、アプリケーションを再起動してみたらなぜか再現しない。

結論としては、XML::SAX::PurePerl が使われる状態で、XMLin() に utf8 flagged な文字列を渡すと死ぬ。

use XML::Simple;
use utf8;
$XML::Simple::PREFERRED_PARSER = 'XML::SAX::PurePerl';
$xml = "<xml>あああ</xml>";
XMLin($xml);

再起動したら再現しなかったのは、XML::SAX を入れた後に XML::LibXML もインストールしていて、XML::LibXML が入ると XML::LibXML::SAX が使われる (PurePerl は使われなくなる) から。

ということで、XML::Simple を使う場合は明示的に PREFERRED_PARSER を指定してやって、思わぬ挙動が起きないようにしましょう。

[追記]

そもそも、XML::Simple には bytes を引数として渡すべきなんじゃないか

http://d.hatena.ne.jp/tokuhirom/20090731/1249001060

これはまったく同意です。SAX::PurePerl 以外では flagged なのを渡しても問題なかったので、気がつきませんでした。

自動的に LibXML が使われるのは、

XML::SAX の仕様的には

明示的な指定が無い限り、最後に環境にインストールされたSAXパーサーを利用する

http://iandeth.dyndns.org/mt/ian/archives/000589.html

ということのようで、今回の自分の環境のように

の順序でインストールすると、実際に使われるパーサは

  • XML::Simple を入れた時点 => XML::Parser
  • XML::SAX を入れた時点 => XML::SAX::PurePerl
  • XML::LibXML を入れた時点 => XML::LibXML::SAX

と変わっていきます。

こけたアプリケーション (mod_perl 上の web アプリ) は、XML::SAX インストール前までは XML::Parser を使っていて、それが動いたまま XML::SAX を入れたら PurePerl が使われて死んだ。それに気がついて再現しようとコードを書いたときには既に XML::LibXML が入っていて、PurePerl が使われないため再現しない! という時系列。

また、一番最後にインストールされた SAX パーサを使う、という仕様は結構いやらしくて、LibXML を入れた後に force install XML::SAX をすると、XML::SAX::PurePerl がデフォルトに復帰してしまいます。

気がつかないうちに PurePerl が使われて妙に遅くなった、みたいなことを回避するためにも、PREFERRED_PARSER を明示するほうが安全かと。

[追記2]
XML::SAX::PurePerl が flagged で死ぬのは RT にも登録されてました。#19367: parse_string() crashes when handed UTF-8 combining characters