递归XSLT,第2部分

时间:2010-05-25 19:28:56

标签: xml xslt recursion file-inclusion

好的,继续我的问题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 个答案:

答案 0 :(得分:2)

这是一个XSLT 2.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>

编辑:对于XSLT 1.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>

答案 1 :(得分:1)

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

答案 2 :(得分:-1)

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

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

这是否比其他解决方案更好或更差?