这里有两个版本的 XSLT 样式表,它们将处理 XML
你发布的文件,一个为xslt-2.0 介绍了一个方便的
xsl:for-each-group group-starting-with=pattern 这个元素
用例,为了最大的可移植性,一个用于xslt-1.0 使用
XPath 进行分组。两个版本都使用doc/text 作为逻辑
树的根和xsl:apply-templates 充分利用
内置模板规则。注意空格处理。
平面文件转换的更多示例在
SO
和 XSLT 1.0 FAQ,现在在
archive.org.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc/text">
<chapter>
<title>
<xsl:apply-templates select="p[@style='TRH2']"/>
</title>
<research>
<title>
<xsl:apply-templates select="p[@style='TRRef']"/>
</title>
<reftext>
<xsl:apply-templates select="p[@style='TRRefText']"/>
</reftext>
</research>
<sections>
<xsl:for-each-group
select="p[not(@style) or @style='TRH7']"
group-starting-with="p[@style='TRH7']"
>
<title>
<xsl:apply-templates select="self::p[1]"/>
</title>
<paragraphs>
<xsl:for-each select="current-group()[self::p][position()>1]">
<para-text>
<xsl:apply-templates/>
</para-text>
</xsl:for-each>
</paragraphs>
</xsl:for-each-group>
</sections>
</chapter>
</xsl:template>
<xsl:template match="p[@style='TRRefText']">
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="foot-note">
<footnoteref>
<id><xsl:value-of select="@id-rel"/></id>
<xsl:apply-templates/>
</footnoteref>
</xsl:template>
</xsl:transform>
XSLT 1.0 版本(在第三个xsl:template)使用 XPath
将非标题 p 元素分组在当前和之间的表达式
下一个小节标题元素 (p[@style='TRH7']) 和 mode="para"
子句以避免将标题同时作为标题和段落处理。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="doc/text">
<chapter>
<title>
<xsl:apply-templates select="p[@style='TRH2']" />
</title>
<research>
<title>
<xsl:apply-templates select="p[@style='TRRef']" />
</title>
<reftext>
<xsl:apply-templates select="p[@style='TRRefText'] "/>
</reftext>
</research>
<sections>
<xsl:apply-templates select="p[@style='TRH7']" />
</sections>
</chapter>
</xsl:template>
<xsl:template match="p[@style='TRRefText']">
<xsl:value-of select="."/><br/>
</xsl:template>
<xsl:template match="p[@style='TRH7']">
<title><xsl:apply-templates/></title>
<paragraphs>
<xsl:apply-templates mode="para"
select="following-sibling::p[not(@style='TRH7')]
[generate-id(preceding-sibling::p[@style='TRH7'][1])
= generate-id(current())]"
/>
</paragraphs>
</xsl:template>
<xsl:template match="p" mode="para">
<para-text><xsl:apply-templates/></para-text>
</xsl:template>
<xsl:template match="foot-note">
<footnoteref>
<id><xsl:value-of select="@id-rel"/></id>
<xsl:apply-templates/>
</footnoteref>
</xsl:template>
</xsl:transform>
更新:评论中要求的其他解释。
您自己的代码与我发布的非常接近,因此我将详细介绍如何使用 XSLT 1.0 对元素进行分组。文档中的每个小节都由其标题的样式 (p[@style='TRH7']) 触发,激活第三个模板:
<xsl:template match="p[@style='TRH7']">
<title><xsl:apply-templates/></title>
<paragraphs>
<xsl:apply-templates mode="para"
select="following-sibling::p[not(@style='TRH7')]
[generate-id(preceding-sibling::p[@style='TRH7'][1])
= generate-id(current())]"
/>
</paragraphs>
</xsl:template>
此模板发出一个小节标题(使用内置模板规则),然后收集以下非标题段落
(following-sibling::p[not(@style='TRH7')]) 有当前
title 作为最近的逻辑父级。回想一下preceding-sibling 是一个反向轴,所以p[…][1] 指的是反向文档顺序中最近的兄弟。由于following-sibling::p[…] 选择了所有后续的非标题段落,第二个谓词[generate-id(…)] 将选择限制为当前标题的逻辑子项。