DaveP
DaveP

Reputation: 109

Adding data from one node to another in an XML doc using XSLT

I have an XML doc with two sets of data in it. I need to output a HTML table that combines the data.

The XML looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<fdata>
    <RepairHistory>
        <RepairList>
            <RepairJob>
                <RepairRef>56740408</RepairRef>
                <RepairDescription>Baths</RepairDescription>
                <RepairReportedDate>20180515</RepairReportedDate>
                <RepairCompletedDate></RepairCompletedDate>
                <RepairStartDate>20180515</RepairStartDate>
            </RepairJob>
            <RepairJob>
                <RepairRef>56735043</RepairRef>
                <RepairDescription>Basins</RepairDescription>
                <RepairReportedDate>20180515</RepairReportedDate>
                <RepairCompletedDate></RepairCompletedDate>
                <RepairStartDate>20180515</RepairStartDate>
            </RepairJob>
            <RepairJob>
                <RepairRef>56740415</RepairRef>
                <RepairDescription>Showers</RepairDescription>
                <RepairReportedDate>20180515</RepairReportedDate>
                <RepairCompletedDate></RepairCompletedDate>
                <RepairStartDate>20180515</RepairStartDate>
            </RepairJob>
        </RepairList>
    </RepairHistory>
    <map>
        <entry>
            <string>GUID</string>
            <string>be0f53f5-7a09-47cd-9928-0c865b6450a5</string>
        </entry>
        <entry>
            <string>JOBNUMBER</string>
            <string>56740408</string>
        </entry>
    </map>
    <map>
        <entry>
            <string>GUID</string>
            <string>5ce2e8fe-7735-4f98-b3a9-3bd386edb338</string>
        </entry>
        <entry>
            <string>JOBNUMBER</string>
            <string>56740415</string>
        </entry>
    </map>
</fdata>

JOBNUMBER has to match RepairRef. Not all of the data at the bottom matches the data at the top. I need to output the data in a table as follows:

Date       Ref        Details    GUID
20180515   56740408   Baths      be0f53f5-7a09-47cd-9928-0c865b6450a5
20180515   56735043   Basins     No GUID
20180515   56740415   Showers    5ce2e8fe-7735-4f98-b3a9-3bd386edb338

I have tried to output the table using the following xsl, but it doesn't work...

<xsl:template match="/">
    <h3>Repair history</h3>
    <table>
        <tr>
            <th>Date</th>
            <th>Ref</th>
            <th>Details</th>
            <th>GUID</th>
        </tr>               
        <xsl:apply-templates select ="//RepairJob" />
    </table>
</xsl:template>
<xsl:template match="RepairJob">
    <xsl:variable name="JobNumber" select="RepairRef" />
     <tr>
         <td><xsl:value-of select="RepairStartDate"/></td>
         <td><xsl:value-of select="RepairRef"/></td>
         <td><xsl:value-of select="RepairDescription"/></td>
         <td>
            <xsl:choose>
                <xsl:when test="count(//map//entry[2]//string[2] = $JobNumber) &gt; 0">
                     <xsl:value-of select="//map//entry[2]//string[2] = $JobNumber"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:text>No GUID</xsl:text>
                </xsl:otherwise>
            </xsl:choose>                
        </td>
     </tr>
</xsl:template>

I can see that I am not selecting the correct node to display, but the matching is not working either. Can anyone help?

Upvotes: 0

Views: 28

Answers (1)

Tim C
Tim C

Reputation: 70618

Your current xsl:value-of evaluates to a boolean ("true" or "false"). The expression you actually want is this...

<xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>

The xsl:when needs a tweak too, so the whole xsl:choose should look like this:

<xsl:choose>
    <xsl:when test="//map[entry[2]/string[2] = $JobNumber]">
         <xsl:value-of select="//map[entry[2]/string[2] = $JobNumber]/entry[1]/string[2]"/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:text>No GUID</xsl:text>
    </xsl:otherwise>
</xsl:choose>    

However, consider using a key to look up the guids....

<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />

Then your xsl:value-of becomes this

<xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>

Note, this also allows for the entry elements to be in any order within a map element (i.e. the JOBNUMBER could be the first entry, and the GUID the second)

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

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

<xsl:key name="map" match="map" use="entry[string[1] = 'JOBNUMBER']/string[2]" />

<xsl:template match="/">
    <h3>Repair history</h3>
    <table>
        <tr>
            <th>Date</th>
            <th>Ref</th>
            <th>Details</th>
            <th>GUID</th>
        </tr>               
        <xsl:apply-templates select ="//RepairJob" />
    </table>
</xsl:template>

<xsl:template match="RepairJob">
    <xsl:variable name="JobNumber" select="RepairRef" />
     <tr>
         <td><xsl:value-of select="RepairStartDate"/></td>
         <td><xsl:value-of select="RepairRef"/></td>
         <td><xsl:value-of select="RepairDescription"/></td>
         <td>
            <xsl:choose>
                <xsl:when test="key('map',$JobNumber)">
                     <xsl:value-of select="key('map',$JobNumber)/entry[string[1] = 'GUID']/string[2]"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:text>No GUID</xsl:text>
                </xsl:otherwise>
            </xsl:choose>                
        </td>
     </tr>
</xsl:template>
</xsl:stylesheet>

Upvotes: 1

Related Questions