XSLT - 为什么多个节点在输出中合并(Group By)

时间:2015-04-28 06:36:54

标签: xslt xslt-2.0

清一色 我试图了解2个节点合并的根本原因。

输入XML

<?xml version='1.0' encoding='UTF-8'?>
<Employees>
    <Employee>
        <Employee_ID>E00001</Employee_ID>
        <Legal_Name Descriptor="John Doe" />         
        <lastName>Doe</lastName>
        <firstName>John</firstName>
        <P_From_Date>2015-04-01-08:00</P_From_Date>
        <P_End_Date>2015-12-31-08:00</P_End_Date>

        <Transaction>            
            <Plan Descriptor="Plan A" />            
            <effective_date>2015-03-22-08:00</effective_date>
                    <end_date>2015-10-22</end_date>
            <Annual_Cost>6000</Annual_Cost>
        </Transaction>

        <Transaction>            
            <Plan Descriptor="Plan A" />            
            <effective_date>2015-02-03-08:00</effective_date>
            <Annual_Cost>4000</Annual_Cost>
        </Transaction>

        <Transaction>            
            <Plan Descriptor="Plan A" />            
            <effective_date>2013-02-03-08:00</effective_date>
            <Annual_Cost>3000</Annual_Cost>
        </Transaction>


        <Transaction>            
            <Plan Descriptor="Plan B" />            
            <effective_date>2014-12-03-08:00</effective_date>
            <Annual_Cost>12000</Annual_Cost>
        </Transaction>  

        <Transaction>            
            <Plan Descriptor="Plan B" />            
            <effective_date>2014-10-03-08:00</effective_date>
            <Annual_Cost>1000</Annual_Cost>
        </Transaction>  

   </Employee>       

     <Employee>
        <Employee_ID>E00002</Employee_ID>
        <Legal_Name Descriptor="John Doe" />         
        <lastName>Test</lastName>
        <firstName>Jane</firstName>
        <P_From_Date>2015-01-01-08:00</P_From_Date>

        <Transaction>            
            <Plan Descriptor="Plan D" />            
            <effective_date>2015-05-22-08:00</effective_date>
            <Annual_Cost>12000</Annual_Cost>
        </Transaction>

        <Transaction>            
            <Plan Descriptor="Plan D" />            
            <effective_date>2014-03-01-08:00</effective_date>
            <Annual_Cost>9000</Annual_Cost>
        </Transaction>

        <Transaction>            
            <Plan Descriptor="Plan C" />            
            <effective_date>2014-12-03-08:00</effective_date>
            <Annual_Cost>3000</Annual_Cost>
        </Transaction>


        <Transaction>            
            <Plan Descriptor="Plan C" />            
            <effective_date>2013-01-03-08:00</effective_date>
            <Annual_Cost>3000</Annual_Cost>
        </Transaction>


     </Employee>     
</Employees>

和XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"

    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <!-- TODO customize transformation rules 
         syntax recommendation http://www.w3.org/TR/xslt 
    -->
    <xsl:variable name="newline" select="'&#xd;'"/>
    <xsl:variable name="delimiter">,</xsl:variable>
    <xsl:variable name="qualifier">
        <xsl:text>"</xsl:text>
    </xsl:variable>

    <xsl:template match="/Employees">
        <EES>
            <xsl:apply-templates/>
        </EES>
    </xsl:template>

    <xsl:template match="Employee">

        <EE>

        <EID>
        <xsl:value-of select="Employee_ID" />
        </EID>        

        <FULNAME>
        <xsl:value-of select="Legal_Name/@Descriptor"/>
        </FULNAME>
        <LNAME>
        <xsl:value-of select="lastName"/>
        </LNAME>        
        <FNAME>
        <xsl:value-of select="firstName"/>
        </FNAME>


        <xsl:variable name="bework">
            <xsl:for-each-group select="Transaction" group-by="Plan/@Descriptor" >
                <berow>
                    <CurrentGroup>
                        <xsl:value-of select="current-grouping-key()"/>
                    </CurrentGroup>
                    <parm_from_date><xsl:value-of select="../P_From_Date" /></parm_from_date>
                    <xsl:for-each select="current-group()">
                        <begin-date><xsl:copy-of select="effective_date" /></begin-date>

                        <include-flag >
                            <xsl:choose>
                                <xsl:when test="xs:date( substring(effective_date,1,10)) &gt;=  xs:date(substring(../P_From_Date,1,10))">
                                    <xsl:text>Y</xsl:text>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:text>N</xsl:text>
                                </xsl:otherwise>
                            </xsl:choose>

                        </include-flag>
                        <adj-begin-date>
                            <xsl:choose>
                                <xsl:when test="xs:date(effective_date) &gt;= xs:date(../P_From_Date_1) ">
                                    <xsl:value-of select="effective_date"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select="../P_From_Date_1"/>
                                </xsl:otherwise>
                             </xsl:choose>
                        </adj-begin-date>
                        <end_date>
                            <xsl:value-of select="current-group()/end_date"/>
                        </end_date>
                        <Cost>
                            <xsl:value-of select="Annual_Cost"/>
                        </Cost>
                    </xsl:for-each>
                </berow>
            </xsl:for-each-group>
        </xsl:variable>

        <xsl:for-each select="$bework/berow">


            <CGR>
            <xsl:value-of select="CurrentGroup" />
            </CGR>



            <PFRMDT>            
            <xsl:value-of select="parm_from_date" />
            </PFRMDT>


            <BDT>
            <xsl:value-of select="begin-date" />
            </BDT>


            <FLAG>
            <xsl:value-of select="include-flag" />
            </FLAG>


            <ADJBGNDT>
            <xsl:value-of select="$qualifier"/>
            </ADJBGNDT>


            <EDATE>            
            <xsl:value-of select="$qualifier"/>
            </EDATE>

            <CO>
            <xsl:value-of select="Cost" />                        
            </CO>


        </xsl:for-each>

        </EE>
    </xsl:template>
</xsl:stylesheet>

结果

<?xml version="1.0" encoding="UTF-8"?>
<EES xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <EE>
        <EID>E00001</EID>
        <FULNAME>John Doe</FULNAME>
        <LNAME>Doe</LNAME>
        <FNAME>John</FNAME>
        <CGR>Plan A</CGR>
        <PFRMDT>2015-04-01-08:00</PFRMDT>
        <BDT>2015-03-22-08:00 2015-02-03-08:00 2013-02-03-08:00</BDT>
        <FLAG>N N N</FLAG>
        <ADJBGNDT>"</ADJBGNDT>
        <EDATE>"</EDATE>
        <CO>6000 4000 3000</CO>
        <CGR>Plan B</CGR>
        <PFRMDT>2015-04-01-08:00</PFRMDT>
        <BDT>2014-12-03-08:00 2014-10-03-08:00</BDT>
        <FLAG>N N</FLAG>
        <ADJBGNDT>"</ADJBGNDT>
        <EDATE>"</EDATE>
        <CO>12000 1000</CO>
    </EE>        

    <EE>
        <EID>E00002</EID>
        <FULNAME>John Doe</FULNAME>
        <LNAME>Test</LNAME>
        <FNAME>Jane</FNAME>
        <CGR>Plan D</CGR>
        <PFRMDT>2015-01-01-08:00</PFRMDT>
        <BDT>2015-05-22-08:00 2014-03-01-08:00</BDT>
        <FLAG>Y N</FLAG>
        <ADJBGNDT>"</ADJBGNDT>
        <EDATE>"</EDATE>
        <CO>12000 9000</CO>
        <CGR>Plan C</CGR>
        <PFRMDT>2015-01-01-08:00</PFRMDT>
        <BDT>2014-12-03-08:00 2013-01-03-08:00</BDT>
        <FLAG>N N</FLAG>
        <ADJBGNDT>"</ADJBGNDT>
        <EDATE>"</EDATE>
        <CO>3000 3000</CO>
    </EE>    
</EES>

如果你看看John Doe的BDT节点,那么有2个日期。尽管循环通过当前组,但是样本计划的2个节点将被添加到同一节点。

是什么造成这种情况?那么应该采取什么措施来解决这个问题呢?我将不得不使用变量,因为我将不得不做更多的操作。但那是另一天。

感谢您提供一些见解。

2 个答案:

答案 0 :(得分:2)

也许你只需要改变:

<BDT>
<xsl:value-of select="begin-date" />
</BDT>

为:

