XSL 风格:在 XSLT 中使用 <xsl:for-each select> 或 <xsl:template match> 或其他解决方案?
Posted
技术标签:
【中文标题】XSL 风格:在 XSLT 中使用 <xsl:for-each select> 或 <xsl:template match> 或其他解决方案?【英文标题】:XSL style: using <xsl:for-each select> or <xsl:template match> or other solutions in XSLT? 【发布时间】:2021-12-30 03:11:00 【问题描述】:我正在学习使用 XSL 将 XML 解析为 html/XHTML。
XLST <xsl:for-each>
元素是允许循环的语言的核心元素。然而,这里和其他地方的帖子建议使用这对于初学者来说很常见(我就是!)而且风格很差。
我的问题是:<xsl:for-each>
循环有哪些更好的选择(如更高效/优雅/更好的风格),为什么?
在下面的示例中,我使用嵌套的<xsl:for-each>
和<xsl:choose>
元素通过条件<xsl:when>
测试循环遍历所需的节点。这可以正常工作并选择我需要的节点,但感觉相当笨重......
您的智慧和见解将不胜感激!
我的示例 XML 是由斯坦福 HIVdb 数据库查询生成的报告: https://hivdb.stanford.edu/hivdb/by-sequences/
XSD 架构在这里: https://hivdb.stanford.edu/DR/schema/sierra.xsd
我的示例 XML 报告在这里: https://github.com/delfair/xml_examples/blob/main/Resistance_1636677016671.xml
我的 XSLT 示例:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<title>Example Report</title>
</head>
<body>
<h3>Significant mutations</h3>
<xsl:for-each select=".//geneData">
<xsl:choose>
<xsl:when test="gene='HIV1PR'">
Protease inhibitor mutations<br/><br/>
</xsl:when>
<xsl:when test="gene='HIV1RT'">
Reverse transcriptase inhibitor mutations<br/><br/>
</xsl:when>
<xsl:when test="gene='HIV1IN'">
Integrase inhibitor mutations<br/><br/>
</xsl:when>
</xsl:choose>
<table>
<xsl:for-each select=".//mutation">
<xsl:choose>
<xsl:when test="classification='PI_MAJOR' or classification='PI_MINOR' or classification='NRTI' or classification='NNRTI' or classification='INI_MAJOR' or classification='INI_MINOR'">
<tr>
<td>Class</td>
<td>Mutation</td>
</tr>
<tr>
<td><xsl:value-of select="classification"/></td>
<td><xsl:value-of select="mutationString"/></td>
</tr>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</table><br/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
生成的 HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Example Report</title>
</head>
<body>
<h3>Significant mutations</h3>
Protease inhibitor mutations<br><br><table></table>
<br>
Reverse transcriptase inhibitor mutations<br><br><table>
<tr>
<td>Class</td>
<td>Mutation</td>
</tr>
<tr>
<td>NNRTI</td>
<td>K103N</td>
</tr>
</table>
<br>
Integrase inhibitor mutations<br><br><table></table>
<br>
</body>
</html>
【问题讨论】:
【参考方案1】:我猜你所说的“高级风格”是指使用模板(进行模式匹配)而不是xsl:for-each
“循环”。
您的代码的核心功能可以转换如下:
...
<h3>Significant mutations</h3>
<xsl:apply-templates select=".//geneData" />
<table>
<xsl:apply-templates select=".//mutation" />
</table><br/>
</body>
</html>
</xsl:template>
<!-- End of main template matching "/" -->
<xsl:template match="geneData[gene='HIV1PR']">Protease inhibitor mutations<br/><br/></xsl:template>
<xsl:template match="geneData[gene='HIV1RT']">Reverse transcriptase inhibitor mutations<br/><br/></xsl:template>
<xsl:template match="geneData[gene='HIV1IN']">Integrase inhibitor mutations<br/><br/></xsl:template>
<xsl:template match="geneData" /> <!-- Discard all not matched 'geneData' elements -->
<xsl:template match="mutation[classification='PI_MAJOR' or classification='PI_MINOR' or classification='NRTI' or classification='NNRTI' or classification='INI_MAJOR' or classification='INI_MINOR']">
<tr>
<td>Class</td>
<td>Mutation</td>
</tr>
<tr>
<td><xsl:value-of select="classification"/></td>
<td><xsl:value-of select="mutationString"/></td>
</tr>
</xsl:template>
<xsl:template match="mutation" /> <!-- Discard all not matched 'mutation' elements -->
</xsl:stylesheet>
在上面的代码中,两组节点(.//geneData
和.//mutation
)都被传递给xsl:apply-templates
,xsl:apply-templates
将生成的节点传递给所有模板。那些匹配的人将被处决。因此,简短的 xsl:template
带有谓词(match="..."
的 [...]
部分)替换代码的 xsl:when
s。
这应该是 XSLT 开发的“标准”方法。在实践中,xsl:for-each
可能更适合代码清晰的用例,但通常这两种方法是可以互换的。
【讨论】:
模板优于 for-each 的最大原因之一是可扩展性。您可以导入给定的样式表并定义具有相同匹配标准的新模板,该匹配标准将覆盖原始模板行为。认为类似于扩展 java 类并覆盖继承的函数。提取 for-each 逻辑并移入模板类似于将工作单元重构为实用函数,然后如果您需要推理它的作用,并且无需复制/粘贴即可更轻松地自定义和覆盖较小的工作单元其余不变。 这真的很有用 - 谢谢。 @zx485 您的 示例正在做一些我难以理解的事情......!如果条件测试 匹配,它将返回“classification”和“mutationString”元素的预期值。但是,当没有匹配项时,它会返回“mutation”元素的所有 7 个子元素的值。我错过了什么? 好点。我没有测试过代码。问题是所有<mutation>
元素都被xsl:apply-templates
传递,但不是所有都匹配。不匹配的由 XSLT 的default templates 处理。解决方案很简单:像<xsl:template match="mutation" />
这样为超集添加一个空模板。这将匹配并丢弃所有与更具体的模板规则不匹配的<mutation>
元素。
太好了 - 非常感谢。在这里短时间内学到了很多东西...【参考方案2】:
首先,xsl:for-each
不用于“循环”。它没有退出条件,也无法将一次迭代的结果传递给另一次迭代。
接下来,使用xsl:for-each
不仅限于初学者,也不是“糟糕的风格”——尽管您可能在这里或其他任何地方读过。
xsl:for-each
指令只不过是在特殊情况下使用的快捷方式。一般方法分为两个阶段:
首先,您选择一组节点并告诉处理器将模板应用于它们;
接下来,处理器找到与所选节点集中每个节点最匹配的模板并应用它。
如果 (1) 您希望对选定集中的所有节点应用统一处理,并且 (2) 不需要递归应用模板或重新使用它,您可以简单地告诉处理器:获取这些节点并将此模板应用于它们。
这正是xsl:for-each
指令的作用。它有一个select
属性来选择要处理的节点,它的内容是一个模板,以应用于所选的节点集。不多也不少。这里没有范式转变。没有“推式”与“拉式”。没有善恶之分。
xsl:for-each
的唯一问题在于它是样式表作者工具箱中的唯一工具。正如我所说,它是一种可以在特殊情况下使用的捷径。当这些情况不适用时,使用它会导致代码非常糟糕。
【讨论】:
啊循环描述来自w3schools.com/xml/xsl_for_each.asp 所以不是我的! 感谢您的回复 - 改进我的代码是目标 @DerekFairleyW3Schools
只是一个教程站点,而不是参考。尽管有他们的名字,但他们与万维网联盟 (W3C) 没有任何关联。唯一的规范性参考是 XSLT 规范,例如 w3.org/TR/1999/REC-xslt-19991116。以上是关于XSL 风格:在 XSLT 中使用 <xsl:for-each select> 或 <xsl:template match> 或其他解决方案?的主要内容,如果未能解决你的问题,请参考以下文章
在 XSLT 2.0 中定义变量时使用 xsl:copy-of