我想为糟糕的头衔道歉 - 我真的不知道如何更好地说出来。我目前正在开发一个XSLT 1.0脚本(使用xsltproc),它将简单的XML格式转换为适合PDF生成器使用的文本表示。
在我的XML格式中,只有三个元素:<book>
,<chapter>
和<section>
。但是,由于DTD的某些令人讨厌的功能,我很难编写适当的XSLT脚本来转换文档。这是描述其关系的DTD:
<!ELEMENT book ((chapter|section)*)>
<!ELEMENT chapter (section*)>
<!ELEMENT section (#PCDATA)>
这是执行翻译的我的XSLT样式表:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:strip-space elements="*"/>
<xsl:template match="section">
<xsl:if test="not(preceding-sibling::section)">@BeginSections
</xsl:if>
<xsl:text>@Section @Begin
</xsl:text>
<xsl:apply-templates/>
<xsl:text>@End @Section
</xsl:text>
<xsl:if test="not(following-sibling::section)">@EndSections
</xsl:if>
</xsl:template>
<xsl:template match="chapter">
<xsl:if test="not(preceding-sibling::chapter)">@BeginChapters
</xsl:if>
<xsl:text>@Chapter @Begin
</xsl:text>
<xsl:apply-templates/>
<xsl:text>@End @Chapter
</xsl:text>
<xsl:if test="not(following-sibling::chapter)">@EndChapters
</xsl:if>
</xsl:template>
<xsl:template match="/book">
<xsl:text>@Book @Begin
</xsl:text>
<xsl:apply-templates/>
<xsl:text>@End @Book
</xsl:text>
</xsl:template>
</xsl:stylesheet>
现在,这里有一个棘手的部分和我的问题:DTD可以将<section>
个元素作为<book>
的直接子元素。但是,我仍然需要产生相同的输出,就好像/book/section
元素实际上是/book/chapter/section
一样。
因此例如:<book><section/><chapter/></book>
变为(我缩进输出以获得更好的可读性)
@Book @Begin
@BeginChapters
@Chapter @Begin
@BeginSections
@Section @Begin
@End @Section
@EndSections
@End @Chapter
@Chapter @Begin
@End @Chapter
@EndChapters
@End @Book
所以我想做的是调整我的XSLT脚本,以便'section'模板以某种方式调用'chapter'模板,以防给定的<section>
元素不在{{1}内}。我怎么能这样做?
对于它的价值,这是另一个例子。不在<chapter>
中的多个<section>
元素应合并为一个元素。因此,<chapter>
产生三个章节的输出(第一章有三个章节,第二章没有,第三章有一个章节。)
答案 0 :(得分:2)
此样式表:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:strip-space elements="*"/>
<xsl:template match="chapter|section" mode="chapter" name="makeChapter">
<xsl:text> @Chapter @Begin
</xsl:text>
<xsl:apply-templates select="self::chapter/node()[1]|
self::section"
mode="section"/>
<xsl:text> @End @Chapter
</xsl:text>
<xsl:apply-templates
select="self::chapter/following-sibling::node()[1]|
self::section/following-sibling::chapter[1] "
mode="chapter"/>
</xsl:template>
<xsl:template match="section" mode="makeSection" name="makeSection">
<xsl:text> @Section @Begin
</xsl:text>
<xsl:apply-templates/>
<xsl:text> @End @Section
</xsl:text>
<xsl:apply-templates select="following-sibling::node()[1]/self::section"
mode="makeSection"/>
</xsl:template>
<xsl:template match="section" mode="section">
<xsl:text> @BeginSections
</xsl:text>
<xsl:call-template name="makeSection"/>
<xsl:text> @EndSections
</xsl:text>
</xsl:template>
<xsl:template match="book/chapter|book/section">
<xsl:text> @BeginChapters
</xsl:text>
<xsl:call-template name="makeChapter"/>
<xsl:text> @EndChapters
</xsl:text>
</xsl:template>
<xsl:template match="book">
<xsl:text>@Book @Begin
</xsl:text>
<xsl:apply-templates select="node()[1]"/>
<xsl:text>@End @Book
</xsl:text>
</xsl:template>
</xsl:stylesheet>
输出:
@Book @Begin
@BeginChapters
@Chapter @Begin
@BeginSections
@Section @Begin
@End @Section
@EndSections
@End @Chapter
@Chapter @Begin
@End @Chapter
@EndChapters
@End @Book
注意:细粒度遍历,将相邻的书籍/部分分组为一章。
修改:更正了章节的兄弟进程。
使用此输入:
<book>
<section/>
<section/>
<section/>
<chapter/>
<section/>
</book>
输出:
@Book @Begin
@BeginChapters
@Chapter @Begin
@BeginSections
@Section @Begin
@End @Section
@Section @Begin
@End @Section
@Section @Begin
@End @Section
@EndSections
@End @Chapter
@Chapter @Begin
@End @Chapter
@Chapter @Begin
@BeginSections
@Section @Begin
@End @Section
@EndSections
@End @Chapter
@EndChapters
@End @Book
修改:更好的命名模板,以便了解。
注意:五条规则:book
规则“打开”一本书并处理第一个孩子; book/section|book/chapter
规则(总是第一个,因为细粒度横向)“打开”书籍章节,调用makeChapter
; makeChapter
规则,“打开”章节,如果上下文为chapter
,则处理第一个子项;如果上下文section
处于section
模式,则处理自身,如果上下文为{,则处理下一个兄弟{如果chapter
模式中的上下文为chapter
(意味着下一章),则{1}}或section
之后; chapter
模式中的section
规则(因为逐节点进程,它将始终匹配邻居section
的第一个sections
)“打开”章节部分调用{{1 }} 规则; sections
规则“打开”流程子项的一个部分,然后以makeSection
模式(此规则)处理下一个兄弟makeSection
。
答案 1 :(得分:0)
你需要先将孤儿部分包装成一章......
我们为此创建一个变量来保存包裹的元素
<xsl:variable name="orphan">
<chapter>
<xsl:for-each select="/book/section">
<xsl:copy-of select="." />
</xsl:for-each>
</chapter>
</xsl:variable>
然后当您在/book
匹配模板中应用模板时,您还需要使用这个新创建的变量
<xsl:apply-templates select="chapter|exslt:node-set($orphan)"/>
并使用exslt
添加名称空间
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
最终结果是
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common">
<xsl:output method="html" encoding="iso-8859-1"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="orphan">
<chapter>
<xsl:for-each select="/book/section">
<xsl:copy-of select="." />
</xsl:for-each>
</chapter>
</xsl:variable>
<xsl:template match="section">
<xsl:if test="not(preceding-sibling::section)">@BeginSections
</xsl:if>
<xsl:text>@Section @Begin
</xsl:text>
<xsl:apply-templates/>
<xsl:text>@End @Section
</xsl:text>
<xsl:if test="not(following-sibling::section)">@EndSections
</xsl:if>
</xsl:template>
<xsl:template match="chapter">
<xsl:if test="not(preceding-sibling::chapter)">@BeginChapters
</xsl:if>
<xsl:text>@Chapter @Begin
</xsl:text>
<xsl:apply-templates/>
<xsl:text>@End @Chapter
</xsl:text>
<xsl:if test="not(following-sibling::chapter)">@EndChapters
</xsl:if>
</xsl:template>
<xsl:template match="/book">
<xsl:text>@Book @Begin
</xsl:text>
<xsl:apply-templates select="chapter|exslt:node-set($orphan)"/>
<xsl:text>@End @Book
</xsl:text>
</xsl:template>
</xsl:stylesheet>