今更Perlと思うかも…ですが、XML解析や処理などにおいては大いに役立ち、過去にも書籍が出ていたりします。そして、世間で使用されているXMLライブラリですが、以下の2つがあります。
- XML::Simple
- XML::LibXML
XML::Simpleは操作がすごいシンプルなのですが、色々と問題があります。一つはnameで紐づけたXMLタグに対し、出力される順序が保証されないことです。なので、順番を整えるためにXML::XSLTを使用するなどして対応します。
$xslt_file = "change.xslt"; #変更予定の形式
my $xslt = XML::XSLT -> new($xslt_file);
$xslt -> transform($xml); #抽出したXML
$output = $xslt -> toString;
ところが、XML::XSLTだと出力結果に対し改行コードが全部消えてしまうので、XSLT上で逐一改行コード制御が必要になり、相当手間がかかります。
対するXML::LibXSLTの場合は、改行コードがしっかりと保持されます。ですが、その場合はXML::LibXMLでないとうまくデータを読み込めません。しかも、LibXMLはかなり使用に癖があり、使いづらさがあります。
なので、一長一短の2つのライブラリを合体させてみます。
実際にやってみた
結論からいえば、libXMLでの入力を、ファイルからではなくて、XML::Simpleで出力されたXMLオブジェクトで取り込むようにします。
また、LibXMLは世間で紹介されている方法だとうまくいかないことが多いので、海外サイトや公式チュートリアルなどを参考に、構築してみたのが以下のプログラムです。
※各種ライブラリはCPANなどからインストールしておいてください。
use XML::Simple;
use XML::LibXSLT;
use XML::LibXML;
$xslt = "test.xslt"; #XSLTファイル
#まずはXML::SimpleでデータをXMLにする
my $x = new XML::Simple;
my $xml = qq(<?xml version="1.0" encoding="UTF-8" ?>\n);
$Val = {
'result' => $result #任意のXMLデータ群
};
$xml .= $x -> XMLout($Val, RootName => 'root', NoAttr=>1); #XMLデータの抽出
my $xml = XML::LibXML -> load_xml(string => $xml); #データとして取り込む(string設定必須)
my $xslt = XML::LibXSLT->new()->parse_stylesheet_file($xslt_file); #XSLTの取り込み
my $result = $xslt->transform($xml); #変換
$output.= $xslt -> output_as_chars($result); #出力
$output =~ s/(version=\"1.0\")/$1 encode=\"utf-8\"/g; #ヘッダ情報にエンコード情報を付与
ポイントとしては、load_xmlのときにキー設定をstring
にしておくこと(こうしないと、オブジェクトを読み込めない)、それから出力時にはoutput_as_chars
としておくことです。あと、ヘッダ情報にエンコードが付与されないようなので、あとでヘッダ情報だけ挿入しておきます。
これで、各種ファイルで取り込んだXMLの値を自在に変形でき、改行コードを保持したXMLファイルが簡単に作れます。
XSLTファイルについて
XSLTファイルでの制御も簡潔になります。データソースをXML::Simpleで作成してあるので、XSLTファイルもデータを紐づけるnameプロパティと階層化制御のループ処理以外のこまごまとした関数は、ほとんど使用しなくて済むようになります(フォームのvalueプロパティの要領で、適切な位置にはめこんでいくだけ)。
XML::Parserを使ってみた結果
XML::Simpleはそのままだと動作が遅いとのことで、XML::Parserを導入してみたのですが、制御タイムは変わりませんでした。XML::Simpleで制御しているのはEXCELからのデータエクスポートとルート情報の紐づけだけなので、干渉してこないのだと思います。