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"/>

但我真的不明白如何将其带到下一步,或者这是否是正确的方法。

4

3 回答 3

7

这个解决方案比 Richard 提出的解决方案更简单、更高效,同时更通用:

这种转变

<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 方法

于 2008-12-30T06:45:01.540 回答
5

这就是我的做法,使用 Muenchean 方法。谷歌“xslt muenchean”从更聪明的人那里获得更多信息。可能有一个聪明的方法,但我会把它留给其他人。

请注意,我避免在 xml 元素名称的开头使用大写字母,例如“文件”,但这取决于您。

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

这个想法是,使用它的文本值来键入每个 File 元素。然后仅显示与键控元素相同的文件值。要检查它们是否相同,请使用 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。

我已经按文件对输出进行了排序。我不确定你是否想要那个。

我希望这有帮助。

于 2008-12-30T01:12:05.113 回答
0

这个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>
于 2013-09-19T04:41:40.947 回答