続・続・JavaでXMLをフォーマットする

またも、別の方法があるというコメントいただきました。(id:toolkit:20060719)

transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, ”yes”);
transformer.setOutputProperty(javax.xml.transform.OutputKeys.METHOD, ”xml”);
transformer.setOutputPropert(org.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, ”2”);

なので、記事を書いた責任上(?)検証してみます。

実は、最初にこれと似たような事を試しました。

でも、うまく行かなかったんですねぇ。

OutputKeys.INDENT , "yes"

のみ指定していて、それでうまく行かなくてスタイルシート当てようかと思ったんです。

いただいたコメントでの、一番最後の指定がキモですねー。

org.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, ”2”

うーん、これは気がつかなかった。

Xalanが必要だった

この、org.apache.xml.serializer.OutputPropertiesFactoryですが、てっきりXercesに入っているのかと思いきや、Xalanに入っているものでした。

しかも、メインのxalan.jarじゃなくて、最近切り離されたらしい(?)serializer.jarに入っています。

使用したコードたち

in.xml

<?xml version="1.0" encoding="UTF-8"?><test><hoge>ほげ</hoge><fuga>ふが</fuga></test>

動作検証用のコード

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

public class OutputPropertiesFactoryTest01 {

	public static void main(String[] args) throws Exception {
		// フォーマットしたいXML
		File inXml = new File("in.xml");
		// フォーマットしたXML
		File outXml = new File("out.xml");

		// フォーマットしたいXMLのDOMオブジェクトを作る
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
		InputStream in = new FileInputStream(inXml);
		Document doc = docBuilder.parse(in);
		in.close();

		// Transformerを用意する。
		TransformerFactory transFactory = TransformerFactory.newInstance();
		Transformer transformer = transFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		transformer.setOutputProperty(OutputKeys.METHOD, "xml");
		transformer.setOutputProperty(
            org.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT,"2");
		  
		 //書き出し
		DOMSource source = new DOMSource(doc);
		OutputStream out = new FileOutputStream(outXml);
		Result result = new StreamResult(out);
		transformer.transform(source, result);
		out.close();
	}
}

実行してみました。

serializer.jarだけをクラスパスに通して実行します。

out.xml

<?xml version="1.0" encoding="UTF-8"?>
<test>
<hoge>ほげ</hoge>
<fuga>ふが</fuga>
</test>

ありゃ?改行はされたけど、インデントされてません。


しょうがないので、xalan.jarもクラスパスに含めて実行します。

out.xml

<?xml version="1.0" encoding="UTF-8"?>
<test>
  <hoge>ほげ</hoge>
  <fuga>ふが</fuga>
</test>

うまく行きました。


っつーことは、結局この方法をとる場合は、XSLTプロセッサ本体もJDK以外のものを使わないと駄目みたい。
これもLSSerializerの時の一緒で、JDKに入っているものが古い(?)のでしょうか。

OutputPropertiesFactoryなるもの

Eclipseさんに聞いてみると、JDKにはcom.sun.org.apache.xml.internal.serializer.OutputPropertiesFactoryがある事が分かりました。

試しに、こっちの方を使って実験してみました。

JDK
改行はされるけどインデントはつかない
Xalan
成功

ちょっと混乱気味(笑)

結局、Xalan本体が必要で、OutputKeys.INDENTに"yes"を指定しただけでは駄目で、OutputPropertiesFactory.S_KEY_INDENT_AMOUNTに数値を指定しなきゃ、という結論でしょうか??


これもJDKの実装がXalanの新しいものになれば、そのうち解決されるって話ですかね??

ちょっと疑問

JDKだけでXSLする場合、スタイルシートには

xalan:indent-amount="2"

という記述をすれば良いのですが、これって、内部的に同じ事してるんじゃないかな?と勘ぐってみたり。
でも、動かないのはなぜでしょう・・・??深追いはしませんけど(笑)


しかし、インデントをつける方法はいろいろあって、しかもノーマルなJDKにはまともに実装されていないなんて想像してませんでした。ここまでこの話題で引っ張るとも思わなかったし(笑)


ちょっとごちゃごちゃしちゃったんで、後で結果をまとめておきます。