<BDT>
<xsl:copy-of select="begin-date" />
</BDT>

在XSLT 2.0中,xsl:value-of将生成单个文本节点,连接所有匹配节点的值,用空格(或另一个分隔符,如果指定)分隔。

补充说明:

  

是什么引起了这个?

您的节点合并到单个文本节点的原因是当您尝试从$ bework变量中获取它们时使用xsl:value-of以便将它们写入输出树。

考虑以下简化示例:

<强> XML

<root>
    <item>
        <category>A</category>
        <amount>1000</amount>
    </item>
    <item>
        <category>A</category>
        <amount>500</amount>
    </item>
    <item>
        <category>A</category>
        <amount>250</amount>
    </item>
    <item>
        <category>B</category>
        <amount>600</amount>
    </item>
    <item>
        <category>B</category>
        <amount>300</amount>
    </item>
</root>

XSLT 2.0

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

<xsl:template match="/root">
    <xsl:variable name="groups">
        <xsl:for-each-group select="item" group-by="category">
            <group category="{current-grouping-key()}">
                <xsl:for-each select="current-group()">
                    <amount>
                        <xsl:value-of select="amount"/>
                    </amount>
                </xsl:for-each>
            </group>
        </xsl:for-each-group>
    </xsl:variable>
    <output>    
        <xsl:for-each select="$groups/group">
            <group category="{@category}">
                <copy-of-amount>    
                    <xsl:copy-of select="amount"/>
                </copy-of-amount>       
                <for-each-amount>   
                    <xsl:for-each select="amount">
                        <new-node value="{.}"/>
                    </xsl:for-each>
                </for-each-amount>      
                <sum-of-amount> 
                    <xsl:value-of select="sum(amount)"/>
                </sum-of-amount>        
                <value-of-amount>   
                    <xsl:value-of select="amount"/>
                </value-of-amount>      
            </group>
        </xsl:for-each>
    </output>       
</xsl:template>

</xsl:stylesheet>

<强>结果

<?xml version="1.0" encoding="UTF-8"?>
<output>
   <group category="A">
      <copy-of-amount>
         <amount>1000</amount>
         <amount>500</amount>
         <amount>250</amount>
      </copy-of-amount>
      <for-each-amount>
         <new-node value="1000"/>
         <new-node value="500"/>
         <new-node value="250"/>
      </for-each-amount>
      <sum-of-amount>1750</sum-of-amount>
      <value-of-amount>1000 500 250</value-of-amount>
   </group>
   <group category="B">
      <copy-of-amount>
         <amount>600</amount>
         <amount>300</amount>
      </copy-of-amount>
      <for-each-amount>
         <new-node value="600"/>
         <new-node value="300"/>
      </for-each-amount>
      <sum-of-amount>900</sum-of-amount>
      <value-of-amount>600 300</value-of-amount>
   </group>
</output>

如您所见,在$myVar变量中,每个group包含2-3个不同的amount个节点。您可以复制它们,总结它们或为它们中的每一个创建一些东西。但是,当你这样做时:

<xsl:value-of select="amount"/>

您正在解决当前群组中所有金额节点的问题,您将得到一个包含所有节点的结果,相同如下:

<xsl:value-of select="sum(amount)"/>

根据要寻址的所有节点数量返回结果。

  

应采取什么措施来解决这个问题?

在您告诉我们您想要获得的实际结果之前,我们不会知道这一点。在下面的评论中你说:

  

最终结果实际上是每位员工的总成本。

如果是这样,上面的例子显示了如何获得它。

答案 1 :(得分:2)

bework变量的内部,您为每个berow群组创建了Transaction元素,然后使用<xsl:for-each select="current-group()">为每个begin-date输出Transaction 1}}在该组中,没有进一步构造或包装它们。使用您的输入意味着berow元素可以包含两个或三个begin-date元素。

然后你有<xsl:for-each select="$bework/berow">和里面

        <BDT>
        <xsl:value-of select="begin-date" />
        </BDT>

将选择并输出两个或三个begin-date元素的字符串值。

我不确定您要为BDT输出哪个值,您可以使用例如<xsl:value-of select="begin-date[1]"/><xsl:value-of select="begin-date[last()]"/>仅输出先前创建的第一个或最后一个元素的字符串值。