XSLT - Select 两个节点之间的节点(基于数组)?

XSLT - Select Nodes (Based On Arrays) Between Two Nodes?

我现在有以下XML:

<Example>
        <SiteCode null="no">1ExampleSite</SiteCode>
        <SiteName null="yes"/>
        <CustomerPIN null="no">1234567</CustomerPIN>
        <CustomerLastName null="no">Test </CustomerLastName>
        <CustomerFirstName null="no">Example</CustomerFirstName>
        <CustomerMiddleName null="yes"/>
        <CustomerDOB null="no">2000-01-01 00:00:00.000</CustomerDOB>
        <CustomerGender null="no">F</CustomerGender>
        <CustomerAddressLine1 null="no">1234 Easy ST</CustomerAddressLine1>
        <CustomerAddressLine2 null="yes"/>
        <CustomerCity null="no">Hartford</CustomerCity>
        <CustomerState null="no">CT</CustomerState>
        <CustomerZipCode null="no">123456</CustomerZipCode>
        <CustomerRaceCode null="no">White</CustomerRaceCode>
        <CustomerRaceName null="yes"/>
        <CustomerReferral1Phone>8881234567</CustomerReferral1Phone>
        <CustomerReferral2Phone>8881234567</CustomerReferral2Phone>
        <CustomerReferral3Phone>8881234567</CustomerReferral3Phone>
        <CustomerOrderNumber>48290275</CustomerOrderNumber>
        <ShopDesignationNumber>4572</ShopDesignationNumber>
        <CustomerFirstOrderDate>2020-11-13</CustomerFirstOrderDate>
        
</Example>

我还有以下 XSLT:

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

  <xsl:template match="/Example">
    <xsl:variable name="addr" select="CustomerAddressLine1 | CustomerAddressLine2 | CustomerCity | CustomerState | CustomerZipCode" />
    <xsl:variable name = "referralContact1" select="*[starts-with(local-name(), 'CustomerReferral1')]"/>
    <xsl:variable name = "referralContact2" select="*[starts-with(local-name(), 'CustomerReferral2')]"/>
    <xsl:variable name = "referralContact3" select="*[starts-with(local-name(), 'CustomerReferral3')]"/>

    <Information>
        <xsl:apply-templates select="$addr[1]/preceding-sibling::*[starts-with(name(), 'Customer')]"/>
        <HomeAddress>
            <Address>
                <xsl:apply-templates select="$addr" />
            </Address>
        </HomeAddress>
        <xsl:apply-templates select="$addr[last()]/following-sibling::*[starts-with(name(), 'Customer')]"/>

        <CustomerReferrals>
          <xsl:apply-templates select="$referralContact1"/>
          <xsl:apply-templates select="$referralContact2"/>
          <xsl:apply-templates select="$referralContact3"/>
        </CustomerReferrals>
    </Information>
</xsl:template>

<xsl:template match="*">
    <xsl:element name="{substring-after(name(), 'Customer')}">
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template> 

<xsl:template match="CustomerGender">
    <Gender>
        <Code>
            <xsl:apply-templates/>
        </Code>
  </Gender>
</xsl:template>

<xsl:template match="CustomerDOB">
    <DateOfBirth>
        <DateNode>
            <xsl:apply-templates/>
        </DateNode>
  </DateOfBirth>
</xsl:template>

<xsl:template match="*[starts-with(local-name(), 'CustomerReferral')]">
  <xsl:element name="{replace(local-name(), 'CustomerReferral[\d]', '')}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

</xsl:stylesheet>

这是我希望的输出:

<?xml version="1.0" encoding="UTF-8"?>
<Information>
<PIN>1234567</PIN>
<LastName>Test </LastName>
<FirstName>Example</FirstName>
<MiddleName/>
<DateOfBirth><DateNode>2000-01-01 00:00:00.000</DateNode></DateOfBirth>
<Gender><Code>F</Code></Gender>
<HomeAddress>
  <Address>
   <AddressLine1>1234 Easy ST</AddressLine1>
   <AddressLine2/>
   <City>Hartford</City>
   <State>CT</State>
   <ZipCode>123456</ZipCode>
  </Address>
