给出以下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"/>
但我真的不明白如何将它带到下一步,或者这是否是正确的方法。
答案 0 :(得分: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 method。
答案 1 :(得分: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。
我按文件对输出进行了排序。我不确定你是否想要那个。
我希望这会有所帮助。
答案 2 :(得分: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>