简体   繁体   中英

Complete xslt newbie. How do I merge these 2 xml sources in xslt version 1.0. applying grouping and sorting?

The Transformation source

Data1.xml

 <DEALERSHIP reg_number="01234567">
 <MANUFACTURER name="VAUXHALL">
     <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20060331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
    <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20040331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
     <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>2.5</CAR_ENG>
        <CAR_MODEL>CALIBRA</CAR_MODEL>
        <CAR_REG_DATE>20030331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>BLUE</COLOUR>
    </CAR>
</MANUFACTURER>
<MANUFACTURER name="FORD">
     <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>2.5</CAR_ENG>
        <CAR_MODEL>CMAX</CAR_MODEL>
        <CAR_REG_DATE>20050331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
    </MANUFACTURER>

The external data Data2.xml

<DEALERSHIP reg_number="01234567">
<MANUFACTURER name="VAUXHALL">
    <CAR>
        <CAR_VIN>XXX12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20060331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>SS00 RRY</CAR_REG>
        <COLOUR>BLUE</COLOUR>
    </CAR>
    <CAR>
        <CAR_VIN>XXX12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20060331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>SS00 RRY</CAR_REG>
        <COLOUR>BLUE</COLOUR>
    </CAR>
</MANUFACTURER>
<MANUFACTURER name="TOYOTA">
    <CAR>
        <CAR_VIN>XXX12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>Corolla</CAR_MODEL>
        <CAR_REG_DATE>20030812</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>SS00 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
</MANUFACTURER>

required output

<DEALERSHIP reg_number="01234567">
<MANUFACTURER name="VAUXHALL">
    <CAR>
        <CAR_VIN>XXX12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20060331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>SS00 RRY</CAR_REG>
        <COLOUR>BLUE</COLOUR>
    </CAR>
    <CAR>
        <CAR_VIN>XXX12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20060331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>SS00 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
    <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>ASTRA</CAR_MODEL>
        <CAR_REG_DATE>20040331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
    <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>2.5</CAR_ENG>
        <CAR_MODEL>CALIBRA</CAR_MODEL>
        <CAR_REG_DATE>20030331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>BLUE</COLOUR>
    </CAR>

</MANUFACTURER>
<MANUFACTURER name="FORD">
    <CAR>
        <CAR_VIN>12345678901234567</CAR_VIN>
        <CAR_ENG>2.5</CAR_ENG>
        <CAR_MODEL>CMAX</CAR_MODEL>
        <CAR_REG_DATE>20050331</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>HH54 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
</MANUFACTURER>
<MANUFACTURER name="TOYOTA">
    <CAR>
        <CAR_VIN>XXX12345678901234567</CAR_VIN>
        <CAR_ENG>1.9</CAR_ENG>
        <CAR_MODEL>Corolla</CAR_MODEL>
        <CAR_REG_DATE>20030812</CAR_REG_DATE>
        <TRANSMISSION>A</TRANSMISSION>
        <CAR_REG>SS00 RRY</CAR_REG>
        <COLOUR>RED</COLOUR>
    </CAR>
</MANUFACTURER>

My first attempt xsl

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns:externalSupplier="externalSupplier://uk.co.skyline.XslConnector"
xmlns:consol="consol://uk.co.skyline.Xslconsolidated"
exclude-result-prefixes="externalSupplier consol">

<xsl:output method="xml" />



<!-- *** Parameters *** -->
<!-- ****************** -->
<xsl:param name="dealerNo"></xsl:param>  <!-- ** dealer Reg No being processed -->
<xsl:param name="supplier"></xsl:param> 
<xsl:param name="cust"></xsl:param>          


<!-- Load xml from external into variables -->
<!-- ************************************* -->
<!--
    <xsl:variable name="externalStuff" select="externalSupplier:getData(
    $dealerNo,  $supplier, $cust)"></xsl:variable>
-->

<xsl:variable name="externalStuff" 
select="document('Data2.xml')/*"></xsl:variable>



