3

我浏览了XSLT Grouping ExamplesUsing for-each-group for high performance XSLT。我对每个组都有问题。

我的 XML

<?xml version="1.0" encoding="UTF-8"?>
<body>
   <p name="h-title" other="main">Introduction</p>
   <p name="h1-title " other="other-h1">XSLT and XQuery</p>
   <p name="h2-title" other=" other-h2">XSLT</p>
   <p name="">
      <p1 name="bold"> XSLT is used to write stylesheets.</p1>
   </p>
   <p name="h2-title " name="other-h2">XQuery</p>
   <p name="">
      <p1 name="bold"> XQuery is used to query XML databases.</p1>
   </p>
   <p name="h3-title" name="other-h3">XQuery and stylesheets</p>
   <p name="">
      <p1 name="bold"> XQuery is used to query XML databases.</p1>
   </p>
   <p name="h1-title " other="other-h1">XSLT and XQuery</p>
   <p name="h2-title " other=" other-h2">XSLT</p>
</body>

 </p>

我想要的输出

<?xml version="1.0" encoding="UTF-8"?>
<body>
   <p name="h-title " other="main">Introduction</p>
   <h1>
      <p name="h1-title " other="other-h1"> XSLT and XQuery </p>
      <h2>
         <p name="h2-title " other="other-h2">XSLT</p>
         <p name="">
            <p1 name="bold">XSLT is used to write stylesheets.
            </p1>
         </p>
      </h2>
      <h2>
         <p name="h2-title " other="other-h2"> XQuery is used to query XML databases    
         </p>
         <p name="">
            <p name="bold"> XQuery is used to query XML databases.</p>
         </p>
         <h3>
            <p name="h3-title " name="other-h3">XQuery and stylesheets</p>
            <p name="">
         <p1 name="bold"> XQuery is used to query XML databases.</p1>
           </p>
        </h3>
      </h2>
</h1>

<h1>
            <p name="h1-title " other="other-h1">XSLT and XQuery</p>
       <h2>   
            <p name="h2"-title other=" other-h2">XSLT</p>
       </h2>
</h1>
</body>

我试过这个。(不工作)

<xsl:template match="body">


        <body>
            <xsl:for-each-group select="*" group-starting-with="@h1-title"      >
                <h1>
                    <xsl:for-each select="current-group()[self:: h1-title]">
                        <xsl:value-of select="."/> 
                        </xsl:for-each> 
                </h1>
            </xsl:for-each-group>

            <xsl:for-each-group select="*" group-starting-with="@h2-title"      >
                <h2>
                    <xsl:for-each select="current-group()[self::h2-title/@h2-title]">
                        <xsl:value-of select="."/>
                    </xsl:for-each> 
                </h2>
            </xsl:for-each-group>

            <xsl:for-each-group select="*" group-starting-with="@h3-title"      >
                <h3>
                    <xsl:for-each select="current-group()[self::h2-title/@h3-title]">
                        <xsl:value-of select="."/>
                    </xsl:for-each> 
                </h3>
            </xsl:for-each-group>

        </body>

  </xsl:template>

有人会告诉我获得我想要的结果的正确方法吗?

4

3 回答 3

5

for-each-group这是一个在递归函数中使用的 XSLT 2.0 样式表(我更喜欢使用 XSLT 2.0 的命名模板):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.com/mf"
  exclude-result-prefixes="xs mf">

<xsl:param name="prefix" as="xs:string" select="'h'"/>
<xsl:param name="suffix" as="xs:string" select="'-title'"/>

<xsl:output method="html" version="4.0" indent="yes"/>

<xsl:function name="mf:group" as="node()*">
  <xsl:param name="items" as="node()*"/>
  <xsl:param name="level" as="xs:integer"/>
  <xsl:for-each-group select="$items" group-starting-with="p[@name = concat($prefix, $level, $suffix)]">
    <xsl:choose>
      <xsl:when test="not(self::p[@name = concat($prefix, $level, $suffix)])">
        <xsl:apply-templates select="current-group()"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="h{$level}">
          <xsl:apply-templates select="."/>
          <xsl:sequence select="mf:group(current-group() except ., $level + 1)"/>
        </xsl:element>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each-group>
