我想调用和模板并将输出保存到变量。我想保存包括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标记/标记。
编辑:
到目前为止,我已经尝试过两个发布的答案,但是
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
上使用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字符串中的唯一值是innerhtml。从JSONItemDetails模板调用DataTableRow,而不是得到以下结果:
<tr><td>Some Label</td><td>Some Data</td></tr>
我得到了一个结果
Some LabelSome Data
这是一个非常简单的问题,它提供了大量的信息,但是我得到的反馈似乎暗示着,如果我在XSLT / HTML中处理JSON,那我做的事情就不正确。
任何人都可以帮助解决我的问题吗?在JSONItemDetails中创建HTML标记时,为什么从DataTableRow的输出中剥离HTML标记?
编辑2:
我的代码中发生了几件事,这些问题导致剥夺了HTML标签的问题。终于可以解决导致问题的原因,现在我一直在尝试找出解决问题的方法。这是其他说明。
能够确认,
xsl:value-of
正在从模板调用的输出中剥离HTML标记。 xsl:copy-of
显示我期望的标签。这对我了解问题的根源有很大帮助,但也使我能够确定其他问题。xsl-copy
不能用于在标签的value =属性下输出。我理解为什么,但是如果我朝前进的方向前进,我对如何处理这个问题一无所知。即使我找到了解决#1的方法,也必须弄清楚如何在HTML标记中转义双引号。即:在value =属性中,双引号无效。我知道撇号会起作用,但是这些双引号是通过在某些标签下使用
xsl:attribute
指定类或特殊样式创建的。我有一个模板,可以用反斜杠转义双引号,但是调用它也会剥去我的HTML标记,而且我看不到为什么-所以我不知道如何解决它。我将在下面发布此模板的代码。如果我有解决上述2个问题的方法,则可以按照我所追求的方向进行操作,但是我乐于听取有关为什么不应该在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>
和<td>
格式(类),还要包含包装实际数据,例如<strike
,<strong>
,颜色规范和字体大小,甚至特定表行是display:block
还是display:none
。我绝对不想在客户端的JQuery脚本中编写任何逻辑代码。在我的XSLT模板中,这是非常简单的
xsl:for-each
,它可以在服务器上创建这些字符串并将结果填充到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代码进行了很好的格式化和着色。这很容易阅读,但是我无法使其与我发布的代码一起使用。一切都是黑色文本,并左对齐。我已尝试纠正此问题,但它对我不起作用。
编辑#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
是无效的,并会产生错误。因此,我将<
和相应的<
都更改为>
。这不会产生任何错误,但是会导致整个<input>
及其内容在呈现页面时显示为字符串。 (我正在Firefox中查看页面)。我没有尝试使用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>
这是来自渲染网页的粘贴。我收到“未终止的字符串文字”错误,它指向第二行第一个
<tr>
之前的撇号。我假设这是因为字符串具有空格,将其分布在多行上。空格是制作调用模板的原样。我不知道如何禁用此whitespacing,否则是否会有所作为。我将对此进行一些搜索,但是有人知道是否有办法为呼叫模板关闭空白吗?
最佳答案
尝试使用<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"
只要您的属性名称中没有空格
关于xslt-1.0 - 调用XSLT模板并将所有输出保存到变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11183138/