タグ別アーカイブ: XSLT/XPath

速習XSLT超入門1(明日に迫るXSLT超入門2ウェビナー )

ゴールデンウィークも明け、XSLT超入門2のウェビナーが明日に迫りました。

セミナーのようなイベントでは、ナンバリングによって「初回参加してないからどうしよう」と尻込みされてしまう方がいるかもしれません。ということで、第1回をおさらいする記事を用意しました。
「これだけ見ておけば大丈夫」というよりは、書籍におけるあらすじと目次のようなものと考えてください。

導入として「XSLTを活用する自動組版の流れとして、XSLTがどの部分の役割を果たすか」を紹介しています。元の文書にあるコンテンツから目次を生成したりできます。
このウェビナー内では大きく触れていませんが(PDF自動生成超入門の内容なので)、「生成時まで内容が決定できないこと」をオブジェクトとしてレイアウトできるのがXSL-FOとなります。弊社製品Antenna House XSL Formatterによる拡張要素・プロパティも、この視点で眺めてみるとスタイルシート設計に役立つのではないでしょうか。

書籍の完成状態を例にして、抽象的な「構造」について紹介し、それをXMLで表現することについて触れています。

XMLを変換するにあたって、「どの部分を変換するか」という指定が必要になります。XSLTでは、そのためにXPathを使うよ、ということを紹介しています。XPathはXSLTから独立するほど多様な機能がありますが、「XML上の特定位置を指定する」ことは基本といって良いでしょう。そのために「ノード」という形でXML文書を解釈し、ノード間の関係としてXML上の位置を指定できるようにしています。関係の方向性として「軸」があり、不足する指定を補う「述部」がある、という紹介をしています。

より実際的な説明として、弊社の過去記事を紹介しておきます。
XSLTを学ぶ (1) XMLのツリーモデルとXPath/XSLTのツリーモデルではルートの意味が違う

明日のウェビナーが「基本文法編」ということで、では「XSLTの基本」は何を紹介しているんだ、疑問があるでしょう。ここでは初学者にとって概念的にあまり馴染みがないであろう、XSLTを構築する基本であるxsl:template@matchとxsl:apply-templatesについてを図を用いて紹介しています。文法は比較的資料があるため、図示に注力した形です。後半のデモでトラブルがあった関係でウェビナーと動画で違いが生じてしまっていますが、このmatchとapplyの関係はコインソータに似ている、ということを覚えておくと良いでしょう。

ウェビナーではトラブルのあったデモについては動画を撮り直しています。
デモを通し、xsl:template@matchとxsl:apply-templatesでXML文書を処理していく様子を紹介しています。

全体を通して、「学習開始で環境構築に悩むよりプレイグラウンドなどを利用するのも良い」「業務利用としてXMLエディタは十分ペイする」「変換元のXML文書としてCommonMark文書が難易度として丁度良いのではないか」といった話をしており、「初学者が取り組みやすい形を提示する」をサブテーマとしていました。

明日のウェビナーではこの第1回からの流れを受けて、基本的なコードリーディングによる学習ができる段階まで持っていけるようにすることを目標にしています。ご参加をお待ちしています。


XSL-FO 試行錯誤 カレンダーを自動生成したい(その月のマスの最初の日を取得する)

XSL-FO 試行錯誤 カレンダーを自動生成したい(構想編)の続きとなります。

大抵のカレンダーにおいて、ある月の表における最初の日付は「1日」ではありません。日曜始まりのカレンダーなら、「その月の1日が含まれる週の日曜日の日付」を取得する必要があります。このとき同様に「その月の最終日が含まれる週の土曜日」も考える必要がありますが、今回は割愛します。

XSLT 2.0からは日付関連の関数が使えるので、これを使っていくことにします。

<xsl:transform 
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:fn="http://www.w3.org/2005/xpath-functions"
 xmlns:fo="http://www.w3.org/1999/XSL/Format"
 xmlns:axf="http://www.antennahouse.com/names/XSL/Extensions"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"
 xmlns:cal="urn:calendar"
 exclude-result-prefixes="xs fn">...</xsl:transform>

ルートはこんな感じです。foやaxfは今回登場しません。XSLT 2.0からは型の時点でエラーを検知したりといったことが可能なので、XMLSchemaの名前空間はかかせません。xpath-functionsの名前空間は宣言しなくとも使えますが、自作関数との区別用に明示しています。独自に実装する名前空間はcalというprefixを付けることにします(functionのnameには名前空間の明示が必要になります)。

 <xsl:function name="cal:getWeekDay" as="xs:integer">
   <xsl:param name="day" as="xs:date"/>
     <xsl:sequence select="$day => fn:format-date('[F]') => cal:weekDayInteger()"/>
 </xsl:function>

 <xsl:function name="cal:weekDayInteger" as="xs:integer">
   <xsl:param name="wd" as="xs:string"/>
   <xsl:choose>
     <xsl:when test="$wd eq 'sunday'">
       <xsl:sequence select="0"/> 
     </xsl:when>
     <xsl:when test="$wd eq 'monday'">
       <xsl:sequence select="1"/> 
     </xsl:when>
     ...
     <xsl:otherwise>     
       <xsl:message terminate="yes" select="'Invalid input'"/>
   </xsl:otherwise> 
 </xsl:choose>
 </xsl:function>

曜日を0-6のxs:integerで取得することにします。日付の曜日自体はfn:format-date(‘[F]’)で取得できますが、これをxs:integerに置き換えます。これは次回以降、moduloを使って日付の表を埋めていくためです。

