この章の内容は次のとおりです。
Oracleでは、Java、C、C++およびPL/SQL用のeXtensible Stylesheet Language Transformation(XSLT)処理を提供しています。この章では、XSLT Processor for Javaに焦点を当てます。XSLTは、W3Cのインターネット標準でバージョン1.0があり、現在バージョン2.0も開発中です。XSLTはXPathも使用します。XPathは、XSLTで使用されるナビゲーショナル言語であり、対応するバージョンがあります。XSLT Processor for Javaは、XSLTとXPathの1.0標準、およびXSLTとXPathの2.0標準の草案を実装します。特定のバージョンの詳細は、READMEを参照してください。
XSLTは関数ベースの言語であり、一般に変換を実行するには入力ドキュメントおよびスタイルシートのDOMを必要としますが、Java実装はストリームベースのパーサーであるSAXを使用して、より少ないリソースで効率的に変換を実行するために、スタイルシート・オブジェクトを作成します。このスタイルシート・オブジェクトを再利用して、スタイルシートを再解析せずに複数のドキュメントを変換できます。
XSLT Processor for Javaには、高性能な機能が追加されています。スレッド・セーフであるため、1つのXSLT Processor for Javaおよびスタイルシート・オブジェクトを使用して、複数のファイルを処理できます。複数のスレッドでドキュメント・インスタンスのクローンを安全に使用することもできます。
XSLT Processor for Javaは、変換するXML文書と、そのXMLの変換に適用するXSLTスタイルシートの2つの入力によって動作します。この2つの各入力には実際に複数の入力が可能です。1つのスタイルシートを使用して複数のXML入力を変換できます。複数のスタイルシートを単一のXML入力にマップできます。
XML Parser for JavaでXSLT Processorを実装するには、XSLProcessorクラスを使用します。
図4-1に、XSLProcessorクラスで使用するプロセス全体を示します。手順は次のとおりです。
XSLProcessorオブジェクトを作成して、Javaコードで次に示すメソッドを使用します。使用可能なメソッドの一部を次に示します。
removeParam() - パラメータを削除
RESETPARAM() - すべてのパラメータを削除
setParam() - 変換用のパラメータを設定
setBaseURL() - スタイルシート内の相対参照のベースURLを設定
setEntityResolver() - スタイルシート内の相対参照のエンティティ・リゾルバを設定
setLocale() - エラー・レポートのロケールを設定
XSLProcessor.newXSLStylesheet()メソッドに対して次の入力パラメータのいずれかを使用し、スタイルシート・オブジェクトを作成します。
java.io.Reader
java.io.InputStream
XMLDocument
java.net.URL
これにより、スレッドセーフで複数のXSL Processorで使用できるスタイルシート・オブジェクトが作成されます。
手順2のいずれかのXML入力をDOMパーサーに渡し、parser.getDocumentでXML入力オブジェクトを作成することでDOMオブジェクトを作成します。
XML入力とスタイルシート・オブジェクトが、(2に示したいずれかの入力パラメータをそれぞれ使用して)XSL Processorに入力されます。
XSLProcessor.processXSL(xslstylesheet, xml instance)
XSL変換の結果は、次のいずれかになります。
XML文書オブジェクトの作成
出力ストリームへの書込み
SAXイベントとしてのレポート
HTMLとは異なり、XMLではすべての開始タグに終了タグが必要であり、タグの大文字と小文字は区別されます。
次の例には多くのコメントがあります。入力として、1つのXML文書と1つのXSLスタイルシートが使用されています。
public class XSLSample
{
public static void main(String args[]) throws Exception
{
if (args.length < 2)
{
System.err.println("Usage: java XSLSample xslFile xmlFile.");
System.exit(1);
}
// Create a new XSLProcessor.
XSLProcessor processor = new XSLProcessor();
// Register a base URL to resolve relative references
// processor.setBaseURL(baseURL);
// Or register an org.xml.sax.EntityResolver to resolve
// relative references
// processor.setEntityResolver(myEntityResolver);
// Register an error log
// processor.setErrorStream(new FileOutputStream("error.log"));
// Set any global paramters to the processor
// processor.setParam(namespace, param1, value1);
// processor.setParam(namespace, param2, value2);
// resetParam is for multiple XML documents with different parameters
String xslFile = args[0];
String xmlFile = args[1];
// Create a XSLStylesheet
// The stylesheet can be created using one of following inputs:
//
// XMLDocument xslInput = /* using DOMParser; see later in this code */
// URL xslInput = new URL(xslFile);
// Reader xslInput = new FileReader(xslFile);
InputStream xslInput = new FileInputStream(xslFile);
XSLStylesheet stylesheet = processor.newXSLStylesheet(xslInput);
// Prepare the XML instance document
// The XML instance can be given to the processor in one of
// following ways:
//
// URL xmlInput = new URL(xmlFile);
// Reader xmlInput = new FileReader(xmlFile);
// InputStream xmlInput = new FileInputStream(xmlFile);
// Or using DOMParser
DOMParser parser = new DOMParser();
parser.retainCDATASection(false);
parser.setPreserveWhitespace(true);
parser.parse(xmlFile);
XMLDocument xmlInput = parser.getDocument();
// Transform the XML instance
// The result of the transformation can be one of the following:
//
// 1. Return a XMLDocumentFragment
// 2. Print the results to a OutputStream
// 3. Report SAX Events to a ContentHandler
// 1. Return a XMLDocumentFragment
XMLDocumentFragment result;
result = processor.processXSL(stylesheet, xmlInput);
// Print the result to System.out
result.print(System.out);
// 2. Print the results to a OutputStream
// processor.processXSL(stylesheet, xmlInput, System.out);
// 3. Report SAX Events to a ContentHandler
// ContentHandler cntHandler = new MyContentHandler();
// processor.processXSL(stylesheet, xmlInput, cntHandler);
}
}
oraxslは、複数のXML文書へのスタイルシートの適用に使用するコマンドライン・インタフェースです。このインタフェースはその動作を決定する複数のコマンドライン・オプションを受け入れます。oraxslは、$ORACLE_HOME/binディレクトリに含まれています。oraxslを使用するには、次の条件を確認します。
環境変数CLASSPATHが、XML Parser for Javaバージョン2に付属するxmlparserv2.jarファイルを指すように設定されていること。
環境変数PATHが、JDK 1.2以上に付属するJavaインタプリタを検索できること。
次の構文を使用して、oraxslを起動します。
oraxsl options source stylesheet result
oraxslには、スタイルシート、変換するXMLファイルおよび結果ファイル(オプション)を指定します。結果ファイルを指定しない場合、変換したドキュメントは標準出力に出力されます。複数のXML文書をスタイルシートによって変換する必要がある場合は、-lまたは-dオプションを-sおよび-rオプションと組み合せて使用します。表4-1では、これらのオプションとその他のオプションについて説明します。
| オプション | 用途 |
|---|---|
|
|
変換するファイルを保持するディレクトリ(デフォルトの動作では、ディレクトリ内のすべてのファイルが処理されます)。そのディレクトリ内にあるファイルの特定のサブセットのみ(1つのファイルのみなど)を処理する必要がある場合は、 |
|
|
デバッグ・モード(デフォルトでは、デバッグ・モードは無効になっています)。 |
|
|
エラーおよび警告を書き込むファイルです。 |
|
|
ヘルプ・モード( |
|
|
含める拡張子( |
|
|
変換するファイルのリスト(処理するファイルを明示的にリストできます)。 |
|
|
結果を保存するディレクトリ( |
|
|
パラメータのリストです。 |
|
|
結果に使用する拡張子( |
|
|
使用するスタイルシート( |
|
|
処理に使用するスレッドの数(複数のスレッドを使用すると、複数のドキュメントを処理する際のパフォーマンスを向上できます)。 |
|
|
冗長モード(一部のデバッグ情報が出力され、処理中に発生した問題の追跡に役立つ場合があります)。 |
|
|
警告を表示(デフォルトでは、警告はオフになっています)。 |
|
|
除外する拡張子。 |
XSLT処理用のXML拡張関数を使用すると、XSLT Processor for Javaのユーザーは、任意のJavaメソッドをXSLの式からコールできます。
これらはOracleの拡張機能ですが、XSLT 1.0標準は実装定義の拡張関数も備えています。これらの関数を使用するスタイルシートは、様々なプロセッサで実行する場合、相互運用可能ではありません。関数は言語および実装に固有です。
この項の内容は次のとおりです。
Javaの拡張関数は、次のURIで始まる名前空間にあります。
http://www.oracle.com/XSL/Transform/java/
次の名前空間にある拡張関数は、classnameクラスのメソッドを参照します。
http://www.oracle.com/XSL/Transform/java/classname
たとえば、次の名前空間を使用して、XSLの式からjava.lang.Stringメソッドをコールできます。
http://www.oracle.com/XSL/Transform/java/java.lang.String
メソッドがクラスの非staticメソッドの場合、最初のパラメータはメソッドが起動されるインスタンスとして使用され、残りのパラメータがメソッドに渡されます。
拡張関数がstaticメソッドの場合、その拡張関数のすべてのパラメータが、静的関数へのパラメータとして渡されます。
次のXSLの静的関数の例では、13が出力されます。
<xsl:stylesheet
xmlns:math="http://www.oracle.com/XSL/Transform/java/java.lang.Math">
<xsl:template match="/">
<xsl:value-of select="math:ceil('12.34')"/>
</xsl:template>
</xsl:stylesheet>
|
注意: XSLクラス・ローダーは、静的に追加されたJARおよびwrapper.classpathによって指定されたCLASSPATHのパスのみ認識します。
|
拡張関数newは、クラスの新しいインスタンスを作成し、コンストラクタとして動作します。
次のコンストラクタ関数の例では、HELLO WORLDが出力されます。
<xsl:stylesheet
xmlns:jstring="http://www.oracle.com/XSL/Transform/java/java.lang.String">
<xsl:template match="/">
<!-- creates a new java.lang.String and stores it in the variable str1 -->
<xsl:variable name="str1" select="jstring:new('Hello World')"/>
<xsl:value-of select="jstring:toUpperCase($str1)"/>
</xsl:template>
</xsl:stylesheet>
拡張関数の結果は、XSLに定義されている次の5つの型、およびXSLT 2.0で定義されている単純なXML Schemaデータ型を含むどの型でも出力できます。
NodeSet
Boolean
String
Number
Resulttree
これらのデータ型は、変数に格納したり、他の拡張関数に渡したりできます。
結果の型がXSLに定義されている5つの型のうちの1つである場合、その結果はXSLの式の結果として戻すことができます。
次に、戻り値拡張関数を示すXSLの例を示します。
<!-- Declare extension function namespace -->
<xsl:stylesheet xmlns:parser =
"http://www.oracle.com/XSL/Transform/java/oracle.xml.parser.v2.DOMParser"
xmlns:document =
"http://www.oracle.com/XSL/Transform/java/oracle.xml.parser.v2.XMLDocument" >
<xsl:template match ="/"> <!-- Create a new instance of the parser, store it in
myparser variable -->
<xsl:variable name="myparser" select="parser:new()"/>
<!-- Call a non-static method of DOMParser. Since the method is a non-static
method, the first parameter is the instance on which the method is called. This
is equivalent to $myparser.parse('test.xml') -->
<xsl:value-of select="parser:parse($myparser, 'test.xml')"/>
<!-- Get the document node of the XML Dom tree -->
<xsl:variable name="mydocument" select="parser:getDocument($myparser)"/>
<!-- Invoke getelementsbytagname on mydocument -->
<xsl:for-each
select="document:getElementsByTagName($mydocument,'elementname')">
...
</xsl:for-each> </xsl:template>
</xsl:stylesheet>
パラメータ数と型に基づくオーバーロードがサポートされます。XSLに定義されたとおりに5つのXSL型の間で暗黙的な型変換が実行されます。型変換は、(String、Number、Boolean、ResultTree)間、およびNodeSetから(String、Number、Boolean、ResultTree)に対して暗黙的に実行されます。相互に暗黙的な変換が可能な2つの型に基づくオーバーロードは許可されません。
これらのOracle XSL拡張関数の定義は次のとおりです。いずれも前にxmlns:ora="http://www.oracle.com/XSL/Transform/java"が付加されます。
この要素は、xsl:outputと類似した最上位レベル要素として使用できます。この要素には、同様の機能を持つxsl:outputのすべての属性があります。その他、識別子として使用するname属性があります。ora:outputをテンプレートで使用する場合、useおよびhref属性のみを使用できます。useでは、使用する最上位レベルのora:outputを指定し、hrefは出力URLを示します。
次に、ora:node-setおよびora:outputの使用例を示します。
次のように入力するとします。
$ oraxsl foo.xml slides.xsl toc.html
foo.xmlは任意の入力XMLファイルです。次の出力が得られます。
目次を含むtoc.htmlスライド・ファイル
スライド1を含むslide01.htmlファイル
スライド2を含むslide02.htmlファイル
<!--
| Illustrate using ora:node-set and ora:output
|
| Both extensions depend on defining a namespace
| with the uri of "http://www.oracle.com/XSL/Transform/java"
+-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ora="http://www.oracle.com/XSL/Transform/java">
<!-- <xsl:output> affects the primary result document -->
<xsl:output mode="html" indent="no"/>
<!--
| <ora:output> at the top-level enables all attributes
| that <xsl:output> enables, but you must provide the
| additional "name" attribute to assign a name to
| these output settings to be used later.
+-->
<ora:output name="myOutput" mode="html" indent="no"/>
<!--
| This top-level variable is a result-tree fragment
+-->
<xsl:variable name="fragment">
<slides>
<slide>
<title>First Slide</title>
<bullet>Point One</bullet>
<bullet>Point Two</bullet>
<bullet>Point Three</bullet>
</slide>
<slide>
<title>Second Slide</title>
<bullet>Point One</bullet>
<bullet>Point Two</bullet>
<bullet>Point Three</bullet>
</slide>
</slides>
</xsl:variable>
<xsl:template match="/">
<!-- | We cannot "de-reference" a result-tree-fragment to
| navigate into it with an XPath expression. However, using
| the ora:node-set() built-in extension function, you can
| "cast" a result-tree fragment to a node-set which *can*
| then be navigated using XPath. Since we'll use the node-set
| of <slides> twice later, we save the node-set in a variable.
+-->
<xsl:variable name="slides" select="ora:node-set($fragment)"/>
<!--
| This <html> page will go to the primary result document.
| It is a "table of contents" for the slide show, with
| links to each slide. The "slides" will each be generated
| into *secondary* result documents, each slide having
| a file name of "slideNN.html" where NN is the two-digit
| slide number
+-->
<html>
<body>
<h1>List of All Slides</h1>
<xsl:apply-templates select="$slides" mode="toc"/>
</body>
</html>
<!--
| Now go apply-templates to format each slide
+-->
<xsl:apply-templates select="$slides"/>
</xsl:template>
<!-- In 'toc' mode, generate a link to each slide we match -->
<xsl:template match="slide" mode="toc">
<a href="slide{format-number(position(),'00')}.html">
<xsl:value-of select="title"/>
</a><br/>
</xsl:template>
<!--
| For each slide matched, send the output for the current
| <slide> to a file named "slideNN.html". Use the named
| output style defined earlier called "myOutput".
<xsl:template match="slide">
<ora:output use="myOutput href="slide{format-number(position(),'00')}.html">
<html>
<body>
<xsl:apply-templates select="title"/>
<ul>
<xsl:apply-templates select="*[not(self::title)]"/>
</ul>
</body>
</html>
</ora:output>
</xsl:template>
<xsl:template match="bullet">
<li><xsl:value-of select="."/></li>
</xsl:template>
<xsl:template match="title">
<h1><xsl:value-of select="."/></h1>
</xsl:template>
</xsl:stylesheet>
この項では、XSLおよびXSLT Processor for Javaについてのヒントを示します。次の内容が含まれます。
2つのXML文書をマージするには、DOM APIまたはXSLTベースの方法を使用できます。
DOM APIを使用する場合、ソースDOM文書からDOMノードをコピーした後、コピー先のDOM文書にそのコピーを追加する必要があります。この操作を実行するには、WRONG_DOCUMENT_ERRなどのDOM文書の所有者エラーを回避する必要があります。異なるXML文書間でDOM文書のフラグメントまたはDOMノードをコピーして貼り付けるには、DOM 2で導入されたimportNode()メソッドおよびDOM 3で導入されたadoptNode()メソッドを使用できます。
Document doc1 = new XMLDocument();
Element element1 = doc1.createElement("foo");
Document doc2 = new XMLDocument();
Element element2 = doc2.createElement("bar");
element2 = doc1.importNode(element2);
element1.appendChild(element2);
Document doc1 = new XMLDocument();
Element element1 = doc1.createElement("foo");
Document doc2 = new XMLDocument();
Element element2 = doc2.createElement("bar");
element2 = doc1.adoptNode(element2);
element1.appendChild(element2);
adoptNode()とimportNode()の使用の違いは、adoptNode()を使用する場合、ソースDOMノードが元のDOM文書から削除されるのに対し、importNode()を使用する場合、ソース・ノードが変更または削除されない点です。
マージ操作が単純な場合、XSLTベースの方法も使用できます。たとえば、次のような2つのXML文書があるとします。
<messages>
<msg>
<key>AAA</key>
<num>01001</num>
</msg>
<msg>
<key>BBB</key>
<num>01011</num>
</msg>
</messages>
<messages>
<msg>
<key>AAA</key>
<text>This is a Message</text>
</msg>
<msg>
<key>BBB</key>
<text>This is another Message</text>
</msg>
</messages>
次に、<key/>要素値の一致に基づいて、2つのXML文書(demo1.xmlおよびdemo2.xml)をマージするスタイルシートの例を示します。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:variable name="doc2" select="document('demo2.xml')"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="msg">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<text><xsl:value-of select="$doc2/messages/msg[key=current()/key]/text"/>
</text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
コマンドラインで次の行を入力します。
$ oraxsl demo1.xml demomerge.xsl
次のマージした結果が得られます。
<messages>
<msg>
<key>AAA</key>
<num>01001</num>
<text>This is a Message</text>
</msg>
<msg>
<key>BBB</key>
<num>01011</num>
<text>This is another Message</text>
</msg>
</messages>
この方法は、サイズが大きいファイルの場合は、2つの表間での同等のデータベースの結合ほど明らかに効果的ではありません。この例は、XMLファイルのみを処理する場合の方法を示しています。
CDATAのコンテンツはテキストのみです。山カッコをエスケープせずにテキスト・コンテンツを出力する場合、次のようになります。
<xsl:value-of select="/OES_MESSAGE/PAYLOAD" disable-output-escaping="yes"/>
XSLTは<xsl:output>のすべてのオプションを完全にサポートしています。XSLスタイルシードは整形式のXML文書である必要があります。<BR>要素ではなく、<BR/>を使用してください。<xsl:output method="html"/>は、XSLTエンジンが変換の結果を書き出す場合、それが適切なHTML文書になるようにリクエストします。XSLTエンジンが読み取る内容は、整形式のXMLになります。
XMLからHTMLへの変換を実行するXSLスタイルシートがあるとします。空の要素(<input type="text"/>)で終了するHTMLタグ以外はすべて適切に機能します。たとえば、次のスタイルシートでは、<input>要素でHTML文書を作成しています。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
...
<input type="text" name="{NAME}" size="{DISPLAY_LENGTH}" maxlength="{LENGTH}">
</input>
...
</xsl:stylesheet>
次の形式でHTMLをレンダリングします。
<HTML>...<input type="text" name="in1" size="10" maxlength="20"/> ... </HTML>
Internet Explorerではこれを処理できますが、Netscapeでは処理できません。ブラウザ間に準拠したHTMLをXSLを使用して完全に生成する方法はあるでしょうか。
次のように表示されている場合の問題の解決策を示します。
<input ... />
次のようにではありません。
<input ...></input>
この場合、XSLProcessor.processXSL()のコール方法が正しく使用されていないため、HTML出力が行われていない可能性があります。次を使用します。
void processXSL(style,sourceDoc,PrintWriter)
次は使用しません。
DocumentFragment processXSL(style,sourceDoc)
user_tab_columns表から列名を使用してデータを入力するHTMLフォームを生成するためのXSLコードを次に示します。
<xsl:template match="ROW">
<xsl:value-of select="COLUMN_NAME"/>
<INPUT NAME="{COLUMN_NAME}"/>
</xsl:template>
正しいURIは次のとおりです。
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
次のように使用するとします。
xmlns:xsl="-- any other string here --"
この場合、正しい出力は得られません。
XML Parser for Javaのリリース2.0.2.8以上では、1つのXMLおよびXSLから複数の結果を生成する<ora:output>をサポートしています。
XSLTは、XMLからXML、XMLからHTML、またはXMLから他のテキストベースの形式に変換します。逆の場合の変換はどうでしょうか。
HTMLの場合、TidyまたはJTidyなどのユーティリティを使用して、HTMLを、XSLTを使用して変換できる整形式のHTMLに変換できます。構造化されていないテキスト形式の場合、次のWebサイトにあるXFlatなどのユーティリティを試してみることができます。
複数のスレッドでは、1つのXSLProcessorおよびXSLStylesheetのインスタンスを使用して、同時変換を実行できます。XMLファイルごとに1つのXSLProcessorおよびXSLStylesheetのインスタンスのみで複数のファイルを処理している場合、スレッドを使用して同時に処理できます。
複数のスレッドでドキュメントのクローンを安全に使用できます。public void setParam(String,String)によって発生するoracle.xml.parser.v2.XSLStylesheetクラスのXSLExceptionメソッドはサポートされています。コンストラクタが設定したグルーバル領域を別のスレッドにコピーすると、このメソッドは機能します。このメソッドは、XML Parser for Javaのリリース2.0.2.5以降でサポートされています。