(注意:我已根据建议在my earlier question发布了变体)
给定一个具有以下结构的输入xml文件:
<widgets>
<widget shape="square" material="wood" color="red" />
<widget shape="square" material="metal" color="blue" />
<widget shape="square" material="plastic" color="green" />
<widget shape="square" material="kevlar" color="red" />
<widget shape="round" material="metal" color="orange" />
<widget shape="round" material="wood" color="green" />
<widget shape="round" material="kevlar" color="blue" />
<widget shape="diamond" material="plastic" color="blue" />
<widget shape="diamond" material="wood" color="brown" />
<widget shape="diamond" material="metal" color="red" />
</widgets>
以下信息:
如何使用XSLT输出以下结构?
<table>
<tr id="diamond">
<td class="kevlar"></td>
<td class="metal red"></td>
<td class="plastic blue"></td>
<td class="wood brown"></td>
</tr>
<tr id="round">
<td class="kevlar blue"></td>
<td class="metal orange"></td>
<td class="plastic"></td>
<td class="wood green"></td>
</tr>
<tr id="square">
<td class="kevlar green"></td>
<td class="metal blue"></td>
<td class="plastic green"></td>
<td class="wood red"></td>
</tr>
</table>
答案 0 :(得分:3)
此转化:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kShapeByVal" match="@shape"
use="."/>
<xsl:key name="kMaterByVal" match="@material"
use="."/>
<xsl:key name="kcolorByVal" match="@color"
use="."/>
<xsl:key name="kColorByShapeAndMat" match="@color"
use="concat(../@shape, '+', ../@material)"/>
<xsl:variable name="vShapes" select=
"/*/*/@shape
[generate-id()
=
generate-id(key('kShapeByVal',.)[1])
]
"/>
<xsl:variable name="vMaterials" select=
"/*/*/@material
[generate-id()
=
generate-id(key('kMaterByVal',.)[1])
]
"/>
<xsl:variable name="vColors" select=
"/*/*/@color
[generate-id()
=
generate-id(key('kcolorByVal',.)[1])
]
"/>
<xsl:template match="/*">
<table>
<xsl:for-each select="$vShapes">
<xsl:sort select="."/>
<xsl:variable name="vShape" select="."/>
<tr id="{.}">
<xsl:for-each select="$vMaterials">
<xsl:sort select="."/>
<xsl:variable name="vMat" select="."/>
<xsl:variable name="vShapeMatColors" select=
"key('kColorByShapeAndMat',
concat($vShape, '+', $vMat)
)
"/>
<xsl:if test="not($vShapeMatColors)">
<td class="{$vMat}"></td>
</xsl:if>
<xsl:for-each select="$vShapeMatColors">
<td class="{concat($vMat, ' ', .)}"></td>
</xsl:for-each>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
应用于提供的XML文档时:
<widgets>
<widget shape="square" material="wood" color="red" />
<widget shape="square" material="metal" color="blue" />
<widget shape="square" material="plastic" color="green" />
<widget shape="square" material="kevlar" color="red" />
<widget shape="round" material="metal" color="orange" />
<widget shape="round" material="wood" color="green" />
<widget shape="round" material="kevlar" color="blue" />
<widget shape="diamond" material="plastic" color="blue" />
<widget shape="diamond" material="wood" color="brown" />
<widget shape="diamond" material="metal" color="red" />
</widgets>
产生想要的结果:
<table>
<tr id="diamond">
<td class="kevlar"/>
<td class="metal red"/>
<td class="plastic blue"/>
<td class="wood brown"/>
</tr>
<tr id="round">
<td class="kevlar blue"/>
<td class="metal orange"/>
<td class="plastic"/>
<td class="wood green"/>
</tr>
<tr id="square">
<td class="kevlar red"/>
<td class="metal blue"/>
<td class="plastic green"/>
<td class="wood red"/>
</tr>
</table>
一切如何运作:
使用Muenchian方法进行分组,我们在变量$vShapes
,$vMaterials
和$vColors
中找到所有不同的形状,材质和颜色
我们为<tr>
$vShapes
对于$vMaterials
中包含的所有可能材料,我们输出一个或多个<td>
元素,其属性为class
,其值由两个独立的案例:
第一种情况是没有为此形状和材质组合指定颜色(key('kColorByShapeAndMat', concat($vShape, '+', $vMat)
为空)。在这种情况下,tha类属性仅包含材料。
第二种情况是指为此形状和材质组合指定了一种或多种颜色。然后,对于每种这样的颜色,输出单独的<td>
元素,并将其class
属性生成为材质和颜色的串联,用空格分隔。
答案 1 :(得分:2)
我对your part 1 of the question does it的回答的变体:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- prepare some keys for later use -->
<xsl:key name="kWidgetsByShape" match="widget" use="@shape" />
<xsl:key name="kWidgetsByMaterial" match="widget" use="@material" />
<xsl:key name="kWidgetsByComposition" match="widget" use="concat(@shape, ',', @material)" />
<!-- select the <widget>s that are the first in their respective @shape -->
<xsl:variable name="vShapes" select="
/widgets/widget[
generate-id()
=
generate-id(key('kWidgetsByShape', @shape)[1])
]
" />
<!-- select the <widget>s that are the first in their respective @material -->
<xsl:variable name="vMaterials" select="
/widgets/widget[
generate-id()
=
generate-id(key('kWidgetsByMaterial', @material)[1])
]
" />
<!-- output basic table structure -->
<xsl:template match="/widgets">
<table title="shapes: {count($vShapes)}, materials: {count($vMaterials)}">
<xsl:apply-templates select="$vShapes" mode="tr">
<xsl:sort select="@shape" />
</xsl:apply-templates>
</table>
</xsl:template>
<!-- output the <tr>s, one for each @shape -->
<xsl:template match="widget" mode="tr">
<tr id="{@shape}">
<xsl:apply-templates select="$vMaterials" mode="td">
<xsl:sort select="@material" />
<xsl:with-param name="vCurrentShape" select="@shape" />
</xsl:apply-templates>
</tr>
</xsl:template>
<!-- output the right number of <td>s in each row, empty or not -->
<xsl:template match="widget" mode="td">
<xsl:param name="vCurrentShape" />
<xsl:variable
name="vWidget"
select="key('kWidgetsByComposition', concat($vCurrentShape, ',', @material))[1]"
/>
<td class="{normalize-space(concat(@material, ' ', $vWidget/@color))}">
<xsl:apply-templates select="$vWidget" />
</td>
</xsl:template>
<xsl:template match="widget">
<xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>
产生:
<table title="shapes: 3, materials: 4">
<tr id="diamond">
<td class="kevlar"></td>
<td class="metal red"></td>
<td class="plastic blue"></td>
<td class="wood brown"></td>
</tr>
<tr id="round">
<td class="kevlar blue"></td>
<td class="metal orange"></td>
<td class="plastic"></td>
<td class="wood green"></td>
</tr>
<tr id="square">
<td class="kevlar red"></td>
<td class="metal blue"></td>
<td class="plastic green"></td>
<td class="wood red"></td>
</tr>
</table>
基本上我在其他答案中说的所有内容仍然适用。
这次我使用了三个<xsl:key>
而不是两个。其中两个用于迭代,另一个用于<widget>
和@shape
查找@material
。
我使用不同的模板模式与<xsl:apply-templates>
而不是<xsl:for-each>
一起使用。这使代码延长了几行,但它有利于清晰度和可读性。
最后一个模板(<xsl:template match="widget">
)仅用于演示目的,向您展示如何继续。它是在<xsl:template match="widget" mode="td">
内调用的,每次<widget>
实际存在一次。
答案 2 :(得分:1)
正如eft的一篇文章所述,这是一个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"
exclude-result-prefixes="xs"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kColorByShapeAndMat" match="@color"
use="concat(../@shape, '+', ../@material)"/>
<xsl:template match="/*">
<xsl:for-each-group select="*/@shape" group-by=".">
<xsl:sort select="."/>
<xsl:variable name="vShape" select="current-grouping-key()"/>
<tr id="{.}">
<xsl:for-each-group select="/*/*/@material" group-by=".">
<xsl:sort select="."/>
<xsl:variable name="vMat" select="."/>
<xsl:variable name="vColors"
select="key('kColorByShapeAndMat',
concat($vShape,'+',.)
)"/>
<xsl:for-each select="''[empty($vColors)],$vColors/concat(' ',.)">
<xsl:sort select="."/>
<td class="{concat($vMat,.)}"></td>
</xsl:for-each>
</xsl:for-each-group>
</tr>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
将此转换应用于最初提供的XML文档:
<widgets>
<widget shape="square" material="wood" color="red" />
<widget shape="square" material="metal" color="blue" />
<widget shape="square" material="plastic" color="green" />
<widget shape="square" material="kevlar" color="red" />
<widget shape="round" material="metal" color="orange" />
<widget shape="round" material="wood" color="green" />
<widget shape="round" material="kevlar" color="blue" />
<widget shape="diamond" material="plastic" color="blue" />
<widget shape="diamond" material="wood" color="brown" />
<widget shape="diamond" material="metal" color="red" />
</widgets>
生成所需的结果:
<tr id="diamond">
<td class="kevlar"/>
<td class="metal red"/>
<td class="plastic blue"/>
<td class="wood brown"/>
</tr>
<tr id="round">
<td class="kevlar blue"/>
<td class="metal orange"/>
<td class="plastic"/>
<td class="wood green"/>
</tr>
<tr id="square">
<td class="kevlar red"/>
<td class="metal blue"/>
<td class="plastic green"/>
<td class="wood red"/>
</tr>