6

テンプレートを呼び出して、出力を変数に保存したいと思います。HTMLマークアップを含むすべての出力を保存したいのですが、そうではありません。

たとえば、次の単純なXSLTを考えてみましょう。

<xsl:call-template name="demonstration">

<xsl:template name="demonstration">
    <p>Just a test</p>
</xsl:template>

この単純なテンプレートは<p>Just a test</p>、HTMLページに出力されます。ページのソースを見ると、(もちろん)それがわかります。同じテンプレートを使用してこの例を見てみましょう。ただし、HTMLに出力するだけでなく、出力を変数に保存します。

<xsl:variable name="test">
    <xsl:call-template name="demonstration">
</xsl:variable>
<xsl:value-of select="$test"/>


<xsl:template name="demonstration">
    <p>Just a test</p>
</xsl:template>

変数を表示すると、現在の出力は。のみであることがわかりますJust a test

HTMLマークアップはどこに行きましたか?call-templateの出力を変数に保存できるようにしたいのですが、HTMLマークアップも必要です。

このようなテンプレートを呼び出すときにHTMLタグの損失を回避する方法はありますか?テンプレートを書く別の方法はありますか?多分私が欠けている設定ですか?私は試しましdisable-escape-encodingたが、それは違いはありません(少なくともSafariでは)。

私は両方のニーズにテンプレートを使用することを好みます。それを呼び出すだけで、HTMLページに出力を表示できるようにしたいと思います。また、呼び出しを変数でラップできるようにしたいのですが、テンプレートで指定されているすべてのHTMLタグ/マークアップを含む両方の呼び出しメソッドが重要です。

編集:

これまでに投稿された両方の回答を試しましたがcopy-of、と同じ結果が得られvalue-ofます。実際、私は使用していませんvalue-of。問題を再現する方法を示しているだけです。これが私がやろうとしていることのより完全な説明です。

REST応答から受け取ったかなり大きなXMLを変換するために使用されるスタイルシートを用意します。スタイルシートには、html用に設定された出力メソッドがあります。

このスタイルシートのテンプレートの1つは、テーブルに最大4行のデータを表示する方法について多くの意思決定を行います。2つの列があり、1つはラベル用、もう1つはデータ用です。

行われる意思決定には、ラベルのテキストとクラスが含まれます。ラベルが緑色の場合もあれば、赤色の場合もあります。データ列には、テキストや数字を含めることができます。一部のテキストは太字である場合があり、一部は色付けされている場合があります。これらの属性を決定する前提条件はたくさんあります。

単一のアイテムの詳細を表示する場合は、このテンプレートで完了しますが、これらのアイテムの1つで、多数の属性を選択できます。たとえば、サイズと色があるかもしれません。ユーザーが選択するサイズや色に応じて、価格が異なる場合があります。特定のアイテムの在庫がない場合があります。価格が異なる場合は、ユーザーにとっての節約額も異なります。アイテムによって送料が無料になる場合や、先行予約のみの場合があります。たくさんの属性があります。

そして、私はこれらの前提条件のすべてを無害化し<tr><td></td><td></td></tr>、いくつかのそれほど単純ではないロジックによって派生した非常に単純なテキストとデータで満たされたものを構築するテンプレートを持っています。

このテンプレートをモジュール化して、どのアイテムでも呼び出すことができるようにしました。ラベルのテキスト、データ、クラス、およびその他のいくつかを指定できるように、パラメーター化されています。

Webページが読み込まれると、デフォルト/メインアイテムが、価格の範囲や節約の範囲などの一般的な情報とともに表示されます。

ただし、ユーザーが色またはサイズを選択する場合、これらのテーブル行を正しいデータで更新する必要があります。XMLからのデータをすでに処理しており、別のサーバーリクエストを作成するのは非常にコストがかかるため、さまざまな種類のアイテムすべてのすべてのデータを含むJSON文字列配列を作成しました。このJSONを非表示の入力コントロールのvalue属性に保存します。他にも方法はありますが、これはうまくいき、私には扱いやすいようです。

これらのテーブル行を作成するロジックはサーバーのスタイルシートにあるため、すべてのアイテムに対してこのロジックをすべて実行してから、計算された文字列をクライアントに渡します。これがどのように見えるかの例を次に示します。

