タグ別アーカイブ: XPath

【動画公開】XSLT超入門3, CSS組版スライドと補足

ちょっと一息アンテナハウスウェビナー「XSLT超入門 3 XPathについて」を終えました。ご視聴いただいた方、ありがとうございます。お時間の合わなかった方、動画が公開されたので、ぜひご覧ください。


Zoomの注釈機能

発表直前に気付いたのですが、Zoomの「画面の共有」ボタンの横に、いつの間にやら「注釈」というボタンが増えていたので早速使ってみました。いかがでしたでしょうか。

PowerPointスライドと違い(おそらくほとんどの)PDFビューアにはレーザポインタ機能がないので、助かりますね。(他機能として、テキストも表示できます。スライド表示中に「これ補足した方がいいかな」といった思いつきでテキストを追加したりはPDF注釈でも実現できますが、操作の切り換えの手間があるため、Zoomにあるに越したことはない、くらいの感想です。)

CSS組版スライド

担当したここ数回のウェビナーではPowerPointでスライドを作成していました。今回も途中まではそうだったのですが、内容の大幅な修正を行うついでにAH FormatterでHTML+CSS組版によるPDFスライドに移行しました。

以前XSL-FOでスライドを組版したことを紹介しました。スライド発表程度の長さであれば単一HTMLでもあまりテキスト分量はかさみません。今回、HTMLが650行弱、CSSは240行程度でした。実作業としてはSCSSで記述して変換していたので……240行程度でした。まあ、セレクタ部分は簡潔になりますが、改行・インデントとしてはあまり変わらないんですね。CSSについてはAH CSS Formatterの既定のCSSを読み込んだ上でなので、0から組むのであればもう少し嵩むでしょう。

VS CodeでSCSSを編集 with AH CSS Extension

SCSSの編集はVS Codeで行いました。VS CodeでのCSSの補完・サジェストについては、@media printなども最初からある程度対応しています。
加えて先日、AH CSS Formatterの豊富な拡張仕様を入力補助してくれるAH CSS Formatter Extensionが公開されたので早速使っています。SCSSファイル編集でも有効なようです。

https://github.com/AntennaHouse/ahformatter-vscode-css-ja

「HTMLソースでスライドを作成する」というと、気になるのは「発表スライドとWebページを同時作成できるか?」という点ではないでしょうか。結論としては「内容次第」ということに落ち着いてしまうのですが、「ある程度まで同一ソースで作成する」ことを目標とすると、方針として押さえるべき点は次のことでしょう。

  • HTMLソースはWebページ向けが主、スライドは従
    • スライド組版時はdisplay:noneにする情報を決めておく
    • スライド向けCSSを先行する場合、とくに注意するのはfloat配置とheightの100%

もちろん、広告を目的とするWebページなどで一度に入る情報量を絞ることなどはよくあります。ここではある程度技術的内容のプレゼンとそのWebページ化を前提としています。

スマホ向けとPC・タブレット向けのページを同じソースで作成する場合にスマホ向けをメインにレイアウトすることを「モバイル・ファースト」といったりしますが、Webページとプレゼンテーションの関係はもう一捻りあります。
モバイルとそれ以外で異なるのは、主に画面サイズです(より正しくは前提とする通信環境によるリソースの出し分けなどもあります)。
一方、Webページとプレゼンにおいて、Webページはそれのみですが、プレゼンは(通常)「スライド+発表者+発表」で構成されます。この違いは「読者側が受動的か能動的か」「発表者による注意対象・情報量のコントロール」といった差異を生みます。

HTMLソースの話に戻ると、ある変換を行うとき、情報量は落とす方が楽で、足すのはかなり困難です。
つまり「より情報量を必要とする方を主とし、そうでないものを従とする」ということになります。
プレゼンは発表という追加情報を前提とするため、スライドにWebページと同じ情報量を与えると情報量が過多になりがちです。

CSSでは手軽に表示を隠蔽する方法としてdisplay:noneがあります。これは、プレゼンでは口頭で与える情報を、Webページではテキストで表示する、という目的にも使えます。

@media print {
.web-page-only {
  display:none;
  }
}

前提として、「スライド上隠蔽されても前後の話は分かるようにする」というライティングが必要です。なので、パラグラフライティングの基本や、DITAにおけるshortdescのように、「これは最低限最初に伝える」という文章から書いていくよいでしょう。

ページ向け媒体ではフロートはページの上端、下端を前提に書けます。これはpositionについても同様です。

XSLT超入門3の補足

さて、発表の補足です。

何度か述べましたが、XPathの特長は、簡単には「ある文書で、目的の箇所を簡便な記法で指定できる」「取り出した値の加工が行える」ことにあります。ただ「hoge/fuga/…」と書きつらねるだけでなく、軸や述部といった機能・概念を使うことで、他の言語、というかDOMインターフェイスでは難しいこともスマートに行える、ということをXSLT超入門3ではフューチャーしたのですが、いかがでしたでしょうか。
「ここが分からないのでここをじっくりやってほしい」などの要望を、メール・SNS・動画へのコメントなどでいただけると幸いです。

さて、発表について、事前の予定から大きく削った箇所としては2箇所です。データモデルと、XPath 1.0のコードを3.1でリライトするという内容です。

データモデルについては、XPath 2.0でいかに整理されたか、3.0(3.1)で何が拡充されたか、の詳細を削りました。このあたりについてはW3CのXDMのページに図示があるので内部処理モデルに興味のある方はご参照ください。

