调用 XSLT 模板并将所有输出保存到变量

2024-01-09

我想调用模板并将输出保存到变量中。我想保存ALL包括 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 标记到哪里去了?我希望能够将调用模板的输出保存到变量中,但我也需要 HTML 标记。

有没有办法避免在调用这样的模板时丢失 HTML 标签?是否有不同的方式来编写我的模板?也许我缺少一个设置?我试过了disable-escape-encoding,但这没有什么区别(至少在 Safari 中)。

我更喜欢使用该模板来满足这两种需求:我希望能够仅调用它并将输出放在 HTML 页面中以供查看。我还希望能够将调用包装在变量中,但重要的是这两种调用方法都包括模板中指定的所有 HTML 标签/标记。

EDIT:

到目前为止我已经尝试过两个发布的答案,但是copy-of给我相同的结果value-of。事实上,我没有使用value-of,我只是展示如何复制问题。这是我正在尝试做的事情的更彻底的解释。

有一个样式表,用于转换从 REST 响应接收到的相当大的 XML。样式表的输出方法设置为 html。

此样式表中的模板之一针对如何在表中显示多达 4 行数据做出了大量决策。有两列,一列用于标签,另一列用于数据。

所做的决策包括标签的文本是什么以及什么类别 - 有时标签是绿色,有时是红色等。数据列可以包含文本和/或数字。有些文本可能是粗体,有些可能是彩色的。确定这些属性有很多先决条件。

如果我要显示单个项目的详细信息,我将使用此模板完成,但其中一个项目可以选择多个属性。例如,可能有尺寸和颜色。根据用户选择的尺寸或颜色,价格可能不同,特定商品可能缺货,如果价格不同,那么将为用户带来不同的节省。不同的商品可能可以免费送货,或者只能预订。有很多属性。

所以我有一个模板,可以对所有这些先决条件进行分析并构建一个<tr><td></td><td></td></tr>充满了非常简单的文本和由一些不那么简单的逻辑得出的数据。

我已经模块化了这个模板,以便我可以为任何一项调用它。它是参数化的,因此我可以指定标签文本、数据、类和其他一些内容。

加载网页时,会显示默认/主要项目以及一般信息 - 例如价格范围和节省范围等。

但是,当用户选择颜色或尺寸时,需要使用正确的数据更新这些表行。我已经处理了来自 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 ...

]"
/>

正如您所看到的,上面的隐藏输入有一个 value 属性,其中包含一个包含 HTML 标记的 JSON 对象。我已经看到了这个工作,但为了让 JSON 正常工作,我必须转义所有标签。 & 符号必须是&amp;,那么就有&lt;, &gt;并且还用反斜杠转义所有引号。我已经按照这种方式对其进行了编码,并且它可以工作 - 尽管看起来很讨厌,但它可以工作,并且它可以防止我必须往返于服务器 - 它可以防止我将所有逻辑用于在客户端创建这些字符串。

这些都与我遇到的问题无关,但我希望摆脱 JSON 和 XSL/XML 教授的所有评论,他们挑战我在一个句子中使用 JSON 和 XSLT(或 HTML) ...

现在,我希望我能够(非常简单地)展示我遇到的确切问题。首先,它与 JSON 关系不大。它实际上与使用无关xsl:value-of over xsl:copy-of.

这里是我创建一个隐藏输入字段的地方,其 value 属性包含 JSON 字符串:

<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 模板有一些逻辑,每次我需要编写这些行之一时,这些逻辑都会很混乱。首先,所有这些行都必须存在于 HTML 中。如果没有数据,则将它们设置为以下样式display:none。这很重要,因为除非有有效的选择器,否则我无法用 JQuery 脚本中的数据填充它们......

问题是,在完成所有这些之后,DataTableRow 在像这样直接调用时工作得很好:

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

在上面的调用中,HTML标签全部输出到我的网页中。他们看起来很好,他们的阶级和风格都是正确的。我可以从模板中的任何位置这样调用它,并且效果很好。我确信这里或那里存在错误,并且我可能会发现更好/更有效的编码方式,但它基本上适用于 HTML。

