【问题标题】:How to group XML nodes by three levels of node value in XSLT 1?如何在 XSLT 1 中按三个级别的节点值对 XML 节点进行分组?
【发布时间】:2014-09-02 18:22:24
【问题描述】:

我是 XSLT 的新手。我一直在这里阅读 XSLT 文档和其他帖子。我被困了一段时间,不知道下一步该做什么。 原始 XML:

<?xml version="1.0" encoding="UTF-8"?>
<COVERAGE>
    <FILE>
        <RUNDATE>2014-04-16</RUNDATE>
        <RUNTIME>20:11:20</RUNTIME>
        <GROUPID1>GROUP ID1</GROUPID1>
        <GROUPID2>GROUP ID2</GROUPID2>
        <USERID>ABC</USERID>
        <MODULE>
            <NAME>Module1</NAME>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITA</EXTNAME>
                    <OV>N</OV>
                    <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                    <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                    <UNEXECUTED>60.1 70.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITC</EXTNAME>
                    <OV>N</OV>
                    <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                    <UNEXECUTED>40.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITB</EXTNAME>
                    <OV>N</OV>
                    <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                    <EXECUTED>59.1 60.1</EXECUTED>
                    <UNEXECUTED>46.1 55.1</UNEXECUTED>
                </CSECT>
            </UNIT>
        </MODULE>
    </FILE>
    <FILE>
        <RUNDATE>2014-04-16</RUNDATE>
        <RUNTIME>20:12:16</RUNTIME>
        <GROUPID1>GROUP ID3</GROUPID1>
        <GROUPID2>GROUP ID4</GROUPID2>
        <USERID>BDF</USERID>
        <MODULE>
            <NAME>Module1</NAME>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITA</EXTNAME>
                    <OV>Y</OV>GOV>
                    <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                    <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                    <UNEXECUTED>60.1 70.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITB</EXTNAME>
                    <OV>N</OV>GOV>
                    <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                    <UNEXECUTED>40.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITC</EXTNAME>
                    <OV>N</OV>GOV>
                    <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                    <EXECUTED>59.1 60.1</EXECUTED>
                    <UNEXECUTED>46.1 55.1</UNEXECUTED>
                </CSECT>
            </UNIT>
        </MODULE>
    </FILE>
    <FILE>
        <RUNDATE>2014-04-16</RUNDATE>
        <RUNTIME>20:12:16</RUNTIME>
        <GROUPID1>GROUP ID3</GROUPID1>
        <GROUPID2>GROUP ID4</GROUPID2>
        <USERID>BDF</USERID>
        <MODULE>
            <NAME>Module2</NAME>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITA</EXTNAME>
                    <OV>Y</OV>GOV>
                    <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                    <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                    <UNEXECUTED>60.1 70.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITB</EXTNAME>
                    <OV>N</OV>GOV>
                    <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                    <UNEXECUTED>40.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <UNITDATE>2014-03-14</UNITDATE>
                <UNITTIME>15:15:00</UNITTIME>
                <TYPE>1</TYPE>
                <CSECT>
                    <EXTNAME>UNITC</EXTNAME>
                    <OV>N</OV>GOV>
                    <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                    <EXECUTED>59.1 60.1</EXECUTED>
                    <UNEXECUTED>46.1 55.1</UNEXECUTED>
                </CSECT>
            </UNIT>
        </MODULE>
    </FILE>
</COVERAGE>

如果 MODULE/NAME、UNIT/UNITDATE、UNIT/UNITTIME、CSECT/EXTNAME 相同,我想首先将 EXECUTED、UNEXECUTED 节点组合在一起。

