【问题标题】:Stuck with recursive functions in XSLT 2.0卡在 XSLT 2.0 中的递归函数
【发布时间】:2012-06-28 06:48:50
【问题描述】:

这是我的 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>

其中一位 Stack Overflow 成员为此发布了 xslt 解决方案。但它不适用于上述 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>

我该如何解决这种情况?

【问题讨论】:

    标签: xml xslt xpath xslt-1.0 xslt-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>
    

    【讨论】:

      【解决方案2】:

      这是另一种样式表,在 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>
      

      【讨论】:

        猜你喜欢
        • 2012-12-03
        • 2011-03-07
        • 1970-01-01
        • 2022-01-22
        • 2020-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多