我正在尝试编写一个XSLT,根据标题级别将HTML文件组织到不同的部分级别。以下是我的意见:
<html>
<head>
<title></title>
</head>
<body>
<h1>HEADER 1 CONTENT</h1>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<h2>Header 2 CONTENT</h2>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
</body>
</html>
我现在正在使用一个相当简单的结构,因此这个模式将暂时保持不变。我需要这样的输出......
<document>
<section level="1">
<header1>Header 1 CONTENT</header1>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<section level="2">
<header2>Header 2 CONTENT</header2>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
</section>
</section>
</document>
我一直在使用这个例子:Stackoverflow Answer
然而,我无法让它完全按照我的需要去做。
我正在使用Saxon 9在开发的Oxygen中运行xslt。我将在生产中使用cmd / bat文件。仍然撒克逊人9.如果可能的话,我想处理多达4个嵌套的部分级别。
非常感谢任何帮助!
我需要补充一下,因为我遇到了另一个规定。我以前可能应该想到这个。
我遇到以下代码示例
<html>
<head>
<title></title>
</head>
<body>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<h1>Header 2 CONTENT</h1>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
</body>
</html>
正如您所看到的,<p>
是<body>
的孩子,而在我的第一个片段中,<p>
始终是标题级别的子级。我想要的结果与上述相同,只是当我遇到<p>
的{{1}}孩子时,它应该包含在<body>
中。
<section level="1">
答案 0 :(得分:6)
这是一个XSLT 2.0样式表:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="2.0">
<xsl:output indent="yes"/>
<xsl:function name="mf:group" as="node()*">
<xsl:param name="elements" as="element()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$elements" group-starting-with="*[local-name() eq concat('h', $level)]">
<xsl:choose>
<xsl:when test="self::*[local-name() eq concat('h', $level)]">
<section level="{$level}">
<xsl:element name="header{$level}"><xsl:apply-templates/></xsl:element>
<xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
</section>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/html">
<document>
<xsl:apply-templates select="body"/>
</document>
</xsl:template>
<xsl:template match="body">
<xsl:sequence select="mf:group(*, 1)"/>
</xsl:template>
</xsl:stylesheet>
它应该按照你的要求行事,虽然它不会停留在四个嵌套级别,而是只要找到h[n]
元素就可以停止。
答案 1 :(得分:4)
XSLT 1.0解决方案(基本上由Jenni Tennison借用):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="html">
<document><xsl:apply-templates/></document>
</xsl:template>
<xsl:template match="body">
<xsl:apply-templates select="h1" />
</xsl:template>
<xsl:key name="next-headings" match="h6"
use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
self::h3 or self::h4 or
self::h5][1])" />
<xsl:key name="next-headings" match="h5"
use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
self::h3 or self::h4][1])" />
<xsl:key name="next-headings" match="h4"
use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
self::h3][1])" />
<xsl:key name="next-headings" match="h3"
use="generate-id(preceding-sibling::*[self::h1 or self::h2][1])" />
<xsl:key name="next-headings" match="h2"
use="generate-id(preceding-sibling::h1[1])" />
<xsl:key name="immediate-nodes"
match="node()[not(self::h1 | self::h2 | self::h3 | self::h4 |
self::h5 | self::h6)]"
use="generate-id(preceding-sibling::*[self::h1 or self::h2 or
self::h3 or self::h4 or
self::h5 or self::h6][1])" />
<xsl:template match="h1 | h2 | h3 | h4 | h5 | h6">
<xsl:variable name="vLevel" select="substring-after(name(), 'h')" />
<section level="{$vLevel}">
<xsl:element name="header{$vLevel}">
<xsl:apply-templates />
</xsl:element>
<xsl:apply-templates select="key('immediate-nodes', generate-id())" />
<xsl:apply-templates select="key('next-headings', generate-id())" />
</section>
</xsl:template>
<xsl:template match="/*/*/node()" priority="-20">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
将此转换应用于以下XML文档:
<html>
<body>
<h1>1</h1>
<p>1</p>
<h2>1.1</h2>
<p>2</p>
<h3>1.1.1</h3>
<p>3</p>
<h2>1.2</h2>
<p>4</p>
<h1>2</h1>
<p>5</p>
<h2>2.1</h2>
<p>6</p>
</body>
</html>
生成了想要的结果:
<document>
<section level="1">
<header1>1</header1>
<p>1</p>
<section level="2">
<header2>1.1</header2>
<p>2</p>
<section level="3">
<header3>1.1.1</header3>
<p>3</p>
</section>
</section>
<section level="2">
<header2>1.2</header2>
<p>4</p>
</section>
</section>
<section level="1">
<header1>2</header1>
<p>5</p>
<section level="2">
<header2>2.1</header2>
<p>6</p>
</section>
</section>
</document>
答案 2 :(得分:2)
XSLT 1.0中更常规的分组
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kHeaderByPreceding"
match="body/*[starts-with(name(),'h')]"
use="generate-id(preceding-sibling::*
[starts-with(name(),'h')]
[substring(name(current()),2)
> substring(name(),2)][1])"/>
<xsl:key name="kElementByPreceding"
match="body/*[not(starts-with(name(),'h'))]"
use="generate-id(preceding-sibling::*
[starts-with(name(),'h')][1])"/>
<xsl:template match="node()|@*" mode="copy">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body">
<document>
<xsl:apply-templates select="key('kHeaderByPreceding','')"/>
</document>
</xsl:template>
<xsl:template match="body/*[starts-with(name(),'h')]">
<section level="{substring(name(),2)}">
<xsl:element name="header{substring(name(),2)}">
<xsl:apply-templates mode="copy"/>
</xsl:element>
<xsl:apply-templates select="key('kElementByPreceding',
generate-id())"
mode="copy"/>
<xsl:apply-templates select="key('kHeaderByPreceding',
generate-id())"/>
</section>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
输出:
<document>
<section level="1">
<header1>HEADER 1 CONTENT</header1>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<p>Level 1 para</p>
<section level="2">
<header2>Header 2 CONTENT</header2>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
<p>Level 2 para</p>
</section>
</section>
</document>
使用更复杂的输入样本,如:
<body>
<h1>1</h1>
<p>1</p>
<h2>1.1</h2>
<p>2</p>
<h3>1.1.1</h3>
<p>3</p>
<h2>1.2</h2>
<p>4</p>
<h1>2</h1>
<p>5</p>
<h2>2.1</h2>
<p>6</p>
</body>
输出:
<document>
<section level="1">
<header1>1</header1>
<p>1</p>
<section level="2">
<header2>1.1</header2>
<p>2</p>
<section level="3">
<header3>1.1.1</header3>
<p>3</p>
</section>
</section>
<section level="2">
<header2>1.2</header2>
<p>4</p>
</section>
</section>
<section level="1">
<header1>2</header1>
<p>5</p>
<section level="2">
<header2>2.1</header2>
<p>6</p>
</section>
</section>
</document>
答案 3 :(得分:0)
我能够为上面的附录找到一些工作。我在body模板中添加了逻辑来测试标头标签。它可能不适用于所有情况,但它对我的任务表现良好。
<xsl:template match="body">
<xsl:choose>
<xsl:when test="descendant::h1">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<section level="1">
<item>
<block ccm="yes" onbup="no" quickref="no" web="no">
<xsl:apply-templates/>
</block>
</item>
</section>
</xsl:otherwise>
</xsl:choose>
</xsl:template>