转换后的 XML 应如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<COVERAGE>
    <FILE>
      <MODULE>
        <NAME>Module1</NAME>
            <UNIT>
                <CSECT>
                    <EXTNAME>UNITA</EXTNAME>
                    <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                    <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                    <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                    <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                    <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                    <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                    <UNEXECUTED>60.1 70.1</UNEXECUTED>
                    <UNEXECUTED>60.1 70.1</UNEXECUTED>
                    <UNEXECUTED>60.1 70.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <CSECT>
                    <EXTNAME>UNITC</EXTNAME>
                    <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                    <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                    <EXECUTED>59.1 60.1</EXECUTED><EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                    <EXECUTED>59.1 60.1</EXECUTED>
                    <UNEXECUTED>40.1</UNEXECUTED>
                    <UNEXECUTED>46.1 55.1</UNEXECUTED>
                    <UNEXECUTED>46.1 55.1</UNEXECUTED>
                </CSECT>
            </UNIT>
            <UNIT>
                <CSECT>
                    <EXTNAME>UNITB</EXTNAME>
                    <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                    <EXECUTED>59.1 60.1</EXECUTED>
                    <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                    <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                    <UNEXECUTED>46.1 55.1</UNEXECUTED>
                    <UNEXECUTED>40.1</UNEXECUTED>
                    <UNEXECUTED>40.1</UNEXECUTED>
                </CSECT>
            </UNIT>
        </MODULE>
    </FILE>
    <FILE>
        <MODULE>
            <NAME>Module2</NAME>
                <UNIT>
                    <CSECT>
                        <EXTNAME>UNITA</EXTNAME>
                        <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                        <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                        <UNEXECUTED>60.1 70.1</UNEXECUTED>
                    </CSECT>
                </UNIT>
                <UNIT>
                    <CSECT>
                        <EXTNAME>UNITB</EXTNAME>
                        <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                        <UNEXECUTED>40.1</UNEXECUTED>
                    </CSECT>
                </UNIT>
                <UNIT>
                    <CSECT>
                        <EXTNAME>UNITC</EXTNAME>
                        <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                        <EXECUTED>59.1 60.1</EXECUTED>
                        <UNEXECUTED>46.1 55.1</UNEXECUTED>
                    </CSECT>
                </UNIT>
            </MODULE>
        </FILE>
    </COVERAGE>

这个XSL是我阅读后得到的:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" />
     <xsl:key name="moduleKey" match="MODULE" use="NAME" />
    <xsl:key name="unitKey" match="UNIT" use="concat(CSECT/EXTNAME, '_', UNITDATE,'_',UNITTIME)" />

    <xsl:template match="COVERAGE">
        <COVERAGE>

            <xsl:for-each select="FILE">
            <FILE>
                <RUNDATE>
                    *
                </RUNDATE>
                <RUNTIME>
                    *
                </RUNTIME>
                <GROUPID1>
                    *
                </GROUPID1>
                <GROUPID2>
                    *
                </GROUPID2>
                <USERID>
                    *
                </USERID> 
                <xsl:for-each select="MODULE[count( . | key('moduleKey', NAME)[1]) = 1]">
                <MODULE>
                    <NAME>
                        <xsl:value-of select="NAME" />
                    </NAME>
                    <xsl:for-each select="UNIT[count( . | key('unitKey', concat(CSECT/EXTNAME, '_', UNITDATE,'_',UNITTIME))[1]) = 1]">
                        <UNIT>

                                <TYPE>
                                    <xsl:value-of select="TYPE" />
                                </TYPE>
                                <UNITDATE>
                                    <xsl:value-of select="COMPILEDATE" />
                                </UNITDATE>
                                <UNITTIME>
                                    <xsl:value-of select="COMPILETIME" />
                                </UNITTIME>
                                <CSECT>
                                <EXTNAME>
                                    <xsl:value-of select="CSECT/EXTNAME" />
                                </EXTNAME>
                                <OV>
                                    *
                                </OV>
                            <xsl:copy-of select="key('unitKey', concat(CSECT/EXTNAME, '_', UNITDATE,'_',UNITTIME))/CSECT/EXECUTED" />
                            <xsl:copy-of select="key('unitKey', concat(CSECT/EXTNAME, '_', UNITDATE,'_',UNITTIME))/CSECT/UNEXECUTED" />
                            </CSECT>
                        </UNIT>
                    </xsl:for-each>
                </MODULE>   
                </xsl:for-each>
                </FILE>
            </xsl:for-each>


        </COVERAGE>

    </xsl:template>

</xsl:stylesheet>

但是这个 XSL 不能产生我们想要的东西。非常感谢。

