2012-07-20 40 views
3

我通过了XSLT Grouping ExamplesUsing for-each-group for high performance XSLT。我对每个组都有问题。xsl:为每组需求提供帮助

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

我的通缉输出

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

会有人告诉我正确的方式来获得我想要的结果呢?

回答

5

这里是一个递归函数使用for-each-group一个XSLT 2.0样式表(I更喜欢用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> 

当我应用该样式表与撒克逊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> 
+0

这真的很棒。正是我想要的。其他两个答案也非常好。我从这个问题中学到很多东西。非常感谢你。^_ ^ – Setinger 2012-07-21 14:51:02

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()摆脱他们的。)

+0

你是清醒的。^_ ^非常感谢Michael。白色空间不在那里。将会有很多for-each-group的嵌套,就像我喜欢h1,h2,h3,h4等一样。我认为你说的其他方式会很棒。我会试试看,如果我发现问题,告诉你。该提案+1。^_^ – Setinger 2012-07-20 20:05:04

+0

谢谢米歇尔。我跟马丁的答案一起去。^ _^ – Setinger 2012-07-21 14:54:05

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 attribu上施加TE):

<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属性的值为"h-title",则只需进行微小的更改。

如果有更多的层级是必要的,这个只需要机械将相应or条款,按键的定义,并附加所有name属性的值与相应的新的字符串管道分隔的字符串。

在这里,我已经改编并重新使用了一个解决方案,Jeni Tennison给出了类似的问题。

+0

非常感谢Dimitre。我希望使用xsl:for-each-group。我仍在尝试。我会告诉你是否有问题。 – Setinger 2012-07-21 04:48:33

+0

@Setinger:如果我在你的位置,我会采取基于关键的解决方案 - 仅仅因为它可以工作,并且是最有效的解决方案之一(次线性,接近于O(1))。我的意思是'xsl:for-each-group'最可能不会更有效率。 – 2012-07-21 05:09:13

+0

Dimitre,你认为使用递归调用(如Michael在另一个答案中所述)会比这更低效吗?是这样吗? – Setinger 2012-07-21 05:11:14