2008-12-30 85 views
8

考虑下面的XML片段:独特的因素,分组

<Problems> 
    <Problem> 
    <File>file1</File> 
    <Description>desc1</Description> 
    </Problem> 
    <Problem> 
    <File>file1</File> 
    <Description>desc2</Description> 
    </Problem> 
    <Problem> 
    <File>file2</File> 
    <Description>desc1</Description> 
    </Problem> 
</Problems> 

我需要生成类似

<html> 
    <body> 
    <h1>file1</h1> 
    <p>des1</p> 
    <p>desc2</p> 
    <h1>file2</h1> 
    <p>des1</p> 
    </body> 
</html> 

我用钥匙试了,像

<xsl:key name="files" match="Problem" use="File"/> 

,但我不真的不明白如何进行下一步,或者如果这是正确的方法。

回答

5

下面是我如何使用Muenchean方法来做到这一点。谷歌的'xslt muenchean'获得更聪明人的更多信息。可能有一个聪明的办法,但我会把它留给别人。请注意,我避免在xml元素名称的开头使用大写字母,例如'File',但这取决于您。

<?xml version="1.0"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="html"/> 
    <xsl:key name="files" match="/Problems/Problem/File" use="./text()"/> 
    <xsl:template match="/"> 
     <html> 
      <body> 
       <xsl:apply-templates select="Problems"/> 
      </body> 
     </html> 
    </xsl:template> 
    <xsl:template match="Problems"> 
     <xsl:for-each select="Problem/File[generate-id(.) = generate-id(key('files', .))]"> 
      <xsl:sort select="."/> 
      <h1> 
       <xsl:value-of select="."/> 
      </h1> 
      <xsl:apply-templates select="../../Problem[File=current()/text()]"/> 
     </xsl:for-each> 
    </xsl:template> 
    <xsl:template match="Problem"> 
     <p> 
      <xsl:value-of select="Description/text()"/> 
     </p> 
    </xsl:template> 
</xsl:stylesheet> 

这个想法是,使用它的文本值键入每个文件元素。然后只显示文件值,如果它们与键控相同。要检查它们是否相同,请使用generate-id。有一种类似的方法可以比较匹配的第一个元素。我无法告诉你哪种效率更高。

我已经使用Marrowsoft Xselerator,我最喜欢的xslt工具测试了代码,虽然不再可用,afaik。我得到的结果是:

<html> 
<body> 
<h1>file1</h1> 
<p>desc1</p> 
<p>desc2</p> 
<h1>file2</h1> 
<p>desc1</p> 
</body> 
</html> 

这是使用msxml4。

我已经通过File对输出进行了排序。我不确定你是否想要。

我希望这会有所帮助。

+0

完美,谢谢!并欢迎muenchean小费... – rjohnston 2008-12-30 02:01:58

+0

干杯。我甚至没有注意到你是澳大利亚的同胞,否则我会用更多的口语。 :) – 2008-12-30 02:03:50

7

该解决方案是一点点比理查德提出了一个更简单,更高效,同时更一般的

这种转变

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<!--           --> 
<xsl:key name="kFileByVal" match="File" 
     use="." /> 
<!--           --> 
<xsl:key name="kDescByFile" match="Description" 
     use="../File"/> 
<!--           --> 
    <xsl:template match="/*"> 
    <html> 
     <body> 
     <xsl:for-each select= 
     "*/File[generate-id() 
       = 
       generate-id(key('kFileByVal',.)[1])]"> 
     <h1><xsl:value-of select="."/></h1> 
     <xsl:for-each select="key('kDescByFile', .)"> 
      <p><xsl:value-of select="."/></p> 
     </xsl:for-each> 
     </xsl:for-each> 
     </body> 
    </html> 
    </xsl:template> 
</xsl:stylesheet> 

当应用于提供的XML文档

<Problems> 
    <Problem> 
     <File>file1</File> 
     <Description>desc1</Description> 
    </Problem> 
    <Problem> 
     <File>file1</File> 
     <Description>desc2</Description> 
    </Problem> 
    <Problem> 
     <File>file2</File> 
     <Description>desc1</Description> 
    </Problem> 
</Problems> 

可生产想要的结果

<html> 
    <body> 
     <h1>file1</h1> 
     <p>desc1</p> 
     <p>desc2</p> 
     <h1>file2</h1> 
     <p>desc1</p> 
    </body> 
</html> 

请注意第一<xsl:key>以及如何使用第二<xsl:key>,我们找到所有“Description”元素是一个“File兄弟姐妹的简单匹配模式“具有给定值的元素。

我们可以使用更多的模板来代替<xsl:for-each>拉动处理,但这是一个非常简单的情况,该解决方案真正受益于更短,更紧凑和更易读的代码。

还要注意,在XSLT 2.0一个通常会使用<xsl:for-each-group>指令代替Muenchian method的。

0

This XSLT 1.0解决方案也将诀窍。比其他解决方案更简洁!

<xsl:template match="/">   
    <html><body> 
     <xsl:for-each select="//File[not(.=preceding::*)]"> 
     <h1><xsl:value-of select="." /></h1> 
     <xsl:for-each select="//Problem[File=current()]/Description"> 
      <p><xsl:value-of select="." /></p> 
     </xsl:for-each> 
     </xsl:for-each> 
    </body></html> 
    </xsl:template> 

结果:

<html xmlns="http://www.w3.org/1999/xhtml"> 
    <body> 
    <h1>file1</h1> 
    <p>desc1</p> 
    <p>desc2</p> 
    <h1>file2</h1> 
    <p>desc1</p> 
    </body> 
</html>