【问题讨论】:

    标签: xml transform xslt-1.0


    【解决方案1】:

    我首先想要的是,将 EXECUTED、UNEXECUTED 节点组合在一起 如果 MODULE/NAME、UNIT/UNITDATE、UNIT/UNITTIME、CSECT/EXTNAME 是 一样。

    我在您的输出中没有看到这一点 - 可能是因为它对于 SO 问题来说太复杂了。无论如何,如果这是您想要的,您应该使用一个(单个)键来连接组的四个特征。

    例如,下面的样式表:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="sect" match="CSECT" use="concat(../../NAME, '_', ../UNITDATE, '_', ../UNITTIME, '_', EXTNAME)"/>
    
    <xsl:template match="/">
        <COVERAGE>
            <!-- FOR EACH DISTINCT GROUP -->
            <xsl:for-each select="COVERAGE/FILE/MODULE/UNIT/CSECT[count(. | key('sect', concat(../../NAME, '_', ../UNITDATE, '_', ../UNITTIME, '_', EXTNAME))[1]) = 1]">
                <MODULE>
                    <NAME><xsl:value-of select="../../NAME"/></NAME>
                    <UNIT>
                        <UNITDATE><xsl:value-of select="../UNITDATE"/></UNITDATE>
                        <UNITTIME><xsl:value-of select="../UNITTIME"/></UNITTIME>
                        <CSECT>
                            <EXTNAME><xsl:value-of select="EXTNAME"/></EXTNAME>
                            <!-- GET RECORDS IN CURRENT GROUP -->
                            <xsl:for-each select="key('sect', concat(../../NAME, '_', ../UNITDATE, '_', ../UNITTIME, '_', EXTNAME))">
                                <xsl:copy-of select="EXECUTED | UNEXECUTED"/>
                            </xsl:for-each>
                        </CSECT>
                    </UNIT>
                </MODULE>
            </xsl:for-each>
        </COVERAGE>
    </xsl:template>
    
    </xsl:stylesheet>
    

    当应用于您的输入时,将产生以下输出:

    <?xml version="1.0" encoding="UTF-8"?>
    <COVERAGE>
       <MODULE>
          <NAME>Module1</NAME>
          <UNIT>
             <UNITDATE>2014-03-14</UNITDATE>
             <UNITTIME>15:15:00</UNITTIME>
             <CSECT>
                <EXTNAME>UNITA</EXTNAME>
                <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                <UNEXECUTED>60.1 70.1</UNEXECUTED>
                <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                <UNEXECUTED>60.1 70.1</UNEXECUTED>
             </CSECT>
          </UNIT>
       </MODULE>
       <MODULE>
          <NAME>Module1</NAME>
          <UNIT>
             <UNITDATE>2014-03-14</UNITDATE>
             <UNITTIME>15:15:00</UNITTIME>
             <CSECT>
                <EXTNAME>UNITC</EXTNAME>
                <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                <UNEXECUTED>40.1</UNEXECUTED>
                <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                <EXECUTED>59.1 60.1</EXECUTED>
                <UNEXECUTED>46.1 55.1</UNEXECUTED>
             </CSECT>
          </UNIT>
       </MODULE>
       <MODULE>
          <NAME>Module1</NAME>
          <UNIT>
             <UNITDATE>2014-03-14</UNITDATE>
             <UNITTIME>15:15:00</UNITTIME>
             <CSECT>
                <EXTNAME>UNITB</EXTNAME>
                <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                <EXECUTED>59.1 60.1</EXECUTED>
                <UNEXECUTED>46.1 55.1</UNEXECUTED>
                <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                <UNEXECUTED>40.1</UNEXECUTED>
             </CSECT>
          </UNIT>
       </MODULE>
       <MODULE>
          <NAME>Module2</NAME>
          <UNIT>
             <UNITDATE>2014-03-14</UNITDATE>
             <UNITTIME>15:15:00</UNITTIME>
             <CSECT>
                <EXTNAME>UNITA</EXTNAME>
                <EXECUTED>43.1 46.1 47.1 50.1 51.1 52.1 56.1 58.1 63.1 65.1</EXECUTED>
                <EXECUTED>73.1 74.1 75.1 78.1 79.1</EXECUTED>
                <UNEXECUTED>60.1 70.1</UNEXECUTED>
             </CSECT>
          </UNIT>
       </MODULE>
       <MODULE>
          <NAME>Module2</NAME>
          <UNIT>
             <UNITDATE>2014-03-14</UNITDATE>
             <UNITTIME>15:15:00</UNITTIME>
             <CSECT>
                <EXTNAME>UNITB</EXTNAME>
                <EXECUTED>32.1 36.1 37.1 38.1 44.1 50.1 56.1 57.1 58.1</EXECUTED>
                <UNEXECUTED>40.1</UNEXECUTED>
             </CSECT>
          </UNIT>
       </MODULE>
       <MODULE>
          <NAME>Module2</NAME>
          <UNIT>
             <UNITDATE>2014-03-14</UNITDATE>
             <UNITTIME>15:15:00</UNITTIME>
             <CSECT>
                <EXTNAME>UNITC</EXTNAME>
                <EXECUTED>32.1 34.1 35.1 38.1 39.1 43.1 44.1 51.1 52.1 53.1</EXECUTED>
                <EXECUTED>59.1 60.1</EXECUTED>
                <UNEXECUTED>46.1 55.1</UNEXECUTED>
             </CSECT>
          </UNIT>
       </MODULE>
    </COVERAGE>
    

    编辑:

    每个FILE节点有一个MODULE节点,一个MODULE节点有多个UNIT 节点,每个UNIT节点一个CSNEXT节点,每个CSNEXT节点有很多 已执行或未执行的节点。

    好的,但您并没有告诉我们具体如何对它们进行分组。并且很难通过查看输出文件来判断 - 除非想要花费数小时比较输入和输出。即使那样,答案也不会完全清楚,因为你的输出肯定是错误的:这个节点:

    <UNEXECUTED>60.1 70.1</UNEXECUTED>
    

    在您的输出中出现 次 - 但在输入中仅出现 次!!

    我猜你想要类似下面的内容,首先按 MODULE(即按 FILE/MODULE)分组,然后按 CSECT 分组(即按 UNIT/CSECT):

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="module" match="MODULE" use="NAME"/>
    <xsl:key name="sect" match="CSECT" use="concat(../../NAME, '_', ../UNITDATE, '_', ../UNITTIME, '_', EXTNAME)"/>
    
    <xsl:template match="/">
        <COVERAGE>
            <!-- FOR EACH DISTINCT MODULE -->
            <xsl:for-each select="COVERAGE/FILE/MODULE[count(. | key('module', NAME)[1]) = 1]">
                <FILE>
                    <MODULE>
                        <NAME><xsl:value-of select="NAME"/></NAME>
                            <!-- FOR EACH DISTINCT CSECT -->
                            <xsl:for-each select="UNIT/CSECT[count(. | key('sect', concat(../../NAME, '_', ../UNITDATE, '_', ../UNITTIME, '_', EXTNAME))[1]) = 1]">
                                <UNIT>
                                    <CSECT>
                                        <EXTNAME><xsl:value-of select="EXTNAME"/></EXTNAME>
                                        <!-- GET RECORDS IN CURRENT GROUP -->
                                        <xsl:for-each select="key('sect', concat(../../NAME, '_', ../UNITDATE, '_', ../UNITTIME, '_', EXTNAME))">
                                            <xsl:copy-of select="EXECUTED | UNEXECUTED"/>
                                        </xsl:for-each>
                                    </CSECT>
                                </UNIT>
                            </xsl:for-each>
                        </MODULE>
                    </FILE>
                </xsl:for-each>
        </COVERAGE>
    </xsl:template>
    
    </xsl:stylesheet>
    

    【讨论】:

    • 感谢您的回复。但是 MODULE 节点不再组合在一起(请参阅原始 XML 文件)。
    • @kelly66 恐怕我不知道那是什么意思。让我建议您 (1) 最小化 XML 输入文件以仅显示此问题所必需的内容; (2) 以类似的方式修复输出文件(目前完全无法理解); (3) 用很多话解释所请求的转换背后的逻辑是什么。到目前为止,您所说的只是我在回答开头引用的内容 - 这正是我的建议所做的。
    • 对不起迈克尔,英语不是我的第一语言。我修改了我想转换成的 XML。更容易理解。希望你知道我的意思。 XML结构基本上是:每个FILE节点有一个MODULE节点,一个MODULE节点有多个UNIT节点,每个UNIT节点有一个CSNEXT节点,每个CSNEXT节点有很多EXECUTED或UNEXECUTED节点。谢谢你的帮助,迈克尔!
    • @kelly66 查看我帖子的编辑。 -- 我不认为你的英语是这里的问题。
    • 非常感谢你,迈克尔,工作就像一个魅力。我在这里的第一篇文章,但学到了很多东西。再次感谢您。
    猜你喜欢
    • 2021-03-14
    • 1970-01-01
    • 2012-08-07
    • 2023-01-24
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 2015-05-15
    • 1970-01-01
    相关资源
    最近更新 更多