</xsl:function>

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

<xsl:template match="body">
  <xsl:copy>
    <xsl:sequence select="mf:group(*, 1)"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

当我将带有 Saxon 9 的样式表应用于输入时

<body>
   <p name="h-title" other="main">Introduction</p>
   <p name="h1-title" other="other-h1">XSLT and XQuery</p>
   <p name="h2-title" other=" other-h2">XSLT</p>
   <p name="">
      <p1 name="bold"> XSLT is used to write stylesheets.</p1>
   </p>
   <p name="h2-title" other="other-h2">XQuery</p>
   <p name="">
      <p1 name="bold"> XQuery is used to query XML databases.</p1>
   </p>
   <p name="h3-title" other="other-h3">XQuery and stylesheets</p>
   <p name="">
      <p1 name="bold"> XQuery is used to query XML databases.</p1>
   </p>
   <p name="h1-title" other="other-h1">XSLT and XQuery</p>
   <p name="h2-title" other=" other-h2">XSLT</p>
</body>

我得到结果

<body>
   <p name="h-title" other="main">Introduction</p>
   <h1>
      <p name="h1-title" other="other-h1">XSLT and XQuery</p>
      <h2>
         <p name="h2-title" other=" other-h2">XSLT</p>
         <p name="">

            <p1 name="bold"> XSLT is used to write stylesheets.</p1>

         </p>
      </h2>
      <h2>
         <p name="h2-title" other="other-h2">XQuery</p>
         <p name="">

            <p1 name="bold"> XQuery is used to query XML databases.</p1>

         </p>
         <h3>
            <p name="h3-title" other="other-h3">XQuery and stylesheets</p>
            <p name="">

               <p1 name="bold"> XQuery is used to query XML databases.</p1>

            </p>
         </h3>
      </h2>
   </h1>
   <h1>
      <p name="h1-title" other="other-h1">XSLT and XQuery</p>
      <h2>
         <p name="h2-title" other=" other-h2">XSLT</p>
      </h2>
   </h1>
</body>
于 2012-07-21T10:27:53.173 回答
3

此转换使用键和句柄h1-titleh6-title

<xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>

     <xsl:template match="body">
       <xsl:apply-templates select="p[@name='h1-title']" />
     </xsl:template>

     <xsl:key name="next-headings" match="p[@name='h6-title']"
       use="generate-id(preceding-sibling::p
                         [ @name='h1-title'
                        or @name='h2-title'
                        or @name='h3-title'
                        or @name='h4-title'
                        or @name='h5-title'
                        ][1])" />
     <xsl:key name="next-headings" match="p[@name='h5-title']"
       use="generate-id(preceding-sibling::p
                         [ @name='h1-title'
                        or @name='h2-title'
                        or @name='h3-title'
                        or @name='h4-title'
                        ][1])" />
     <xsl:key name="next-headings" match="p[@name='h4-title']"
       use="generate-id(preceding-sibling::p
                         [ @name='h1-title'
                        or @name='h2-title'
                        or @name='h3-title'
                        ][1])" />
     <xsl:key name="next-headings" match="p[@name='h3-title']"
       use="generate-id(preceding-sibling::p
                         [  @name='h1-title'
                        or @name='h2-title'
                        ][1])" />
     <xsl:key name="next-headings" match="p[@name='h2-title']"
       use="generate-id(preceding-sibling::p
                         [@name='h1-title'][1])" />

     <xsl:key name="immediate-nodes" match=
     "node()[not(self::p)
           or
            not(contains('|h1-title|h2-title|h3-title|h4-title|h5-title|h6-title|',
                         concat('|',@name,'|')
                        )
                )]"
       use="generate-id(preceding-sibling::p
             [contains('|h1-title|h2-title|h3-title|h4-title|h5-title|h6-title|',
                       concat('|',@name,'|')
                       )
             ][1])" />

     <xsl:template match=
      "p[contains('|h1-title|h2-title|h3-title|h4-title|h5-title|h6-title|',
                  concat('|',@name,'|')
                  )]">
       <xsl:variable name="vLevel" select="substring(@name,2,1)" />
       <xsl:element name="h{$vLevel}">
          <xsl:copy-of select="."/>
          <xsl:apply-templates select="key('immediate-nodes', generate-id())" />
          <xsl:apply-templates select="key('next-headings', generate-id())" />
       </xsl:element>
     </xsl:template>

     <xsl:template match="/*/node()" priority="-20">
       <xsl:copy-of select="." />
     </xsl:template>