<input id="hfData" type="hidden" value="[
{"ProductID": "00001", "Color": "Beige", "Size": "14"},
{"ProductID": "00002", "Color": "Black", "Size": "14"},
{"ProductID": "00003", "Color": "Blue", "Size": "10"},
{"ProductID": "00004", "Color": "Pink", "Size": "10"},
{"ProductID": "00005", "Color": "Yellow", "Size": "10"}
]"
/>

次に、ユーザーがドロップダウンリストの選択を変更したり、属性を変更したりするたびにトリガーされる小さなJQueryスクリプトがあります。スクリプトは上記のJSONを解析し、現在構成されているProductIDを判別します。

注:このように二重引用符が散在していると、入力コントロールのvalue属性は無効になるため、実際にはすべて&quot;です。見やすくするために、ここでは二重引用符を示しています。

ProductIDが決定されると、この編集で前述したように、ページはさまざまな詳細で更新されます。これらの詳細は、私が作成したさらに別のJSONオブジェクトから取得されます。製品の詳細はすべてXSLTスタイルシートで認識されているため、ここでJSON文字列配列を作成しています。

<input id="hfDetails" type="hidden" value="[
{"ProductID": "00001", "ListPrice": "<tr class="collapsed"><td class="datalabel">List Price:</td><td class="datainfo"></td></tr>", "YourPrice": "<tr><td class="datalabel">Price:</td><td class="datainfo">$23.99 & is elegible for <span class="freeshipping">FREE</span> shipping <a href="#">Details</a></td></tr>"},

... etc, etc, etc ...

]"
/>

ご覧のとおり、すぐ上の非表示の入力には、HTMLマークアップを含むJSONオブジェクトを含むvalue属性があります。この動作を見てきましたが、JSONを正しく動作させるには、すべてのタグをエスケープする必要があります。アンパサンドが必要です&amp;。次に、があり&lt;&gt;すべての引用符をバックスラッシュでエスケープします。私はそれをすべてそのようにコーディングしました、そしてそれは機能します-見るのは厄介ですが、それは機能し、サーバーへのラウンドトリップをする必要がありません-それは私がクライアント側でこれらの文字列を作成するためのすべてのロジックを置くことを防ぎます。

これは私が抱えている問題とは何の関係もありませんが、JSONとXSLT(またはHTML)を1つの文に含めることで私に挑戦しているJSONとXSL/XMLの教授からのコメントをすべて取り除くことを望んでいます..。。

今、私が抱えている問題を(非常に簡単に)示すことができるといいのですが。まず、JSONとの関係は非常に限られています。xsl:value-ofoverを使用することとは実質的に何の関係もありませんxsl:copy-of

ここで、JSON文字列を含むvalue属性を使用して非表示の入力フィールドを作成します。

<input>
  <xsl:attribute name="id">
    <xsl:value-of select="$id"/>
  </xsl:attribute>
  <xsl:attribute name="type">
    <xsl:text>hidden</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="runat">
    <xsl:text>server</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="value">
    <xsl:text>[</xsl:text>

    <xsl:for-each select="Items/Item">
          <xsl:call-template name="JSONItemDetails">
            <xsl:with-param name="offerNode" select="Offers"/>
            <xsl:with-param name="lastitem" select="position() = last()"/>
          </xsl:call-template>
    </xsl:for-each>

    <xsl:text>]</xsl:text>
  </xsl:attribute>
</input>