XPath 3.1でのリライトについては、題材として丁度良いバランスのものを見つけるのはかなり難しかったため、構成から削除しました。「XPath 1.0でまともに処理できるように記述した上で、XPath 3.1で明らかに改善されるように書き直す」ということの難しさは、「そもそも1.0で書けなかった処理を拡充したものがほとんど」「1.0では(XSLT上では)独自functionの定義もできないので、XSLTも多分に含むことになる」といった点にあると感じます。XSLT 1.0のコードでの超絶技巧についてはfunctXライブラリが有名でしょうか。

超絶技巧はおいておいて、もう少し簡単なものはあります。

XPath 1.0(XSLT 1.0)
<xsl:template match="*[contains(@class, ' topic/ph ')]"> 
  ...
</xsl:template>
XPath 3.0(3.1) (XSLT 3.0)
<xsl:template match="*[contains-token(@class, 'topic/ph')]"> 
  ...
</xsl:template>

行っているのはDITAの@class処理で、スペース区切りで続く文字列の部分一致判定です。contains()では含まれてさえいれば真を返してしまうので、topic/ph前後にスペースは必須です。ないと「mytopic/ph」なども一致してしまいます。
contains-token()は、なんだったら名指しで「HTMLやDITAのclass処理に使えるよ」と書かれているのでさもありなん。

コア関数についてはかなり駆け足で紹介しました。コード例をバーッと載せていましたが、コードをしっかり読んでもらうことは意図していません。
これは構成上割と悩みどころで、関数名と処理内容を淡々と流すのであれば、ウェビナーよりもWebサイトや本などを参照するように誘導しても良いと考えています。あるいは数時間~数日のワークショップであれば、サンプル、基本、応用、基本、……といった進め方をしたと思います。

とくにXPath 1.0では、関数型言語らしい「関数を組み合わせて使う」ことはなかなか難しいため、その有用性のアピールを分かりやすく行うのは難しくなっています。

発表中、contains()が(2.0)となっていましたがこれは記述ミスで、XPath 1.0からあります。


XProcの紹介

makeが良い、悪い」という話が一部で盛り上がっているのを見ました。
結局ツールなので、「用途に向いていなくても慣れなどを取る」か「学習が必要でも別のものを使う価値が見合う」かといった、状況に合わせた選択が必要になるでしょう。個人的にはファイルの絶対パスをハードコードするのは可能な限り避けてほしいですが。

ビルドツールにも様々な得手不得手があるものですが、XMLに特化したものもあります。そんな導入で、XProcの話題です。

XProc: An XML Pipeline LanguageはXML文書やその他の処理を記述するための言語です*1
W3CのページのAbstractには“Pipelines generally accept zero or more XML documents as input and produce zero or more XML documents as output.”とあります。ステップ処理として記述ができる、ファイル操作についてもある程度はできるなど他にもありますが、XSLTとは目的も書き味も結構違います。

2010年に1.0がW3C仕様として勧告された後、勧告としてはそれが最新の状態がまだ続いています。しかし、2020年8月19日にXProc 3.0の最終ドラフトが上がっており*2、期待が持てるようになってきました。Editor’s Draftは8月31日に更に更新されていますが。ちなみに2.0は諸般の事情で見送られています。

また、XMLPressから2020年4月に『XProc 3.0 Programmer Reference』(Erik Siegel)*3が刊行されています。W3CのXProc仕様のEditorであるNorman Tovey-Walsh氏による推薦文もついていますし、Erik Siegel氏も XProc 3.0 editorial teamのメンバーです。
XProc 3.0についてはドラフト版仕様とこの本を読むのが確実でしょう。

XProc 3.0でのもっとも大きな改良点はXSLT同様にXPath 3.1に対応したことだと思いますが、今回はXProcの雰囲気だけ紹介します。


<!-- 1. Pipeline document: -->
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0">

  <!-- 2. Pipeline port declarations: -->
  <p:input port="source" primary="true" />
  <p:output port="result" primary="true" />
  <!-- 3. Convert document to HTML: -->
  <p:xslt>
    <p:with-input port="stylesheet" href="xsl/basic-conversion.xsl" />
  </p:xslt>
</p:declare-step>

上のコードは『XProc 3.0 Programmers Reference』*3「Getting started with XProc」にある例です。
<p:declare-step>がルート要素です。inputやoutputを省略できる<p:pipeline>も使用できますが、
2行程度では大して労力は変わらないですね。

XMLSchemaのネームスペースが宣言されているのは、XSLT同様asで型を明示できるからです。

書かれた処理を簡単に書けば、「入力をxsl/basic-conversion.xslのXSLTで処理し、出力へ渡す」ということになります。しかし、よくわからないプロパティportの存在がありますね。それぞれの指定で想像が付くかと思いますが、inputによる入力を処理対象(source)というportにつなぎ、with-inputによる入力は(XSLT)スタイルシートというportにつなぎ、このスタイルシートによってsourceを処理します。そしてresultというportにつながった出力へ結果を渡す、という流れです。こう書くと同語反復をしているように感じられるかと思いますが、それぞれのportにきたものをステップ処理していく流れが直観に近い記述で書けるということです。他のportとしては、スキーマの検証のスキーマを渡すためのschemaなどがあります。
<p:xslt><p:validate-with-xml-schema>はデフォルトのstepです。portは決まったstepと結びついていて、portに渡したものは結びついたstepが記述されていれば、そのstepで処理されます。デフォルトのstepとport、そして独自のstepと結びついた独自のportを記述していくことがXProcの基本となります。

