web-dev-qa-db-ja.com

XSLTドキュメントのテンプレートはどの順序で実行され、ソースXMLまたはバッファリングされた出力と一致しますか?

XSLTについていつも私を困惑させてきたことがあります。

  1. テンプレートはどの順序で実行され、そして
  2. 実行すると、(a)元のソースXMLと一致するか、(b)その時点までのXSLTの現在の出力と一致しますか?

例:

<person>
  <firstName>Deane</firstName>
  <lastName>Barker</lastName>
</person>

XSLTのフラグメントを次に示します。

<!-- Template #1 -->
<xsl:template match="/">
  <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="/person/firstName">
  First Name: <xsl:value-of select="firstName"/>
</xsl:template>

これに関する2つの質問:

  1. テンプレート#1が最初に実行されると想定しています。私がこれを仮定する理由がわかりません-それが文書の最初に表示されるという理由だけですか?
  2. テンプレート#2は実行されますか?ソースXMLのノードに一致しますが、このテンプレートに到達するまでに(2番目に実行されると仮定して)、「firstName」ノードは出力ツリーにありません。

それで、「後の」テンプレートは、「前の」テンプレートで発生したものに守られますか、それとも「前」に変換されたものを無視して、ソース文書で動作しますか? (これらの単語はすべて引用符で囲まれています。最初の段階でテンプレートの順序がどのように決定されるかほとんどわからない場合、時間ベースの問題について議論するのが難しいためです...)

上記の例では、ルートノード( "/")で一致するテンプレートがあります。実行が完了すると、出力からすべてのノードが本質的に削除されます。この場合、最初のテンプレートが完了した後、一致するものがないため、これは他のすべてのテンプレートの実行を先取りしますか?

ここまでは、操作したノードが出力に表示されないため、後のテンプレートが実行されないことに関心がありましたが、逆はどうですか? 「前の」テンプレートは、「後の」テンプレートで何かを行えるノードを作成できますか?

上記と同じXMLで、次のXSLを検討してください。

<!-- Template #1 -->
<xsl:template match="/">
  <fullName>
    <xsl:value-of select="firstName"/> <xsl:value-of select="lastName"/>
  </fullName>
</xsl:template>

<!-- Template #2 -->
<xsl:template match="//fullName">
  Full Name: <xsl:value-of select="."/>
</xsl:template>

テンプレート#1は、「fullName」という新しいノードを作成します。テンプレート#2は同じノードで一致します。テンプレート#2に到達するまでに「fullName」ノードが出力に存在するため、テンプレート#2は実行されますか?

私は、XSLTの「禅」について深く知らないことに気付きます。これまで、私のスタイルシートはルートノードに一致するテンプレートで構成されていましたが、その後は完全に手続き型です。私はこれをするのにうんざりしています。むしろ実際にXSLTを正しく理解したいので、私の質問です。

66
Deane

あなたの質問が大好きです。あなたはあなたがまだ理解していないことについて非常に明確です。物事を結び付けるものが必要です。 "XSLTの仕組み" を読むことをお勧めします。これは、あなたが尋ねている質問に正確に対処するために書いた章です。それがあなたのために物事を結びつけるかどうか聞いてみたいです。

あまり正式ではありませんが、私はあなたのそれぞれの質問に答えるのに一苦労します。

  1. テンプレートはどの順序で実行され、そして
  2. 実行すると、(a)元のソースXMLと一致するか、(b)その時点までのXSLTの現在の出力と一致しますか?

XSLT処理の任意の時点で、ある意味で、(a)と(b)として識別する2つのコンテキストがあります:source tree、そして、あなたが結果ツリーのどこにいるか。ソースツリーのどこにいるのかをcurrent nodeと呼びます。 XPathを使用して処理するノードの任意のセットを選択すると、ソースツリー全体を変更およびジャンプできます。ただし、概念的には、結果ツリーを同じ方法で「ジャンプ」することはありません。 XSLTプロセッサは、それを整然と構築します。最初に、結果ツリーのルートノードを作成します。その後、子を追加し、結果をドキュメント順に(深さ優先)構築します。 [あなたの投稿は、XSLT実験のためにソフトウェアの視覚化を再び取り上げる動機になります...]

スタイルシート内のテンプレートルールの順序は重要ではありません。スタイルシートを見ただけでは、テンプレートルールがインスタンス化される順序、ルールがインスタンス化される回数、またはルールがまったくインスタンス化されるかどうかはわかりません。 (_match="/"_は例外です。トリガーされることはいつでも確認できます。)

テンプレート#1が最初に実行されると想定しています。私がこれを仮定する理由がわかりません-それが文書の最初に表示されるという理由だけですか?