<xsl:template name="JSONItemDetails">
    <xsl:param name="offerNode" select="."/>
    <xsl:param name="attributesNode" select="ItemAttributes"/>
    <xsl:param name="listprice" select="0"/>
    <xsl:param name="lastitem" select="false()"/>

    <!-- Product ID -->
    <xsl:text>{</xsl:text>
    <xsl:text>&quot;ProductID&quot;: </xsl:text>
    <xsl:text>&quot;</xsl:text>
    <xsl:value-of select="./ProductID"/>
    <xsl:text>&quot;,</xsl:text>

    <xsl:for-each select="msxml:node-set($offerNode)">

        <!-- Title -->
        <xsl:text>&quot;Title&quot;: </xsl:text>
        <xsl:text>&quot;</xsl:text>
        <xsl:call-template name="escapeQuote">
            <xsl:with-param name="pText">
            <xsl:call-template name="title">
                <xsl:with-param name="node" select="$attributesNode" />
            </xsl:call-template>
            </xsl:with-param>
        </xsl:call-template>
        <xsl:text>&quot;,</xsl:text>

        <!-- List Price -->
        <xsl:text>&quot;ListPrice&quot;: </xsl:text>
        <xsl:text>&quot;</xsl:text>
        <xsl:call-template name="escapeQuote">
            <xsl:with-param name="pText">
            <xsl:call-template name="DataTableRow">
                <xsl:with-param name="label" select="'List Price:'" />
                <xsl:with-param name="data" select="./Price/FormattedPrice" />
                <xsl:with-param name="dataid" select="'listprice'" />
            </xsl:call-template>
            </xsl:with-param>
        </xsl:call-template>
        <xsl:text>&quot;</xsl:text>

    </xsl:for-each>

    <xsl:text>}</xsl:text>
    <xsl:if test="$lastitem != true()">
        <xsl:text>,</xsl:text>
    </xsl:if>

DataTableRowテンプレートは多くのことを実行し、ほとんどの場合一貫性を提供しますが、行と列の作成に使用されるすべての漂遊HTMLタグもクリーンアップします。これがそのテンプレートです。

<xsl:template name="DataTableRow">
    <xsl:param name="label" select="''"/>
    <xsl:param name="data" select="''"/>
    <xsl:param name="dataid" select="''"/>
    <xsl:param name="concat" select="''"/>
    <xsl:param name="class" select="''"/>

    <tr>
        <xsl:choose>
        <xsl:when test="$data = not(string(.))">
          <xsl:attribute name="style">
            <xsl:text>display:none</xsl:text>
          </xsl:attribute>
        </xsl:when>
        <xsl:otherwise>
           <xsl:attribute name="style">
           <xsl:text>display:block</xsl:text>
        </xsl:attribute>
        </xsl:otherwise>
        </xsl:choose>

        <!-- Label Column -->
        <td>
            <div>
                <xsl:choose>
                <xsl:when test="$class = 'bigmaroon'">
                    <xsl:attribute name="class">
                        <xsl:text>datalabel maroon</xsl:text>
                    </xsl:attribute>
               </xsl:when>
               <xsl:otherwise>
                    <xsl:attribute name="class">
                       <xsl:text>datalabel</xsl:text>
                    </xsl:attribute>
               </xsl:otherwise>
               </xsl:choose>
               <xsl:value-of select="$label"/>
           </div>
        </td>

        <!-- Data Column -->
        <td class="datainfo">
            <xsl:attribute name="id">
                <xsl:value-of select="$dataid"/>
            </xsl:attribute>

            <xsl:choose>
            <xsl:when test="$class = 'strike'">
                <strike>
                    <xsl:value-of select="$data" />
                </strike>
                <xsl:value-of select="$concat"/>
            </xsl:when>
            <xsl:when test="$class = 'maroon'">
                <span class="maroon">
                    <xsl:value-of select="$data" />
                </span>
                <xsl:value-of select="$concat"/>
            </xsl:when>
            <xsl:when test="$class = 'bigmaroon'">
                <span class="bigmaroon">
                    <xsl:value-of select="$data" />
                </span>
                <xsl:value-of select="$concat"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$data" />
                <xsl:value-of select="$concat" />
            </xsl:otherwise>
            </xsl:choose>
        </td>
    </tr>

DataTableRowテンプレートには、これらの行の1つを書き込む必要があるたびに実行するのが面倒なロジックが少しあります。まず、これらの行はすべてHTMLに存在する必要があります。データがない場合は、のスタイルで設定されdisplay:noneます。有効なセレクターがない限り、JQueryスクリプトからのデータを入力できないため、これは重要です...

問題は、このすべての後、DataTableRowが次のように直接呼び出されたときに正常に機能することです。

<xsl:call-template name="DataTableRow"/>

上記の呼び出しでは、HTMLタグはすべて私のWebページに出力されます。彼らは見栄えが良く、クラスとスタイルは正しいです。テンプレートのどこからでもこのように呼び出すことができ、問題なく動作します。あちこちにバグがあると確信しており、これをコーディングするためのより良い/より効率的な方法に光が当たるかもしれませんが、基本的にはHTMLでうまく機能します。

