代码之家  ›  专栏  ›  技术社区  ›  Frerich Raabe

如何使XSLT模板能够处理缺少的父元素?

  •  0
  • Frerich Raabe  · 技术社区  · 15 年前

    我想为这个糟糕的头衔道歉-我真的不知道该怎么说才好。我目前正在开发一个XSLT 1.0脚本(使用 xsltproc

    在我的XML格式中,只有三个元素: <book> , <chapter> <section>

    <!ELEMENT book ((chapter|section)*)>
    <!ELEMENT chapter (section*)>
    <!ELEMENT section (#PCDATA)>
    

    下面是执行转换的my 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使得 <章节> <预订> /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'模板 <章节> 元素不在 <第章>

    <章节> 不在 < 应该合并成一个。因此, <book><section/><section/><section/><chapter/><section/></book> 生成三章的输出(第一章有三个部分,第二章没有,第三章有一个部分)。

    2 回复  |  直到 15 年前
        1
  •  2
  •   user357812 user357812    15 年前

    此样式表:

    <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 制造章 规则,“打开”一章,如果上下文是 chapter section 两者都在 部分 部分 在里面 部分 统治 模式(因为每个节点的进程总是匹配第一个 sections 对于相邻的 )“打开”章节 makeSection 规则; 规则“打开”一个部分一个进程的子进程,然后处理下一个兄弟进程 部分 在里面 模式(此规则)。

        2
  •  0
  •   Gabriele Petrioli    15 年前

    你需要先把孤立的部分包装成一个单独的章节。。

    我们创建一个变量来保存包装好的元素

    <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>