XSLT 1.0 - 使用具有不等属性计数但属性名称相同的元素生成HTML表

时间:2018-03-27 12:43:40

标签: html xml xslt xslt-1.0

我需要使用属性名称作为标题生成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

Output HTML Table

我尝试将以下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()

2 个答案:

答案 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>