XSLT:从不同的节点创建组合 table

XSLT: Create combined table from distinct nodes

我想创建一个组合 table,其中包含来自 xml 源中不同子节点的数据。

来源(大致)如下:请注意,我已经简化了示例。 "Modes" 节点在更大的 xml 文档中向下几层,"BLOCK" 节点包含的不仅仅是 "CODE" children。 数据是从三个不同的节点 Mode1、Mode2 和 Mode3 收集的。源文档中有更多 ModeX (X=1..10) 节点,但它们没有进入这个特定的 table.

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="sample.xsl" type="text/xsl"?>
<root>
  <!-- there are "a lot" more levels between root and Modes -->
  <Modes>
    <Mode1>
      <KOMMENTAR>Header 1</KOMMENTAR>
      <TEST>
        <NUMBER>5</NUMBER>
        <FLAG>0</FLAG>
      </TEST>
      <TEST>
        <NUMBER>6</NUMBER>
        <FLAG>0</FLAG>
      </TEST>
      <TEST>
        <NUMBER>7</NUMBER>
        <FLAG>1</FLAG>
        <BLOCK>
          <CODE>1.7.1 - Message</CODE>
        </BLOCK>
      </TEST>
      <TEST>
        <NUMBER>8</NUMBER>
        <FLAG>1</FLAG>
        <BLOCK>
          <CODE>1.8.1 - Message</CODE>
        </BLOCK>
        <BLOCK>
          <CODE>1.8.2 - Message</CODE>
        </BLOCK>
      </TEST>
      <TEST>
        <NUMBER>9</NUMBER>
        <FLAG>0</FLAG>
        <BLOCK>
          <CODE>1.9.1 - Message</CODE>
        </BLOCK>
      </TEST>
    </Mode1>
    <Mode2>
      <KOMMENTAR>Header 2</KOMMENTAR>
      <TEST>
        <NUMBER>5</NUMBER>
      </TEST>
      <TEST>
        <NUMBER>6</NUMBER>
        <BLOCK>
          <CODE>2.6.1 - Message</CODE>
        </BLOCK>
        <BLOCK>
          <CODE>2.6.2 - Message</CODE>
        </BLOCK>
        <BLOCK>
          <CODE>2.6.2 - Message</CODE>
        </BLOCK>
      </TEST>
      <TEST>
        <NUMBER>7</NUMBER>
        <BLOCK>
          <CODE>2.7.1 - Message</CODE>
        </BLOCK>
      </TEST>
      <TEST>
        <NUMBER>8</NUMBER>
      </TEST>
      <TEST>
        <NUMBER>9</NUMBER>
      </TEST>
    </Mode2>
    <Mode3>
      <KOMMENTAR>Header 3</KOMMENTAR>
      <TEST>
        <NUMBER>5</NUMBER>
      </TEST>
      <TEST>
        <NUMBER>6</NUMBER>
      </TEST>
      <TEST>
        <NUMBER>7</NUMBER>
        <BLOCK>
          <CODE>3.7.1 - Message</CODE>
        </BLOCK>
        <BLOCK>
          <CODE>3.7.2 - Message that spans over several lines</CODE>
        </BLOCK>
      </TEST>
      <TEST>
        <NUMBER>8</NUMBER>
      </TEST>
      <TEST>
        <NUMBER>9</NUMBER>
      </TEST>
    </Mode3>
    <Mode9>
      Contains some other data
    </Mode9>
  </Modes>
</root>

所需的输出是 table,如下所示(在 HTML 中):

     | Header 1                     | Header 2              | Header 3              |
Test | Flag | Nr. | Message         | Nr. | Message         | Nr. | Message         |
5    | 0    |     |                 |     |                 |     |                 |
6    | 0    |     |                 | 1   | 2.6.1 - Message |     |                 |
     |      |     |                 | 2   | 2.6.2 - Message |     |                 |
     |      |     |                 | 3   | 2.6.3 - Message |     |                 |
7    | 1    | 1   | 1.7.1 - Message | 1   | 2.7.1 - Message | 1   | 3.7.1 - Message |
     |      |     |                 |     |                 | 2   | 3.7.2 - Message |
     |      |     |                 |     |                 |     | that spans over |
     |      |     |                 |     |                 |     | several lines   |
