使用XSL生成动态HTML表

时间:2010-06-30 07:09:45

标签: xml xslt

我有以下XML数据:

<Activity>
<ObjectGroup type="default">
<Object id="1874" name="PR1010Date" type="reference label" index="10" columnNo="0" dynamic="true">
      <Description>Date</Description>      
      <Value instance="0">30/06/2010</Value>
    </Object>
    <Object id="1875" name="PR1020LoggedBy" type="reference label" index="20" columnNo="1" dynamic="true">
      <Description>Request Logged By</Description>     
      <Value>Site Administrator</Value>
    </Object>
    <Object id="1876" name="PR1030Comments" type="large text box" index="30" columnNo="0" dataType="Text">
      <Description>Comments</Description>      
      <Value instance="0">Test</Value>
    </Object>
<ObjectGroup>
</Activity>

我需要创建一个XSL,它将产生以下输出:

<html>
<table>
<tr>
<td width="50%">30/06/2010</td>
<td width="50%">Site Admin</td>
</tr>
<tr>
<td width="100%">Test</td>
</tr>
</table>

在上面的XML中,index属性和columnNo驱动生成了多少行和列。最终结果是在ColumnNo上确定的,因此如果ObjectGroup具有带有增量columnNo的对象,那么它们将全部呈现为一行,每列具有适当的宽度。

2 个答案:

答案 0 :(得分:1)

此转化

<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="kObjByCol" match="Object"
  use="@columnNo"/>

 <xsl:key name="kRow" match="Object[@columnNo != 0]"
  use="generate-id(preceding-sibling::Object
                                 [@columnNo = 0][1]
                   )"/>

 <xsl:template match="/*">
   <html>
     <table border="1">
       <xsl:apply-templates select=
       "key('kObjByCol', 0)"/>
     </table>
   </html>
 </xsl:template>

 <xsl:template match="Object">
   <xsl:variable name="vcellNodes"
    select=".|key('kRow',generate-id())"/>
   <tr>
     <xsl:apply-templates mode="row" select=
        "$vcellNodes">
       <xsl:with-param name="pCount"
            select="count($vcellNodes)"/>
     </xsl:apply-templates>
   </tr>
 </xsl:template>

 <xsl:template match="Object" mode="row">
   <xsl:param name="pCount"/>
   <td width="{100 div $pCount}%">
     <xsl:value-of select="Value"/>
   </td>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档(更正为格式良好):

<Activity>
    <ObjectGroup type="default">
        <Object id="1874" name="PR1010Date"
         type="reference label" index="10"
         columnNo="0" dynamic="true">
            <Description>Date</Description>
            <Value instance="0">30/06/2010</Value>
        </Object>
        <Object id="1875" name="PR1020LoggedBy"
         type="reference label" index="20"
         columnNo="1" dynamic="true">
            <Description>Request Logged By</Description>
            <Value>Site Administrator</Value>
        </Object>
        <Object id="1876" name="PR1030Comments"
         type="large text box" index="30"
         columnNo="0" dataType="Text">
            <Description>Comments</Description>
            <Value instance="0">Test</Value>
        </Object>
    </ObjectGroup>
</Activity>

生成想要的正确结果

<html>
    <table border="1">
        <tr>
            <td width="50%">30/06/2010</td>
            <td width="50%">Site Administrator</td>
        </tr>
        <tr>
            <td width="100%">Test</td>
        </tr>
    </table>
</html>

答案 1 :(得分:1)

根据Dimitre的出色回应,另一张没有按键的样式表。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:template match="ObjectGroup">
        <html>
            <table border="1">
                <xsl:apply-templates select="Object[@columnNo=0]"/>
            </table>
        </html>
    </xsl:template>
    <xsl:template match="Object">
        <xsl:variable name="td"
                      select=".|following-sibling::*[@columnNo!=0][
                                      count(current()|
                                            preceding-sibling::*[@columnNo=0][1]
                                      )=1]"/>
        <tr>
            <xsl:apply-templates select="$td/Value">
                <xsl:with-param name="td-count" select="count($td)"/>
            </xsl:apply-templates>
        </tr>
    </xsl:template>
    <xsl:template match="Value">
        <xsl:param name="td-count"/>
        <td width="{100 div $td-count}%">
            <xsl:value-of select="."/>
        </td>
    </xsl:template>
</xsl:stylesheet>

但是,大多数XSLT处理器允许使用密钥。

现在,对于colspan:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:variable name="td-max">
        <xsl:call-template name="max">
            <xsl:with-param name="nodes" select="/*/*/Object[@columnNo=0]"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:template name="max">
        <xsl:param name="nodes"/>
        <xsl:if test="$nodes">
            <xsl:variable name="head"
                              select="count($nodes[1]/following-sibling::*[@columnNo!=0][
                                          count($nodes[1]|
                                                preceding-sibling::*[@columnNo=0][1]
                                          )=1])+1"/>
            <xsl:variable name="tail">
                <xsl:call-template name="max">
                    <xsl:with-param name="nodes" select="$nodes[position() > 1]"/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:choose>
                <xsl:when test="$head > $tail or $tail=''">
                    <xsl:value-of select="$head"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$tail"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
    <xsl:template match="ObjectGroup">
        <html>
            <table border="1">
                <xsl:apply-templates select="Object[@columnNo=0]"/>
            </table>
        </html>
    </xsl:template>
    <xsl:template match="Object">
        <xsl:variable name="td"
                          select=".|following-sibling::*[@columnNo!=0][
                                          count(current()|
                                                preceding-sibling::*[@columnNo=0][1]
                                          )=1]"/>
        <tr>
            <xsl:apply-templates select="$td/Value">
                <xsl:with-param name="td-count" select="count($td)"/>
            </xsl:apply-templates>
        </tr>
    </xsl:template>
    <xsl:template match="Value">
        <xsl:param name="td-count"/>
        <td width="{100 div $td-count}%">
            <xsl:if test="position()=last() and $td-max > $td-count">
                <xsl:attribute name="colspan">
                    <xsl:value-of select="$td-max - $td-count + 1"/>
                </xsl:attribute>
            </xsl:if>
            <xsl:value-of select="."/>
        </td>
    </xsl:template>
</xsl:stylesheet>

修改:添加了动态colspan。如果你不关心多个colspan="1",它可能会更简洁。

编辑2 :更好的最大模式。