如何在 XSLT 中使用 for 循环并根据迭代获取节点值

How to use for loop in XSLT and get node values based on the iteration

我们如何在 XSLT 中使用 for 循环?

我有这个要求,我想将下面显示的 xml 转换成逗号分隔的文件。 CSV 文件中的行数等于员工报告条目的 COBRA_Records_within_Range 个节点数。除 COBRA_Records_within_Range 节点的子元素值外,3 行中的所有值都相同。我能够创建 3 行,但无法检索 COBRA_Records_within_Range 的子元素的值。我想 运行 对特定节点的计数进行 for 循环,然后根据迭代检索其子元素。在下面的示例中,有 3 个 COBRA_Records_within_Range nodes。所以循环应该 运行 for count(COBRA_Records_within_Range)`然后在每次迭代中我需要来自其子节点的值。例如,如果是第 2 次迭代,则 Eligibility_Reason 在 CSV 输出中应显示为 'Dependent Children - Loss of dependent child status under the plan rules'。

有人可以帮我解决这个问题吗?

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <wd:Report_Entry xmlns:wd="urn:com.workday/bsvc">
        <wd:Employee_ID>111111</wd:Employee_ID>
        <wd:Worker>John Smith</wd:Worker>
        <wd:Employee_Last_Name>Smith</wd:Employee_Last_Name>
        <wd:Employee_First_Name>John</wd:Employee_First_Name>
        <wd:COBRA_Records_within_Range>
            <wd:Qualifying_Event_Date>2015-10-04</wd:Qualifying_Event_Date>
            <wd:Eligibility_Reason>Dependent Children - Loss of dependent child status under the
                plan rules</wd:Eligibility_Reason>
            <wd:Benefit_Plan1>Dental-US - Delta Dental PPO BREG</wd:Benefit_Plan1>
        </wd:COBRA_Records_within_Range>
        <wd:COBRA_Records_within_Range>
            <wd:Qualifying_Event_Date>2015-10-05</wd:Qualifying_Event_Date>
            <wd:Eligibility_Reason>Dependent Children - Loss of dependent child status under the
                plan rules</wd:Eligibility_Reason>
            <wd:Benefit_Plan1>Healthcare FSA - Tri-Ad FSA Residential US</wd:Benefit_Plan1>
        </wd:COBRA_Records_within_Range>
        <wd:COBRA_Records_within_Range>
            <wd:Qualifying_Event_Date>2015-10-05</wd:Qualifying_Event_Date>
            <wd:Eligibility_Reason>Spouse - Divorce or legal separation of the covered
                employee</wd:Eligibility_Reason>
            <wd:Test>0</wd:Test>
            <wd:Benefit_Plan1>Medical/Vision-US - Empire Blue Cross &amp; Blue Shield
                EPO</wd:Benefit_Plan1>
        </wd:COBRA_Records_within_Range>
    </wd:Report_Entry>
</root>

这是预期的输出 -

111111, John Smith, Smith, John, 2015-10-04, Dependent Children - Loss of dependent child status under the plan rules, Dental-US - Delta Dental PPO BREG
111111, John Smith, Smith, John, 2015-10-05, Dependent Children - Loss of dependent child status under the plan rules, Healthcare FSA - Tri-Ad FSA Residential US
111111, John Smith, Smith, John, 2015-10-05, Spouse - Divorce or legal separation of the covered employee, Medical/Vision-US - Empire Blue Cross &amp; Blue Shield EPO

这是我创建的 XSLT -

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:op="http://www.w3.org/2005/xpath-functions"
    xmlns:wd="urn:com.workday/bsvc"
    exclude-result-prefixes="xsd op wd"    
    version="2.0">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">      
        <File>            
            <Header separator=",">
                <xsl:call-template name="printHeader"/>
            </Header>            

            <xsl:for-each select="root/wd:Report_Entry">
                <xsl:variable name="cobrarecordcount" as="xsd:integer" select="count(wd:COBRA_Records_within_Range)"/>
                <xsl:variable name="record" select="."/>

                <xsl:for-each select="1 to $cobrarecordcount"> 
                    <xsl:call-template name="printRecord">
                        <xsl:with-param name="record" select="$record"></xsl:with-param>
                    </xsl:call-template>
                </xsl:for-each>                                                         
            </xsl:for-each>             
        </File>         

    </xsl:template>

    <xsl:template name="printHeader">   
        <xsd:element><xsl:text>Employee ID</xsl:text></xsd:element>
        <xsd:element><xsl:text>Employee Last Name</xsl:text></xsd:element>
        <xsd:element><xsl:text>Employee First Name </xsl:text></xsd:element>        
        <xsd:element><xsl:text>Qualifying Event Date</xsl:text></xsd:element>
        <xsd:element><xsl:text>Qualifying Event Type</xsl:text></xsd:element>        
        <xsd:element><xsl:text>Benefit Plan 1</xsl:text></xsd:element>
    </xsl:template> 

    <xsl:template name="printRecord">
        <xsl:param name="record"/>

                <Line separator=","  quoteStyle="double" quoteWhenMatches=".*[a-zA-Z].*">
                    <!--Employee Id-->
                    <xsd:element>
                        <xsl:value-of select="$record/wd:Employee_ID"/>
                    </xsd:element>     
                    <!--Employee Last Name-->
                    <xsd:element>
                        <xsl:value-of select="$record/wd:Employee_Last_Name"/>
                    </xsd:element>      
                    <!--Employee First Name-->
                    <xsd:element>
                        <xsl:value-of select="$record/wd:Employee_First_Name"/>
                    </xsd:element>  
                    <!--Qualifying Event Date-->
                    <xsd:element>
                        <xsl:value-of select="($record/wd:COBRA_Records_within_Range[position()]/wd:Qualifying_Event_Date)"/>
                    </xsd:element> 
                    <!--Qualifying Event Type-->
                    <xsd:element>
                        <xsl:value-of select="($record/wd:COBRA_Records_within_Range[position()]/wd:Eligibility_Reason)"/>
                    </xsd:element> 
                    <!--Benefit-->
                    <xsd:element>
                        <xsl:value-of select="$record/wd:COBRA_Records_within_Range[position()]/wd:Benefit_Plan1"/>
                    </xsd:element> 
                </Line>
    </xsl:template>    
</xsl:stylesheet>

我在您尝试的 XSLT 中找不到任何韵律或理由。可以通过以下方式非常简单地实现预期结果:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wd="urn:com.workday/bsvc">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>

<xsl:template match="wd:Report_Entry">
    <xsl:variable name="common">
        <xsl:value-of select="wd:Employee_ID" />
        <xsl:text>, </xsl:text>
        <xsl:value-of select="wd:Worker" />
        <xsl:text>, </xsl:text>
        <xsl:value-of select="wd:Employee_Last_Name" />
        <xsl:text>, </xsl:text>
        <xsl:value-of select="wd:Employee_First_Name" />
        <xsl:text>, </xsl:text>
    </xsl:variable> 
    <xsl:for-each select="wd:COBRA_Records_within_Range">
        <xsl:copy-of select="$common"/>
        <xsl:value-of select="wd:Qualifying_Event_Date" />
        <xsl:text>, </xsl:text>
        <xsl:value-of select="wd:Eligibility_Reason" />
        <xsl:text>, </xsl:text>
        <xsl:value-of select="wd:Benefit_Plan1" />
        <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

wd:COBRA_Records_within_Range 元素的谓词中,您指定了 position(),但这并没有为您提供 xsl:for-each 中序列的当前值。 position() 是上下文节点的位置。

您只想筛选 position() 等于您 xsl:for-each 中的当前数字的人。

为此,请向您的模板添加另一个参数,并在您调用它时传递当前值:

在下面的示例中,我创建了一个名为 rangeIndex 的参数并使用了 shorthand 谓词 [$rangeIndex],但也可以使用 [position() = $rangeIndex]:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:op="http://www.w3.org/2005/xpath-functions"
    xmlns:wd="urn:com.workday/bsvc"
    exclude-result-prefixes="xsd op wd"    
    version="2.0">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">      
        <File>            
            <Header separator=",">
                <xsl:call-template name="printHeader"/>
            </Header>            

            <xsl:for-each select="root/wd:Report_Entry">
                <xsl:variable name="cobrarecordcount" as="xsd:integer" select="count(wd:COBRA_Records_within_Range)"/>
                <xsl:variable name="record" select="."/>

                <xsl:for-each select="1 to $cobrarecordcount"> 
                    <xsl:call-template name="printRecord">
                        <xsl:with-param name="record" select="$record"></xsl:with-param>
                        <xsl:with-param name="rangeIndex" select="."/>
                    </xsl:call-template>
                </xsl:for-each>                                                         
            </xsl:for-each>             
        </File>         

    </xsl:template>

    <xsl:template name="printHeader">   
        <xsd:element><xsl:text>Employee ID</xsl:text></xsd:element>
        <xsd:element><xsl:text>Employee Last Name</xsl:text></xsd:element>
        <xsd:element><xsl:text>Employee First Name </xsl:text></xsd:element>        
        <xsd:element><xsl:text>Qualifying Event Date</xsl:text></xsd:element>
        <xsd:element><xsl:text>Qualifying Event Type</xsl:text></xsd:element>        
        <xsd:element><xsl:text>Benefit Plan 1</xsl:text></xsd:element>
    </xsl:template> 

    <xsl:template name="printRecord">
        <xsl:param name="record"/>
        <xsl:param name="rangeIndex"/>
        <Line separator=","  quoteStyle="double" quoteWhenMatches=".*[a-zA-Z].*">
            <!--Employee Id-->
            <xsd:element>
                <xsl:value-of select="$record/wd:Employee_ID"/>
            </xsd:element>     
            <!--Employee Last Name-->
            <xsd:element>
                <xsl:value-of select="$record/wd:Employee_Last_Name"/>
            </xsd:element>      
            <!--Employee First Name-->
            <xsd:element>
                <xsl:value-of select="$record/wd:Employee_First_Name"/>
            </xsd:element>  
            <!--Qualifying Event Date-->
            <xsd:element>
                <xsl:value-of select="($record/wd:COBRA_Records_within_Range[$rangeIndex]/wd:Qualifying_Event_Date)"/>
            </xsd:element> 
            <!--Qualifying Event Type-->
            <xsd:element>
                <xsl:value-of select="($record/wd:COBRA_Records_within_Range[$rangeIndex]/wd:Eligibility_Reason)"/>
            </xsd:element> 
            <!--Benefit-->
            <xsd:element>
                <xsl:value-of select="$record/wd:COBRA_Records_within_Range[$rangeIndex]/wd:Benefit_Plan1"/>
            </xsd:element> 
        </Line>
    </xsl:template>    
</xsl:stylesheet>

这是一个更简单、更简短的解决方案,根本不使用<xsl:for-each>

我。 XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:wd="urn:com.workday/bsvc">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vHeader" 
      select="string-join(/*/*/*[not(self::wd:COBRA_Records_within_Range)], ', ')"/>

  <xsl:template match="wd:COBRA_Records_within_Range">
    <xsl:value-of select="string-join(($vHeader, *), ', '), '&#xA;'"/>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>

二. XSLT 1.0

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:wd="urn:com.workday/bsvc">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vHeader">
   <xsl:apply-templates 
        select="/*/*/*[not(self::wd:COBRA_Records_within_Range)]" mode="header"/>
 </xsl:variable>

  <xsl:template match="wd:COBRA_Records_within_Range">
    <xsl:value-of select="$vHeader"/><xsl:text>, </xsl:text>
    <xsl:apply-templates mode="header"/>
    <xsl:value-of select="'&#xA;'"/>
  </xsl:template>

  <xsl:template match="*[text()]" mode="header">
    <xsl:if test="not(position() = 1)">, </xsl:if>
    <xsl:value-of select="."/>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>