問題:上記の非表示の入力フィールドを作成すると、HTMLタグを取得できません。JSON文字列に格納される唯一の値はinnerhtmlです。DataTableRowは、JSONItemDetailsテンプレートから呼び出され、次の結果を取得する代わりに呼び出されます。

<tr><td>Some Label</td><td>Some Data</td></tr>

私はの結果を得る

Some LabelSome Data

これは、このような単純な質問をするための非常に多くの情報でしたが、XSLT / HTML内でJSONを処理している場合、正しく処理されていないことを示唆するような応答が返ってきました。

誰かが私の問題を手伝ってくれる?JSONItemDetailsでHTMLタグを呼び出してJSON文字列を作成すると、DataTableRowの出力からHTMLタグが削除されるのはなぜですか?

編集#2:

HTMLタグが削除されるというこの問題を引き起こしているコードで、いくつかのことが起こっています。ついに問題の原因を把握し、問題を解決する方法を模索しています。ここに追加の注意事項があります。

xsl:value-ofテンプレート呼び出しの出力からHTMLタグを削除していることを確認できます。xsl:copy-of期待するタグを表示します。これは、問題がどこにあるかを理解する上で大きな助けになりますが、他の問題を特定することもできます。

  1. xsl-copyタグのvalue=属性の下での出力には使用できません。私はその理由を理解していますが、私が進んでいた方向に進んだ場合、この問題をどのように処理するかはわかりません。
  2. #1の解決策を見つけたとしても、HTMLマークアップの二重引用符をエスケープする方法を理解する必要があります。つまり、value=属性内では二重引用符は無効です。アポストロフィが機能することは理解していますが、これらの二重引用符は、xsl:attribute特定のタグの下でクラスまたは時折のスタイルを指定するために使用して作成されます。バックスラッシュで二重引用符をエスケープするテンプレートがありますが、それを呼び出すとHTMLタグも削除され、理由がわかりません。そのため、修正方法がわかりません。このテンプレートのコードを以下に投稿します。

上記の2つの問題の回避策があれば、これまでの方向に進むことができますが、JSONの表示とデータを混在させてはいけない理由についてのアドバイスを聞くことができます。

JQueryスクリプトに表示ロジックを配置するというアイデアが気に入らないため、この2つを組み合わせています。私のスクリプトはこのロジックを知らないままにしておきたいです。JQueryでは、これらのJSONオブジェクトを使用してProductIDにドリルダウンし、次のようにテーブル行を置き換えるのは簡単です。

        var details = $.parseJSON($("#[id*='hfDetails']").val());

        var filteredDetails = $.grep(details, function (n) {
            return n.ProductID == ProductID;
        });

        if (filteredDetails.length > 0) {
            var product = filteredDetails[0];

            $("div#title").html(product.Title);

            $("td#listprice").parent().html(product.ListPrice);
            $("td#price").parent().html(product.Price);
            $("td#yousave").parent().html(product.YouSave);
        }

Dimitreのアドバイスに従って、JSON文字列から表示を削除すると、突然、jqueryスクリプトに多くのロジックを追加して、(クラス)を提供<tr>および<td>フォーマットするだけでなく、実際のデータをラップするロジック(、、、<strikeなど)を追加する必要があります<strong>。色の仕様とフォントサイズ、または特定のテーブル行がadisplay:blockか。かdisplay:none。クライアントのJQueryスクリプトでそのロジックをコーディングしたくありません。

xsl:for-eachXSLTテンプレートでは、サーバー上でこれらの文字列を作成し、上記のスクリプトを使用できるように結果をJSONオブジェクトに詰め込むのは非常に簡単です。確かに、見るのは醜いデータです。

別の注意点として、表示をデータから分離するかどうかに関係なく、表示の可能性が1つしかないアイテムが1つしかない場合は、XMLの処理に使用する現在のXSLTテンプレートが引き続き必要になります。商品が1つしかない場合、商品の属性(色、サイズなど)を変更するためのコントロールがページに表示されないため、JSONは画像に表示されません。