</xsl:stylesheet>

应用于此 XML 文档时(更正提供的一个并使用属性的统一值name):

<body>
        <p name="h1-title" other="main">Introduction</p>
        <p name="h2-title" other="other-h2">XSLT and XQuery</p>
        <p name="h3-title" other=" other-h3">XSLT</p>
        <p name="">
                <p1 name="bold"> XSLT is used to write stylesheets.</p1>
        </p>
        <p name="h2-title" other="other-h2">XQuery</p>
        <p name="">
                <p1 name="bold"> XQuery is used to query XML databases.</p1>
        </p>
        <p name="h3-title" other="other-h3">XQuery and stylesheets</p>
        <p name="">
                <p1 name="bold"> XQuery is used to query XML databases.</p1>
        </p>
        <p name="h1-title" other="other-h1">XSLT and XQuery</p>
        <p name="h2-title" other=" other-h2">XSLT</p>
</body>

产生了想要的正确结果

<h1>
   <p name="h1-title" other="main">Introduction</p>
   <h2>
      <p name="h2-title" other="other-h2">XSLT and XQuery</p>
      <h3>
         <p name="h3-title" other=" other-h3">XSLT</p>
         <p name="">
            <p1 name="bold"> XSLT is used to write stylesheets.</p1>
         </p>
      </h3>
   </h2>
   <h2>
      <p name="h2-title" other="other-h2">XQuery</p>
      <p name="">
         <p1 name="bold"> XQuery is used to query XML databases.</p1>
      </p>
      <h3>
         <p name="h3-title" other="other-h3">XQuery and stylesheets</p>
         <p name="">
            <p1 name="bold"> XQuery is used to query XML databases.</p1>
         </p>
      </h3>
   </h2>
</h1>
<h1>
   <p name="h1-title" other="other-h1">XSLT and XQuery</p>
   <h2>
      <p name="h2-title" other=" other-h2">XSLT</p>
   </h2>
</h1>

请注意

这种转换解决了生成层次结构的主要问题。如果要求顶级name属性具有 value ,则只需要进行微不足道的更改"h-title"

如果需要更多的层次结构级别,则只需将相应的or子句机械地添加到键的定义中,并将所有属性值的管道分隔字符串附加到name相应的新字符串中。

在这里,我改编并重新使用了 Jeni Tennison 为类似问题提供的解决方案。

于 2012-07-21T03:35:09.427 回答
2

您的每个分组步骤都将原始元素集作为输入,而您需要每个步骤处理由前一个分组步骤生成的组。还有很多其他错误,例如 h1-title 不是属性名称。

它需要是这样的:

<xsl:for-each-group select="*" group-starting-with="*[@name='h1-title']">
<h1>
  <xsl:choose>
    <xsl:when test="@name='h1-title'">
      <xsl:for-each-group select="current-group()" group-starting-with="*[name='h2-title']">
        <xsl:choose>
        <h2>
          ... similar logic for the next level ...
        </h2>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select="current-group()"/>
    </xsl:otherwise>
  </xsl:choose>
</h1>
</xsl:for-each-group>

您可以根据要处理的级别进行任意深度的嵌套;或者如果你想处理一个不确定的数字,你可以把代码放在一个命名模板中,然后进行递归调用来处理下一层。在最内层,省略xsl:choose并执行xsl:copy-of select="current-group()

(我刚刚注意到“名称”属性中的尾随空格。如果这些真的存在,您需要将它们包含在比较测试中,或者normalize-space()去掉它们。)

于 2012-07-20T19:56:50.780 回答