在xslt 2.0中使用递归函数?

时间:2012-06-28 06:48:51

标签: xml xslt xpath xslt-1.0 xslt-2.0

这是我的XML文档。

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
    <w:body>
        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text1-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
                <w:pStyle w:val="Heading2" /> 
            </w:pPr>
            <w:r>
                <w:t>Text2-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text3-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text4-</w:t>
            </w:r>  
        </w:p>

         <w:p>
            <w:pPr>
                <w:pStyle w:val="Heading3" /> 
            </w:pPr>
            <w:r>
                <w:t>Text2.1-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
                <w:pStyle w:val="Heading2" /> 
            </w:pPr>
            <w:r>
                <w:t>Text5-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text6-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
                <w:pStyle w:val="Heading3" /> 
            </w:pPr>
            <w:r>
                <w:t>Text7-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text8-</w:t>
            </w:r>  
        </w:p>  

        <w:p>
            <w:pPr>
                <w:pStyle w:val="Heading1" /> 
            </w:pPr>
            <w:r>
                <w:t>Text9-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text10-</w:t>
            </w:r>  
        </w:p>

<w:p>
            <w:pPr>
                <w:pStyle w:val="Heading2" /> 
            </w:pPr>
            <w:r>
                <w:t>Text11-</w:t>
            </w:r>  
        </w:p>

        <w:p>
            <w:pPr>
            </w:pPr>
            <w:r>
                <w:t>Text12-</w:t>
            </w:r>  
        </w:p>


    </w:body>
    </w:document>

其中一个发布xslt解决方案的Stackoverflow成员。但它不适用于上面提到的xml文档。

XSLT文件:

<xsl:stylesheet 
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xpath-default-namespace="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs w mf">

<xsl:output indent="yes"/>

<xsl:function name="mf:group" as="element()*">
  <xsl:param name="paragraphs" as="element()*"/>
  <xsl:param name="level" as="xs:integer"/>
  <xsl:for-each-group select="$paragraphs" group-starting-with="p[pPr/pStyle/@w:val = concat('Heading', $level)]">
    <xsl:choose>
      <xsl:when test="self::p[pPr/pStyle/@w:val = concat('Heading', $level)]">
        <xsl:element name="Heading{$level}">
          <Title><xsl:value-of select="r/t"/></Title>
          <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
        </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="current-group()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each-group>
</xsl:function>

<xsl:template match="document">
  <Document>
    <xsl:sequence select="mf:group(body/p, 1)"/>
  </Document>
</xsl:template>

<xsl:template match="p">
  <Paragraph>
    <xsl:value-of select="r/t"/>
  </Paragraph>
</xsl:template>

</xsl:stylesheet>

我要求的输出是:

<document>
   <paragraph>Text1-</paragraph>
   <Heading2>
      <Title>Text2-</Title>
      <paragraph>Text3-</paragraph>
      <paragraph>Text4-</paragraph>
     <Heading3>
      <Title>Text2.1-</Title>
      </Heading3>
    </Heading2>
      <Heading2>
         <Title>Text5-</Title>
         <paragraph>Text6-</paragraph>
         <Heading3>
            <Title>Text7-</Title>
            <paragraph>Text8-</paragraph>
         </Heading3>
      </Heading2>
   <Heading1>
      <Title>Text9-</Title>
      <paragraph>Text10-</paragraph>
      <Heading2>
      <Title>Text11-</Title>
      <paragraph>Text12-</paragraph>
   </Heading2>
   </Heading1>
</document>

生成的输出为:

<Document>
   <Paragraph>Text1-</Paragraph>
   <Paragraph>Text2-</Paragraph>
   <Paragraph>Text3-</Paragraph>
   <Paragraph>Text4-</Paragraph>
   <Paragraph>Text2.1-</Paragraph>
   <Paragraph>Text5-</Paragraph>
   <Paragraph>Text6-</Paragraph>
   <Paragraph>Text7-</Paragraph>
   <Paragraph>Text8-</Paragraph>
   <Heading1>
      <Title>Text9-</Title>
      <Paragraph>Text10-</Paragraph>
      <Heading2>
         <Title>Text11-</Title>
         <Paragraph>Text12-</Paragraph>
      </Heading2>
   </Heading1>