*1 https://www.w3.org/TR/xproc/
*2 https://spec.xproc.org/lastcall-2020-08/head/xproc/
*3 https://xmlpress.net/publications/xproc-3-0/



XSLT 3.0(XPath 3.1)JSONをXMLに変換せずに利用する

先週はXMLの形にしたJSONを、XMLにしないまま扱う方法について紹介します。CSLはでてきません。

XPath 3.1*1で名前にJSONが入る関数は4つです。

  • fn:parse-json($json-text as xs:string?, $options as map(*))
  • fn:json-doc($href as xs:string?, $options as map(*))
  • fn:json-to-xml($json-text as xs:string?, $options as map(*))
  • fn:xml-to-json($input as node()?, $options as map(*))

$optionsについては説明しません。JSONをテキストとして引数にとるのがfn:parse-json()fn:json-to-xml()
fn:json-doc()は外部JSONファイルを読み込むときなどに指定します。fn:unparsed-text()で取得したテキストをfn:parse-json()へ適用するのと
大体同じことを行います。今回取り上げるのはfn:parse-json()fn:json-doc()についてです。

mapとarray

関係する名前空間は次に挙げるものです。

  • fn=”http://www.w3.org/2005/xpath-functions”
  • map=”http://www.w3.org/2005/xpath-functions/map”
  • array=”http://www.w3.org/2005/xpath-functions/array”

XPath 3.1のmap構造とarray構造は一般的なプログラミング言語におけるそれとほぼ同じもので、JSONオブジェクトをそのままの形で格納できます。
先週登場したJSONを見てみましょう。

{
    "items": [
        {
            "id": "7646893/2E3MJB9A",
            "type": "book",
            "title": "スタイルシート開発の基礎",
            "publisher": "アンテナハウス株式会社",
            "publisher-place": "Tokyo",
            "event-place": "Tokyo",
            "ISBN": "978-4-900552-23-4",
            "language": "ja",
            "author": [
                {
                    "family": "アンテナハウス株式会社",
                    "given": ""
                }
            ],
            "issued": {
                "date-parts": [
                    [
                        2016,
                        5
                    ]
                ]
            }
        }
    ]
}

このJSONを外部ファイルとして取り込むには次のように記述します。

<xsl:param name="input" >'exported-data.json'</xsl:param>
...
<xsl:variable as="map(*)" name="jsonMap" select="fn:json-doc($input)"/>
<!-- または -->
<xsl:variable as="xs:string" name="json-text" select="fn:unparsed-text($input)"/>
<xsl:variable as="map(*)" name="jsonMap" select="parse-json($json-text)"/>

XMLに変換した先週と、mapやarrayを活用する今回はこの後がまったく異なります。

次の記述で、先頭のtitleを一息で取得します。

<xsl:value-of select="map:get(array:head(map:get($jsonMap, 'items')), 'title')" />

まず、最も外側のmapから、itemsのキーを持つ要素を取得します(map:get($jsonMap, 'items'))。
キーitemsの値はarray型です。このままmap:get()を行おうとしてもできませんから、arrayの先頭を取り出すarray:head()を使用しています。ところで上の記述はパイプ演算子を使えば次のように書けます。

<xsl:value-of select="map:get($jsonMap, 'items') => array:head() => map:get('title')" />

mapのarrayでは、map:find()を利用することで指定したキーの値を配列で得ることもできます。挙動の詳細はXPath 3.1*1かXSLT 3.0*2のページを確認してください。

typeの値が何種類かに決まっていて、その種類を元にif文の判定をしたいのであれば、次のように書けます。

     <xsl:variable as="map(*)" name="item" select="map:get($jsonMap, 'items') => array:head()" />
      <xsl:if test="map:get($item,  'type') =  'book' or 'proceedings'">
      <xsl:value-of select="map:get($item, 'type')" /> <!-- book -->
      </xsl:if>

もちろんXMLの構造に変換しても同じことはできますが、より一般的なプログラミング言語に近い形で扱えています。

mapやarrayとして扱うために、JSONテキストとして記述してパースしたり、select="map{"key":value}"のような書き方をする以外に、<xsl:map><map-entry>を使うことができます。

mapの操作はエントリの追加やキー指定での削除などの他、map:merge()によるmapの統合、map:for-each()によるmap要素単位での関数適用などが可能です。arrayの方はarray:fold-left()array:fold-right()の畳み込み関数や、array:for-each()でarrayの要素ごとに関数適用などなど、XML形式を扱うよりもすっきりした構文で記述が可能な関数が用意されています。

注意しなければならないこととして、mapであるかarrayであるかを間違えるとうまく値を取り出せません。$itemnodeでもないので、直接xml-to-json()は使えません。

仕様やSaxonのドキュメントとにらめっこをしながら紹介してきたXSLT 3.0とXPath 3.1のJSONの扱いですが、誤りなどありましたらご指摘ください。

*1 https://www.w3.org/TR/xpath-31/
*2 https://www.w3.org/TR/xslt-30/


参考資料



XSLTを学ぶ (11)論理値(Boolean)と関連する基本的な関数のいくつか

第9回[1]では、式の生成規則を辿っている途中に幾つかわからない項目が出てきました。その一つは、ノード集合に対する演算の扱いです。ここでは論理演算や比較の演算におけるノード集合の扱いを調べてみます。

論理値(Booleans)[2]

or式は左辺と右辺の両方のオペランドをboolean関数と同じように論理値に変換して評価します。どちらかが真であれば真となり、そうでないとき偽となります。boolean関数はXPathで規定するコア関数の一種であり、引数を論理値に変換します。ノード集合は空でないとき真となります(詳しくは下に紹介しました)。

