0

我正在努力使用 XSLT。我被困在程序领域。基本上我有一些从数据库生成的 XML,看起来有点像这样:

<?xml version="1.0" encoding="iso-8859-1"?>
<report>
    <generated_dtm>2013-03-08T18:57:26+00:00</generated_dtm>

    <range>
        <start_dtm>2013-02-21T17:52:00+00:00</start_dtm>
        <end_dtm>2013-03-08T17:52:00+00:00</end_dtm>
    </range>

    <sensor site_code="A0001" unit_no="1" sensor_no="1">
        <name>Food</name>
        <mu_symbol>°C</mu_symbol>
    </sensor>

    <sensor site_code="A0001" unit_no="1" sensor_no="2">
        <name>Air</name>
        <mu_symbol>°C</mu_symbol>
    </sensor>

    <readings>
        <slot slot_dtm="2013-02-21T17:50:00+00:00">
            <sensor sensor_no="1">
                <v>10</v>
                <status_code>IR</status_code>
                <status_desc>In Range</status_desc>
            </sensor>
            <sensor sensor_no="2">
                <v>20</v>
                <status_code>Lo</status_code>
                <status_desc>Low</status_desc>
                </sensor>
        </slot>

        <slot slot_dtm="2013-02-21T18:00:00+00:00">
            <sensor sensor_no="2">
                <v>21</v>
                <status_code>Lo</status_code>
                <status_desc>Low</status_desc>
            </sensor>
            <sensor sensor_no="1">
                <v>11</v>
                <status_code>IR</status_code>
                <status_desc>In Range</status_desc>
            </sensor>
        </slot>
    </readings>
</report>

我试图以 HTML 表中的读数结束,每个传感器都是一列,每一行的时间都在左侧,如下所示:

Time                      | Food | Air
-------------------------------------
2013-02-21T17:50:00+00:00 | 10   | 11
2013-02-21T18:00:00+00:00 | 20   | 22

Although the order of the time-slots is guaranteed to be ascending so I don't need to sort them (there could be 1000's), the problem is that within each time-slot the order of the sensors cannot be guaranteed, so I thought I would loop through the sensors I used to create the table headers each time and select the correct sensor from each slot as I iterate through the slots. Although this doesn't work you'll probably get what I tried to do (I realise now why it doesn't work.. variables do not behave how I expected!) : -

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="report">
  <html>
  <head>
    <title>Report</title>
  </head>
  <body>
      <table border="0" width="100%" bgcolor="#ffffff" cellspacing="0" cellpadding="2">
        <tr>
          <td class="column_head_above" width="70">Time</td>
          <xsl:for-each select="sensor">
            <td class="column_head_above"><xsl:value-of select="name"/><xsl:text> </xsl:text><xsl:value-of select="mu_symbol"/></td>
          </xsl:for-each>
        </tr>

        <!-- go through each time slot -->

        <xsl:for-each select="readings/slot">
          <tr>
            <xsl:variable name="sdtm" select="@slot_dtm" /> 

            <td class="table_data"><xsl:value-of select="$sdtm"/></td>

            <!-- go through each sensor header -->

            <xsl:for-each select="../sensor">
              <xsl:variable name="sno" select="@sensor_no" /> 
              <td>
                <xsl:value-of select="../readings/slot[@slot_dtm=$sdtm]/sensor[@sensor_no=$sno]/v"/>
                <xsl:value-of select="../readings/slot[@slot_dtm=$sdtm]/sensor[@sensor_no=$sno]/status_desc"/>
              </td>
            </xsl:for-each>

          </tr>
        </xsl:for-each>

        <!-- end: go through each time slot -->

      </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

There can be 100's or even 1000's of time-slots, this is just a small example. I can adjust the hierarchy of the XML if it helps, but I cannot put the sensors in order within each time-slot without some serious rework of the database query. I'm hoping that isn't necessary.

Originally I had XML where the slots were separated out like this:

<readings>
    <slot slot_dtm="2013-02-21T17:50:00+00:00">
        <sensor sensor_no="1">
            <v>10</v>
            <status_code>IR</status_code>
            <status_desc>In Range</status_desc>
        </sensor>
    </slot>

    <slot slot_dtm="2013-02-21T17:50:00+00:00">
        <sensor sensor_no="2">
            <v>20</v>
            <status_code>Lo</status_code>
            <status_desc>Low</status_desc>
            </sensor>
    </slot>

    <slot slot_dtm="2013-02-21T18:00:00+00:00">
        <sensor sensor_no="1">
            <v>11</v>
            <status_code>IR</status_code>
            <status_desc>In Range</status_desc>
        </sensor>
    </slot>

    <slot slot_dtm="2013-02-21T18:00:00+00:00">
        <sensor sensor_no="2">
            <v>21</v>
            <status_code>Lo</status_code>
            <status_desc>Low</status_desc>
        </sensor>
    </slot>
</readings>

Which involved a much simpler database query! Here I could guarantee the order, but the XQuery processor I'm using (Qt's QXmlQuery) does not support for-each-group so I could not find a way to group based on time.

