小提琴在这里。我正在尝试从内容表元数据创建一个唯一标识符,以供将来在文本数据库中使用。我使用
<xsl:for-each..
编写了一个递归调用模板,但我没有得到预期的结果。
来源:
<html lang="en">
<head>
<title>Title</title>
</head>
<body>
<h1 data-toc="1" data-stub="Title1">Title 1—General</h1>
<h2 data-toc="5" data-stub="Part1">PART 1—DEFINITIONS</h2>
<h3 data-toc="9" data-stub="Sec1">§ 1 Definitions.</h3>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec1.1">§ 1.1 Foo.</h3>
<p>foo</p>
<p>foo</p>
<h2 data-toc="5" data-stub="Part2">PART 2—THE COMMITTEE</h2>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec2">§ 2 Definitions.</h3>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec2.1">§ 2.1 Foo.</h3>
<p>foo</p>
<p>foo</p>
<h2 data-toc="5" data-stub="Part3">PART 3—THE COUNCIL</h2>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec3">§ 3 Definitions.</h3>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec3.1">§ 3.1 Foo.</h3>
<p>foo</p>
<p>foo</p>
</body>
</html>
样式表:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xhtml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@data-toc]">
<xsl:variable name="build-callcode">
<xsl:call-template name="build-call-code">
<xsl:with-param name="this-toc-level" select="@data-toc"/>
<xsl:with-param name="this-stub" select="@data-stub"/>
</xsl:call-template>
</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="data-callcode" select="$build-callcode"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="build-call-code">
<xsl:param name="this-toc-level"/>
<xsl:param name="this-stub"/>
<xsl:for-each select="preceding::*[@data-toc < $this-toc-level][1]">
<xsl:call-template name="build-call-code">
<xsl:with-param name="this-toc-level" select="@data-toc"/>
<xsl:with-param name="this-stub" select="concat(@data-stub, $this-stub)"/>
</xsl:call-template>
<xsl:value-of select="concat(@data-stub, $this-stub)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
期望的结果:
<html lang="en">
<head>
<title>Title</title>
</head>
<body>
<h1 data-toc="1" data-stub="Title1" data-callcode="Title1">Title 1—General</h1>
<h2 data-toc="5" data-stub="Part1" data-callcode="Title1Part1">PART 1—DEFINITIONS</h2>
<h3 data-toc="9" data-stub="Sec1" data-callcode="Title1Part1Sec1">§ 1 Definitions.</h3>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec1.1" data-callcode="Title1Part1Sec1.1">§ 1.1 Foo.</h3>
<p>foo</p>
<p>foo</p>
<h2 data-toc="5" data-stub="Part2" data-callcode="Title1Part2">PART 2—THE COMMITTEE</h2>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec2" data-callcode="Title1Part2Sec2">§ 2 Definitions.</h3>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec2.1" data-callcode="Title1Part2Sec2.1">§ 2.1 Foo.</h3>
<p>foo</p>
<p>foo</p>
<h2 data-toc="5" data-stub="Part3" data-callcode="Title1Part3">PART 3—THE COUNCIL</h2>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec3" data-callcode="Title1Part3Sec3">§ 3 Definitions.</h3>
<p>foo</p>
<p>foo</p>
<h3 data-toc="9" data-stub="Sec3.1" data-callcode="Title1Part3Sec3.1">§ 3.1 Foo.</h3>
<p>foo</p>
<p>foo</p>
</body>
</html>
试试这个:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xhtml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@data-toc]">
<xsl:variable name="build-callcode">
<!-- No need to call the template with any params -->
<xsl:call-template name="build-call-code"/>
</xsl:variable>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="data-callcode" select="$build-callcode"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="build-call-code">
<xsl:variable name="this-toc-level" select="@data-toc"/>
<!-- Since all nodes are siblings let's use preceding-sibling::* -->
<xsl:for-each select="preceding-sibling::*[@data-toc < $this-toc-level][1]">
<!-- No need to call the template with any params -->
<xsl:call-template name="build-call-code"/>
</xsl:for-each>
<!-- Put the current @data-stub AFTER for-each-->
<xsl:value-of select="@data-stub"/>
</xsl:template>
</xsl:stylesheet>
使用 XSLT 3(无论如何,当前支持的版本,例如 Saxon 都是 XSLT 3 处理器),您可以使用
xsl:accumulator
s:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:accumulator name="data-toc" as="xs:integer*" initial-value="()">
<xsl:accumulator-rule
match="*[@data-toc]"
select="let $current-data-toc := xs:integer(@data-toc) return ($value[. lt $current-data-toc], $current-data-toc)"/>
</xsl:accumulator>
<xsl:accumulator name="data-call-code" as="xs:string*" initial-value="()">
<xsl:accumulator-rule match="*[@data-toc]"
select="let $count := count(accumulator-before('data-toc'))
return $value[position() lt $count], @data-stub"/>
</xsl:accumulator>
<xsl:mode on-no-match="shallow-copy" use-accumulators="data-toc data-call-code"/>
<xsl:template match="*[@data-toc]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="data-callcode" select="accumulator-before('data-call-code') => string-join()"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>