XSLT - 格式化逗号分隔列表为表格格式

时间:2013-08-05 19:46:59

标签: xslt

Web服务正在返回以下xml格式的人员列表。这些值用分号分隔。我需要在2或3列中显示值(需要变量)。

期望的结果:

<table border="1">
 <tr>
  <td>Smith, John</td>
  <td>Jackson, Samuel</td>
  <td>Wayne, Bruce</td>
 </tr>
 <tr>
  <td>Cosby, Bill</td>
  <td>Kent, Clarke</td>
  <td>Leno, Jay</td>
 </tr>
 <tr>
  <td>OBrian, Conan</td>
  <td> </td>
  <td> </td>
 </tr>
</table>

Xml样本

 <?xml version="1.0" encoding="UTF-8"?> <PI>Smith, John; Jackson,
 Samuel; Wayne, Bruce; Cosby, Bill; Kent, Clarke; Leno, Jay; OBrian,
 Conan; </PI>

2 个答案:

答案 0 :(得分:0)

这是一个XSLT 2.0解决方案,其列数作为请求的变量。这采用了我在课堂上教授的一种技术,强调分组不一定是XML中的值(正如许多XSLT用户所假设的那样),而是可以通过任意计算(在这种情况下,是除法的结果)。 / p>

[已编辑以显示具有不同列数的多个调用]

t:\ftemp>type names.xml 
<?xml version="1.0" encoding="UTF-8"?> <PI>Smith, John; Jackson,
Samuel; Wayne, Bruce; Cosby, Bill; Kent, Clarke; Leno, Jay; OBrian,
Conan; </PI>
t:\ftemp>call xslt2 names.xml names.xsl names.out.xml "cols=3" 

t:\ftemp>type names.out.xml 
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
   <tr>
      <td>Smith, John</td>
      <td>Jackson, Samuel</td>
      <td>Wayne, Bruce</td>
   </tr>
   <tr>
      <td>Cosby, Bill</td>
      <td>Kent, Clarke</td>
      <td>Leno, Jay</td>
   </tr>
   <tr>
      <td>OBrian, Conan</td>
      <td/>
      <td/>
   </tr>
</table>

t:\ftemp>call xslt2 names.xml names.xsl names.out.xml "cols=2" 

t:\ftemp>type names.out.xml 
<?xml version="1.0" encoding="UTF-8"?>
<table border="1">
   <tr>
      <td>Smith, John</td>
      <td>Jackson, Samuel</td>
   </tr>
   <tr>
      <td>Wayne, Bruce</td>
      <td>Cosby, Bill</td>
   </tr>
   <tr>
      <td>Kent, Clarke</td>
      <td>Leno, Jay</td>
   </tr>
   <tr>
      <td>OBrian, Conan</td>
      <td/>
   </tr>
</table>

t:\ftemp>type names.xsl 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">

<xsl:output indent="yes"/>

<xsl:param name="cols" required="yes"/>

<xsl:template match="PI">
  <table border="1">
    <!--determine population and group by number of columns-->
    <xsl:for-each-group select="tokenize(.,';\s+')"
                        group-by="(position()-1) idiv $cols">
      <tr>
        <!--put members into the row-->
        <xsl:for-each select="current-group()">
          <td>
            <xsl:value-of select="normalize-space(.)"/>
          </td>
        </xsl:for-each>
        <!--filler for the last row-->
        <xsl:if test="position()=last()">
          <xsl:for-each select="count(current-group())+1 to $cols">
            <td/>
          </xsl:for-each>
        </xsl:if>
      </tr>
    </xsl:for-each-group>
  </table>
</xsl:template>

</xsl:stylesheet>
t:\ftemp>rem

答案 1 :(得分:0)

请接受霍尔曼的解决方案。他的技术是现场的。

XSLT 2.0解决方案

您的示例输入数据表示以分号终止列表,而不是以分号分隔的列表。

此XSLT 2.0样式表可以使用以分号终止的列表或以分号分隔的列表正常工作。它需要一个输入参数col-count,默认为3.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="col-count" select="3" />

<xsl:template match="/*">
 <table border="1">
  <xsl:variable name="names" select="tokenize(.,';')[normalize-space(.)]" />
  <xsl:for-each-group select="$names , for $x in 1 to
    ($col-count - (count($names) mod $col-count)) mod $col-count return ''"
                      group-by="(position() - 1) idiv $col-count"> 
   <tr>
    <xsl:for-each select="current-group()">
     <td><xsl:value-of select="normalize-space(.)" /></td>
    </xsl:for-each>
   </tr>
  </xsl:for-each-group>
 </table>
</xsl:template>

</xsl:stylesheet>

注意:

(1)注意解决方案的简洁性,以及没有笨拙的xsl:if陈述。计算序列的使用(与输入文档节点选择相反)不仅限于group-by属性,还可以应用于select。计算序列的使用表明了更具功能性的观点,而不是程序观点。

(2)如果您希望输出对旧浏览器安全,请替换...

  <xsl:for-each-group select="$names , for $x in 1 to
    ($col-count - (count($names) mod $col-count)) mod $col-count return ''"
                      group-by="(position() - 1) idiv $col-count"> 

...与......

  <xsl:for-each-group select="$names , for $x in 1 to
    ($col-count - (count($names) mod $col-count)) mod $col-count return '&#160;'"
                      group-by="(position() - 1) idiv $col-count"> 

XSLT 1.0解决方案

如果您坚持使用XSLT 1.0,那么您可以使用这个等效但效率较低的样式表......

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:esl="http://exslt.org/common"
  xmlns:so="http://stackoverflow.com/questions/18066463"
  version="1.0"
  exclude-result-prefixes="xsl so esl">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="col-count" select="3" /><!-- Must be 2 or 3. -->

<xsl:variable name="empty-rtf">
  <so:name>&#160;</so:name>
</xsl:variable>
<xsl:variable name="empty" select="esl:node-set($empty-rtf)/*" />


<xsl:template match="/*">
  <table border="1">
    <xsl:variable name="names-rtf">
      <xsl:call-template name="split">
        <xsl:with-param name="list" select="." />
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="names" select="esl:node-set($names-rtf)/*" />
    <xsl:for-each select="$names[(position() mod $col-count) = 1]">
      <xsl:variable name="row" select="position() - 1" />
      <tr>
        <xsl:apply-templates select="$names[floor((position() - 1) div $col-count) = $row]" />
        <xsl:if test="position()=last()">
         <xsl:for-each select="($names|$empty)[position() &lt;=
                               (($col-count - (count($names) mod $col-count)) mod $col-count)]">
           <xsl:apply-templates select="$empty" />
         </xsl:for-each>  
        </xsl:if>  
      </tr>
    </xsl:for-each>  
  </table>
</xsl:template>

<xsl:template name="split">
  <xsl:param name="list" />
  <xsl:choose>
    <xsl:when test="contains($list,';')">
      <xsl:call-template name="split">
        <xsl:with-param name="list" select="substring-before($list,';')" />
      </xsl:call-template>
      <xsl:call-template name="split">
        <xsl:with-param name="list" select="substring-after($list,';')" />
      </xsl:call-template>
    </xsl:when>  
    <xsl:when test="normalize-space($list)">
      <so:name><xsl:value-of select="normalize-space($list)" /></so:name>
    </xsl:when>  
    <xsl:otherwise />
  </xsl:choose>
</xsl:template>

<xsl:template match="so:*" priority="2">
  <td><xsl:value-of select="." /></td>
</xsl:template>  

</xsl:stylesheet>