and式は両方のオペランドをboolean関数と同じように論理値に変換して評価します。両方が真であれば真となります。そうでないとき偽となります。

第9回の規則[23]の関係式(RelationalExpr)すなわち <=、<、>=、>、および等価式(EqualityExpr)すなわち=または!=、は左辺と右辺の二つのオペランドを次のように比較します。(ノード集合が関係するときだけを取り上げてみます。)

ノード集合同士を比較するときは、最初のノード集合と二つ目のノード集合の中に、ノードの文字列値同士を比較したときに真になるようなノードが含まれている時に限り、その比較は真になります。

ノード集合と数値の比較では、ノードの文字列値をnumber関数で数値に変換し、数値同士として比較します。ノード集合の中に、数値同士を比較して真になるようなノードが含まれていれば、比較が真になります。

ノード集合と文字列値の比較では、ノードの文字列値と他方の文字列値を比較し、文字列値同士を比較して真になるようなノードが含まれていれば、比較が真になります。

ノード集合と論理値の比較では、ノード集合をboolean関数で論理値にして、他方の論理値と比較します。

XPathには基本的な関数が規定されています。次に今日出てきた関数を紹介します。

boolean関数[3]
形式: boolean boolean(object)

boolean関数は引数を次のように論理値に変換します。
・数値は、ポジティブゼロでもネガティブゼロでもなく、NaN(Not-a-Number:数値でない)でもないとき真となります。
・ノード集合は空でないとき真となります。
・文字列は、長さがゼロでないとき真となります。
・四つの基本型以外のオブジェクトはその型依存の方法で論理値に変換されます。

number関数[4]
形式: number number(object?)

number関数は引数を次のように論理値に変換します。
・数値の後が空白で、前にオプションのマイナス記号、さらにその前のオプションの空白から構成される文字列は、その文字列によって表現される数学的値に最も近いIEEE 754数に変換されます。それ以外の文字列はNaNに変換されます。
・論理値の真は1に変換され、論理値の偽は0に変換されます。
・ノード集合は最初にstring関数を使ったように文字列に変換され、次いで引数が文字列のときと同様に数値に変換されます。
・四つの基本型以外のオブジェクトはその型依存の方法で数値に変換されます。

もし、引数が省略されたときは、既定値として文脈ノードのみから成るノード集合が使われます。

string関数[5]
形式: string string(object?)

文字列関数はオブジェクトを次のように文字列に変換します。
・ノード集合はノード集合の中で文書順で最初のノードの文字列値を返すことで文字列値に変換されます。ノード集合が空のときは空文字列が返されます。
・数値は次のように文字列に変換されます。
 ・NaNは文字列NaNに変換されます。
 ・ポジティブゼロは文字列0に変換されます。
 ・ネガティブゼロは文字列0に変換されます。
 ・ポジティブな無限大は文字列Infinityに変換されます。
 ・ネガティブな無限大は文字列-Infinityに変換されます。
 ・数値が整数型のときは、小数点や先行するゼロのない十進数で表現されます。数値がマイナスのときは、マイナス記号が先行します。
 ・整数でないときは、小数点を含み、小数点の前に少なくとも1個の十進数があり、小数点の後に少なくとも1個の十進数があり、マイナスのときは、マイナス記号が先行する形式となります。不要な先行するゼロがあってはならず、少数点の後には他のIEEE 754と区別するのに必要なだけの数値がなければなりません。
・論理値の偽は文字列falseになり、論理値の真は文字列trueになります。
・四つの基本型以外のオブジェクトはその型依存の方法で文字列に変換されます。

もし、引数が省略されたときは、既定値として文脈ノードのみから成るノード集合が使われます。

なお、string 関数は表示用に整形するために用意されているものではありません。整形にはXSLTのxsl:number要素を使います。

[1] XSLTを学ぶ(9)ステップの文法を追求する-述部(Predicates)と式
[2] 3.4 Booleans
[3] boolean関数
[4] number関数
[5] string関数

前回:
XSLTを学ぶ(10)式によるノード集合の作成、ノード集合の和集合、フィルター式

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


XSLTを学ぶ (10)式によるノード集合の作成、ノード集合の和集合、フィルター式

前回[1]の定義[19]にあるようにロケーションパスをパス式として使うことができます。そのときパス式はロケーションパスによって選択したノードの集合を返します。

ノードの集合は、’|’オペレータで和集合にできます(前回の定義[18])。定義[18]だけでは分かりませんが、XPath仕様書[2]には「オペランドはノード集合でなければならない」とされています。

以上により、の中では、ロケーションパスでノード集合を作り、ノード集合の和集合を作ることができます。

定義[19]では、パス式の生成方法にFilterExpr(フィルター式)を使う方法もあります。フィルター式には、FilterExprの後に述部(Predicate)を付けるものがあります。このときの述部は「ロケーションパスで使われるときと同様に式をフィルターするために使われる。フィルターされる式がノード集合にならなければエラーとなる。述部は、ノード集合をchild軸に関してフィルターする」とされています。

つまり、パス式がFilterExpr Predicateで生成されるとき、FilterExprはノード集合でなければなりません。

FilterExprは基本式(PrimaryExpr)で構成されます。基本式の中でノード集合を生成できるのは、VariableReference(変数参照)、()で囲った式、FunctionCallです。

[15] PrimaryExpr ::= VariableReference
| ‘(‘ Expr ‘)’
| Literal
| Number
| FunctionCall