私は間違いなくこれを「正しく」コーディングしたいので、経験から聞いていただければ幸いです。私をそこに導いたすべての考慮事項を知らずに、最終結果について賛成または反対のアドバイスを効果的に行うことができるとは思いません。

これは、二重引用符をエスケープするために使用したテンプレートです。

<!--
Escape quotes with \ for JSON strings
-->
<xsl:template name="escapeQuote">
  <xsl:param name="pText" select="."/>

  <xsl:if test="string-length($pText) >0">
    <xsl:copy-of select="substring-before(concat($pText, '&quot;'), '&quot;')"/>

    <xsl:if test="contains($pText, '&quot;')">
      <xsl:text>\"</xsl:text>

      <xsl:call-template name="escapeQuote">
        <xsl:with-param name="pText" select="substring-after($pText, '&quot;')"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:if>

</xsl:template>

このテンプレートは、入力文字列からHTMLタグを取り除きます。私はそれをこのように呼びます:

<xsl:variable name="output3">
    <xsl:call-template name="escapeQuote">
        <xsl:with-param name="pText">
        <xsl:call-template name="DataTableRow">
            ... with-params ...
        </xsl:call-template>
        </xsl:with-param>
    </xsl:call-template>
</xsl:variable>

すぐに行う:

<xsl:copy-of select="$output3"/>

HTMLタグが存在しなくなったため、二重引用符も存在しないことを示します。ただし、これを行うと、すべてのHTMLタグが表示されます。

<xsl:variable name="output3">
    <xsl:call-template name="DataTableRow">
        ... with-params ...
    </xsl:call-template>
</xsl:variable>
<xsl:copy-of select="$output3"/>

このすべてを読んでくれてありがとう-そしてところで、私はXSLコードがうまくフォーマットされて色付けされている他の投稿を見ます。かなり読みやすくなっていますが、投稿しているコードで動作させることができません。すべてが黒のテキストで、左寄せされています。私はそれを修正しようとしましたが、うまくいきません。

編集#3:

Dimitreは、非表示の入力フィールドのvalue属性で文字通り引用符を使用できるとコメントしたので、それを念頭に置いて実験を行いました。どこを見ても落とし穴があるようです。

<input value='something containing " literally'/>

value=コンテンツを二重引用符ではなくアポストロフィでラップするために、次の<input>ように非表示を作成してみました。

<input>
  <xsl:attribute name="id">
    <xsl:value-of select="$id"/>
  </xsl:attribute>
  <xsl:attribute name="type">
    <xsl:text>hidden</xsl:text>
  </xsl:attribute>
  <xsl:attribute name="runat">
    <xsl:text>server</xsl:text>
  </xsl:attribute>
  <xsl:text> value='[</xsl:text>
  <xsl:copy-of select="$output"/>
  <xsl:text>]'</xsl:text>
</input>    

タグは終了後に属性(およびその内容)で<input...>レンダリングされるため、これは機能しません。value=</input>

そこで、xsl:attribute要素を削除して、で形成しましたxsl:text。残念ながら、<内部を使用することxsl:textは無効であり、エラーが発生します。そこで、に対応するに変更<しました。これによりエラーは発生しませんでしたが、ページがレンダリングされるときに全体とその内容が文字列として表示されます。(Firefoxでページを表示しています)。&lt;><input>

これもCDATAでコーディングしようとはしていません。これも機能しないと確信しており、後者と同じ結果が得られるためです。すべてが文字列として表示されます。

肩をすくめる

編集#4:

OJayは、JSONを非表示フィールドのvalue=属性に配置するというアイデアを削除することを推奨しました。私はこれをずっと考えてきましたが、コードをどれだけ変更する必要があるのか​​わかりませんでした。彼が私がどれだけ変えなければならないかという例を示したとき、私はそれを選ぶことにしました。

残念ながら、別の落とし穴があります。

<script type="text/javascript">

