Adee
Adee

Reputation: 67

Combine 2 xslt into a single one

I have 2 xslt's. Both are working fine separately one after the other. Is there any way to combine the 2 xslt into a single one?

The first xslt is using accumulators for lookup:

<?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:map="http://www.w3.org/2005/xpath-functions/map"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:wd="urn:com.workday/bsvc"
        xmlns:ws="urn:com.workday/workersync"
        exclude-result-prefixes ="wd map xs ws"
        version="3.0">           
        <xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="CompanyLookup CurrentLookupValue"/>            
        <xsl:output method="xml" indent="yes"/>        
        
        <xsl:accumulator name="CurrentLookupValue" as="xs:string" initial-value="''" streamable="yes">
            <xsl:accumulator-rule match="wd:Employee_ID/text()" select="."/>
        </xsl:accumulator>
                
        <xsl:accumulator name="CompanyLookup" as="map(xs:string,xs:string)" initial-value="map{}" streamable="yes">
            <xsl:accumulator-rule match="wd:Company/text()" select="map:put($value, accumulator-before('CurrentLookupValue'),string(.))"/>
        </xsl:accumulator>     
        
        <xsl:template match="root/row" mode="report-in-mem">      
            <row>
                <xsl:variable name="company" select="accumulator-before('CompanyLookup')( normalize-space( employee_id ) )"/>                       
                <employeeID><xsl:value-of select="employee_id"/></employeeID>
                <company><xsl:value-of select="$company"/></company>
                <State><xsl:value-of select="State" /></State>
                <Work_days><xsl:value-of select="work_days"/></Work_days> 
            </row>       
        </xsl:template>
    </xsl:stylesheet>

The second XSLT is grouping the output of first xslt based on employeeID:

<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="root">
        <root>
            <xsl:for-each-group select="row" group-by="employeeID">
                <row>
                    <employee_id><xsl:value-of select="employeeID" /></employee_id>
                    <company><xsl:value-of select="company"/></company>                 
                    <xsl:for-each select="current-group()">                            
                        <Data> 
                            <State><xsl:value-of select="State" /></State>
                            <Work_days><xsl:value-of select="Work_days"/></Work_days>
                        </Data> 
                    </xsl:for-each>
                </row>    
            </xsl:for-each-group>
        </root>
    </xsl:template>
</xsl:stylesheet>

The input XML is an Aggregated data from two sources:

<?xml version="1.0" encoding="utf-8"?>
<AggregatedData>
   <wd:Report_Data xmlns:wd="urn:com.workday/bsvc">
      <wd:Report_Entry>
         <wd:Employee_ID>xyz1</wd:Employee_ID>
         <wd:Company>ABC</wd:Company>
      </wd:Report_Entry>
      <wd:Report_Entry>
         <wd:Employee_ID>xyz2</wd:Employee_ID>
         <wd:Company>ABC</wd:Company>
      </wd:Report_Entry>
   </wd:Report_Data>
<root>
    <row>
        <employee_id>xyz2</employee_id>
        <State>TX</State>
        <work_days>0</work_days>
    </row>
    <row>
        <employee_id>xyz2</employee_id>
        <State>CA</State>
        <work_days>10</work_days>
    </row>

</root>
</AggregatedData>

Final output looks like:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <row>
      <employee_id>xyz2</employee_id>
      <company>ABC</company>
      <Data>
         <State>TX</State>
         <Work_days>0</Work_days>
      </Data>
      <Data>
         <State>CA</State>
         <Work_days>10</Work_days>
      </Data>
   </row>
</root>

Upvotes: 1

Views: 250

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167571

Some element names seem to differ in spelling or case of letters but ignoring streaming a fusion of the two XSLTs would be alike

<xsl:stylesheet 
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:map="http://www.w3.org/2005/xpath-functions/map"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:wd="urn:com.workday/bsvc"
        xmlns:ws="urn:com.workday/workersync"
        exclude-result-prefixes ="wd map xs ws"
        version="3.0">           
        <xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="CompanyLookup CurrentLookupValue"/>            
        <xsl:output method="xml" indent="yes"/>        
        
        <xsl:accumulator name="CurrentLookupValue" as="xs:string" initial-value="''" streamable="yes">
            <xsl:accumulator-rule match="wd:Employee_ID/text()" select="."/>
        </xsl:accumulator>
                
        <xsl:accumulator name="CompanyLookup" as="map(xs:string,xs:string)" initial-value="map{}" streamable="yes">
            <xsl:accumulator-rule match="wd:Company/text()" select="map:put($value, accumulator-before('CurrentLookupValue'),string(.))"/>
        </xsl:accumulator>     
        
   <xsl:template match="root">
        <root>
            <xsl:for-each-group select="row" group-by="employee_id">
                <row>
                    <employee_id><xsl:value-of select="employee_id" /></employee_id>
                    <company><xsl:value-of select="accumulator-before('CompanyLookup')(employee_id)"/></company>                 
                    <xsl:for-each select="current-group()">                            
                        <Data> 
                            <State><xsl:value-of select="State" /></State>
                            <Work_days><xsl:value-of select="work_days"/></Work_days>
                        </Data> 
                    </xsl:for-each>
                </row>    
            </xsl:for-each-group>
        </root>
    </xsl:template>
</xsl:stylesheet> 

In terms of using fn:transform and XPath 3 to chain a sequence of XSLTs you can use e.g.

let $xslts := ($xslt1, $xslt2)
return
    fold-left(
        $xslts, 
        ., 
        function($source, $xslt) { 
            transform(
                map { 
                    'source-node' : $source, 
                    'stylesheet-node' : $xslt 
                }
            )?output 
        }
    )

Upvotes: 3

Related Questions