<xsl:template match="/">


    <xsl:for-each select="DEALERSHIP">

        <xsl:variable name="RegNum" select="@reg_number" />

        <xsl:element name="{name()}">
            <xsl:apply-templates select="." mode="copyAttributes" />

            <xsl:for-each select="MANUFACTURER">
                <xsl:variable name="manName" select="@name" />
                <xsl:apply-templates select=".">

                    <xsl:with-param name="externalMan"
                        select="$externalStuff[@reg_number = 
                        $RegNum]/MANUFACTURER[@name = $manName]" />
                </xsl:apply-templates>
            </xsl:for-each>
        </xsl:element>

    </xsl:for-each>

</xsl:template>

<!-- Process the MANUFACTURER elements -->
<!-- **************************** -->
<xsl:template match="MANUFACTURER">
    <xsl:param name="externalMan" />

    <!-- Get the reg number of the dealer -->
    <xsl:variable name="regNum" select="../@reg_number" />

    <!-- Get the manufacturer name -->
    <xsl:variable name="eManName" select="@name" />

    <xsl:element name="{name()}">
        <xsl:apply-templates select="." mode="copyAttributes" />

        <xsl:variable name="cars" select="CAR" />


        <xsl:apply-templates
            select="CAR | $externalMan/CAR[not (CAR_REG_DATE = 
            $cars/CAR_REG_DATE and CAR_MODEL = $cars/CAR_MODEL ) ]">

            <xsl:sort select="CAR_REG_DATE" data-type="number" 
            order="descending" />
            <xsl:sort select="CAR_MODEL" data-type="number" 
            order="descending" />
        </xsl:apply-templates>

    </xsl:element>

</xsl:template>


<!-- Process the CAR elements -->
<!-- **************************** -->
<xsl:template match="CAR">
    <xsl:param name="externalMan" />

    <xsl:choose>


        <xsl:when test="CAR_VIN">
            <xsl:copy-of select="." />
        </xsl:when>
        <xsl:otherwise>


            <xsl:if
                test="string-length(CAR_REG_DATE) = 8">

                <CAR>
                    <xsl:copy-of select="*" />
                    <CAR_VIN>
                        <xsl:value-of
                            select="consol:getConSol(../../@reg_number, 
                            CAR_REG_DATE, CAR_MODEL)" />
                    </CAR_VIN>
                </CAR>

            </xsl:if>

        </xsl:otherwise>
    </xsl:choose>
</xsl:template>




<!-- Copy the element's attributes to the output -->
<!-- ******************************************* -->
<xsl:template match="DEALERSHIP | MANUFACTURER" mode="copyAttributes">

    <xsl:for-each select="attribute::*">
        <xsl:attribute name="{name()}"><xsl:value-of select="." />
        </xsl:attribute>
    </xsl:for-each>
</xsl:template>

This transformation happens server side initiated through java.

As you can see from the stylesheet I have to include the Data2.xml through a call to some java processing. I have used the document() for debug purposes.

I managed to get the calls from/to the java inputs working and the main structure of the xslt output working. Where a CAR_VIN is not present in any data the call to java happens on transform of every CAR element. The given data includes all CAR_VIN's to avoid call outs to this java.

Its the xsl logic and code specifically relating to the merging that I'm new to and need some help on.

The merge logic is to combine both data sources MANUFACTURERS and child elements. Where both sources have identical CAR elements whose child elements, CAR_REG_DATE and CAR_MODEL content is the same then only include the data from either Data1.xml or Data2.xml whichever has the highest count of that unique combination. If a combinaton has the same amount of data in both data sources then use the transformation source xml, in the above case Data1.xml. The resulting elements need to be ordered by CAR_REG_DATE descending in each individual MANUFATURER element. So in the above data example you can see that the necesary output includes all MANUFACTURERS ; VAUXHALL, FORD, TOYOTA. In the case of Vauxhall Astra 20060331 the output contains the data from Data2.xml ASTRA 20060331 excluding the same data combination from Data1.xml All other cases of Vauxhall Astra (non 20060331) are included in the ouput from Data1.xml all the CARS in each MANUFACTURER being ordered by CAR_REG_DATE descending Ford Manaufacturer data is included in the output from Data1 ordered CAR_REG_DATE desc. Toyota Manaufacturer data is included in the output from Data2 ordered CAR_REG_DATE desc.

