【问题标题】:Pivoting XML using XSLT使用 XSLT 透视 XML
【发布时间】:2010-01-07 09:44:27
【问题描述】:

以这种格式使用 XML:

<?xml version="1.0"?>
<GetResult version="1.0">
<Fetch>
    <StartTime>2004-08-01 00:00:00</StartTime>
    <EndTime>2004-08-01 00:00:00</EndTime>
</Fetch>
<Items>
    <Item>
        <Name>Item Name Number 1</Name>
        <Data>
            <Datum>
                <Timestamp>2004-07-31 16:00:00+00:00</Timestamp>
                <Value><![CDATA[25]]></Value>
            </Datum>
            <Datum>
                <Timestamp>2004-07-31 18:00:00+00:00</Timestamp>
                <Value><![CDATA[35]]></Value>
            </Datum>
        </Data>
    </Item> 
    <Item>
        <Name>Item Number 2</Name>
        <Data>
            <Datum>
                <Timestamp>2004-07-31 16:00:00+00:00</Timestamp>
                <Value><![CDATA[45]]></Value>
            </Datum>
            <Datum>
                <Timestamp>2004-07-31 17:00:00+00:00</Timestamp>
                <Value><![CDATA[55]]></Value>
            </Datum>
            <Datum>
                <Timestamp>2004-07-31 18:00:00+00:00</Timestamp>
                <Value><![CDATA[65]]></Value>
            </Datum>
        </Data>
    </Item> 
</Items>
</GetResult>

我希望能够使用 XSLT 生成这样的表格:

<table>
  <tr>
    <th>Timestamp</th>
    <th>Item Name Number 1</th>
    <th>Item Number 2</th>
  </tr>
  <tr>
    <td>2004-07-31 16:00:00+00:00</td>
    <td>25</td>
    <td>45</td>
  </tr>
  <tr>
    <td>2004-07-31 17:00:00+00:00</td>
    <td></td>
    <td>55</td>
  </tr>
  <tr>
    <td>2004-07-31 18:00:00+00:00</td>
    <td>35</td>
    <td>65</td>
  </tr>
</table>

无论返回多少项目以及每个项目下有多少基准,这都必须工作。我已经阅读了一些其他类似的答案,但没有任何运气。我对 XSLT 相当陌生,它让我发疯。对此的解决方案将不胜感激。

【问题讨论】:

  • 指定 XSLT 的版本?

标签: xml xslt


【解决方案1】:

这是一种利用了相当可怕的 Muenchian Grouping 方法的方法,如果您查看 StackOverflow 中的其他 XSLT 问题,您可能会看到提到过,因此值得了解。在这种情况下,Muenchian Grouping 将用于循环通过 distict Timestamp 元素。

首先,您定义一个键来查找时间戳元素

<xsl:key name="Timestamps" match="Timestamp" use="."/>

因此,如果您使用它来查找 '2004-07-31 16:00:00+00:00' 的键,它将包含两个时间戳,元素,但 '2004-07-31 17:00 :00+00:00' 只会包含一个。

要遍历不同的 Timestamp 元素,您首先要遍历所有 Timestamp 元素,像这样

<xsl:for-each select="//Timestamp">

但是您需要一个 XSL:IF 条件来检查 Timestamp 元素是该值的第一个此类出现。这是通过使用密钥来完成的。如果该元素恰好在键列表中排在第一位,则可以对其进行处理。

<xsl:if test="generate-id(.) = generate-id(key('Timestamps',.)[1])">

generate-id 是要测试两个元素是否相同时使用的方法。总而言之:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:key name="Timestamps" match="Timestamp" use="."/>
   <xsl:template match="/">
      <table>
         <tr>
            <th>Timestamp</th>
            <!-- Output the Item headers -->
            <xsl:for-each select="//Item">
               <th>
                  <xsl:value-of select="Name"/>
               </th>
            </xsl:for-each>
         </tr>
         <!-- Loop through all Timestamps -->
         <xsl:for-each select="//Timestamp">
            <xsl:sort select="."/>
            <!-- Only process the element if it is the first occurence of this value -->
            <xsl:if test="generate-id(.) = generate-id(key('Timestamps',.)[1])">
               <xsl:variable name="Timestamp" select="."/>
               <tr>
                  <td>
                     <xsl:value-of select="."/>
                  </td>
                  <xsl:for-each select="//Item">
                     <td>
                        <!-- Output the relevant Value for the Item -->
                        <xsl:value-of select="Data/Datum[Timestamp=$Timestamp][1]/Value"/>
                     </td>
                  </xsl:for-each>
               </tr>
            </xsl:if>
         </xsl:for-each>
      </table>
   </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 匹配模式不需要'//'操作符。 就够了。 +1 使用密钥。
  • 我也+1,干得好。但是 select 的最终值中的 [1] 是否是绝对必要的?
  • 如果单个 Item 元素中没有任何重复的时间戳,则没有!
  • 感谢你们的帮助。使用此答案使解决方案按要求工作。
【解决方案2】:

您必须执行几个步骤。

  1. 获取项目列表
  2. 获取时间戳列表
  3. 对于每个时间戳,您必须为每个项目打印值

这听起来可能很难,但如果您了解 XSLT 的基础知识,这真的很简单

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="items" select="//Item" />
  <xsl:variable name="timestamps" select="distinct-values(//Timestamp)" />

  <xsl:template match="/" >
     <table>
       <tr>
         <th>Timestamp</th>
         <xsl:for-each select="$items">
           <th><xsl:value-of select="Name" /></th>
         </xsl:for-each>
       </tr>

       <xsl:for-each select="$timestamps">
         <xsl:variable name="stamp" select="."/>
         <tr>
           <td><xsl:value-of select="$stamp" /></td>
           <xsl:for-each select="$items">
             <td><xsl:value-of select=".//Datum[ Timestamp = $stamp ]/Value" /></td>
           </xsl:for-each>
         </tr>
       </xsl:for-each>

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

我希望没有错误。如果他们为他们感到抱歉,但我认为主要思想是明确和正确的。

如果您有任何问题,请随时提出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-27
    • 2011-04-28
    • 1970-01-01
    • 2011-08-29
    • 2022-01-16
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多