Search code examples
xmlxpathxsltxslt-2.0xslt-grouping

XSLT help grouping or matching nodes


I have this XML, that is a series of ContactInfo nodes, some contain the PostAddr, and some contain the PhoneNum. I have to match the ContactInfo node that contains the address with the one that contains the PhoneNum like this - the AddrSource tag value (in the ContactInfo nodes that contain address) should match the PhoneNum Source attribute value(from the ContactInfo nodes that contain phones).

This is a sample of the XML:

<ContactInfo Source="MB" EffDt="2022-10-11">
        <PostAddr>
            <StreetNum>xxx</StreetNum>
            <StreetName>ATLANTA</StreetName>
            <StreetType>HWY</StreetType>
            <Apt>099</Apt>
            <City>MONTGOMERY</City>
            <StateProv>AL</StateProv>
            <PostalCode>1000</PostalCode>
            <County>MONTGOMERY</County>
            <AddrType>S</AddrType>
            <ReportedDt>2019-08-09</ReportedDt>
            <AddrSource>3</AddrSource>
            <AddrCreatedDt>2019-07-22</AddrCreatedDt>
        </PostAddr>
    </ContactInfo>
    <ContactInfo Source="MB" EffDt="2022-10-11">
        <PostAddr>
            <StreetNum>09521</StreetNum>
            <StreetName>STANTON</StreetName>
            <StreetType>WAY</StreetType>
            <Apt>APT 19080</Apt>
            <City>PRATTVILLE</City>
            <StateProv>AL</StateProv>
            <PostalCode>111-111</PostalCode>
            <County>AUTAUGA</County>
            <AddrType>H</AddrType>
            <ReportedDt>2019-04-09</ReportedDt>
            <AddrSource>4</AddrSource>
            <AddrCreatedDt>2019-04-03</AddrCreatedDt>
        </PostAddr>
    </ContactInfo>
    <ContactInfo Source="MB" EffDt="2022-10-11">
        <PhoneNum Source="3" EffDt="2022-10-11">
            <PhoneType>OTHER   </PhoneType>
            <Phone>123224434</Phone>
        </PhoneNum>
    </ContactInfo>
    <ContactInfo Source="MB" EffDt="2022-10-11">
        <PhoneNum Source="4" EffDt="2022-10-11">
            <PhoneType>OTHER   /WIRELESS</PhoneType>
            <Phone>30000</Phone>
        </PhoneNum>
    </ContactInfo>

Ultimately I need to be able to make a table in this format:

<table>
    <tr>
        <td>Address</td>
        <td>Phone</td>
    </tr>
</table>

What should be my approach here? I have been mainly working with for-each loops and choose but I can't seem to find a way to make them work here. I looked a bit into grouping but I wasn't able to apply that either, so I'm a bit stuck with this. Any help would be greatly appreciated.


Solution

  • XSLT has a built-in key mechanism for resolving cross-references. Here's a minimal example:

    XSLT 1.0

    <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:key name="phone" match="PhoneNum" use="@Source" />
    
    <xsl:template match="/*">
        <table border="1">
            <xsl:for-each select="ContactInfo/PostAddr">
                <tr>
                    <td>
                        <xsl:value-of select="City"/>
                    </td>
                    <td>
                        <xsl:value-of select="key('phone', AddrSource)/Phone"/>
                    </td>
                </tr>
            </xsl:for-each>
        </table>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Applied to your input example - after adding a root element! - this will return:

    Result

    <?xml version="1.0" encoding="UTF-8"?>
    <table border="1">
       <tr>
          <td>MONTGOMERY</td>
          <td>123224434</td>
       </tr>
       <tr>
          <td>PRATTVILLE</td>
          <td>30000</td>
       </tr>
    </table>