この章の内容は次のとおりです。
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以降でサポートされています。