「=>」はXSLT 3.0から使える記法で、処理の見た目がすっきりします。cal:weekDayIntegerについてはXSLT 3.0的にはmap{‘sunday’:0, …}のように曜日のstringと対応付ける整数をまとめて、それを展開する形がより望ましいかもしれません。2.0でも外部XMLや、xsl:chooseではなくXPathのifなどにまとめると記述量は減ります。xsl:otherwiseではmessage@terminate=”yes”で処理を強制終了していますが、ライブラリなどとして整備するなら分岐処理前にxsl:assertやxsl:tryなどで対応しておきたいところです。

  <xsl:function name="cal:getFirstDayOfTable" as="xs:date">
   <xsl:param name="firstDay" as="xs:date"/>
   <xsl:param name="weekStart" as="xs:integer"/>
     <xsl:variable name="weekDayOfFD" select="cal:getWeekDay($firstDay)"/>
     <xsl:choose>
       <xsl:when test="$weekDayOfFD eq $weekStart">
         <xsl:sequence select="$firstDay"/>
       </xsl:when>
       <xsl:otherwise>
         <xsl:variable name="dur" select="'P' || string(abs($weekDayOfFD - $weekStart)) || 'D'" as="xs:string"/>
         <xsl:sequence
           select="(xs:dateTime($firstDay) - xs:dayTimeDuration($dur)) =>xs:date()"/>
       </xsl:otherwise>
    </xsl:choose>
 </xsl:function>

その月の最初の日(xs:date)と、左端に来る曜日(xs:integer)を引数にして、初週の左端にくる曜日を取得します。

最初の日の曜日をvariableで持つことで、後で使用しやすくしています。この日が始まりの曜日と一緒なら後の計算はいらないので分岐させます。整数同士の比較です。

一緒でない場合、最初の日から曜日のギャップ分遡った日付を取得する必要があります。

最初の日をdateTimeにキャストし、そこにdayTimeDurationでギャップ分の日をマイナスし、それをxs:dateに戻します。

結果を確認してみましょう。2022年1月のカレンダーの表(日曜始まり)ならば、入力「2022-01-01」に対し「2021-12-26」が期待する結果となります。

<xsl:param name="dateArg" as="xs:date" />
 <xsl:template name="xsl:initial-template">
   <xsl:variable name="weekStart" select="0" as="xs:integer"/>
   <xsl:message>
     <xsl:sequence select="xs:date($dateArg) =>
       cal:getFirstDayOfTable($weekStart)"/>
   </xsl:message>
 </xsl:template>

XSLT 3.0では、ダミーのソースXMLファイルを用意しなくとも上のように「xsl:initial-template」という特殊な名前のテンプレートを使うなどして直接XSLTプログラムを走らせられます。グローバルのパラメータdateArgに入力した月始めのxs:dateを処理した結果を表示してくれます。

果たして私の環境では「2021-12-26」が出力されました。

考慮するケースが足りないかもしれません。無保証であることにくれぐれもご留意ください。

他、関数などに落としこめる事項としては年度の切り換えがあります。これは次回取り組みたいと思います。XSL-FOまでいきませんでした……。

関連記事

XSL-FO 試行錯誤 カレンダーを自動生成したい(構想編)

関連資料

XSL Transformations (XSLT) Version 3.0
W3C Recommendation 8 June 2017



XSLTを学ぶ (2) ノードツリーとノードの親子、子孫関係

前回([1])はXPathでは7つのノードが定義されている、と説明しました。このうち重要なのは、ルートノード、要素ノード、属性ノード、テキストノードです。この4種類のノードについてもう少し詳しく見てみましょう。

例えば次のようなXML文書があったとします。このXMLの文書要素はdocです。

<!–?xml version=”1.0″?–>
 <doc>
  <body>
   <p s=”man1″>Hello! How are you?</p>
   <p s=”man2″>I am fine, thank you.</p>
  </body>
 </doc>

このXMLをXPathのノードツリーとして表しますと次のようになります。
XSLT

ノードには親子(parent, child)になるものがあります。ノードツリーで実線でしめした箇所が親子関係になります。子孫(descendant)ノードとはあるノードの子供と子供の子孫ノードです。

兄弟(sibling)ノードは同じ親の子供ノードです。

親になれるノードはルートノードと要素ノードのみです。子供になれるノードは要素ノードとテキストノードです。ルートノードは最上位ですので親をもちません。逆にテキストノードは最下位ですので子を持ちません。

やっかいなのは属性ノードです。要素には関連する属性があります。要素ノードはそれらの属性ノードの親です。しかし、属性ノードは要素の子ではないと規定されています。また属性ノードは子を持ちません。

属性ノードとして扱われるのは、要素に明示的に指定されているもの、または、DTDでデフォルト値が明示的に規定されているものです。DTDで値が#IMPLEDになっていて要素に指定されていない属性や、xml:lang、xml:spaceのようなある要素に指定されているとき、その子孫に継承することになっている属性は、その子孫では属性ノードとして扱われません。

テキストノードは、要素の内容の文字列をできるだけ長くなるように結合したものです。従って、テキストノードには、直前・直後の兄弟はありません。

要素ノードの文字列値とは、要素ノードの子孫であるテキストノードを、XML文書に現れる順に結合したものです。ルートノードの文字列置はXML文書のすべてのテキストです。

[1] XSLTを学ぶ (1) XMLのツリーモデルとXPath/XSLTのツリーモデルではルートの意味が違う
[2] XPath データモデル

★AH Formatter XML関連出版物の紹介

次回:
XSLTを学ぶ(3)パスとは

初回:
XSLTを学ぶ(1)XMLのツリーモデルとXPath/XSLTのツリーモデルではルートの意味が違う