前回述部に二種類あることに注意しましたが、この違いは()で囲った式と組み合わせると明確になります。

XML文書中で、例えばpが要素ノードであるとき、パス式p(child::p)自体は基本式ではありませんが、(p)は基本式です。

述部[]と組み合わせたとき、次の二種類となります。
(1) p[1]はステップであり、ロケーションパスです。
(2) それに対して、(p)[1]はフィルター式です。

この場合は違いは明確ではありませんが、次のような場合は違いが明確になります。

preceding::foo[1] と(preceding::foo)[1]の比較

preceding軸の定義は次のようになっています。「起点ノードと同じ文書の中で、文書順で起点ノードの前にあるすべてのノードを含みます。但し、先祖と属性ノードと名前空間ノードは除外します。」(XPath 2.2 Axes)

軸には文書順の軸と、逆文書順の軸があります。起点ノードと起点ノードよりも文書の中で後のノードを含む軸は文書順の軸です。起点ノードと起点ノードよりも文書の中で前方のノードを含む軸は逆順の軸です。preceding軸は、逆順の軸に属します(XPath 2.4 Predicates)。

ノード集合のメンバーの軸に関する近接位置(proximity position)とは、文書順の軸ではメンバーを文書順に並べたときの順番、逆順の軸では文書で出現する順序の逆に並べたときの順番です。

そして、述部[]はノード集合を絞り込んで新しいノード集合を作ります。述部の中の式は、ノード集合の各ノードを起点のノードとし、近接位置を文脈位置として評価します。評価結果が真であれば、そのノードは新しいノード集合に含まれます。

そこで、preceding::foo[1] は起点ノードの先行ノードであるfoo要素ノードを文書の逆順にたどり、そのときの1番目のfoo要素ノードを選択します。

(preceding::foo)[1] は[1]に適用する軸はchild軸なので先行ノードであるfoo要素の集合の最初のノードを選択します[3]

[1] XSLTを学ぶ(9)ステップの文法を追求する-述部(Predicates)と式
[2] XML Path Language (XPath) Version 1.0
[3] 3.3 Node-setsには、このように書かれています。ちょっと強弁の印象がありますが。

【広告】
Formatter(XML)関連技術書、PDF関連書籍ご紹介

次回:
XSLTを学ぶ (11)論理値(Boolean)と関連する基本的な関数のいくつか

前回:
XSLTを学ぶ(9)ステップの文法を追求する-述部(Predicates)と式

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


XSLTを学ぶ(9) ステップの文法を追求する-述部(Predicates)と式

第3回[1]からパスの文法を調べてきました。パスの重要な構成要素にステップがあり、ステップ(省略)は軸、ノードテスト、述部(オプションなので必須ではない)から構成されることを調べました。

ステップの最後の構成部品はオプションの述部です。第6回[2]で見ましたが、述部はを[]で囲った形式です。

式とはどんなものでしょうか? まず、XPathの式の生成規則[3]をトップから辿ってみます。

スタートは定義の[14]ですが、式Exprとは、OrExprです。[21]orExprはAndExprを’or’でつなげたものです。そして、[22]AndExprとは、EqualityExprを’and’でつなげたものです。[23]EqualityExprは、RelationalExprを’=’でつなげたものまたは’!=’でつなげたもの。[24]RelationalExprは、AdditiveExprを'<‘ ‘>”<=’ ‘>=’でつなげたもののようです。つまり、このあたりまでは、式はAdditiveExpr(加算式)の論理演算ということになります。

[14] Expr ::= OrExpr
[21] OrExpr ::= AndExpr | OrExpr ‘or’ AndExpr
[22] AndExpr ::= EqualityExpr | AndExpr ‘and’ EqualityExpr
[23] EqualityExpr ::= RelationalExpr
| EqualityExpr ‘=’ RelationalExpr
| EqualityExpr ‘!=’ RelationalExpr
[24] RelationalExpr ::= AdditiveExpr
| RelationalExpr ‘<‘ AdditiveExpr | RelationalExpr ‘>’ AdditiveExpr
| RelationalExpr ‘<=’ AdditiveExpr | RelationalExpr ‘>=’ AdditiveExpr

ということで、さらにAdditiveExprとは何かを見てみます。[25]ではAdditiveExprは、MultiplicativeExprを’+’または’-‘でつなげたものです。

[25] AdditiveExpr ::= MultiplicativeExpr
| AdditiveExpr ‘+’ MultiplicativeExpr
| AdditiveExpr ‘-‘ MultiplicativeExpr

[26]ではMultiplicativeExprとは、UnaryExpr(単項式)、またはMultiplicativeExprにUnaryExprを掛けた(’*’)、またはMultiplicativeExprをUnaryExprで割り算(’div’)、剰余算(’mod’)したものです。
[26] MultiplicativeExpr ::= UnaryExpr
| MultiplicativeExpr MultiplyOperator UnaryExpr
| MultiplicativeExpr ‘div’ UnaryExpr
| MultiplicativeExpr ‘mod’ UnaryExpr
[34] MultiplyOperator ::= ‘*’

UnaryExprは、UnionExprまたはその前にマイナス記号(’-‘)をつけたもの。

[27] UnaryExpr ::= UnionExpr | ‘-‘ UnaryExpr

UnionExprは、ひとつのPathExpr(パス式)、またはそれを’|’で結合したものです。

[18] UnionExpr ::= PathExpr| UnionExpr ‘|’ PathExpr

PathExpr式は、LocationPath(ロケーションパス)、またはFilterExprまたは、FilterExprと相対ロケーションパスを’/’、’//’ で結合したものです。

[19] PathExpr ::= LocationPath
| FilterExpr
| FilterExpr ‘/’ RelativeLocationPath
| FilterExpr ‘//’ RelativeLocationPath

ロケーションパスについては第3回[2]ですでに学びましたが、XMLツリーのノードを選択するものです。ノードの選択結果はノードの集まり(ノード集合)ですが、これに対して、掛け算(’*’)、割り算(’div’)、剰余算(’mod’)、足し算(’+’)、引き算(’-‘)、比較などの演算をするのは少し不思議な気もします。これは後ほど調べてみることにします([5])。

FilterExprの方は、PrimaryExprまたはPrimaryExprに述部(Predicate)を付けたものとなります。ここに出てくる述部はFilterExpr Predicateのように使われますが、ステップの中で出てくる述部の使われ方はAxisSpecifier NodeTest Predicate*です。この2種類の述部の使われ方の違いはなんでしょうか? これも後ほど調べてみましょう([4])。

PrimaryExprは、VariableReference(変数参照)、式を()で囲ったもの、リテラル、数値、FunctionCall(関数呼び出し)のどれかです。ですので、式には数置の四則演算も表現したものも含まれます(よく知っている初歩的な数式も含まれるということで一安心です)。

[20] FilterExpr ::= PrimaryExpr
| FilterExpr Predicate
[15] PrimaryExpr ::= VariableReference
| ‘(‘ Expr ‘)’
| Literal
| Number
| FunctionCall

最も単純なケースでは、一つの数値(Number)だけでも式となります。例えば、次のように下から辿ってみます。
(1) PrimaryExprがNumber:100
(2) FilterExprがPrimaryExpr:100
(3) PathExprがFilterExpr:100
(4) UnionExprがPathExpr:100
(5) UnaryExprがUnionExpr :100
(6) MultiplicativeExprがUnaryExpr:100
(7) AdditiveExprがMultiplicativeExpr:100
(8) RelationalExprがAdditiveExpr :100
(9) EqualityExprがRelationalExpr :100
(10) AndExprがEqualityExpr:100
(11) OrExprがAndExpr:100
(12) ExprがOrExpr:100

ということで、述部に[100]と書くことができます。

まとめますと、述部([]内)にはを書きますが、式としてはロケーションパスを書くこともできますし、また、数値、変数、関数呼び出し、数式を書くこともできる、ということになります。

途中で、いろいろわからない言葉が出てきていますので、次回以降、もう少し詳しく調べてみます。

[1] XSLTを学ぶ (3) パスとは
[2] XSLTを学ぶ (6) ステップの文法を追求するの[8]、[9]式
[3] 3 Expressions
[4] 次回(第10回)のpreceding::foo[1] と(preceding::foo)[1]の比較 の項を参照してください。
[5] ノード集合の論理演算、比較演算については第11回を参照してください。

【広告】★AH Formatter XML関連出版物の紹介

次回:
XSLTを学ぶ (10)式によるノード集合の作成、ノード集合の和集合、フィルター式

前回:
XSLTを学ぶ(8)ステップの文法を追求する-NodeTest

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


XSLTを学ぶ (8) ステップの文法を追求する-NodeTest

前回は軸について調べましたので、今回はNodeTestを調べてみます。XPath仕様では、NodeTestを次のように規定しています[1]

[7] NodeTest ::= NameTest | NodeType ‘(‘ ‘)’ | ‘processing-instruction’ ‘(‘ Literal ‘)’
[37] NameTest ::= ‘*’ | NCName ‘:’ ‘*’ |QName
[38] NodeType ::= ‘comment’|’text’|’processing-instruction’|’node’

まず主ノードタイプを次のように定義します。ステップの文法では軸の指定に続いてNodeTestを記述します。NodeTestでは軸毎に主ノードタイプに該当するノードを選択することになります。

・attribute軸の主ノードタイプは属性
・namespace軸の主ノードタイプは名前空間
・その他の軸の主ノードタイプは要素

文法上は、attribute::text()のような組み合わせができますが、これは無意味なので何も選択しません。

(1) NameTest

NameTestでは要素や属性の名前を指定して一致するものを選択します。

NameTestでは’*’を使えます。’*’は主ノードタイプが何であれすべてのノードに対して真となります。但し、NameTestの前には、軸指定子(AxisSpecifier)が置かれます。軸指定子が省略されないときは、例えば、child::* は起点ノードの子供であるすべての要素ノードを選択します。attribute::*は起点ノードのすべての属性を選択します。

ステップでは軸指定子を省略できます。するとステップは*または@*の形式となります。ステップにおいて*を指定するとchild::*であり、@*とするとAttribute::*です(第6回[3]の[5]と[13]の規定による)。

二番目のNCNameは、名前空間の接頭辞です。接頭辞はXML名前空間の仕様で規定されています[2]

xmlns:svg=”http://www.w3.org/2000/svg”

という名前空間の宣言があるとしますと、NCNameは’svg’にあたります。svg:*はsvg名前区間にあるすべてのノードを選択し、child::svg:*は文脈ノードの子供でsvg名前空間に属する要素ノードをすべて選択します。

QNameは名前空間接頭辞(オプション)で修飾された名前です。

(2) NodeType ‘(‘ ‘)’

NodeTestは、ノードの種類でノードを選択することもできます。ノードの種類は次の4種類です。

comment()
text()
processing-instruction()
node()

例えば、child::text()は起点ノードの子供のテキストノードを選択します。
comment()はコメントノード、processing-instruction()は処理命令のノードを選択します。
node()は任意の種類のノードを選択します。