</Document>

你能帮我解决这个问题......

2 个答案:

答案 0 :(得分:1)

以下是代码的修改,它应该为您在此问题中发布的示例提供所需的输出,只要有w:pPr/w:pStyle/@w:val="HeadingX"的元素,它就会调用函数进行递归分组:

<xsl:stylesheet 
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xpath-default-namespace="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs w mf">

<xsl:output indent="yes"/>

<xsl:function name="mf:group" as="element()*">
  <xsl:param name="paragraphs" as="element()*"/>
  <xsl:param name="level" as="xs:integer"/>
  <xsl:for-each-group select="$paragraphs" group-starting-with="p[pPr/pStyle/@w:val = concat('Heading', $level)]">
    <xsl:choose>
      <xsl:when test="self::p[pPr/pStyle/@w:val = concat('Heading', $level)]">
        <xsl:element name="Heading{$level}">
          <Title><xsl:value-of select="r/t"/></Title>
          <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
        </xsl:element>
      </xsl:when>
      <xsl:when test="current-group()[self::p[pPr/pStyle/@w:val = concat('Heading', $level + 1)]]">
        <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="current-group()"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each-group>
</xsl:function>

<xsl:template match="document">
  <Document>
    <xsl:sequence select="mf:group(body/p, 1)"/>
  </Document>
</xsl:template>

<xsl:template match="p">
  <Paragraph>
    <xsl:value-of select="r/t"/>
  </Paragraph>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:0)

这是另一种样式表,在Saxon上测试过。它有点冗长,但效率更高,因为与Martin的解决方案不同,它将递归限制在最低限度。

<xsl:stylesheet
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:user="http://stackoverflow.com/questions/11239392"
  exclude-result-prefixes="xsl xs fn w user">

<xsl:output indent="yes"/>

<xsl:function name="user:level-of-p" as="xs:string">
 <xsl:param name="para" as="element()" />
 <xsl:sequence select="substring( $para/w:pPr/w:pStyle/@w:val, 8)" />
</xsl:function>

<xsl:template name="grouped-p">
 <xsl:param name="p-nodes" as="element()*" />

 <xsl:variable name="vals" as="xs:integer*">
  <xsl:for-each select="$p-nodes[user:level-of-p(.)]">
   <xsl:sort data-type="number" order="ascending" select="user:level-of-p(.)" />
   <xsl:sequence select="user:level-of-p(.) cast as xs:integer" />
  </xsl:for-each>
 </xsl:variable>
 <xsl:variable name="level" as="xs:integer" select="($vals,-1)[1]" />
 <xsl:choose>
  <xsl:when test="$level = -1">
   <xsl:apply-templates select="$p-nodes" />
  </xsl:when>
  <xsl:otherwise>
   <xsl:variable name="grouping-val" select="concat('Heading',$level)" />
   <xsl:for-each-group select="$p-nodes" group-starting-with="w:p[w:pPr/w:pStyle/@w:val = $grouping-val]">
     <xsl:choose>
      <xsl:when test="user:level-of-p(.)">
       <xsl:element name="{$grouping-val}">
        <Title><xsl:value-of select="w:r/w:t[1]" /></Title>
        <xsl:call-template name="grouped-p">
         <xsl:with-param name="p-nodes" select="current-group()[position() > 1]" />
        </xsl:call-template>
       </xsl:element>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="grouped-p">
         <xsl:with-param name="p-nodes" select="current-group()" />
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
   </xsl:for-each-group>
  </xsl:otherwise>
 </xsl:choose>
</xsl:template>

<xsl:template match="w:document">
  <document>
    <xsl:call-template name="grouped-p">
     <xsl:with-param name="p-nodes" select="w:body/w:p" />
    </xsl:call-template>
  </document>
</xsl:template>

<xsl:template match="w:p">
  <paragraph>
    <xsl:value-of select="w:r/w:t"/>
  </paragraph>
</xsl:template>

</xsl:stylesheet>