有条件地嵌套多个XSLT模板

时间:2011-11-16 16:12:27

标签: xslt

我的XML非常扁平,这是一个例子:

<Rows>
  <Row>
    <ProjectID>1000</ProjectID>
    <Phase>Initiation</Phase>
    <ID>1</ID>
    <Name>Work item 1</Name>
    <Master>1</Master>
  </Row>
  <Row>
    <ProjectID>1000</ProjectID>
    <Phase>Initiation</Phase>
    <ID>2</ID>
    <Name>Work item 2</Name>
    <Master>1</Master>
  </Row>
  <Row>
    <ProjectID>1000</ProjectID>
    <Phase>Closing</Phase>
    <ID>3</ID>
    <Name>Work item 3</Name>
    <Master>3</Master>
  </Row>
  <Row>
    <ProjectID>1000</ProjectID>
    <Phase>Closing</Phase>
    <ID>4</ID>
    <Name>Work item 4</Name>
    <Master>3</Master>
  </Row>
  <Row>
    <ProjectID>1000</ProjectID>
    <Phase>Closing</Phase>
    <ID>5</ID>
    <Name>Work item 5</Name>
    <Master>4</Master>
  </Row>
</Rows>

它们需要以这样的方式嵌套:

**Initiation**
Work item 1
  Work item 2
**Closing**
  Work item 3
    Work item 4
      Work item 5

现在我有一个ProjectID,Phase和Name的模板(作为一个例子,我的实际模板相当大)我开始在ProjectID模板中,按阶段分组和循环,然后按阶段分组和循环-名称。 (所以,我逐个项目得到所有名字的清单)。它仅适用于2个级别(比如工作项目1和2),但第三级(例如工作项目5)已经失去了我。

现在我尝试迭代名称模板中的所有匹配的主字段(其真实代码在此处):

<xsl:template name="Deliverable">
    <!--
Parent == True && Lone == True -> Impossible
Parent == True && Lone == False -> Parent
Parent == False && Lone == False -> Child
Parent == False && Lone = True -> Single deliverable -->

    <xsl:param name="parent" />
    <xsl:param name="lone" />

    <xsl:variable name="Parent">
      <xsl:choose>
        <xsl:when test="count(key('project-phase-deliverables', concat(ProjectNo, '|', Phase, '|', IDField2))) > 1">
          1
        </xsl:when>
        <xsl:otherwise>
          0
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:variable name="ID"><xsl:value-of select="generate-id(IDField1)" /></xsl:variable>

    <tr>
      <xsl:choose>
        <!-- JS for parent deliverable -->
        <xsl:when test="$Parent = 'True'">
          <xsl:attribute name="ID">
            <xsl:value-of select="$ID"/>
          </xsl:attribute>
          <script>$('#<xsl:value-of select="$ID"/>').click(function() { $('.<xsl:value-of select="IDField2"/>').toggle(); });</script>
        </xsl:when>
        <!-- Coloring/attributes for children -->
        <xsl:when test="$parent = 'False' and $lone='False'">
          <xsl:attribute name="style">background: #DEDEFF; display: none;</xsl:attribute>
          <xsl:attribute name="class">
            <xsl:value-of select="IDField2"/>
          </xsl:attribute>
        </xsl:when>
      </xsl:choose>

      <td class="doWhite" style="width: 15px; height: 24px; text-align:right; border-right:none;">
        <p class="normal">
          <!-- Parent deliverable expander arrow -->
          <xsl:if test="$Parent = 1">
            <span class="Expander">&#9654;</span>
          </xsl:if>
        </p>
      </td>
      <td class="doWhite" style="width: 200px; height: 24px; border-left:none;">
        <p class="normal">
          <!-- Child deliverable diamond -->
          <xsl:if test="$parent = 'False' and $lone = 'False'">
            <span class="Expander">&#9670; </span>
          </xsl:if>
          <xsl:value-of select="ItemDescription"/>
        </p>
      </td>
      <td class="doWhite" style="width: 130px; height: 24px">
        <p class="normal">
          <xsl:value-of select="Owner"/>
        </p>
      </td>
      <td style="width: 60px; height: 24px">
        <xsl:call-template name="status"/>
      </td>
      <td class="doWhite">
        <p class="normal">
          <xsl:value-of select="ItemNotes"/>
        </p>
      </td>
    </tr>
    <xsl:if test="$Parent = 1">
      <xsl:for-each select="key('project-phase-deliverables', concat(ProjectNo, '|', Phase, '|', IDField2))[position()!=1]">
        <!-- this doesn't recurse properly :( -->
        <xsl:call-template name="Deliverable" />
      </xsl:for-each>
    </xsl:if>
  </xsl:template>