问题:当我构建上面的隐藏输入字段时,我无法获取任何 HTML 标签。存储在我的 JSON 字符串中的唯一值是 insidehtml。 DataTableRow 是从 JSONItemDetails 模板调用的,而不是获取以下结果:

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

我得到的结果是

Some LabelSome Data

对于提出这样一个简单的问题来说,信息量非常大,但我收到的回复似乎暗示,如果我在 XSLT/HTML 中处理 JSON,我的做法就不正确。

有人可以帮助解决我的问题吗?当我在 JSONItemDetails 中调用 DataTableRow 创建 JSON 字符串时,为什么 HTML 标签会从 DataTableRow 的输出中被删除?

EDIT #2:

我的代码中发生了一些问题,导致了 HTML 标签被删除的问题。我终于掌握了导致问题的原因,现在我一直在努力找出解决问题的方法。以下是附加说明。

我能够确认,xsl:value-of正在从模板调用的输出中剥离我的 HTML 标记。xsl:copy-of显示我期望的标签。这对我理解问题出在哪里有很大帮助,但它也让我能够识别其他问题。

  1. xsl-copy不能用于标签的 value= 属性下的输出。我明白为什么,尽管如果我朝着我要去的方向前进,我不知道如何处理这个问题。
  2. 即使我找到了#1 的解决方案,我也必须弄清楚如何转义 HTML 标记中的双引号。即:双引号在 value= 属性内无效。我知道撇号可以工作,但是这些双引号是通过使用创建的xsl:attribute在某些标签下指定类别或临时风格。我有一个用反斜杠转义双引号的模板,但调用它也会删除我的 HTML 标签,我不明白为什么 - 所以我不知道如何修复它。我将在下面发布此模板的代码。

如果我有解决上述两个问题的方法,我可以按照我一直在做的方向继续,但我愿意听取关于为什么我不应该将数据与 JSON 中的显示混合的建议。

我将两者混合在一起是因为我不喜欢将显示逻辑放入 JQuery 脚本中。我希望我的脚本保持对这种逻辑的无知。在 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> and <td>格式化(类),还有包装实际数据的逻辑,例如<strike, <strong>、颜色规格和字体大小,甚至特定的表格行是否是display:block or a display:none。我绝对不想在客户端的 JQuery 脚本中编写任何逻辑。

这是一个非常简单的xsl:for-each在我的 XSLT 模板中在服务器上创建这些字符串并将结果填充到我的 JSON 对象中,以便我可以使用上面的脚本。不可否认,这些数据很难看。

另请注意,无论我是否将显示与数据分开,如果只有一个项目具有单一的显示可能性,则仍然需要我用来处理 XML 的当前 XSLT 模板。如果只有一个产品,则 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 代码的格式和颜色都很好。它更容易阅读,但我无法让它与我发布的代码一起使用。一切都是黑色文本并且左对齐。我已经尝试纠正这个问题,但它对我不起作用。

EDIT #3:

Dimitre 评论说,可以在隐藏输入字段的值属性中按字面意思使用引号,因此我对此进行了一些实验。我所看到的似乎到处都是陷阱。

<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无效并生成错误。所以我改变了< to &lt;以及相应的>。这不会产生任何错误,但会导致整个<input>及其内容在页面呈现时显示为字符串。 (我正在 Firefox 中查看该页面)。

我还没有尝试用 CDATA 进行编码,因为我很确定这也行不通,而且我会得到与后者相同的结果 - 所有内容都显示为字符串。

shrug

EDIT #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>

这是渲染网页的粘贴。我收到“未终止的字符串文字”错误,它指向第一个字符之前的撇号<tr>- 在第二行。

我假设这是因为字符串中有空格将其分散在几行中。空白是按原样创建调用模板的。我不确定如何禁用此空白,或者这是否会产生影响。我打算对此进行一些谷歌搜索,但是有人知道是否有办法关闭调用模板的空白?


Try <xsl:copy-of select="$test">代替<xsl:value-of ... />

另请注意,<xsl:value-of /> (and <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"

只要您的属性名称中没有空格

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

调用 XSLT 模板并将所有输出保存到变量 的相关文章

随机推荐