この記事では、オーサリング済み XML 文書を扱う際の難題のいくつかに焦点を当てます。この記事で言うオーサリング済み XML 文書というのは、コンテンツ作成者によって作成されたデータ・セットを指し、通常は 1 つの DTD またはスキーマに沿って作成されたものです。多くの環境では、一貫性、効率性、コスト節約など、さまざまな目的のために、ガイドに従った XML オーサリングが行われています。DTD が用意されている場合、100% の一貫性を期待することは決して不合理なことではありません。XML の作成者が DTD によるガイドに従っている (そして制約されている) 場合、作成される結果は確実に予測可能なはずです。
しかし DTD を詳細に調べてみると、ほとんどの場合、かなりの柔軟性があることがわかります。特に、その DTD が汎用的なソースを基に作られている場合、あるいは標準や仕様がベースになっている場合はなおさらです。これは DTD という仕組みの欠点ではありませんが、その XML データ・セットを取り巻くインフラ (高価なパブリッシング・パイプライン、文書管理システム、変換ルーチンなど) で特定の形式の文書が想定されている場合には、DTD の柔軟性が頭痛の種になります。
現実的なソリューションとして、バリエーションを制限するスタイル・ガイドを強制する場合がよくあります。しかし、スタイル・ガイドとデータ・セットとを手作業でクロスチェックする方法は、スタイル・ガイドを実装する手段としてはコストがかかるため、そのチェックは自動化した方がより効率的な方法になります。
コンテンツ作成者が持っているのは、XML データ・セット、そして彼らが実行する必要のある一連のタスクです。このタスクを自動化することはできるのでしょうか?
それに対する答えを得る簡単な方法として、通常は「そのタスクは予測可能で、反復可能、そして定義可能ですか?」という質問がなされますが、それよりも容易なチェック方法として、テキストの内容の構文解析を行わず、以下のように文書の構造に焦点を絞ってチェックする方法があります。
- 新しいセクションにクロスリファレンスのターゲットがあるか?このトピックに他の誰かがリンクする可能性は非常に高いか?
- リストに複数の項目があるか?複数の項目がない場合、それは実際にはリストではありません。
- 安全に関する重要な情報がタスク・リストの前に記述されているか?電気ショックの可能性があることを事前に警告しておくのが最善です。
既知の文書モデルを指定することも、汎用的な文書構造を目指すこともできます。結局のところ、これはスケーラビリティーの問題です。あるユーティリティーで 1 つの目的を果たすことができ、自動化によって時間を節約できるのであれば、ビジネス・ロジックがコードに直結した自己完結の単純なスクリプトを使用するのが妥当な手法かもしれません。ユーザーによるカスタマイズが可能な多目的のユーティリティーに関心がある場合には、より意欲的で構成可能な手法が必要かもしれません。この記事では後者の手法を選ぶことにします。
XSLT はあらゆる用途の XML の処理言語として使用することができます。ただし XSLT が XML 処理の唯一の選択肢というわけではありません。多くの場合は DOM を使って XML の処理が行われており、この記事で紹介する内容の一部も DOM を使って行うことができます。しかし、修正アクションを実行することを検討する場合には、XSLT が理想的なツールであることは明らかです。
この記事では、HTML ラッパーと組み合わされた XSLT サンプル・ユーティリティーをスタンドアロン・アプリケーションとして容易にデプロイする方法を示します。この組み合わせの場合、XSLT はバージョン 1.0 (「参考文献」を参照) が使われており、組み込みスクリプトは Microsoft® の JScript® スクリプト言語で作成されています。
ビジネス・ロジックに従って文書を処理し、一連のエラー・メッセージを返す
最初のステップはビジネス・ロジックを取り込むことです。この演習では、この記事で提供されている XML ソースに対するチェックを作成します。チェックのルールは、これらの文書を作成する人達に提供されるスタイル・ガイドに基づいています。
チェックは、作成されたコンテンツが完全なものになるように設計されており、テキストの内容の分析をするのではなく、文書の構造のチェックをします。こうしたチェックを取り込むための候補としては XPath が理想的です。
XML 語彙の中にチェックをエンコードすることによって、チェックを様式化して一般的な語彙にする
この方法では、文書のエラーを捕捉する最善の方法、エラーをカテゴリー分けする方法、そしてエラーを処理する方法を定義する設計プロセスを実行します。では、なぜこのプロセスを単純に XSLT に組み込まないのでしょう。この手法のメリットは、エラー・チェックを XML 語彙の中にエンコードすることによってユーティリティーが汎用のコードとなり、1 つまたは複数のプロファイルを処理できるようになる点です。これにより、ユーザーはさまざまな文書データに対し、以下のように多様なルール・セットを選択できるようになります。
- 文書をテストするための手段を定義します。この記事の演習では、設計上の決定事項として XPath を使用します。
- 合格か不合格かの判断基準を定義します。XPath ベースの文書チェックを使用することで、XPath 式に従うノードが 1 つ以上あるかどうかのクエリーを実行します。
- 不合格の場合の深刻さを定義します。個々のチェックは以下のようにカテゴリー分けすることができます。
- 強制 (enforceable): このタイプのチェックで 1 度エラーが発生すると、エラー・チェックのプロセスは不合格となります。
- 望ましい (advisable): プロセスは不合格にはなりませんが、エラーの発生したインスタンスがエラーとしてログに記録されます。
- 条件付き (conditional): 強制のバリエーションですが、このチェックは強制よりも複雑です。XPath 式のテストから返されたノードに基づき、さらなるコンテキストのチェックが行われます。
- マッピング・ファイルを作成し、インポートします。マッピング・ファイルには以下の文書チェックを使用する必要があります。
- 文書に対する名前空間を定義します。例えば以下のようにします。
<err:document xmlns:err="http://error.com/mynamespace">
- 各エラーの定義を作成します。
- 上位レベルで文書に対して実行されるエラー・チェックの記述をします。
<err:element type="structure" name="dw-document" context="/dw-document" enforce="yes">
- 要素レベルで実行されるエラー・チェックの記述をします。
<err:element type="element" name="ol" context="./li" pass=">=2"/>
- 文書に対する名前空間を定義します。例えば以下のようにします。
サンプル・テストの完全なセットは、「参考文献」を参照してください。
エラー・チェックの構文を定義できると、多様なデータ・セットに適用できる 1 つまたは複数のルール・セットを定義することができます。
ルール・セット・ファイルを処理するための XSLT を作成する
XSLT には出力ストリームが 2 つある可能性があります。1 つは、ログ・メッセージ、もう 1 つは (修正アクションが実行された場合の) 改善された文書ソースです。
この設計では、XSLT の出力ストリームを使用して改善された新しい文書を作成し、さらに XSLT 拡張機能を使用して別の出力ストリームにログ・メッセージを書き込みます。スタンドアロンの例では、ログ・メッセージを HTML のロギング・ペインに追加します。
エラー・チェックの方法は、最上位レベルの構造チェックと、要素レベルのチェックという 2 つのまったく異なる方法にカテゴリー分けされます。XSLT は最初に最上位レベルのチェックを行います。次に、必要な場合 (つまり最上位レベルのすべてのチェックに合格した場合) には、文書の内容を従来の XSLT テンプレートを使ってチェックします。
XSLT を作成するためには、以下のステップを実行します。
- 組み込みスクリプトを定義するための
script要素を XSLT の中で定義します。最初にロギング環境を作成し、続いてメッセージを保存するための関数を作成します (リスト 1)。
リスト 1. 組み込みスクリプトを XSLT の中で定義する<msxsl:script language="JScript" implements-prefix="xslext"> <![CDATA[ var messages = new Array(); var msgct = 0; function addMsg( msg ){ messages[msgct++] = msg; return ""; } ]]> </msxsl:script>
- メッセージを処理するためのテンプレートを追加します。リスト 2 はそのコードを示しています。
リスト 2. テンプレートを追加する<xsl:template name="handlemsg"> <xsl:param name="msg"/> <xsl:param name="terminate">no</xsl:param> <xsl:param name="lvl">1</xsl:param> <xsl:variable name="logmsg"> <!-- Indent the log messages to help with readability --> <xsl:choose> <xsl:when test="$lvl=2"> • </xsl:when> <xsl:when test="$lvl=3"> • </xsl:when> <xsl:when test="$lvl=4"> • </xsl:when> <xsl:when test="$lvl=4"> • </xsl:when> </xsl:choose> <xsl:value-of select="$msg"/> </xsl:variable> <xsl:variable name="log" select="xslext:addMsg( string( $logmsg ) )"/> <xsl:if test="$terminate='yes'"> <xsl:variable name="errormsg" select="xslext:addMsg( 'ERROR: Error checking caused the process to stop' )"/> <!-- If the error msg force termination, the process must first output all existing log messages --> <xsl:variable name="output" select="xslext:outputMsgs( $logfileout )"/> <xsl:message terminate="yes"></xsl:message> </xsl:if> </xsl:template>
このテンプレートは XSLT のどこからでも呼び出され、メッセージ処理の拡張機能関数に送信されたメッセージを処理します。
- XPath 式を評価する対象となるグローバルな文書変数を使用し、式を渡す先となる関数を作成します。リスト 3 はそのコードを示しています。
リスト 3. グローバル変数を作成する<msxsl:script language="JScript" implements-prefix="xslext"> <![CDATA[ var xpathdoc = null; function setUpXPath( ns, trialexpr ){ var xml = ns.nextNode().xml; try{ xpathdoc = new ActiveXObject( "Msxml2.DOMDocument.3.0" ); xpathdoc.loadXML( xml ); return trialexpr + ": " + xpathdoc.selectNodes( trialexpr ).length; } catch(e) { return "ERROR: " + e.description; } } ]]> </msxsl:script>
リスト 3 に示す関数は、さらに XPath を評価するためのコンテキスト・ノードとして使用する DOM 文書を作成します。
- XSLT のメインの本体から、この初期化関数を呼び出します (リスト 4)。
リスト 4. 初期化関数を追加する<xsl:call-template name="handlemsg"> <xsl:with-param name="msg">Setup ' <xsl:value-of select="xslext:setUpXPath( $root, concat( '//', name($root) ) )"/> '</xsl:with-param> </xsl:call-template>
名前空間接頭辞 (この場合は
xslext) を使用して拡張機能関数が呼び出されている様子に注目してください。この接頭辞により、このカスタム関数と、XSLT によって利用できる標準的な関数 (number()、string()、contains()など) とを区別しています。 - 最上位レベルの文書テストを処理します。
- ルール・セット・ファイルのパラメーターを定義します。
<xsl:param name="rulesetfile"></xsl:param>
このパラメーターをファイルの URI として渡します。スタンドアロンの例では、ユーザーが選択した内容を実行時に取得します。
- 各テストを処理するためのテンプレートを作成します。
xsl:template name="process-check"
このテンプレートは以下のように動作します。まず、
xpathdocをコンテキスト・ノードとして使用してテスト用の式セットを評価する拡張機能関数を、ルール・ファイルの中に作成します。
function evalXPath( exp ){ try{ return xpathdoc.selectNodes( exp ).length; } catch(e) { return "Exception: " + e.description; } }
テストに成功すると、このコードによって整数が返されます (その整数は 1 以上のはずです)。ゼロが返された場合には、テストは適切に実行されたものの、一致するものが見つからなかったことを表します。エラーの説明が返された場合には、この関数が例外をスローしたか、あるいは XPath 式の形式が不適切だったことを意味します。
- この関数を呼び出し、戻り値を変数に保存します。
<xsl:variable name="check" select="xslext:evalXPath( string( $context ) )"/>
ここで、
$contextはerr:elementに対する式ストリング・セットです (例えば/dw-document//meta-dcsubjectなど)。$checkの値が 1 以上で、テストが「強制」に設定されている場合には、テストに合格したことになります。$checkの値が 0 であり、テストが「強制」に設定されていない場合には、テストには合格したものの、ユーザーに対して警告が表示されるはずです。それ以外の場合、テストには不合格であるため、プロセスは停止するはずです。
xsl:messageでterminateを yes に設定すると、強制的に終了することができます (リスト 2)。ログ・メッセージによってテンプレートが呼び出され、terminateパラメーターが yes に設定されます。 - すべての「強制」のテストのためのノードセットを定義し、それらのテストを処理します。
document($rulesetfile)//err:element[@type='structure'][@enforce='yes']
- 「強制」ではない、他のすべての最上位レベルのテストを処理します。
document($rulesetfile)//err:element[@type='structure'][not(@enforce='yes')]
- ルール・セット・ファイルのパラメーターを定義します。
- 要素レベルのテストを処理します。
これらのテストは個々のテンプレートで処理されます。プロセスの汎用性を保つために、XSLT には要素を処理するための単純なテンプレートがあります。
xsl:template match="node()"
この汎用テンプレートの中で、ルール・セットに該当のテストが含まれているかどうかを判断するための変数を設定します。
<xsl:variable name="match" select="document($rulesetfile)//err:element[@type='element'] [@name=$name]"/>
ここで、
$nameは現在の要素の名前として定義されています。$matchが True の場合、このテストのコンテキストが別の拡張機能関数を使って実行されます。この関数は、最上位レベルで XPath を評価する場合と同じように、XSLT の現在のノードを渡し、そのノードに対して式を評価します (リスト 6)。
リスト 6. 式を評価するための関数function evalXPathAgainstNode( node, exp ){ try{ return node.nextNode().selectNodes( exp ).length; } catch(e) { return "Exception: " + e.description; } }
この関数から返された値を構文解析した結果が整数である場合 (つまり戻り値が 0 やエラー・メッセージではない場合) には、その整数は別の関数に渡され、
pass属性で定義される合格か不合格かの判断基準に従って、その整数がテストされます。
<err:element type="element" name="ol" context="./li" pass=">=2" />
ol要素のliという子の数が 2 以上かどうかをテストします (リスト 7)。
リスト 7. li 要素の数をテストするfunction evalExpr( str, pass ){ return eval( str + pass ); } ... <xsl:variable name="eval" select="xslext:evalExpr( $check, $pass )"/>
- XSLT はリスト 8 のようなログ結果を返します。
リスト 8. XSLT によるログ結果Start Setup '//dw-document: 1'... · Check (Top-level document?) '1' · Conditional check '(Document ID missing?) '1' (1==1) == true' · Conditional check '(Article missing?) '1' (1==1) == true' · Conditional check '(Meta field (document type) missing?) '1' (1==1) == true' · Conditional check '(Meta field (subject) missing?) '1' (1==1) == true' · Conditional check '(Article title missing?) '1' (1==1) == true' · Conditional check '(Document author missing?) '1' (1==1) == true' · Conditional check '(Published date missing?) '1' (1==1) == true' · Check (Missing abstract?) '1' · Conditional check '(Dates out of sync?) '0' (00) == 0' · Conditional check '(Broken internal links?) '0' (0==0) == true' · Context checking 'heading' (./a[@name]) '(1==1) == true'... · Error context checking 'heading' (./a[@name]) '(0==1) == false'... · Context checking 'heading' (./a[@name]) '(1==1) == true'... · Context checking 'ol' (./li) '(3>=2) == true'... / End
文書のチェックを作成し、エラーを特定して、再作成を行うというプロセスを構築したら、次のステップとして、要素に対して修正アクションを実行する必要があります。このサンプルには、後から文書に追加するための基本的なコードが含まれています。
ルール・セットから err:onfail 要素が err:element の子とわかると、このコードは以下のいずれかを実行します。
<err:insertbefore></err:insertbefore><err:insertatstart></err:insertatstart><err:insertatend></err:insertatend><err:insertafter></err:insertafter>
insert 要素には文書を修正するための XML タグが含まれています。下記はその一例です。
<err:insertatstart> <a name="function:generate-id()" /></err:insertatstart> |
これを XSLT によって処理する必要があります。
次に、ノードセットに対して繰り返し処理を行うテンプレートを作成します。
<xsl:template name="copy-nodeset"> |
XSLT の該当する場所で、このテンプレートに err:insertbefore 要素、err:insertatstart 要素、err:insertatend 要素、err:insertafter 要素の内容を渡します。例えば以下のようにします。
<-- Add 'err:insertbefore' here -->
<xsl:element name="{name()}">
<xsl:copy-of select="@*"/>
<-- Add 'err:insertatstart' here -->
<xsl:apply-templates/>
<-- Add 'err:insertatend' here -->
</xsl:element>
<-- Add 'err:insertafter' here -->
|
このテンプレートは function:generate-id() メソッドに対して特別な処置を行います。
完全を期すために、内容が文書に挿入される際にロギング処理を追加します。
... · Error context checking 'heading' (./a[@name]) '(0==1) == false'... · Adding content at start of 'heading' · Error context checking 'heading' (./a[@name]) '(0==1) == false'... · Adding content at start of 'heading' ... |
この記事では、XSLT を使用して文書の構造を分析し、一連のビジネス・ルールに従っているかどうかを判断する方法について説明しました。このプロセスは 2 つの点で重要な役割を果たします。第 1 に、コンテンツ作成者にとってのオーサリングという目的達成を支援することができます。例えば、ユーザーはオフラインで作業を行い、これらのテストを何度も実行して特定のタスクを完了できたかどうかを検証することができます。そして第 2 に、文書作成ワークフローの正式な一部として使用することができます。例えば、このユーティリティーを文書リポジトリーのワークフローに組み込み、合格か不合格かの判断基準により、編集、審査、受け入れ、といワークフロー内での管理対象文書の動きを制御することができます。
ビジネス・ロジックを XSLT から切り離すと、ユーティリティーは柔軟になります。1 つのコード・ベースを使用して複数のルール・セットを適用できるため、コードの汎用性が高まります。この記事では、DOM メソッドの代わりに XLST を使用すると、XLST 変換プロセスを使った文書の改善が可能になり、文書を修正する強力な手段となることを示しました。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Example XSLT code | xslt_source.zip | 9KB | HTTP |
学ぶために
- ウィキペディアで Simplified English の項目を読んでください。Simplified English は曖昧さを排除した一貫性のある文書作成スタイルを技術文書作成者に奨励するための標準です。
- XSL Transformations (XSLT) バージョン 1.0 仕様 (W3C、1999年11月) を読み、XSLT の構文とセマンティクスについて学んでください。XSLT は XML 文書を別の XML 文書に変換するための言語です。
- 「拡張機能によるXSLの拡張」(Jared Jackson 著、developerWorks、2002年4月) で拡張機能の利用方法を学んでください。拡張機能は XSL のコア機能を拡張するための手法です。
- 「データ用のXML: EXSLTによってXSLTの機能を拡張する」(Kevin Williams 著、developerWorks、2002年12月) を読み、EXSLT 標準について、また EXSLT によって XSLT 1.0 の機能を拡張する方法を学んでください。
- 「XML名前空間の使用を計画する: 第1回」(David Marston 著、developerWorks、2004年4月) を読み、XML の名前空間を皆さんに最適の方法で活用する方法を学んでください。
- developerWorks の XML ゾーンには XML の領域でのスキルを磨くためのリソースが豊富に用意されています。
- My developerWorks で developerWorks のエクスペリエンスをパーソナライズしてください。
- XML および関連技術において IBM 認定技術者になる方法については、IBM XML certification を参照してください。
- developerWorks の XML ゾーンを XML の技術ライブラリーとして利用してください。広範な話題を網羅した技術記事やヒント、チュートリアル、技術標準、IBM Redbooks などが用意されています。また、他にも XML に関するヒント記事があります。
- developerWorks の Technical events and webcasts で最新情報を入手してください。
- developerWorks on Twitter で、今すぐ参加して developerWorks のツイートをフォローしてください。
- developerWorks podcasts で、ソフトウェア開発者のための興味深いインタビューや議論を聞いてください。
- developerWorks On demand demos をご覧ください。初心者のための製品インストール方法やセットアップのデモから、上級開発者のための高度な機能に至るまで、多様な話題が解説されています。
製品や技術を入手するために
- 「Business Rules Exchange」(Svante Ericsson 著、TPSMG、2004年9月) を読み、文書を作成する際にプロジェクト特有のルールをエンコードするための XML 語彙を検討してください。この語彙は、文書の妥当性検証に使用される DTD やスキーマのさらに上位レベルの作成ルールを規定するために設計されています。
- IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。
議論するために
- XML 関連の Yahoo! グループで議論に加わってください。
- XML zone discussion forums では XML に関する議論が行われています。
- developerWorks コミュニティーで開発者向けのブログ、フォーラム、グループ、ウィキなどを利用しながら、他の developerWorks ユーザーとやり取りしてください。