我想调用并模板并将输出保存到变量中。我想保存输出的 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标记/标记。
修改
到目前为止,我已经尝试了两个已发布的答案,但copy-of
给了我与value-of
相同的结果。实际上,我没有使用value-of
,我只是展示了如何复制问题。以下是对我尝试做的更详尽的解释。
有一个样式表,用于转换从REST响应中收到的相当大的XML。样式表的输出方法设置为html。
此样式表中的一个模板对如何在表中显示多达4行数据做了很多决策。共有2列,一列用于标签,另一列用于数据。
完成的决策包括标签的文本和类别 - 有时标签是绿色,有时是红色等。数据列可以包含文本和/或数字。有些文字可能是粗体,有些可能是彩色的。确定这些属性有许多先决条件。
如果我要显示单个项目的详细信息,我将完成此模板,但其中一个项目可以选择多个属性。例如,可能存在大小和颜色。根据用户选择的尺寸或颜色,价格可能不同,特定商品可能缺货,如果价格不同,则会对用户产生不同的节省。不同的商品可能免运费,或者可能仅提前预订。有很多属性。
所以我有一个模板可以解决所有这些先决条件,并构造一个<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中处理JSON,我似乎一直在暗示我没有做正确的事情。 / HTML。
任何人都可以帮我解决问题吗?当我在JSONItemDetails中调用HTML标记来创建我的JSON字符串时,为什么要从DataTableRow的输出中删除HTML标记?
编辑#2:
我的代码中发生了一些事情,导致HTML标记被剥离的问题。我终于能够解决导致问题的原因,现在我一直试图弄清楚如何解决这个问题。这是附加说明。
我能够确认,xsl:value-of
正在从模板调用的输出中剥离我的HTML标记。 xsl:copy-of
向我展示了我期望的标签。这对我理解问题所处的位置有很大帮助,但它也让我能够识别其他问题。
xsl-copy
不能用于在标记的值=属性下输出。我理解为什么,虽然如果我按照我的方向前进,我仍然不知道如何处理这个问题。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>
和<td>
格式(类) ,以及包装实际数据的逻辑,如<strike
,<strong>
,颜色规格和字体大小,甚至特定表格行是display:block
还是display:none
。我绝对不想在客户端的JQuery脚本中编写任何逻辑代码。
在我的XSLT模板中,这是一个非常简单的xsl:for-each
,用于在服务器上创建这些字符串,并将结果填充到我的JSON对象中,以便我可以使用上面的脚本。不可否认,这是一个丑陋的数据。
另一个注意事项,无论我是否单独显示数据,我仍然需要用于处理我的XML的当前XSLT模板,因为只有一个项目可以显示。如果只有一个产品,JSON没有进入图片,因为页面上没有显示任何控件来改变产品的属性(颜色,大小等)。
我绝对想要编码这个&#34;对&#34;,所以我当然感谢您从经验中获得的。我不认为任何人都可以有效地建议我支持或反对最终结果,而不知道让我在那里做出的所有考虑因素
以下是用于转义双引号的模板:
<!--
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>
这是来自渲染网页的粘贴。我收到了#34;未终止的字符串文字错误&#34;并且它在第一行<tr>
之前指向撇号。
我假设这是因为字符串的空格分布在几行上。制作调用模板时,空格是原样的。我不确定如何禁用这个whitespacing,或者如果它甚至会产生影响。我会在谷歌上做一些谷歌搜索,但是有没有人知道是否有办法为调用模板关闭空格?
答案 0 :(得分:7)
尝试使用<xsl:copy-of select="$test">
代替<xsl:value-of ... />
另请注意,<xsl:value-of />
(和<xsl:copy-of />
相关)应位于模板内,而不是<xsl:stylesheet >....</xsl:stylesheet>
的根级别 - 在我的XSLT调试器中引发错误< / p>
所以对我有用的测试是
<?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"
只要你的房产名称中没有空格
答案 1 :(得分:1)
<xsl:value-of select="$test"/>
您必须使用xsl:copy-of
(或在XSLT 2.0中推荐xsl:sequence
) - 而不是xsl:value-of
。
By definition ,xsl:value-of
输出在select
属性中评估表达式的结果的字符串值。
<xsl:copy-of>
输出(按文档顺序)其select
属性中指定的节点集的所有节点。或者,再次引用W3C XSLT 1.0规范:
“ xsl:copy-of元素可用于将节点集复制到 结果树没有将其转换为字符串“
在XSLT 2.0中,xsl:sequence
可用于按任意顺序输出节点集的节点。