I initially came up with the idea of merging using the union operator where Data2.xml CAR_REG_DATE and CAR_MODEL content value is not the same as Data1.xml CAR_REG_DATE but this would exclude any data from Data2.xml when there was no matching data CAR_REG_DATE and CAR_MODEL combination in Data1.xml. Also (when I thought about it) it will allways exclude data from Data2.xsml when the same combination CAR_REG_DATE and CAR_MODEL was in DATA1.xml irrespective of counts in either data source, which is definately not what I want.

Result from applying stylesheet.xsl

<?xml version="1.0" encoding="UTF-8"?>

12345678901234567 1.9 ASTRA 20060331 A HH54 RRY RED 12345678901234567 1.9 ASTRA 20040331 A HH54 RRY RED 12345678901234567 2.5 CALIBRA 20030331 A HH54 RRY BLUE 12345678901234567 2.5 CMAX 20050331 A HH54 RRY RED

hen I came up with the idea of merging both the source document and external document together into a local variable and passing this merged MANUFACTURER into a template and looping through every CAR element. I can differentiate between Data1.xml and Data2.xml CAR elements by the content of CAR_VIN starting with XXX.So I thought I would loop through every merged CAR element and select counts for source document and external document and include external or exclude source based on the result.

Is this possible and how would I go about doing this or is there a correct way of doing this keeping in mind I am a complete newbie to xslt processing ?

Any help with this problem would be really appreciated.

Hi thanks for the response

I've edited the data set to avoid rsi.

In a nutshell I need to implement the following logic -

Whichever source Data1.xml or Data2.xml has the greater number of unique combination of CAR_REG_DATE and CAR_MODEL (within CAR) include that sources data in the output - exclude the other sources data with the same combination.

All CAR_REG_DATE - CAR_MODEL unique combinations need to be included from both sources. Ordered CAR_REG_DATE descending in each MANUFACTURER, once merged

So taking the Vauxhall MANUFACTURER

unique combination of Astra- 20060331 - Data1.xml has 1 and Data2.xml has 2.

I need to output the Astras 20060331 from Data2.xml set for this combination and disregard Data1.xml Astras 20060331 1 occurence as shown in the requiredoutput.xml above

Dimi I do appreciate your comment . I think this is my problem; I'm trying to look at the problem and break it down into smaller pieces but am failing here. I'm still in shock from discovering that the xsl:variable is immutable. (or so it seems).

This stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kCarByMan-Mod-Date"
             match="CAR"
             use="concat(../@name,'+',CAR_MODEL,'+',CAR_REG_DATE)"/>
    <xsl:variable name="vSource2" select="document('Data2.xml')"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="node()|@*" mode="copy">
        <xsl:call-template name="identity"/>
    </xsl:template>
    <xsl:template match="DEALERSHIP">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*">
                <xsl:with-param name="pSource2" select="$vSource2"/>
            </xsl:apply-templates>
            <xsl:apply-templates
                 select="$vSource2/*/MANUFACTURER[
                                        not(@name = current()/*/@name)
                                     ]">
                <xsl:with-param name="pSource2" select="/"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="MANUFACTURER">
        <xsl:param name="pSource2"/>
        <xsl:copy>
            <xsl:apply-templates select="node()|@*">
                <xsl:with-param name="pSource2" select="$pSource2"/>
            </xsl:apply-templates>
            <xsl:apply-templates
                 select="$pSource2/*/MANUFACTURER[
                                        @name = current()/@name
                                     ]/node()">
                <xsl:with-param name="pSource2" select="/"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="CAR"/>
    <xsl:template
         match="CAR[count(.|key('kCarByMan-Mod-Date',
                                concat(../@name,'+',CAR_MODEL,'+',CAR_REG_DATE)
                            )[1]
                    ) = 1]">
        <xsl:param name="pSource2"/>
        <xsl:variable
             name="vKey"
             select="concat(../@name,'+',CAR_MODEL,'+',CAR_REG_DATE)"/>
        <xsl:variable name="vGroup" select="key('kCarByMan-Mod-Date',$vKey)"/>
        <xsl:for-each select="$pSource2">
            <xsl:variable name="vGroup2"
                          select="key('kCarByMan-Mod-Date',$vKey)"/>
            <xsl:apply-templates
                 select="$vGroup[not(count($vGroup2) > count($vGroup))]"
                 mode="copy"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Output:

<DEALERSHIP reg_number="01234567">
    <MANUFACTURER name="VAUXHALL">
        <CAR>
            <CAR_VIN>12345678901234567</CAR_VIN>
            <CAR_ENG>1.9</CAR_ENG>
            <CAR_MODEL>ASTRA</CAR_MODEL>
            <CAR_REG_DATE>20040331</CAR_REG_DATE>
            <TRANSMISSION>A</TRANSMISSION>
            <CAR_REG>HH54 RRY</CAR_REG>
            <COLOUR>RED</COLOUR>
        </CAR>
        <CAR>
            <CAR_VIN>12345678901234567</CAR_VIN>
            <CAR_ENG>2.5</CAR_ENG>
            <CAR_MODEL>CALIBRA</CAR_MODEL>
            <CAR_REG_DATE>20030331</CAR_REG_DATE>
            <TRANSMISSION>A</TRANSMISSION>
            <CAR_REG>HH54 RRY</CAR_REG>
            <COLOUR>BLUE</COLOUR>
        </CAR>
        <CAR>
            <CAR_VIN>XXX12345678901234567</CAR_VIN>
            <CAR_ENG>1.9</CAR_ENG>
            <CAR_MODEL>ASTRA</CAR_MODEL>
            <CAR_REG_DATE>20060331</CAR_REG_DATE>
            <TRANSMISSION>A</TRANSMISSION>
            <CAR_REG>SS00 RRY</CAR_REG>
            <COLOUR>BLUE</COLOUR>
        </CAR>
        <CAR>
            <CAR_VIN>XXX12345678901234567</CAR_VIN>
            <CAR_ENG>1.9</CAR_ENG>
            <CAR_MODEL>ASTRA</CAR_MODEL>
            <CAR_REG_DATE>20060331</CAR_REG_DATE>
            <TRANSMISSION>A</TRANSMISSION>
            <CAR_REG>SS00 RRY</CAR_REG>
            <COLOUR>BLUE</COLOUR>
        </CAR>
    </MANUFACTURER>
    <MANUFACTURER name="FORD">
        <CAR>
            <CAR_VIN>12345678901234567</CAR_VIN>
            <CAR_ENG>2.5</CAR_ENG>
            <CAR_MODEL>CMAX</CAR_MODEL>
            <CAR_REG_DATE>20050331</CAR_REG_DATE>
            <TRANSMISSION>A</TRANSMISSION>
            <CAR_REG>HH54 RRY</CAR_REG>
            <COLOUR>RED</COLOUR>
        </CAR>
    </MANUFACTURER>
    <MANUFACTURER name="TOYOTA">
        <CAR>
            <CAR_VIN>XXX12345678901234567</CAR_VIN>
            <CAR_ENG>1.9</CAR_ENG>
            <CAR_MODEL>Corolla</CAR_MODEL>
            <CAR_REG_DATE>20030812</CAR_REG_DATE>
            <TRANSMISSION>A</TRANSMISSION>
            <CAR_REG>SS00 RRY</CAR_REG>
            <COLOUR>RED</COLOUR>
        </CAR>
    </MANUFACTURER>
</DEALERSHIP>

Note : Grouping by manufacturer, model and date. Traversing Data1.xml . DEALERSHIP rule (match node from Data1.xml only): copy itself, apply templates to child with Data2.xml as second source, apply templates to manufacturers from Data2.xml not present in Data1.xml . MANUFACTURER rule (match nodes from both Data1.xml and Data2.xml ): copy itself, apply templates to children with pSource2 param as second source, apply templates to CAR belonging to the same manufacturer in second source (this would be empty for manufacturers from Data2.xml not present in Data1.xml ). Empty CAR default rule. "First in a kind" CAR rule: copy current group if there are no more items in the same group from second source (This means that for equal number, both gets copied. Clarify behaviour if this is not desired. )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM