XSLT 包含两个句点
Posted
技术标签:
【中文标题】XSLT 包含两个句点【英文标题】:XSLT contains two periods 【发布时间】:2011-05-26 18:21:12 【问题描述】:我有一个使用无序列表显示导航的 html 文档。当它作为 XML 输出时,它不再使用列表,而是为每个链接分配一个<Description>
。描述的工作方式是为主链接分配数字,如 100、200、300、400 等。这些子导航链接分配如下:200.100、200.200、200.300 等。
XML 如下所示:
<Items>
<PgIndexElementItem>
<Description>100</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
<PgIndexElementItem>
<Description>200</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
<PgIndexElementItem>
<Description>200.100</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
<PgIndexElementItem>
<Description>200.100.100</Description>
<Title>This is the Title</Title>
</PgIndexElementItem>
</Items>
如果你看最后一个 PgIndexElementItem,有三组数字。
我正在尝试使用 XSLT 重新创建无序列表。这是我迄今为止一直在做的事情:
<ul>
<xsl:for-each select="//PgIndexElementItem">
<xsl:if test="not(contains(Description, '.'))">
<li><a href="ResolvedURL/ServerRelative"><xsl:value-of select="Title"/></a>
<ul>
<xsl:for-each select="//PgIndexElementItem">
<xsl:if test="contains(Description, '.')">
<li><a href="ResolvedURL/ServerRelative"><xsl:value-of select="Title"/></a></li>
</xsl:if>
</xsl:for-each>
</ul>
</li>
</xsl:if>
</xsl:for-each>
</ul>
我的问题是,有没有办法指定 contains(Description
以两个句点为目标的描述,例如上面最后一个 PgIndexElementItem?
我希望我解释得足够好。我不知道如何使它不那么混乱。提前致谢!
编辑
o Graduate Program
* How to Apply
* Masters Program
o M.A. Handbook
o FAQs
o Alumni
o Masters Theses
o Sample Thesis Proposals
* M.A. Handbook
* FAQs
* Alumni
* Masters Theses
* Sample Thesis Proposals
【问题讨论】:
你的解析器支持 xpath 2 还是 xslt 2? 不确定。我怎么会发现呢?抱歉,有了这些东西的全新世界。 @jrottier,您使用什么技术运行此代码? 您的 XML 需要一个***元素,即 PgIndexElementItem 元素的父元素。例如。用<root>
...</root>
包围整个事情。
有一个 <Items>
元素围绕着所有 PgIndexElementItem 元素。上面还有很多,但为了简单起见,我没有列出它们。
【参考方案1】:
你可以使用
<xsl:if test="contains(substring-after(Description, '.'), '.')">
...
测试一个元素是否有一个带有两个或更多句点的描述子元素。
但是,我会将您的 XSLT 更改为:
<!-- this part replaces your <ul> code above -->
<ul>
<xsl:apply-templates
select="//PgIndexElementItem[not(contains(Description, '.'))]" />
</ul>
更新了这个模板:
<!-- add this new template -->
<xsl:template match="PgIndexElementItem">
<li>
<a href="ResolvedURL/ServerRelative"><xsl:value-of select="Title"/></a>
<xsl:variable name="prefix" select="concat(Description, '.')"/>
<xsl:variable name="childOptions"
select="../PgIndexElementItem[starts-with(Description, $prefix)
and not(contains(substring-after(Description, $prefix), '.'))]"/>
<xsl:if test="$childOptions">
<ul>
<xsl:apply-templates select="$childOptions" />
</ul>
</xsl:if>
</li>
</xsl:template>
这将避免不必要的代码重复,以及不需要的空 <ul>
元素。
我对描述子项的解释方式做了一些假设......例如“200.100”应该出现在“200”下的子列表中,而不是“100”下。
【讨论】:
@jrottier - 在您发表评论几秒钟后,我添加了一个关于 XSLT 的重要建议。 哇!那太棒了!工作几乎完美!我在几页上测试了它!唯一的问题是某些元素在不应该重复时重复。我在上面的问题中添加了一个示例。 @jrottier:你能告诉我们哪些元素不应该重复吗?并确保您的示例 XML 包含这些元素,无论它们出现在哪里……以及它们的父菜单项。 @jrottier:另外,我们可以假设在您的输入 XML 中,菜单项的出现顺序与您希望它们出现在输出中的顺序相同吗?例如。 “200”总是出现在“200.*”之前? @LarsH 只有当它是第三级导航元素时才会出现问题。如果您查看我在问题中发布的 XML 示例,那么最后一个 PgIndexElementInfo 就是问题所在。而且我确实有一个带有第四级导航元素的页面,它会导致同样的问题。因此,发生这种情况的两个实例将是以下元素:400.300.200.100 和 400.300.100。这有帮助吗?【参考方案2】:使用:
string-length(Description) - string-length(translate(Description, '.','')) = 2
这对任何数字 $n
都有效(而“substring-after”技术在一般情况下根本无法使用):
string-length(Description) - string-length(translate(Description, '.','')) = $n
【讨论】:
+1 聪明...我一直在使用 translate() 寻找类似的东西,但无法做到。 @LarsH:是的,translate()
是最通用的 XPath 函数之一。
还有:string-length(translate(Description,translate(Description,'.',''),''))
@Alejandro: 是的,但是这些太多了translate()
s :) 可能我的回答效率更高。
是的。我认为最外面的translate()
会使表达式达到指数复杂度。【参考方案3】:
我看起来您正在处理嵌套的 PgIndexElementItem
元素 - 如果它们嵌套在多个层中,您应该考虑重构您的 XSL 以使用递归模板(假设每个级别的 HTML 输出是可预测的)。
在编写时,您的 for-each 循环选择当前元素的所有 PgIndexElementItem 后代,然后在遇到内部 for-each 循环时再次执行此操作。可能不是你想要的。
【讨论】:
“当前元素的所有 PgIndexElementItem 后代” - 实际上是根节点 (/) 的所有 PgIndexElementItem 后代。当前节点不考虑;那将是“.//PgIndexElementItem”。但我同意重构......以上是关于XSLT 包含两个句点的主要内容,如果未能解决你的问题,请参考以下文章