ステップで、node()と記述すると、軸を省略したことになり、child::node()を意味します。従って、要素ノードのみを選択します。

[1] 2.3 Node Tests
[2] 3 Declaring Namespaces
[3] XSLTを学ぶ(6)ステップの文法を追求する

【広告】★AH Formatter XML関連出版物の紹介

次回:
XSLTを学ぶ(9) ステップの文法を追求する-述部(Predicates)と式

前回:
XSLTを学ぶ(7)ステップの文法を追求する-軸とは

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


XSLTを学ぶ (7) ステップの文法を追求する-軸とは

パスは、ステップを’/’オペレータで結合したものであること、そしてステップは、AxisSpecifier NodeTest Predicate*の形式または省略形で記述されることを前回まで[1]に学びました。

ステップの例として、child::para[position()=1]を取り上げてみます。child::が軸指定子(AxisSpecifier)であり、あるノードを起点とするとき、その起点ノードの子供を選択します[2]

その後のparaがNodeTestでノードの名前の指定であり、child::paraは、起点ノードの子供で名前がparaのノードを選択します。NodeTestには’*’を使うこともでき、child::*とすると起点ノードの子供であるノードをすべて選択します。

[position()=1]はオプションの述部(Predicate)です。この場合、述部はノード集合の中での最初のノードを選択します。

軸は起点のノードからXML文書のツリーの方向を指定します。第1回[3]で学びましたが、ノードには7種類があり、また、軸の名前にはancestor、ancestor-or-self、attribute、child、descendant、descendant-or-self、following、following-sibling、namespace、parent、preceding、preceding-sibling、selfの13種類があります。

この中でself軸は起点となるノード自身を含みます。起点ノードが要素ノードのとき、self、ancestor、descendant、following、precedingは一つのXML文書の(属性、名前空間を除外して)すべてのノードを網羅します。

例えば、次のようなXML文書を考えてみます。

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

このXML文書をノードのツリーで表します。仮に起点が二つ目の要素ノードpであるとします。このときself::はpノードです。そこを起点にしてancestor、descendant、following、precedingは次のような関係になります。

XSLT2

上の図では、selfの子供pの属性ノード(src=”pic1″など)は除外しています。第2回[4]で説明しましたように属性ノードは子供(child)にはなりません。XSLT仕様では、decendentは、「childまたはchildの子供である」とされています。従って、属性や(名前空間ノードも)decendentにはなりません[5]

また、属性や(名前空間ノードも)はprecedenig、followingからも除外されています。

上の図とは異なりますが、もし、属性ノードsrc=”pic1″がselfであった時は、ancestorはその親であるpノードからルートノードまでとなります[6]。しかし、 following-sibling、preceding-siblingは空です。

child、parentについては第2回[4]で説明しました。child軸、parent軸はそれぞれselfノードの子または親です。child軸は複数の子供ノードを含みます。parent軸はあるとしてもひとつの親ノードのみを含みます。

attribute軸、namespace軸はそれぞれselfの属性ノード、名前空間ノードを含みます。

[1] XSLTを学ぶ(3)パスとは
XSLTを学ぶ(6)ステップの文法を追求する
[2] XPathでは起点ノードを文脈ノードといいます。そして、以下の説明の起点ノードのところは文脈ノードになっています。
[3] XSLTを学ぶ(1)XMLのツリーモデルとXPath/XSLTのツリーモデルではルートの意味が違う
[4] XSLTを学ぶ(2)ノードツリーとノードの親子、子孫関係
[5] 2.2 Axes
[6] XPathの仕様の2.2 Axesには、文脈ノードのancestorは、文脈ノードの親(parent)と親の親、以下同じ、から成るとあります。そして、5.3 Attribute Nodesには、要素はそれに付随する属性ノードの親である、と書いてあります。従って、属性ノードのancestorにはその親である要素ノードを選択することになります。

【広告】

次回:
XSLTを学ぶ(8)ステップの文法を追求する-NodeTest

前回:
XSLTを学ぶ(6)ステップの文法を追求する

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

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


XSLTを学ぶ (6) ステップの文法を追求する

第3回([1])と前回([2])でパスには「式の構成部品としてのパス」(ロケーションパス)と「パターンの記述のためのパス」(パターンパス)の2種類あること、そしてロケーションパスは式の一部でありxsl:要素のselect属性で使われること。パターンパスは、match属性で使われることを説明しました。

パスを構成する文法を調べると、ステップがその基本的な単位になっています。パスはステップを’/’で結合して構成します。

そこで、ステップについてもう少し詳しく調べてみます。

1. ロケーションパスのステップは、XPath仕様[3]で決まっています。第3回でステップの文法の入り口として、次の項を紹介しました。

[4] Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep

ステップは軸の指定子(AxisSpecifier)とノードテスト(NodeTest)とオプション(*)の述部(Predicate)、または、省略形(AbbreviatedStep)から構成します。

ステップの省略形は ‘.’(自分自身、self::node()の省略形)、または’..’(親、parent::node()の省略形)です。

[12] AbbreviatedStep ::= ‘.’ | ‘..’

軸の指定子の定義は、次のようになっています。

[5] AxisSpecifier ::= AxisName ‘::’ | AbbreviatedAxisSpecifier
[6] AxisName ::= ‘ancestor’ | ‘ancestor-or-self’ | ‘attribute’ | ‘child’ | ‘descendant’ | ‘descendant-or-self’ | ‘following’ | ‘following-sibling’ | ‘namespace’ | ‘parent’ | ‘preceding’ | ‘preceding-sibling’ | ‘self’