正如你所料,它无限循环并超时。我觉得我可以使用apply-template来解决我的问题但是如何使用它来有效地按其他字段分组(Phase和ProjectID)?

1 个答案:

答案 0 :(得分:3)

我可能误解了你的要求,但看起来你可能会在这里过于复杂。

首先,我认为你是阶段。所以设置一个如此的键:

<xsl:key name="Phase" match="Row" use="Phase" />

并匹配每个阶段中最顶层的,如下所示:

<xsl:apply-templates 
  select="//Row[generate-id() = generate-id(key('Phase', Phase)[1])]"
  mode="first" />

要获取当前行的“子项”,您可以递归调用模板以匹配元素

<xsl:apply-templates 
  select="//Row[Phase=current()/Phase][Master=current()/ID][Master != ID]" />

因此,给出以下XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="Phase" match="Row" use="Phase"/>

   <xsl:template match="/">
      <Rows>
         <xsl:apply-templates select="//Row[generate-id() = generate-id(key('Phase', Phase)[1])]" mode="first"/>
      </Rows>
   </xsl:template>

   <xsl:template match="Row" mode="first">
      <Phase name="{Phase}">
         <xsl:apply-templates select="key('Phase', Phase)[Master = ID]"/>
      </Phase>
   </xsl:template>

   <xsl:template match="Row" name="Row">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
         <xsl:apply-templates select="//Row[Phase=current()/Phase][Master=current()/ID][Master != ID]"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="@*|node()">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

当应用于您的示例XML时,输出以下内容:

<Rows>
   <Phase name="Initiation">
      <Row>
         <ProjectID>1000</ProjectID>
         <Phase>Initiation</Phase>
         <ID>1</ID>
         <Name>Work item 1</Name>
         <Master>1</Master>
         <Row>
            <ProjectID>1000</ProjectID>
            <Phase>Initiation</Phase>
            <ID>2</ID>
            <Name>Work item 2</Name>
            <Master>1</Master>
         </Row>
      </Row>
   </Phase>
   <Phase name="Closing">
      <Row>
         <ProjectID>1000</ProjectID>
         <Phase>Closing</Phase>
         <ID>3</ID>
         <Name>Work item 3</Name>
         <Master>3</Master>
         <Row>
            <ProjectID>1000</ProjectID>
            <Phase>Closing</Phase>
            <ID>4</ID>
            <Name>Work item 4</Name>
            <Master>3</Master>
            <Row>
               <ProjectID>1000</ProjectID>
               <Phase>Closing</Phase>
               <ID>5</ID>
               <Name>Work item 5</Name>
               <Master>4</Master>
            </Row>
         </Row>
      </Row>
   </Phase>
</Rows>

如果要输出HTML,请尝试以下XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html" indent="yes"/>
   <xsl:key name="Phase" match="Row" use="Phase"/>

   <xsl:template match="/">
      <body>
         <xsl:apply-templates select="//Row[generate-id() = generate-id(key('Phase', Phase)[1])]" mode="first"/>
      </body>
   </xsl:template>

   <xsl:template match="Row" mode="first">
      <h1>
         <xsl:value-of select="Phase"/>
      </h1>
      <ul>
         <xsl:apply-templates select="key('Phase', Phase)[Master = ID]"/>
      </ul>
   </xsl:template>

   <xsl:template match="Row" name="Row">
      <li>
         <xsl:value-of select="Name"/>
         <xsl:if test="//Row[Phase=current()/Phase][Master=current()/ID][Master != ID]">
            <ul>
               <xsl:apply-templates select="//Row[Phase=current()/Phase][Master=current()/ID][Master != ID]"/>
            </ul>
         </xsl:if>
      </li>
   </xsl:template>
</xsl:stylesheet>

这应输出以下HTML

<body>
   <h1>Initiation</h1>
   <ul>
      <li>Work item 1
         <ul>
            <li>Work item 2</li>
         </ul>
      </li>
   </ul>
   <h1>Closing</h1>
   <ul>
      <li>Work item 3
         <ul>
            <li>Work item 4
               <ul>
                  <li>Work item 5</li>
               </ul>
            </li>
         </ul>
      </li>
   </ul>
</body>