8    | 1    | 1   | 1.8.1 - Message |     |                 |     |                 |
     |      | 2   | 1.8.2 - Message |     |                 |     |                 |
9    | 0    |     |                 |     |                 |     |                 |

到目前为止,我能想到的最好的办法是在 TEST 下创建一个 sub-table for-each BLOCK。但这看起来很难看,因为当然 "Nr." 和 "Message" headers 不与 sub-table 的内容列对齐。

我已经阅读了一些关于 Muenchian 分组的文章(http://www.jenitennison.com/xslt/grouping/muenchian.html 和关于 Whosebug 的其他问题)并尝试了示例,但我无法为我的数据获取正确的键。

如果有人能帮我解决键和外部 for-each 循环,我想我可以处理剩下的事情。

提前致谢。

If somebody could help me with the keys and the outer for-each loop, I think I can handle the rest.

恕我直言,外循环很简单,真正的问题在后面。以下是执行外循环的方法:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes" encoding="UTF-8" version="1.0"/>

<xsl:key name="test" match="TEST" use="NUMBER" />

<xsl:template match="/">
    <table border="1">
        <thead>
            <tr>
                <th colspan="2"/>
                <th colspan="2">Header 1</th>
                <th colspan="2">Header 2</th>
                <th colspan="2">Header 3</th>
            </tr>
            <tr>    
                <th>Test</th>
                <th>Flag</th>
                <th>Nr.</th>
                <th>Message</th>
                <th>Nr.</th>
                <th>Message</th>
                <th>Nr.</th>
                <th>Message</th>
            </tr>
        </thead>
        <tbody>
            <xsl:for-each select="(root/Modes/Mode1/TEST | root/Modes/Mode2/TEST | root/Modes/Mode3/TEST)[count(. | key('test', NUMBER)[1]) = 1]">
                <tr>
                    <td><xsl:value-of select="NUMBER"/></td>
                    <td><xsl:value-of select="FLAG"/></td>
                </tr>
            </xsl:for-each>
        </tbody>
    </table>
</xsl:template>

</xsl:stylesheet>

此时你将拥有:

现在真正的问题来了:如果您希望有一列为每条消息编号并使该编号与相邻的消息对齐,则必须为每条不是第一条消息的消息创建一个单独的行它的专栏。您将需要计算每列中的消息数,并使用三者中的最大值作为前两个单元格的行跨度值。

或者,您可以采取简单的方法并执行以下操作:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes" encoding="UTF-8" version="1.0"/>

<xsl:key name="test" match="TEST" use="NUMBER" />

<xsl:template match="/">
    <table border="1">
        <thead>
            <tr>
                <th width="5%">Test</th>
                <th width="5%">Flag</th>
                <th width="30%">Header 1<br/>Message</th>
                <th width="30%">Header 2<br/>Message</th>
                <th width="30%">Header 3<br/>Message</th>
            </tr>
        </thead>
        <tbody>
            <xsl:for-each select="(root/Modes/Mode1/TEST | root/Modes/Mode2/TEST | root/Modes/Mode3/TEST)[count(. | key('test', NUMBER)[1]) = 1]">
                <tr>
                    <td><xsl:value-of select="NUMBER"/></td>
                    <td><xsl:value-of select="FLAG"/></td>
                    <td>
                        <ol>
                            <xsl:for-each select="key('test', NUMBER)[parent::Mode1]/BLOCK">
                                <li>
                                    <xsl:value-of select="CODE"/>
                                </li>
                            </xsl:for-each>
                        </ol>
                    </td>
                    <td>
                        <ol>
                            <xsl:for-each select="key('test', NUMBER)[parent::Mode2]/BLOCK">
                                <li>
                                    <xsl:value-of select="CODE"/>
                                </li>
                            </xsl:for-each>
                        </ol>
                    </td>
                    <td>
                        <ol>
                            <xsl:for-each select="key('test', NUMBER)[parent::Mode3]/BLOCK">
                                <li>
                                    <xsl:value-of select="CODE"/>
                                </li>
                            </xsl:for-each>
                        </ol>
                    </td>
                </tr>
            </xsl:for-each>
        </tbody>
    </table>
</xsl:template>

</xsl:stylesheet>

生产:

可以使用 CSS 进一步按摩塑形。