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

XSLT - Why are multiple nodes getting merged in the output ( Group By)

所有- 我正在尝试了解合并 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 个节点,尽管在当前组中循环,但仍被添加到同一节点。

这是什么原因造成的?应该如何补救?我将不得不使用变量,因为我必须做更多的操作。但那是另一天。

感谢您提供一些见解。

也许你只需要改变:

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

至:

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

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


补充说明:

WHat is causing this?

您的节点合并为单个文本节点的原因是,当您尝试从 $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)"/>

returns 基于所有被寻址的数量节点的结果。

And what should be done to remedy this?

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

the final result is really the total cost per employee.

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

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

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

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

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

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