apply-templates 输出内容的次数超过预期
apply-templates outputs content more times than expected
我是 XSLT 的新手,我不明白为什么根要处理两次(至少这是我对此输出的解释)。
编辑:(我将 Saxon-HE 与 XSLT 2.0 一起使用)但也使用多个在线流程进行了测试,结果始终相同。
XSLT 文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- XResume.xsl: resume.xml ==> resume.xhtml -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="https://github.com/IME-SE8/XResume">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<meta charset="utf-8" />
<meta lang="en" />
<meta name="description" content="Personal Resume and Portfolio" />
<title><xsl:value-of select="resume/personalInformation/name/attribute::shortForm" /> Website</title>
</head>
<body>
<xsl:apply-templates select="resume"/>
</body>
</html>
</xsl:template>
<xsl:template match="resume">
<div class="resume">
<div class="header">
<div class="name"><xsl:value-of select="personalInformation/name" /></div>
<div class="contacts">
<xsl:for-each select="personalInformation/contact">
<div class="contactInformation">
<p><xsl:value-of select="organization" /></p>
<p><xsl:value-of select="address" /></p>
<p><xsl:value-of select="phoneNumber" /></p>
<p><xsl:value-of select="email" /></p>
</div>
</xsl:for-each>
</div>
</div>
<div class="sections">
<xsl:apply-templates />
</div>
</div>
</xsl:template>
<xsl:template match="interests"></xsl:template>
<xsl:template match="education"></xsl:template>
<xsl:template match="skills"></xsl:template>
<xsl:template match="experiences"></xsl:template>
<xsl:template match="projects"></xsl:template>
<xsl:template match="awards"></xsl:template>
</xsl:stylesheet>
XML 文件
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl"
href="https://github.com/IME-SE8/XResume/master/XResume.xsl"?>
<resume
xmlns="https://github.com/IME-SE8/XResume"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">
<personalInformation>
<name first="John" last="Doe" shortForm="JD">John Doe</name>
<contact type="institutional">
<organization>Whosebug Institute of Technology</organization>
<address>Internet</address>
<phoneNumber>+1 (666) 666-9999</phoneNumber>
<email>john@d.oe</email>
</contact>
</personalInformation>
<interests>
<interest>Q and A</interest>
<interest>XSLT</interest>
</interests>
<education></education>
<skills></skills>
<experiences></experiences>
<projects></projects>
<awards></awards>
</resume>
HTML输出
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>Whosebug Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john@d.oe</p>
</div>
</div>
</div>
<div class="sections">
John Doe
Whosebug Institute of Technology
Internet
+1 (666) 666-9999
john@d.oe
</div>
</div>
</body>
</html>
(是的,有那么多空行)
输出 header div
完全没问题,但在 sections div
内部 apply-templates
再次呈现 div header
中的所有信息,但没有 HTML标签。
是否缺少任何 XSLT 处理细节?模板匹配是否以匹配元素现在被视为根或类似元素的方式设置上下文?
您没有提供预期的输出,所以我会猜测您的预期结果。这是一个 XSLT 2.0 解决方案。如果您需要 XSLT 1.0,请发表评论,我可以添加。但请记住,如果您的转换引擎是浏览器,您就没有理由不使用 XSLT 2.0。 (参考 Saxon CE)。
XSLT 2.0 解决方案
此 XSLT 2.0 样式表 ...
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:r="https://github.com/IME-SE8/XResume"
exclude-result-prefixes="r"
version="2.0">
<xsl:output method="html" version="5" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<html>
<head>
<meta lang="en" />
<meta name="description" content="Personal Resume and Portfolio" />
<title><xsl:value-of select="r:resume/r:personalInformation/r:name/@shortForm" /> Website</title>
</head>
<body>
<xsl:apply-templates select="r:resume"/>
</body>
</html>
</xsl:template>
<xsl:template match="r:resume">
<div class="resume">
<div class="header">
<div class="name"><xsl:value-of select="r:personalInformation/r:name" /></div>
<div class="contacts">
<xsl:apply-templates select="r:personalInformation/r:contact" />
</div>
</div>
<div class="sections">
<xsl:apply-templates select="* except r:personalInformation" />
</div>
</div>
</xsl:template>
<xsl:template match="r:contact">
<div class="contactInformation">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="r:organization|r:address|r:phoneNumber|r:email">
<p><xsl:value-of select="." /></p>
</xsl:template>
<xsl:template match="r:education|r:skills|r:experiences|r:projects|r:awards">
<h2><xsl:value-of select="local-name()" /></h2>
<p><xsl:value-of select="." /></p>
</xsl:template>
<xsl:template match="r:interests">
<h2>interests</h2>
<ul>
<xsl:apply-templates />
</ul>
</xsl:template>
<xsl:template match="r:interest">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
<xsl:template match="*" />
</xsl:transform>
...应用于此输入文档时...
<resume
xmlns="https://github.com/IME-SE8/XResume"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">
<personalInformation>
<name first="John" last="Doe" shortForm="JD">John Doe</name>
<contact type="institutional">
<organization>Whosebug Institute of Technology</organization>
<address>Internet</address>
<phoneNumber>+1 (666) 666-9999</phoneNumber>
<email>john@d.oe</email>
</contact>
</personalInformation>
<interests>
<interest>Q and A</interest>
<interest>XSLT</interest>
</interests>
<education></education>
<skills></skills>
<experiences></experiences>
<projects></projects>
<awards></awards>
</resume>
... 将产生此输出 html 页 ....
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>Whosebug Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john@d.oe</p>
</div>
</div>
</div>
<div class="sections">
<h2>interests</h2>
<ul>
<li>Q and A</li>
<li>XSLT</li>
</ul>
<h2>education</h2>
<p></p>
<h2>skills</h2>
<p></p>
<h2>experiences</h2>
<p></p>
<h2>projects</h2>
<p></p>
<h2>awards</h2>
<p></p>
</div>
</div>
</body>
</html>
解释:为什么要对 root 进行两次处理
简而言之,因为您的 <div class="sections"><xsl:apply-templates /></div>
指令没有指定 select 属性。默认 selection 已应用,当时是文档根目录。
源文档中的所有元素都在命名空间中,但您编写的样式表是为了处理没有命名空间中的元素。欢迎来到俱乐部,加入其他 1000 万掉入这个陷阱的人。本质上,您的 resume 元素与 match="resume" 模板不匹配,因此默认模板启动,这会输出没有标签的原始文本。对于解决方案,请搜索 "XSLT default namespace" 并从大约 1000 个答案中选择一个。
重新阅读时,我发现您使用了 xpath-default-namespace="https://github.com/IME-SE8/XResume"
,如果您使用的是 XSLT 2.0 处理器,这应该可以解决问题;如果您使用的是 XSLT 1.0 处理器,则将触发错误.因此,告诉我们您使用的是什么处理器以及您的使用情况可能会很有用(实际上,它总是很有用)运行。
问题出在这里:
<div class="sections">
<xsl:apply-templates />
</div>
这会将模板应用于当前节点 (resume
) 的所有子节点,包括 personalInformation
元素。
由于没有为 personalInformation
指定的匹配模板,XSLT 处理器使用内置 XSLT 模板并应用它们导致输出 personalInformation
的所有后代文本节点的串联元素.
解决方法:
替换:
<div class="sections">
<xsl:apply-templates />
</div>
与:
<div class="sections">
<xsl:apply-templates select="*[not(self::personalInformation)]" />
</div>
转换的结果现在不包含有问题的输出:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>Whosebug Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john@d.oe</p>
</div>
</div>
</div>
<div class="sections"></div>
</div>
</body>
</html>
我是 XSLT 的新手,我不明白为什么根要处理两次(至少这是我对此输出的解释)。
编辑:(我将 Saxon-HE 与 XSLT 2.0 一起使用)但也使用多个在线流程进行了测试,结果始终相同。
XSLT 文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- XResume.xsl: resume.xml ==> resume.xhtml -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="https://github.com/IME-SE8/XResume">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<meta charset="utf-8" />
<meta lang="en" />
<meta name="description" content="Personal Resume and Portfolio" />
<title><xsl:value-of select="resume/personalInformation/name/attribute::shortForm" /> Website</title>
</head>
<body>
<xsl:apply-templates select="resume"/>
</body>
</html>
</xsl:template>
<xsl:template match="resume">
<div class="resume">
<div class="header">
<div class="name"><xsl:value-of select="personalInformation/name" /></div>
<div class="contacts">
<xsl:for-each select="personalInformation/contact">
<div class="contactInformation">
<p><xsl:value-of select="organization" /></p>
<p><xsl:value-of select="address" /></p>
<p><xsl:value-of select="phoneNumber" /></p>
<p><xsl:value-of select="email" /></p>
</div>
</xsl:for-each>
</div>
</div>
<div class="sections">
<xsl:apply-templates />
</div>
</div>
</xsl:template>
<xsl:template match="interests"></xsl:template>
<xsl:template match="education"></xsl:template>
<xsl:template match="skills"></xsl:template>
<xsl:template match="experiences"></xsl:template>
<xsl:template match="projects"></xsl:template>
<xsl:template match="awards"></xsl:template>
</xsl:stylesheet>
XML 文件
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl"
href="https://github.com/IME-SE8/XResume/master/XResume.xsl"?>
<resume
xmlns="https://github.com/IME-SE8/XResume"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">
<personalInformation>
<name first="John" last="Doe" shortForm="JD">John Doe</name>
<contact type="institutional">
<organization>Whosebug Institute of Technology</organization>
<address>Internet</address>
<phoneNumber>+1 (666) 666-9999</phoneNumber>
<email>john@d.oe</email>
</contact>
</personalInformation>
<interests>
<interest>Q and A</interest>
<interest>XSLT</interest>
</interests>
<education></education>
<skills></skills>
<experiences></experiences>
<projects></projects>
<awards></awards>
</resume>
HTML输出
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>Whosebug Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john@d.oe</p>
</div>
</div>
</div>
<div class="sections">
John Doe
Whosebug Institute of Technology
Internet
+1 (666) 666-9999
john@d.oe
</div>
</div>
</body>
</html>
(是的,有那么多空行)
输出 header div
完全没问题,但在 sections div
内部 apply-templates
再次呈现 div header
中的所有信息,但没有 HTML标签。
是否缺少任何 XSLT 处理细节?模板匹配是否以匹配元素现在被视为根或类似元素的方式设置上下文?
您没有提供预期的输出,所以我会猜测您的预期结果。这是一个 XSLT 2.0 解决方案。如果您需要 XSLT 1.0,请发表评论,我可以添加。但请记住,如果您的转换引擎是浏览器,您就没有理由不使用 XSLT 2.0。 (参考 Saxon CE)。
XSLT 2.0 解决方案
此 XSLT 2.0 样式表 ...
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:r="https://github.com/IME-SE8/XResume"
exclude-result-prefixes="r"
version="2.0">
<xsl:output method="html" version="5" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<html>
<head>
<meta lang="en" />
<meta name="description" content="Personal Resume and Portfolio" />
<title><xsl:value-of select="r:resume/r:personalInformation/r:name/@shortForm" /> Website</title>
</head>
<body>
<xsl:apply-templates select="r:resume"/>
</body>
</html>
</xsl:template>
<xsl:template match="r:resume">
<div class="resume">
<div class="header">
<div class="name"><xsl:value-of select="r:personalInformation/r:name" /></div>
<div class="contacts">
<xsl:apply-templates select="r:personalInformation/r:contact" />
</div>
</div>
<div class="sections">
<xsl:apply-templates select="* except r:personalInformation" />
</div>
</div>
</xsl:template>
<xsl:template match="r:contact">
<div class="contactInformation">
<xsl:apply-templates />
</div>
</xsl:template>
<xsl:template match="r:organization|r:address|r:phoneNumber|r:email">
<p><xsl:value-of select="." /></p>
</xsl:template>
<xsl:template match="r:education|r:skills|r:experiences|r:projects|r:awards">
<h2><xsl:value-of select="local-name()" /></h2>
<p><xsl:value-of select="." /></p>
</xsl:template>
<xsl:template match="r:interests">
<h2>interests</h2>
<ul>
<xsl:apply-templates />
</ul>
</xsl:template>
<xsl:template match="r:interest">
<li>
<xsl:value-of select="." />
</li>
</xsl:template>
<xsl:template match="*" />
</xsl:transform>
...应用于此输入文档时...
<resume
xmlns="https://github.com/IME-SE8/XResume"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/IME-SE8/XResume XResume.xsd">
<personalInformation>
<name first="John" last="Doe" shortForm="JD">John Doe</name>
<contact type="institutional">
<organization>Whosebug Institute of Technology</organization>
<address>Internet</address>
<phoneNumber>+1 (666) 666-9999</phoneNumber>
<email>john@d.oe</email>
</contact>
</personalInformation>
<interests>
<interest>Q and A</interest>
<interest>XSLT</interest>
</interests>
<education></education>
<skills></skills>
<experiences></experiences>
<projects></projects>
<awards></awards>
</resume>
... 将产生此输出 html 页 ....
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>Whosebug Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john@d.oe</p>
</div>
</div>
</div>
<div class="sections">
<h2>interests</h2>
<ul>
<li>Q and A</li>
<li>XSLT</li>
</ul>
<h2>education</h2>
<p></p>
<h2>skills</h2>
<p></p>
<h2>experiences</h2>
<p></p>
<h2>projects</h2>
<p></p>
<h2>awards</h2>
<p></p>
</div>
</div>
</body>
</html>
解释:为什么要对 root 进行两次处理
简而言之,因为您的 <div class="sections"><xsl:apply-templates /></div>
指令没有指定 select 属性。默认 selection 已应用,当时是文档根目录。
源文档中的所有元素都在命名空间中,但您编写的样式表是为了处理没有命名空间中的元素。欢迎来到俱乐部,加入其他 1000 万掉入这个陷阱的人。本质上,您的 resume 元素与 match="resume" 模板不匹配,因此默认模板启动,这会输出没有标签的原始文本。对于解决方案,请搜索 "XSLT default namespace" 并从大约 1000 个答案中选择一个。
重新阅读时,我发现您使用了 xpath-default-namespace="https://github.com/IME-SE8/XResume"
,如果您使用的是 XSLT 2.0 处理器,这应该可以解决问题;如果您使用的是 XSLT 1.0 处理器,则将触发错误.因此,告诉我们您使用的是什么处理器以及您的使用情况可能会很有用(实际上,它总是很有用)运行。
问题出在这里:
<div class="sections">
<xsl:apply-templates />
</div>
这会将模板应用于当前节点 (resume
) 的所有子节点,包括 personalInformation
元素。
由于没有为 personalInformation
指定的匹配模板,XSLT 处理器使用内置 XSLT 模板并应用它们导致输出 personalInformation
的所有后代文本节点的串联元素.
解决方法:
替换:
<div class="sections">
<xsl:apply-templates />
</div>
与:
<div class="sections">
<xsl:apply-templates select="*[not(self::personalInformation)]" />
</div>
转换的结果现在不包含有问题的输出:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<meta lang="en">
<meta name="description" content="Personal Resume and Portfolio">
<title>JD Website</title>
</head>
<body>
<div class="resume">
<div class="header">
<div class="name">John Doe</div>
<div class="contacts">
<div class="contactInformation">
<p>Whosebug Institute of Technology</p>
<p>Internet</p>
<p>+1 (666) 666-9999</p>
<p>john@d.oe</p>
</div>
</div>
</div>
<div class="sections"></div>
</div>
</body>
</html>