我想调用模板并将输出保存到变量中。我想保存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 属性将无效,因此它们实际上都是"
。我在这里显示双引号以便于查看。
一旦确定了 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 正常工作,我必须转义所有标签。 & 符号必须是&
,那么就有<
, >
并且还用反斜杠转义所有引号。我已经按照这种方式对其进行了编码,并且它可以工作 - 尽管看起来很讨厌,但它可以工作,并且它可以防止我必须往返于服务器 - 它可以防止我将所有逻辑用于在客户端创建这些字符串。
这些都与我遇到的问题无关,但我希望摆脱 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>"ProductID": </xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="./ProductID"/>
<xsl:text>",</xsl:text>
<xsl:for-each select="msxml:node-set($offerNode)">
<!-- Title -->
<xsl:text>"Title": </xsl:text>
<xsl:text>"</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>",</xsl:text>
<!-- List Price -->
<xsl:text>"ListPrice": </xsl:text>
<xsl:text>"</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>"</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
显示我期望的标签。这对我理解问题出在哪里有很大帮助,但它也让我能够识别其他问题。
-
xsl-copy
不能用于标签的 value= 属性下的输出。我明白为什么,尽管如果我朝着我要去的方向前进,我不知道如何处理这个问题。
- 即使我找到了#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, '"'), '"')"/>
<xsl:if test="contains($pText, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '"')"/>
</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 <
以及相应的>
。这不会产生任何错误,但会导致整个<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>
- 在第二行。
我假设这是因为字符串中有空格将其分散在几行中。空白是按原样创建调用模板的。我不确定如何禁用此空白,或者这是否会产生影响。我打算对此进行一些谷歌搜索,但是有人知道是否有办法关闭调用模板的空白?