我需要使用属性名称作为标题生成HTML表格,并使用属性值作为数据。但是,需要循环的元素并不总是存在所有属性。在这种情况下,需要将空白<td>
添加到表中,以获取所有此类不可用的属性值。
下面是一个示例XML(具有虚拟值的缩小版本)。
<root>
<oelement attr="abc">
<ielement attr="101">
<child attr1="a" attr2="b" attr3="c" attr4="d" attr5="e" />
<child attr1="e" attr2="f" attr3="g" attr4="h" attr5="i" />
</ielement>
<ielement attr="102">
<child attr1="x" attr3="y" attr5="w" />
<child attr1="j" attr3="k" attr5="l" />
</ielement>
</oelement>
<oelement attr="pqr">
<ielement attr="101">
<child attr1="g" attr2="j" attr3="t" attr4="y" attr5="r" />
<child attr1="d" attr2="q" attr3="a" attr4="c" attr5="b" />
</ielement>
<ielement attr="102">
<child attr1="t" attr3="y" attr5="u" />
<child attr1="i" attr3="o" attr5="p" />
</ielement>
</oelement>
<oelement attr="xyz">
<ielement attr="101">
<child attr1="h" attr2="o" attr3="u" attr4="z" attr5="x" />
</ielement>
<ielement attr="103">
<child attr1="q" attr3="w" attr5="e" />
</ielement>
</oelement>
</root>
输出HTML
我尝试将以下XSLT放在一起,但是当在表的相应列中加载数据时,它与列标题中的属性名称不匹配。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:template match="root">
<html>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<tr>
<th>oelement</th>
<th>ielement</th>
<xsl:for-each select="//oelement[1]/ielement[1]/child[1]/@*">
<th><xsl:value-of select="local-name()" /></th>
</xsl:for-each>
</tr>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/@attr" /></td>
<td><xsl:value-of select="ancestor::ielement/@attr" /></td>
<xsl:for-each select="@*">
<td><xsl:value-of select="." /></td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
在添加值并为非匹配列插入空白值时,需要帮助进行列匹配。我被XSLT 1.0
困住了,无法升级到XSLT 2.0
以查找属性名称的distinct-values()
。
答案 0 :(得分:1)
在XLST 1.0中没有很好的解决方案。
但是,您可以使用键来达到所需的效果:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:strip-space elements="*" />
<xsl:key name="child-by-attr-name" match="child/@*" use="name(.)"/>
<xsl:template match="root">
<html>
<body>
<table border="1" cellspacing="0" cellpadding="5">
<!-- skipped for simplicity -->
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/@attr" /></td>
<td><xsl:value-of select="ancestor::ielement/@attr" /></td>
<xsl:variable name="current-child" select="."/>
<xsl:for-each select="(//child/@*)[generate-id(.) = generate-id(key('child-by-attr-name',name(.))[1])]">
<td>
<xsl:value-of select="$current-child/@*[name(.) = name(current())]" />
</td>
</xsl:for-each>
</tr>
</xsl:template>
这里的“技巧”是密钥将按其名称索引所有属性(在每个child
元素中)。通过属性名称查询密钥时,将按文档顺序获取具有该名称的所有属性。然后,您可以使用generate-id()来确保只获取每个名称的第一个。
答案 1 :(得分:1)
成功的关键是循环使用属性名称的正确方法。
为此,我使用了:
attrib
),匹配child
个属性并保存其名称(name()
)。要处理匹配child
的模板中上下文对象的更改,
为了便于阅读,我使用了两个变量:
curr
- 当前child
元素。nn
- 当前属性的名称。所以整个脚本可能如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:strip-space elements="*" />
<xsl:key name="attrib" match="child/@*" use="name()"/>
<xsl:template match="root">
<html>
<body>
<table>
<tr>
<th>oelement</th>
<th>ielement</th>
<xsl:for-each select="//child/@*[generate-id()=generate-id(key('attrib', name())[1])]">
<th><xsl:value-of select="name()"/></th>
</xsl:for-each>
</tr>
<xsl:apply-templates select="//child"/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="child">
<tr>
<td><xsl:value-of select="ancestor::oelement/@attr" /></td>
<td><xsl:value-of select="ancestor::ielement/@attr" /></td>
<xsl:variable name="curr" select="."/>
<xsl:for-each select="//child/@*[generate-id()=generate-id(key('attrib', name())[1])]">
<td>
<xsl:variable name="nn" select="name()"/>
<xsl:value-of select="$curr/@*[name()=$nn]"/>
</td>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>