遍历多个 XPath 结果 - 仅创建一个节点

Iterate Through Multiple XPath Results - Create ONLY ONE NODE

我有下面的例子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> 
        <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>
        <CustomerGender null="no">F</CustomerGender>
        <CustomerRaceCode null="no">White</CustomerRaceCode>
        <CustomerRaceName null="yes"/>
</Example>

我想做的是输出 XML 以便在 HomeAddress/Address 下共享地址节点,如下所示:

<?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>
<HomeAddress>
  <Address>
   <AddressLine1>1234 Easy ST</AddressLine1>
   <AddressLine2/>
   <City>Hartford</City>
   <State>CT</State>
   <ZipCode>123456</ZipCode>
  </Address>
</HomeAddress>
<Gender><Code>F</Code></Gender>
<RaceCode>White</RaceCode>
<RaceName/>
</Information>

这是我当前的 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">
    <Information>
        <xsl:for-each select="*">
            <xsl:apply-templates select=".[starts-with(local-name(), 'CustomerGender')]"/>
            <xsl:apply-templates select=".[starts-with(local-name(), 'CustomerDOB')]"/>
            <xsl:apply-templates select=".[starts-with(local-name(), 'CustomerAddress')
                      or starts-with(local-name(), 'CustomerCity')
                      or starts-with(local-name(), 'CustomerState')
                      or starts-with(local-name(), 'CustomerZipCode')][1]"/>
            <xsl:apply-templates select=".[starts-with(local-name(), 'Customer')
             and not(contains(local-name(), 'Gender'))
             and not(contains(local-name(), 'Address'))
             and not(contains(local-name(), 'City'))
             and not(contains(local-name(), 'State'))
             and not(contains(local-name(), 'ZipCode'))
             and not(contains(local-name(), 'DOB'))]"/>
            
                        
                    </xsl:for-each>
      </Information>
  </xsl:template>

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

<xsl:template match=".[starts-with(local-name(), 'CustomerGender')]">
  <xsl:element name="{replace(local-name(), 'Customer', '')}">
    <Code>
    <xsl:apply-templates/>
    </Code>
  </xsl:element>
</xsl:template>

<xsl:template match=".[starts-with(local-name(), 'CustomerDOB')]">
  <DateOfBirth>
    <DateNode>
    <xsl:apply-templates/>
    </DateNode>
  </DateOfBirth>
</xsl:template>

<xsl:template match=".[starts-with(local-name(), 'CustomerAddress')
                      or starts-with(local-name(), 'CustomerCity')
                      or starts-with(local-name(), 'CustomerState')
                      or starts-with(local-name(), 'CustomerZipCode')]">
  <HomeAddress>
    <Address>
    <xsl:for-each select=".">
    <xsl:element name="{replace(local-name(), 'Customer', '')}">
      <xsl:apply-templates/>
    </xsl:element>
    </xsl:for-each>
    </Address>
  </HomeAddress>
</xsl:template>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </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>
<RaceCode>White</RaceCode>
<RaceName/>
<HomeAddress><Address><AddressLine1>1234 Easy ST</AddressLine1></Address></HomeAddress><HomeAddress><Address><AddressLine2/></Address></HomeAddress>
<HomeAddress><Address><City>Hartford</City></Address></HomeAddress>
<HomeAddress><Address><State>CT</State></Address></HomeAddress>
<HomeAddress><Address><ZipCode>123456</ZipCode></Address></HomeAddress>
</Information>

如何更改此 XSL 逻辑以便获得所需的输出(共享的“地址”节点)而不是当前的(多个“地址”节点)?

对于这方面的任何帮助,我将不胜感激,我可能忽略了前面的示例,但我尝试搜索但找不到任何特定于此场景的内容。

编辑:我知道可以在我拥有的“for-each”之外提取“地址”信息,如下所示:

<xsl:for-each select="*">
            <xsl:apply-templates select=".[starts-with(local-name(), 'CustomerGender')]"/>
            <xsl:apply-templates select=".[starts-with(local-name(), 'CustomerDOB')]"/>
            
            <xsl:apply-templates select=".[starts-with(local-name(), 'Customer')
             and not(contains(local-name(), 'Gender'))
             and not(contains(local-name(), 'Address'))
             and not(contains(local-name(), 'City'))
             and not(contains(local-name(), 'State'))
             and not(contains(local-name(), 'ZipCode'))
             and not(contains(local-name(), 'DOB'))]"/>
            
                        
                    </xsl:for-each>

        <HomeAddress>
            <Address>
        <xsl:apply-templates select="*[starts-with(local-name(), 'CustomerAddress')
                      or starts-with(local-name(), 'CustomerCity')
                      or starts-with(local-name(), 'CustomerState')
                      or starts-with(local-name(), 'CustomerZipCode')]"/>
            </Address>
        </HomeAddress>

但是,我还想保留数据的顺序。如果客户地址信息位于数据中间,我希望它在输出中显示为这样。

有什么理由不能简单的做:

<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="/Example">
    <xsl:variable name="addr" select="CustomerAddressLine1 | CustomerAddressLine2 | CustomerCity | CustomerState | CustomerZipCode" />
    <Information>
        <xsl:copy-of select="* except $addr"/>
        <HomeAddress>
            <Address>
                <xsl:copy-of select="$addr"/>
            </Address>
        </HomeAddress>
    </Information>
</xsl:template>

</xsl:stylesheet>

已添加:

假设地址字段总是在一个连续的块中,预期的输出可以使用:

<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:strip-space elements="*"/>

<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:apply-templates select="$addr[last()]/following-sibling::*[starts-with(name(), 'Customer')]"/>
    </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:stylesheet>