</HomeAddress>
<RaceCode>White</RaceCode>
<RaceName/>
<CustomerReferrals>
  <Phone>8881234567</Phone>
  <Phone>8881234567</Phone>
  <Phone>8881234567</Phone>
</CustomerReferrals>
<OrderNumber>48290275</OrderNumber>
<ShopDesignationNumber>4572</ShopDesignationNumber>
<FirstOrderDate>2020-11-13</FirstOrderDate>
</Information>

这是我当前的输出:

<?xml version="1.0" encoding="UTF-8"?>
<Information>
<PIN>1234567</PIN>
<LastName>Test </LastName>
<FirstName>Example</FirstName>
<MiddleName/>
<DateOfBirth><DateNode>2000-01-01 00:00:00.000</DateNode></DateOfBirth>
<Gender><Code>F</Code></Gender>
<HomeAddress>
  <Address>
   <AddressLine1>1234 Easy ST</AddressLine1>
   <AddressLine2/>
   <City>Hartford</City>
   <State>CT</State>
   <ZipCode>123456</ZipCode>
  </Address>
</HomeAddress>
<RaceCode>White</RaceCode>
<RaceName/>
<Phone>8881234567</Phone>
<Phone>8881234567</Phone>
<Phone>8881234567</Phone>
<OrderNumber>48290275</OrderNumber>
<FirstOrderDate>2020-11-13</FirstOrderDate>
<CustomerReferrals>
  <Phone>8881234567</Phone>
  <Phone>8881234567</Phone>
  <Phone>8881234567</Phone>
</CustomerReferrals>
</Information>

有什么方法可以确保我在“ReferralPhone”转换调用之前只选择地址节点和“ReferralPhone”节点之间的节点,这样我就不会创建这些额外的phone 行数?如果可能的话,我还想保留放入新 XML 中的元素的顺序。

不确定这通常是正确的方法,但它可以满足您的需求。这个想法是找出关键元素的上下文索引,然后为它们之间的节点应用模板。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="3.0">
    
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="/Example">
        <xsl:variable name="addr" select="CustomerAddressLine1 | CustomerAddressLine2 | CustomerCity | CustomerState | CustomerZipCode" />        
        
        <Information>
            <xsl:apply-templates select="$addr[1]/preceding-sibling::*[starts-with(name(), 'Customer')]"/>
            <HomeAddress>                
                <Address>
                    <xsl:apply-templates select="$addr" />
                </Address>
            </HomeAddress>

            <xsl:variable name="lastAddrIndex" select="count(*[contains(local-name(),'ZipCode')][1]/preceding-sibling::*)+1"/>
            <xsl:variable name="firstRefIndex" select="count(*[contains(local-name(),'Phone')][1]/preceding-sibling::*)"/>
            <xsl:variable name="lastRefIndex" select="count(*[contains(local-name(),'Phone')][last()]/preceding-sibling::*)"/>
            
            <!-- process everything before phones -->
            <xsl:apply-templates select="*[(position() &lt;= $firstRefIndex) 
                and (position() &gt; $lastAddrIndex)]"/>
            
            <CustomerReferrals>
                <xsl:apply-templates select="node()[contains(local-name(),'Phone')]"></xsl:apply-templates>                
            </CustomerReferrals>
            
            <!-- process everything after phones -->            
            <xsl:apply-templates select="*[position() &gt; $lastRefIndex]"/>
            
        </Information>
    </xsl:template>
    
    <xsl:template match="*">
        <xsl:choose>
            <xsl:when test="contains(name(), 'Customer')">
                <xsl:element name="{substring-after(name(), 'Customer')}">
                    <xsl:apply-templates/>
                </xsl:element>        
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="{name()}">
                    <xsl:apply-templates/>
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
        
    </xsl:template> 
    
    <xsl:template match="CustomerGender">
        <Gender>
            <Code>
                <xsl:apply-templates/>
            </Code>
        </Gender>
    </xsl:template>
    
    <xsl:template match="CustomerDOB">
        <DateOfBirth>
            <DateNode>
                <xsl:apply-templates/>
            </DateNode>
        </DateOfBirth>
    </xsl:template>
    
    <xsl:template match="*[starts-with(local-name(), 'CustomerReferral')]">
        <xsl:element name="{replace(local-name(), 'CustomerReferral[\d]', '')}">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    
</xsl:stylesheet>