代码之家  ›  专栏  ›  技术社区  ›  Eric

递归XSLT,第2部分

  •  2
  • Eric  · 技术社区  · 15 年前

    好吧,从我的问题开始 here .

    假设我的页面现在是这样的:

    A.xml:

    <page>
        <header>Page A</header>
        <content-a>Random content for page A</content-a>
        <content-b>More of page A's content</content-b>
        <content-c>More of page A's content</content-c>
        <!-- This doesn't keep going: there are a predefined number of sections -->
    </page>
    

    B.xml:

    <page include="A.xml">
        <header>Page B</header>
        <content-a>Random content for page B</content-a>
        <content-b>More of page B's content</content-b>
        <content-c>More of page B's content</content-c>
    </page>
    

    C.xml:

    <page include="B.xml">
        <header>Page C</header>
        <content-a>Random content for page C</content-a>
        <content-b>More of page C's content</content-b>
        <content-c>More of page C's content</content-c>
    </page>
    

    转换后(打开 C.xml )最后我想说:

    <h1>Page C</h1>
    <div>
        <p>Random content for page C</p>
        <p>Random content for page B</p>
        <p>Random content for page A</p>
    </div>
    <div>
        <p>More of page C's content</p>
        <p>More of page B's content</p>
        <p>More of page A's content</p>
    </div>
    <div>
        <p>Yet more of page C's content</p>
        <p>Yet more of page B's content</p>
        <p>Yet more of page A's content</p>
    </div>
    

    我知道我可以用 document(@include) 包含另一个文档。然而,递归有点超出我的能力。

    我该怎么写这样一个转变呢?

    3 回复  |  直到 15 年前
        1
  •  2
  •   Tomalak    15 年前

    下面是一个XSLT2.0解决方案:

    <xsl:stylesheet 
      version="2.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    >
      <xsl:template match="page">
        <xsl:variable name="pages">
          <xsl:apply-templates select="." mode="load" />
        </xsl:variable>
    
        <xsl:copy>
          <h1><xsl:value-of select="header" /></h1>
          <!-- you say there is a fixed number of names, so this should be OK -->
          <xsl:for-each select="'content-a','content-b','content-c'">
            <div>
              <xsl:apply-templates select="$pages/page/*[name() = current()]" />
            </div>
          </xsl:for-each>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="page" mode="load">
        <xsl:sequence select="." />
        <xsl:apply-templates select="document(@include)" mode="load" />
      </xsl:template>
    
      <xsl:template match="content-a|content-b|content-c">
        <p><xsl:value-of select="." /></p>
      </xsl:template>
    </xsl:stylesheet>
    

    编辑:对于XSLT1.0,等效解决方案如下所示:

    <xsl:stylesheet 
      version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:exsl="http://exslt.org/common"
    >
      <xsl:template match="page">
        <xsl:variable name="pages-rtf"><!-- rtf = result tree fragment -->
          <xsl:apply-templates select="." mode="load" />
        </xsl:variable>
        <xsl:variable name="pages" select="exsl:node-set($pages-rtf)" />
    
        <!-- you say there is a fixed number of names, so this should be OK -->
        <xsl:variable name="nodes-rtf">
          <content-a/><content-b/><content-c/>
        </xsl:variable>
        <xsl:variable name="nodes" select="exsl:node-set($nodes-rtf)" />
    
        <xsl:copy>
          <h1><xsl:value-of select="header" /></h1>
          <xsl:for-each select="$nodes">
            <div>
              <xsl:apply-templates select="$pages/page/*[name() = name(current())]" />
            </div>
          </xsl:for-each>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="page" mode="load">
        <xsl:copy-of select="." />
        <xsl:apply-templates select="document(@include)" mode="load" />
      </xsl:template>
    
      <xsl:template match="content-a|content-b|content-c">
        <p><xsl:value-of select="." /></p>
      </xsl:template>
    </xsl:stylesheet>
    
        2
  •  1
  •   nuqqsa    15 年前
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
      <xsl:template match="page">
        <h1>
          <xsl:value-of select="header"/>
        </h1>
        <div>
          <xsl:apply-templates select="." mode="content-a"/>
        </div>
        <div>
          <xsl:apply-templates select="." mode="content-b"/>
        </div>
        <div>
          <xsl:apply-templates select="." mode="content-c"/>
        </div>
      </xsl:template>
    
      <xsl:template match="page" mode="content-a">
        <p><xsl:value-of select="content-a"/></p>
        <xsl:if test="@include">
          <xsl:apply-templates select="document(@include)" mode="content-a"/>
        </xsl:if>    
      </xsl:template>
    
      <xsl:template match="page" mode="content-b">
        <p><xsl:value-of select="content-b"/></p>
        <xsl:if test="@include">
          <xsl:apply-templates select="document(@include)" mode="content-b"/>
        </xsl:if>
      </xsl:template>
    
      <xsl:template match="page" mode="content-c">
        <p><xsl:value-of select="content-c"/></p>
        <xsl:if test="@include">
          <xsl:apply-templates select="document(@include)" mode="content-c"/>
        </xsl:if>
      </xsl:template>
    
      <xsl:template match="page" mode="header">
        <xsl:value-of select="header"/>
        <xsl:if test="@include">
          <xsl:apply-templates select="document(@include)" mode="header"/>
        </xsl:if>
      </xsl:template>
    
    </xsl:stylesheet>
    
        3
  •  -1
  •   Eric    15 年前

    这是我提出的解决方案。这个只加载每个子页面一次,而不是每个内容加载一次:

    <xsl:template match="content-a | content-b | content-c">
        <p><xsl:value-of select="."></p>
    </xsl:template>
    
    <xsl:template name="get-content">
        <xsl:param name='page' />
        <xsl:param name='content-a' />
        <xsl:param name='content-b' />
        <xsl:param name='content-c' />
        <xsl:choose>
            <xsl:when test="$page/@include">
                <xsl:for-each select="document($page/@include)/page"> <!--set context-->
                    <xsl:call-template name="get-members">
                        <xsl:with-param name="content-a" select="$content-a | content-a" />
                        <xsl:with-param name="content-b" select="$content-b | content-b" />
                        <xsl:with-param name="content-c" select="$content-c | content-c" />
                    </xsl:call-template>
                </xsl:xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <div>
                    <xsl:apply-templates select="$content-a" />
                </div>
                <div>
                    <xsl:apply-templates select="$content-b" />
                </div>
                <div>
                    <xsl:apply-templates select="$content-c" />
                </div>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    
    <xsl:template match="page">
        <h1><xsl:value-of select="header"></h1>
        <xsl:call-template name="get-members">
            <xsl:with-param name="content-a" select="content-a" />
            <xsl:with-param name="content-b" select="content-b" />
            <xsl:with-param name="content-c" select="content-c" />
        </xsl:call-template>
    </xsl:template>
    

    这对你来说是好还是坏?

    推荐文章