XML を整形する各種実装についてのサーベイ及び,車輪の再発明への導入。
さてトートツですが,XML を整形することを考えてみます。「機械が処理するにゃあそんなに問題ないけど,人間にとっての可読性を上げることによる利は考えられるよねぇ。」というわけで,このサイトにおいてある XHTML であるとか XML/RDF であるとかをキレーに出力したいという欲があってもよいものでしょう。
というわけで,ツール探しです。ざっと見たところ,いくつかありそうです。
まずはよく見るやりかた。libxml2 についてくる xmllint というツールを利用します。これは元々は XML の検証を行うツールなのですが,ついでに整形も行うことができるというわけ。
整形を行うには,--format
オプションをつければよく,別途 XMLLINT_INDENT 環境変数を与えることによりインデント文字を指定することもできます(デフォルトは二文字のスペース)。
% cat in.xml | env XMLLINT_INDENT=' ' xmllint --format - > out.xml
また,xmlpretty というツールも利用できそうです。これは,XML::Handler::YAWriter という CPAN モジュールの整形機能を利用するためのラッパースクリプトのようですね。
% cat in.xml | xmlpretty [options...] > out.xml
設定はいろいろあるので詳しくは POD を。
また,C 言語の整形ツールである indent に慣れているのであれば,XML Indent があります。indent ほど大量のオプションはありませんが,普通に -i 4
(インデント幅4)とかが利きますね。
% cat in.xml | xmlindent -i 4 > out.xml
さて,しかしながらこれら手法だとすべての要素を一律に整形することしかできないのですね。たとえば,xml:space の値によって空白を壊すか否かを制御することは(たぶん)できないし,「HTML の body 要素は内容がデカくなることが多いからインデントしないでくれ」とかそういう要望にも応えられないわけです。
そこで,そういった細かい制御ができるようなプログラムも開発されてるみたいで,たとえば xmlformat があります。これは,どの XML 要素に対してインデントをどれだけするかとか,改行をいくつ入れるとか,インライン要素のように改行を入れないとか,内側の空白を保つとか,とても細かく指定することができます。
% cat in.xml | xmlformat --config-file=my.conf > out.xml
この my.conf にひたすら指定を入れればいいというわけ。指定の入れ方は本家参照。
ただ,それでも,まだやや不満があるんですね。たとえば HTML の ins/del 要素とかは,ブロック要素としてもインライン要素としても振舞う怪しげな要素で,整形結果もそれが存在する構造上の位置に応じて変えてくれると嬉しいなぁとか思ったりするのですよねぇ。
こういった利用には XPath とマッチングで宣言的に設定が書ける実装があると幸せになれるかもしれません。というわけで,XSL 変換 を用いた解決策を探してみる価値はありそうです。以下のようなメリットが考えられそうですので。
まず前知識。XSLT には出力を整形する仕様がある(indent="yes"
を用いる)のですが,これは変換で新しく生成した要素ノードの間に空白を埋めるためのもので必ずしも出力が綺麗にならなかったり,また,細かい制御ができない等の問題があったりします。実装の独自拡張を探してみたりすると,Saxon という実装では,インデント幅の調整ができるようです(saxon:indent-spaces 属性を用いる)。しかしながらこれらは,前述の要素ごとの整形ができない等の問題があるため,やはりこれだけでは物足りなさそう。
そこで,スタイルシートにより要素ごとに差異を持たせ整形をする試みがやっぱりあります。XSLTで効率的にインデントする方法では,空白を取り払ったノード群に改行やスペースを自前で挿入することにより再整形を実現しています。
しかしながら,いくらか改善の余地がありそうです。
<br />
型に落としたい場合と <script></script>
型に落としたい場合があるというわけで,再発明してみる価値を見出したのでした。XML を XSLT で整形するへつづく。