Sorry this is so long, I hope someone can help at least point me in the right direction.

Thanks.

4

2 回答 2

1

This should do it:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="allSensors" select="/report/sensor" />

  <xsl:template match="report">
    <html>
      <head>
        <title>Report</title>
      </head>
      <body>
        <table border="0" width="100%" bgcolor="#ffffff"
               cellspacing="0" cellpadding="2">
          <tr>
            <td class="column_head_above" width="70">Time</td>
            <xsl:apply-templates select="sensor" />
          </tr>
          <xsl:apply-templates select="readings/slot" />
        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="report/sensor">
    <td class="column_head_above">
      <xsl:value-of select="concat(name, ' ', mu_symbol)"/>
    </td>
  </xsl:template>

  <xsl:template match="slot">
    <xsl:variable name="currentSensors" select="sensor" />
    <tr>
      <td class="table_data">
        <xsl:value-of select="@slot_dtm"/>
      </td>

      <xsl:apply-templates select="$allSensors/@sensor_no">
        <xsl:with-param name="currentSlot" select="current()/@slot_dtm" />
      </xsl:apply-templates>
    </tr>
  </xsl:template>

  <xsl:template match="@sensor_no">
    <xsl:param name="currentSlot" />

    <td>
      <xsl:variable name="matchingSensor"
                    select="/report/readings/slot[@slot_dtm = $currentSlot]
                            /sensor[@sensor_no = current()]" />
      <xsl:value-of select="concat($matchingSensor/v, ' - ', 
                                       $matchingSensor/status_desc)" />
    </td>
  </xsl:template>
</xsl:stylesheet>

I did some cleanup here, but the main points are:

  • Keeping a variable reference to the sensor definitions for easy access
  • Creating a variable reference to the current slot's sensors, which can be referenced inside the for-each.

When run on your sample input, this produces:

<html>
  <head>
    <META http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Report</title>
  </head>
  <body>
    <table border="0" width="100%" bgcolor="#ffffff" cellspacing="0" cellpadding="2">
      <tr>
        <td class="column_head_above" width="70">Time</td>
        <td class="column_head_above">Food °C</td>
        <td class="column_head_above">Air °C</td>
      </tr>
      <tr>
        <td class="table_data">2013-02-21T17:50:00+00:00</td>
        <td>10 - In Range</td>
        <td>20 - Low</td>
      </tr>
      <tr>
        <td class="table_data">2013-02-21T18:00:00+00:00</td>
        <td>11 - In Range</td>
        <td>21 - Low</td>
      </tr>
    </table>
  </body>
</html>
于 2013-03-08T20:51:01.117 回答
0

Here's an update after spending some time figuring it out. Thanks to JLRishe for his answer too. Between that and what I've worked out it is starting to become clear (until the next problem!).

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="report">
  <html>
  <head>
    <title>Report</title>
  </head>
  <body>
      <table border="0" width="100%" bgcolor="#ffffff" cellspacing="0" cellpadding="2">
        <tr>
          <td class="column_head_above" width="70">Time</td>
          <xsl:apply-templates select="sensors/sensor">
            <xsl:sort select="@sensor_no" />
          </xsl:apply-templates>
        </tr>

        <!-- go through each time slot -->

        <xsl:for-each select="readings/slot">
          <tr>
            <td class="table_data"><xsl:value-of select="@slot_dtm"/></td>

            <!-- go through each sensor header -->

            <xsl:apply-templates select="sensor">
              <xsl:sort select="@sensor_no" />
            </xsl:apply-templates>

          </tr>
        </xsl:for-each>

        <!-- end: go through each time slot -->

      </table>
  </body>
  </html>
</xsl:template>

<xsl:template match="slot/sensor">
    <td>
        <xsl:value-of select="@sensor_no"/> -
        <xsl:value-of select="v"/> -
        <xsl:value-of select="status_desc"/>
    </td>
</xsl:template>

<xsl:template match="sensors/sensor">
    <td class="column_head_above">
        <xsl:value-of select="name"/><xsl:text> </xsl:text><xsl:value-of select="mu_symbol"/>
    </td>
</xsl:template>

</xsl:stylesheet>
于 2013-03-08T21:01:27.127 回答