StAXとXSLTを連携させる

Java SE 6で導入されたStAXを扱うため,XSLTも機能拡張されています。具体的にはXSLTトランスフォーマへの入力となるソース,そして出力となるリザルトに,StAX用のクラスが提供されたのです。

そこで,今週はXSLTとStAXを連携させてみましょう。ただし,本記事はStAXの紹介記事であるため,XSLTに関する説明は必要最低限に留めておきます。

まずは単純にStAX用のソースもしくはリザルトを使用するところからはじめてみます。

StAX用のソースはjavax.xml.transform.stax.StAXSrouceクラス,そしてリザルトはjavax.xml.transform.stax.StAXResultクラスとなります。

先週までの紹介で,StAXにはカーソルAPIとイベントイテレータAPIがあることはお分かりだと思います。ソース,そしてリザルトもこの2種類のAPIを扱うことができます。

StAXSourceクラスのコンストラクタには次の2種類があり,引数の型の違いによりカーソルAPIとイベントイテレータAPIを切り替えることができます。

  • StAXSource(XMLStreamReader xmlStreamReader)
  • StAXSrouce(XMLEventReader xmlEventReader)

同様にStAXResultクラスのコンストラクタもオーバロードされています。

  • StAXResult(XMLStreamWriter xmlStreamWriter)
  • StAXResult(XMLEventWriter xmlEventWriter)

ここではカーソルAPIを使うことにしましょう。

サンプルのソース StAXTransformSample1.java

このサンプルでは,XSLTは何も変換せずに(恒等変換),ソースからリザルトにコピーを行ないます。

    public StAXTransformSample1(String xmlfile) {
        XMLInputFactory inFactory = XMLInputFactory.newInstance();
        XMLOutputFactory outFactory = XMLOutputFactory.newInstance();
       
        XMLStreamReader reader = null;
        BufferedInputStream stream = null;
        XMLStreamWriter writer = null;
        
        try {
            // ソースの生成
            stream = new BufferedInputStream(
                         new FileInputStream(xmlfile));
            reader = inFactory.createXMLStreamReader(stream);
            Source source = new StAXSource(reader);
 
            // リザルトの生成
            StringWriter stringWriter = new StringWriter();
            writer = outFactory.createXMLStreamWriter(stringWriter);
            Result result = new StAXResult(writer);
 
            // XSLTトランスフォーマの生成
            TransformerFactory transFactory = 
                TransformerFactory.newInstance();
            Transformer transformer = transFactory.newTransformer();
            
            // 変換
            transformer.transform(source, result);
 
            System.out.println(stringWriter);
        } catch (TransformerConfigurationException ex) {
            ex.printStackTrace();
        } catch (TransformerException ex) {
            ex.printStackTrace();
        } catch (FileNotFoundException ex) {
            System.err.println(xmlfile + " が見つかりません");
        } catch (XMLStreamException ex) {
            System.err.println(xmlfile + " の読み込みに失敗しました");
        } finally {
            // パーサ,ストリームのクローズ
            if (reader != null) {
                try {
                    reader.close();
                } catch (XMLStreamException ex) {}
            }
            if (writer != null) {
                try {
                    writer.close();
                } catch (XMLStreamException ex) {}
            }
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException ex) {}
            }
        }
    }

赤字で示した部分がソースおよびリザルトを生成している部分です。

ソースを生成する前にXMLStreamReaderオブジェクトを生成します。そして,そのXMLStreamReaderオブジェクトをStAXSrouceクラスのコンストラクタの引数に指定します。同様にリザルトもXMLStreamWritterオブジェクトを生成し,StAXResultクラスのコンストラクタに指定します。

青字で示したのがXSLTの処理部分です。XSLTトランスフォーマもファクトリを介して生成します。実際に変換を行なうのが,Transformerクラスのtransformメソッドです。transformメソッドの第1引数の型がSourceインタフェース,第2引数の型がResultインタフェースです。

このサンプルを見ると,StreamSourceクラスやDOMResultクラスを扱うのとほとんど変りません。XSLTの使い方が分かっていれば,StAXに対応させるのは簡単だということが分かります。

では,実行してみましょう。変換を行なうのは,今月何度も登場しているnames.xmlです。

<?xml version="1.0" encoding="utf-8"?>
<names>
  <name>
    <first>Bob</first>
    <last>Dylan<last>
  </name>
  <name>
    <first>Paul</first>
    <last>Simon</last>
  </name>
  <name>
    <first>Pete</first>
    <last>Seeger</last>
  </name>
</names>

実行結果を以下に示します。

C:\stax>java StAXTransformSample1 names.xml
<?xml version="1.0"?><names>
  <name>
    <first>Bob</first>
    <last>Dylan</last>
  </name>
  <name>
    <first>Paul</first>
    <last>Simon</last>
  </name>
  <name>
    <first>Pete</first>
    <last>Seeger</last>
  </name>
</names>

names要素の開始位置は異なりますが,内容はnames.xmlと同一であることが分かります。