var hfDetails = [{ProductID: '000001',Title: '<h3>American Apparel Sheer Jersey Chemise X-Small-Asphalt</h3>',Condition: 'New',ListPrice: '<tr style="display:block">
    <td>
      <div class="datalabel">0List Price:</div>
    </td>
    <td class="datainfo" id="listprice">$24.99</td>
  </tr>',Price: '<tr style="display:block">
    <td>
      <div class="datalabel maroon">Sale:</div>
    </td>
    <td class="datainfo" id="price"><span class="bigmaroon">$15.49</span></td>
  </tr>',YouSave: '<tr style="display:block">
    <td>
      <div class="datalabel">You Save:</div>
    </td>
    <td class="datainfo" id="yousave"><span class="maroon">$9.50 (38%)</span></td>
  </tr>'},

 ....

]
</script>

これは、レンダリングされたWebページからの貼り付けです。「未終了の文字列リテラル」というエラーが表示され、最初の行の直前、<tr>つまり2行目のアポストロフィを指しています。

これは、文字列に空白が複数の行に広がっているためだと思います。空白は、呼び出しテンプレートの作成からそのままです。このホワイトスペースを無効にする方法や、それで違いが出るかどうかはわかりません。これについてグーグルを行うつもりですが、call-templateの空白をオフにする方法があるかどうか誰かが知っていますか?

4

2 に答える 2

12

<xsl:copy-of select="$test">代わりに 試してください<xsl:value-of ... />

また、<xsl:value-of />(そして<xsl:copy-of />そのことについては)、のルートレベルではなく、テンプレート内にある必要があることに注意<xsl:stylesheet >....</xsl:stylesheet>してください-XSLTデバッガーでエラーをスローします

だから私のために働いたテストは

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">    
  <xsl:output method="xml" indent="yes"/>

  <xsl:variable name="test">
    <xsl:call-template name="demonstration" />
  </xsl:variable>

  <xsl:template match="/">
    <xsl:copy-of select="$test"/>
  </xsl:template>

  <xsl:template name="demonstration">
    <p>Just a test</p>
  </xsl:template>

</xsl:stylesheet>

Value-ofは変数の値を取得します。また、XMLであるため、値はノードの値、つまりタグ間のデータと同じになります。のコピーは、完全なコピー、タグ、およびすべてを正確に出力します


編集私の始めたコメントによると、私が意味していたのは次のようなものでした:

XSLTから

<script type="text/javascript">
var hfDetails = [{"ProductID": "00001", "ListPrice": '<tr class="collapsed"><td class="datalabel">List Price:</td><td class="datainfo"></td></tr>', "YourPrice": '<tr><td class="datalabel">Price:</td><td class="datainfo">$23.99 & is elegible for <span class="freeshipping">FREE</span> shipping <a href="#">Details</a></td></tr>'},

... etc, etc, etc ...

];
</script>

非表示の入力フィールドの代わりに。これにより、グローバルなjavascript配列オブジェクトが作成されます。本質的には、

var details = $.parseJSON($("#[id*='hfDetails']").val());

その後、あなたはただすることができます

var details = hfDetails;

コードを変更する必要はありません。

アポストロフィと引用符のエスケープが心配な場合。どちらかを使用してjavascriptで文字列を定義し、もう一方を文字列で安全に使用できます。

"ListPrice" : '<tr class="collapsed"><td class="datalabel">List Price:</td><td class="datainfo"></td></tr>'

有効であり、

"ListPrice" : "<tr class='collapsed'><td class='datalabel'>List Price:</td><td class='datainfo'></td></tr>"

また、オブジェクトのプロパティを引用符で囲む必要はありません。

"ProductID" : "00001"

することができます

ProductID : "00001"

プロパティ名にスペースがない限り

于 2012-06-25T03:58:42.683 に答える
2
<xsl:value-of select="$test"/>

の代わりに-を使用する必要がありますxsl:copy-of(またはxsl:sequenceXSLT 2.0で推奨されています)xsl:value-of

定義によりxsl:value-ofの式を評価した結果の文字列値を出力しselectます。

<xsl:copy-of>属性で指定されたノードセットのすべてのノードを(ドキュメント順に)出力しselectます。または、W3C XSLT 1.0仕様を再度引用するには、次のようにします。

xsl:copy-of要素を使用すると、ノードセットを文字列に変換せずに結果ツリーにコピーできます。」

XSLT 2.0ではxsl:sequence、ノードセットのノードを任意の順序で出力するために使用できます

于 2012-06-25T03:53:49.803 に答える