軸の指定子は、軸の名前(AxisName)と’::’または省略形の軸指定子(AbbreviatedAxisSpecifier)で、軸の名前は’ancestor’から’self’まで13種類あります。

[13] AbbreviatedAxisSpecifier ::= ‘@’?

省略形の軸指定子はなにも指定しないか’@’です。なにも指定しないとデフォルトはchild::です。
@はattribute::の省略形です。その他、’//’は/descendant-or-self::node()/の省略形とされています(規則[11]省略)。

NodeTest以下は次のようになっていますが、次回以降もう少し詳しく調べてみます。

[7] NodeTest ::= NameTest | NodeType ‘(‘ ‘)’ | ‘processing-instruction’ ‘(‘ Literal ‘)’
[37] NameTest ::= ‘*’ | NCName ‘:’ ‘*’ | QName  
[38] NodeType ::= ‘comment’ | ‘text’ | ‘processing-instruction’ | ‘node’

NCName、QNameはXML名前空間([4])で規定されています。

[8] Predicate ::= ‘[‘ PredicateExpr ‘]’
[9] PredicateExpr ::= Expr 

述部は式(Expr)を'[‘ ‘]’で囲ったものです。

2. パターンの構成要素であるステップ(ステップパターン)は、XSLT仕様([5])で決まっています。その文法は次の通りです。

[5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
[6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
| (‘child’ | ‘attribute’) ‘::’

NodeTest、Predicate、AbbreviatedAxisSpecifierはXPathのステップを参照しています。XSLT仕様はXPath仕様を利用して作られていることがよく分かります。

[1] XSLTを学ぶ(3)パスとは
[2] XSLTを学ぶ(5)パターンの記述のためのパス
[3] XML Path Language (XPath) Version 1.0
[4] Namespaces in XML 1.0 (Third Edition)
[5] XSL Transformations (XSLT) Version 1.0
[6] 『スタイルシート開発の基礎』

【広告】★AH Formatter XML関連出版物の紹介

次回:
XSLTを学ぶ(7)ステップの文法を追求する-軸とは

前回:
XSLTを学ぶ(5)パターンの記述のためのパス

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


XSLTを学ぶ (5) パターンの記述のためのパス

前回[1]は、「式の構成部品としてのパス」を調べてみました。これはXPath仕様[2]で規定されているものです。

もう一つは、「パターンの記述のためのパス」があります。こちらはXSLT仕様[3]で規定されています。本書(『スタイルシート開発の基礎』[4])では、パターンについては、3.7.1 パタン(p.33)で触れています。しかし、パスの役割についてはあまり詳しく記述されていませんので、仕様書で少し詳しく調べてみます。

式は主にselectの属性の値として設定します。XSLT V1.0ではselect属性は次の要素に定義されています。

xsl:variable
xsl:param
xsl:apply-templates (ノード集合式のみ)
xsl:value-of (文字列式のみ)
xsl:with-param
xsl:sort (文字列式のみ)
xsl:copy-of
xsl:if(論理式のみ)
xsl:when(論理式のみ)
xsl:for-each(ノード集合式のみ)

パターンは、スタイルシート規則(xsl:template)のmatch属性の値として規定されています。これを含めてmatch属性が使えるのは次の三つの要素です。

xsl:template
xsl:key
xsl:number

XSLTのxsl:templateの説明の項(5 Template Rules)にはパターンについて書いてあります。それを読みますと、select属性の式は、主にソース文書から処理の対象とするノードのリストを作るのに使われるのに対して、パターンはノードに適用するテンプレート規則を識別するのに使うという関係のようです。

パターンの構文は、式の構文のサブセットです。具体的には次のようになっています。

[1] Pattern ::= LocationPathPattern
| Pattern ‘|’ LocationPathPattern

パターンは一つのロケーションパスパターンまたは、それを’/’オペレータでつなげたものです。

[2] LocationPathPattern ::= ‘/’ RelativePathPattern?
| IdKeyPattern ((‘/’ | ‘//’) RelativePathPattern)?
| ‘//’? RelativePathPattern

[3] IdKeyPattern ::= ‘id’ ‘(‘ Literal ‘)’
| ‘key’ ‘(‘ Literal ‘,’ Literal ‘)’

IDkeyPatternは式では出てきませんでしたが、IDまたはkeyとなっています。あとは式としてのロケーションパスの生成規則で出てきたもの(第(3)回[5]を参照)と大よそ対応しているようです。

[4] RelativePathPattern ::= StepPattern
| RelativePathPattern ‘/’ StepPattern
| RelativePathPattern ‘//’ StepPattern
[5] StepPattern ::= ChildOrAttributeAxisSpecifier NodeTest Predicate*
[6] ChildOrAttributeAxisSpecifier ::= AbbreviatedAxisSpecifier
| (‘child’ | ‘attribute’) ‘::’

ChildOrAttributeAxisSpecifierは、child軸とattribute軸のみになっています。

[1] XSLTを学ぶ (4) 式の構成部品としてのパスの使い方
[2] XML Path Language (XPath) Version 1.0
[3] XSL Transformations (XSLT) Version 1.0
[4] 『スタイルシート開発の基礎』
[5] XSLTを学ぶ (3) パスとは

【広告】★AH Formatter XML関連出版物の紹介

次回:
XSLTを学ぶ (6) ステップの文法を追求する

前回:
XSLTを学ぶ (4) 式の構成部品としてのパスの使い方

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


Pages: 1 2 Next