いや。ドキュメントの最後に配置しても、最初に呼び出されます。テンプレートルールの順序は重要ではありません(同じノードに一致する同じ優先度のテンプレートルールが複数ある場合のエラー状態を除きます;その場合でも、実装者にとってはオプションであり、そのような動作に依存しないでください)。 XSLTプロセッサを実行するたびにalwaysが最初に発生するのは_<xsl:apply-templates select="/"/>_の仮想呼び出しであるため、最初に呼び出されます。 1つの仮想呼び出しにより、結果ツリー全体が構築されます。それ以外では何も起こりません。テンプレートルールを定義することにより、その命令の動作をカスタマイズまたは「構成」することができます。

テンプレート#2は実行されますか?ソースXMLのノードに一致しますが、このテンプレートに到達するまでに(2番目に実行されると仮定して)、「firstName」ノードは出力ツリーにありません。

テンプレート#2(または他のテンプレートルール)は、_<xsl:apply-templates/>_ルール内のどこかに_match="/"_呼び出しがなければトリガーされません。持っていない場合、_match="/"_以外のテンプレートルールはトリガーされません。このように考えてください。テンプレートルールがトリガーされるためには、入力内のノードと一致するだけではいけません。 processに選択したノードと一致する必要があります(_<xsl:apply-templates/>_を使用)。逆に、処理を選択した回数だけノードに一致し続けます。

[_match="/"_テンプレート]は、最初のテンプレートが完了した後、一致するものが何もないため、他のすべてのテンプレートの実行を先取りしますか?

そのルールは、_<xsl:apply-templates/>_を含むどこにも残りを先取りします。ソースツリーで処理できるcouldノードはまだたくさんあります。彼らはいつもそこにいて、ピッキングの機が熟しています。それぞれを必要な回数だけ処理します。ただし、テンプレートルールを使用してそれらを処理する唯一の方法は、_<xsl:apply-templates/>_を呼び出すことです。

ここまでは、操作したノードが出力に表示されないため、後のテンプレートが実行されないことに関心がありましたが、逆はどうですか? 「前の」テンプレートは、「後の」テンプレートで何かを行えるノードを作成できますか?

「以前の」テンプレートが新しいノードを作成して処理するわけではありません。 「以前の」テンプレートは、同じ命令(_<xsl:apply-templates_)を使用して、ソースツリーからより多くのノードを順番に処理します。毎回異なるパラメーター(コンテキストとselect属性によって決定される処理するノード)を使用して、同じ「関数」を再帰的に呼び出すと考えることができます。

最終的に、同じ「関数」(_<xsl:apply-templates>_)の再帰呼び出しのツリー構造スタックが得られます。そして、このツリー構造は、実際の結果に対してisomorphicです。誰もがこれを認識しているわけでも、このように考えているわけでもありません。それは、効果的な視覚化ツールがまだないためです...まだです。

テンプレート#1は、「fullName」という新しいノードを作成します。テンプレート#2は同じノードで一致します。テンプレート#2に到達するまでに「fullName」ノードが出力に存在するため、テンプレート#2は実行されますか?

いや。一連の処理を行う唯一の方法は、そのように明示的に設定することです。新しい_$tempTree_要素を含む変数(_<fullName>_)を作成し、この_<xsl:apply-templates select="$tempTree">_のようにitを処理します。 XSLT 1.0でこれを行うには、変数参照を拡張関数(たとえば、exsl:node-set())でラップする必要がありますが、XSLT 2.0ではそのまま機能します。

元のソースツリーからノードを処理する場合でも、構築する一時ツリーで処理する場合でも、どちらの方法でも、処理するノードを明示的に指定する必要があります。

取り上げなかったのは、XSLTがその暗黙的な動作をすべて取得する方法です。また、組み込みテンプレートルールも理解する必要があります。ルートノード(_match="/"_)の明示的なルールさえも含まないスタイルシートを常に書いています。代わりに、ルートノードの組み込みルール(子にテンプレートを適用)に依存します。これは、要素ノードの組み込みルールと同じです。したがって、入力の大部分を無視し、XSLTプロセッサに自動的に走査させ、興味のあるノードに出会ったときだけ、特別なことをすることができます。または、すべてを再帰的にコピーする単一のルール(ID変換と呼ばれる)を記述し、必要な場合にのみオーバーライドして、入力に増分変更を加えることもできます。 「XSLTの仕組み」を読んだ後、次の課題は「アイデンティティ変換」を調べることです。

