如何让我的XSLT模板处理缺少的父元素?

时间:2010-11-03 13:40:04

标签: xslt

我想为糟糕的头衔道歉 - 我真的不知道如何更好地说出来。我目前正在开发一个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&#xa;</xsl:if>
  <xsl:text>@Section @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Section&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::section)">@EndSections&#xa;</xsl:if>
</xsl:template>

<xsl:template match="chapter">
  <xsl:if test="not(preceding-sibling::chapter)">@BeginChapters&#xa;</xsl:if>
  <xsl:text>@Chapter @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Chapter&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::chapter)">@EndChapters&#xa;</xsl:if>
</xsl:template>

<xsl:template match="/book">
  <xsl:text>@Book @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Book&#xa;</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>产生三个章节的输出(第一章有三个章节,第二章没有,第三章有一个章节。)

2 个答案:

答案 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&#xa;</xsl:text>
        <xsl:apply-templates select="self::chapter/node()[1]|
                                     self::section"
                             mode="section"/>
        <xsl:text>  @End @Chapter&#xa;</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&#xa;</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>    @End @Section&#xa;</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&#xa;</xsl:text>
        <xsl:call-template name="makeSection"/>
        <xsl:text>    @EndSections&#xa;</xsl:text>
    </xsl:template>
    <xsl:template match="book/chapter|book/section">
        <xsl:text>  @BeginChapters&#xa;</xsl:text>
        <xsl:call-template name="makeChapter"/>
        <xsl:text>  @EndChapters&#xa;</xsl:text>
    </xsl:template>
    <xsl:template match="book">
        <xsl:text>@Book @Begin&#xa;</xsl:text>
        <xsl:apply-templates select="node()[1]"/>
        <xsl:text>@End @Book&#xa;</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&#xa;</xsl:if>
  <xsl:text>@Section @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Section&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::section)">@EndSections&#xa;</xsl:if>
</xsl:template>

<xsl:template match="chapter">
  <xsl:if test="not(preceding-sibling::chapter)">@BeginChapters&#xa;</xsl:if>
  <xsl:text>@Chapter @Begin&#xa;</xsl:text>
  <xsl:apply-templates/>
  <xsl:text>@End @Chapter&#xa;</xsl:text>
  <xsl:if test="not(following-sibling::chapter)">@EndChapters&#xa;</xsl:if>
</xsl:template>

<xsl:template match="/book">
  <xsl:text>@Book @Begin&#xa;</xsl:text>
  <xsl:apply-templates select="chapter|exslt:node-set($orphan)"/>
  <xsl:text>@End @Book&#xa;</xsl:text>
</xsl:template>
</xsl:stylesheet>