使用XSL处理来自平面数据结构的层次结构

时间:2013-11-22 00:50:08

标签: xml xslt unique distinct hierarchy

新手问题。我必须创建一个这样的交叉表报告:

                       SC     TIN
Total                   3       2
  South East            2       0
    Gold Coast          2       0
  North                 1       2
    Cairns              0       1
    Townsville          1       1 

使用下面的(简化)XML:

<DATASETLIST>
 <DATASET entity="SC">
  <ROW>
   <District>GOLD COAST</District> 
   <Region>SOUTH EAST</Region> 
  </ROW>
  <ROW>
   <District>GOLD COAST</District> 
   <Region>SOUTH EASTERN</Region> 
  </ROW>
  <ROW>
   <District>TOWNSVILLE</District> 
   <Region>NORTH</Region> 
  </ROW>
 </DATASET>
 <DATASET entity="TIN">
  <ROW>
   <District>TOWNSVILLE</District> 
   <Region>NORTH</Region> 
  </ROW>
  <ROW>
   <District>CAIRNS</District> 
   <Region>NORTH</Region> 
  </ROW>
 </DATASET>
</DATASETLIST>

我实际上已经得到了主要报告,但层次结构是硬编码的(例如,计数SC与“黄金海岸”相关联)但由于可能数据不佳,我可能会遗漏一些节点,所以我想建立一个基于的独特层次结构数据,然后我可以为每个节点调用我的“计数”模板。

我已经得到了一个观点(见下文),我可以在其中生成一个区域排序列表,但它不是唯一的,并且区域不在每个区域内排序。我以为我可以以某种方式创建一个独特的层次结构变量,然后用xsl:for-each逻辑处理它。

但我不知道这是可行的还是推荐的方法。这可能是一个非常简单的解决方案,但我不知道如何去做。请用简单(并且清楚解释)的术语表达任何答案。

<xsl:for-each select="//Region[not(Region=preceding-sibling::Region)]">
    <xsl:sort select="."/>
    <xsl:for-each select=".">
        <xsl:value-of select="."/> - 
        <xsl:for-each select="../District"> 
            <xsl:value-of select="."/><br/>
        </xsl:for-each>
    </xsl:for-each>
</xsl:for-each>

1 个答案:

答案 0 :(得分:1)

这就是你如何做到的。不过,我担心这并不容易和直截了当。输入XMl的结构方式使得访问相关节点变得更加困难。

例如,在某些地方,您需要访问自然不在上下文中的节点。为了解决这个问题,我使用变量和参数。

但它非常通用,即您可以处理任意数量的DATASETSRegionsDistricts。它创建一个HTML表来显示计数。

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
  <xsl:element name="html">
     <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

<xsl:template match="DATASETLIST">
  <xsl:element name="head">
     <xsl:element name="title">
        <xsl:text>DATASET counts</xsl:text>
     </xsl:element>
  </xsl:element>
  <xsl:element name="body">
     <xsl:element name="table">
        <xsl:attribute name="border">1</xsl:attribute>
        <xsl:call-template name="tbl"/>
     </xsl:element>
  </xsl:element>
</xsl:template>

<xsl:template name="tbl">
  <xsl:param name="regions" select="distinct-values(DATASET/ROW/Region)"/>
  <xsl:param name="rows" select="DATASET/ROW"/>
  <xsl:param name="datasets" select="DATASET"/>

  <!--header row-->
  <xsl:element name="tr">
     <xsl:element name="th"/>
     <xsl:for-each select="$datasets">
        <xsl:element name="th">
           <xsl:value-of select="@entity"/>
        </xsl:element>
     </xsl:for-each>
  </xsl:element>
  <!--Totals row-->
  <xsl:element name="tr">
     <xsl:element name="td">
        <xsl:element name="b">
           <xsl:text>Total</xsl:text>
        </xsl:element>
     </xsl:element>
     <xsl:for-each select="$datasets">
        <xsl:element name="td">
           <xsl:value-of select="count(ROW)"/>
        </xsl:element>
     </xsl:for-each>
  </xsl:element>
  <xsl:element name="tr"/>
  <!--region rows-->
  <xsl:for-each select="$regions"> 
     <xsl:variable name="region" select="."/>       
     <xsl:element name="tr">
        <xsl:element name="td">
           <xsl:element name="b">
              <xsl:value-of select="."/>
           </xsl:element>
        </xsl:element>
        <xsl:for-each select="$datasets">
           <xsl:element name="td">
              <xsl:value-of select="count(ROW/Region[.=$region])"/>
           </xsl:element>
        </xsl:for-each>
     </xsl:element>
     <!--district rows-->
     <xsl:for-each select="distinct-values($rows[Region=$region]/District)">
        <xsl:variable name="district" select="."/>
        <xsl:element name="tr">
           <xsl:element name="td">
              <xsl:value-of select="."/>
           </xsl:element>
           <xsl:for-each select="$datasets">
              <xsl:element name="td">
                 <xsl:value-of select="count(ROW/District[.=$district])"/>
              </xsl:element>
           </xsl:for-each>
        </xsl:element>
     </xsl:for-each>
  <xsl:element name="tr"/>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>