私は、XSLTの「禅」について深く知らないことに気付きます。これまで、私のスタイルシートはルートノードに一致するテンプレートで構成されていましたが、その後は完全に手続き型です。私はこれをするのにうんざりしています。むしろ実際にXSLTを正しく理解したいので、私の質問です。

私はあなたに拍手を送ります。 「赤い丸薬」を服用する時が来ました:read "How XSLT Works"

83
Evan Lenz

テンプレート常にソースXMLで一致します。したがって、2つ以上のテンプレートが同じノードに一致しない限り、順序は実際には重要ではありません。その場合、やや直感に反して、last一致するテンプレートを持つルールがトリガーされます。

6
mirod

最初の例では、入力XMLの処理を開始するとルートから開始し、それがスタイルシート内でルート要素に一致する唯一のテンプレートであるため、テンプレート#1が実行されます。スタイルシートで2番目であっても、1番目に実行されます。

この例では、テンプレート1を使用してルート要素を既に処理しており、ルートの後に処理する要素がもうないため、テンプレート2は実行されません。追加のテンプレートを使用して他の要素を処理したい場合は、変更する必要があります。

<xsl:template match="/">
  <xsl:apply-templates/>
</xsl:template>

これにより、関心のある各要素のテンプレートを定義し、手順を実行するのではなく、より論理的な方法でxmlを処理できます。

また、現在のコンテキスト(ルート)にはfirstName要素がなく、person要素のみであるため、この例では何も出力されないことに注意してください。

<xsl:template match="/">
  <xsl:value-of select="person/firstName"/> <xsl:value-of select="person/lastName"/>
</xsl:template>

ルートから始めて、その要素に一致するテンプレートを探し、それらの指示に従って出力を生成すると、xmlをステップスルーしていると思う方が簡単です。 XSLTは入力ドキュメントを出力に変換し、変換の開始時に出力ドキュメントが空になるようにします。出力は、変換の一部として使用されるのではなく、それからの出力にすぎません。

2番目の例では、テンプレートは出力ではなく入力xmlに対して実行されるため、テンプレート#2は実行されません。

2
Chris R

エヴァンの答えは基本的に良いものです。

ただし、不足しているように思われることの1つは、マッチングを行わずにコードのチャンクを「呼び出す」機能です。これは-少なくとも一部の人々の意見では-より良い構造化を可能にします。

私が意味することを示すために、私は小さな例を作成しました。

<xsl:template match="/" name="dotable">
<!-- Surely the common html part could be placed somewhere else -->
    <!-- the head and the opening body -->
<html>
<head><title>Salary table details</title></head>

    <body>
<!-- Comments are better than nothing -->
    <!-- but that part should really have been somewhere else ... -->

<!-- Now do what we really want here ... this really is making the table! -->

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

<!-- Now close out the html -->
</body>
</html>
<!-- this should also really be somewhere else -->

<!-- This approach works, but leads to horribly monolithic code -->
    <!-- Further - it leads to templates including code which is strictly -->
    <!-- not relevant to them. I've not found a way round this yet -->
</xsl:template>

しかし、少しいじって、最初に2つの一致するテンプレートがある場合はコードの最後のテンプレートが選択されるというヒントを利用してから、コードを再構築しました(ここにはすべて表示されていません)動作し、うまくいけば正しいコードを生成し、必要なデータを表示します-

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- <?xml version="1.0"?>-->

<xsl:template name="dohtml">
  <html>
      <xsl:call-template name="dohead" />
      <xsl:call-template name="dobody" />
  </html>
</xsl:template>

<xsl:template name="dohead">
<head>
    <title>Salary details</title>
</head>
</xsl:template>

<xsl:template name="dobody">
<body>
    <xsl:call-template name="dotable" />
</body>
</xsl:template>

<xsl:template match="/entries" name="dotable">

<h1>Salary Table</h1>
<table border = "3" width="80%">
<xsl:for-each select="//entry">
    <tr>
        <td><xsl:value-of select="name" /></td>
        <td><xsl:value-of select="firstname" /></td>
        <td><xsl:value-of select="age" /></td>
        <td><xsl:value-of select="salary" /></td>
    </tr>
</xsl:for-each>
</table>

</xsl:template>

<xsl:template  match="/" name="main">
            <xsl:call-template name="dohtml" />
</xsl:template> 

[すべてが表示されない場合は、上記のコードを上下にスクロールします]

これが機能する方法は、メインテンプレートが常に一致する-/に一致する

これには、呼ばれるコードのチャンク(テンプレート)があります。

これは、/で別のテンプレートを照合することはできないことを意味しますが、名前付きノード(この場合はxmlの最上位ノードであるエントリ)で明示的に照合することはできます。

コードを少し変更すると、上記の例が生成されます。

0
user2151421