Search code examples
xmlxsltxpath-1.0

How can I filter source XML and then pass the result on again as XML to process normally


I have a xslt and xml processing workflow where the document is processed from the root match and then various other processing elements are triggered. I now need to take that same workflow and inject a pre processing step that will filter the data at one of the child levels and then continue processing as before.

Current Input A (xmlA) -> Template (xslt1) = Output

Pre Processing Input A (xmlA) -> Filter out unwanted data (xmlNEW) -> Template (xslt1) = Output (without filtered data)

I need to inject it before the handling of the document as there are way too many templates already in place that would break if I filtered the data further in the processing workflow.

I have the filter working in a separate test document , I just cannot get it to work when trying to use variables and params in template calls.

Sample XML:

<?xml version="1.0" encoding="UTF-16"?>
<MultiDocumentLevel>
    <DocumentLevel>
        <OrderLevel>
            <AllowedFormat>
                <HeaderLevel>
                    <HeaderSort>12345</HeaderSort>
                    <HeaderCode>12345</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>9999</HeaderSort>
                    <HeaderCode>9999-1</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>0000</HeaderSort>
                    <HeaderCode>0000</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>8989</HeaderSort>
                    <HeaderCode>8989</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>7777</HeaderSort>
                    <HeaderCode>123</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>123</HeaderSort>
                    <HeaderCode>14</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>1</HeaderSort>
                    <HeaderCode>1</HeaderCode>
                </HeaderLevel>
            </AllowedFormat>
        </OrderLevel>
    </DocumentLevel>
</MultiDocumentLevel>

Main Template:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="html"/>

    <xsl:template match="/">
        <xsl:apply-templates select="MultiDocumentLevel"/>
    </xsl:template>

    <!--    ****************************************************************************************************************** -->
    <!--        Name:           MultiDocumentLevel                                                                                           -->
    <!--                        Xsl's Main Template - Start of Builder                                                                                           -->
    <!--    ****************************************************************************************************************** -->
    
    <xsl:template match="MultiDocumentLevel">
        <xsl:apply-templates select="DocumentLevel/OrderLevel/AllowedFormat/HeaderLevel"/>
    </xsl:template> 

    <!--    ****************************************************************************************************************** -->
    <!--        Name:           HeaderLevel                                                                                    -->
    <!--                        Xsl's HeaderLevel Template - Process main data                                                 -->
    <!--    ****************************************************************************************************************** -->

    <xsl:template match="HeaderLevel">
        <!-- Process all nodes under HeaderLevel-->  
        <xsl:value-of select="concat(position(),'  -  ',HeaderCode,'  -  ',.)"/><br/>
    </xsl:template> 
    
</xsl:stylesheet>

Filter Template

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes"/>

<xsl:variable name="allowedheaders">
    <headers status="AllowedToPrint">
        <HeaderSort printHeader="12345" name="Allowed1"/>
        <HeaderSort printHeader="123" name="Allowed2"/>
        <HeaderSort printHeader="0000" name="Allowed3"/>
    </headers>
</xsl:variable>

    
 <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>   
    
    
    <xsl:template match="/">
            <xsl:apply-templates select="@*|node()" />
    </xsl:template>

    <xsl:template match="HeaderLevel">  
                  <!-- filter the data at headerlevel based on lookup in allowedheaders variable -->
                    <xsl:if test="HeaderSort=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader 
                    or HeaderCode=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader">
                    <xsl:copy>
                        <xsl:apply-templates select="@*|node()"/>
                    </xsl:copy>
                </xsl:if>
    </xsl:template> 

</xsl:stylesheet>

Desired Output that can be used by additional templates and transforms :

<MultiDocumentLevel>
    <DocumentLevel>
        <OrderLevel>
            <AllowedFormat>
                <HeaderLevel>
                    <HeaderSort>12345</HeaderSort>
                    <HeaderCode>12345</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>0000</HeaderSort>
                    <HeaderCode>0000</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>7777</HeaderSort>
                    <HeaderCode>123</HeaderCode>
                </HeaderLevel>
                <HeaderLevel>
                    <HeaderSort>123</HeaderSort>
                    <HeaderCode>14</HeaderCode>
                </HeaderLevel>
            </AllowedFormat>
        </OrderLevel>
    </DocumentLevel>
</MultiDocumentLevel>

Solution

  • If you want to have a variable that contains the original XML document with only the disallowed HeaderLevel elements removed and then apply templates to this variable, you will need to build your stylesheet along these lines:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <xsl:output method="html"/>
    
    <xsl:variable name="allowedheaders">
        <headers status="AllowedToPrint">
            <HeaderSort printHeader="12345" name="Allowed1"/>
            <HeaderSort printHeader="123" name="Allowed2"/>
            <HeaderSort printHeader="0000" name="Allowed3"/>
        </headers>
    </xsl:variable>
    
    
    <!-- main template -->
    
    <xsl:template match="/">
        <!-- pre-filter -->
        <xsl:variable name="filteredInput">
            <xsl:apply-templates mode="filter"/>
        </xsl:variable>
        <!-- continue processing with the remaining templates -->
        <xsl:apply-templates select="msxsl:node-set($filteredInput)/MultiDocumentLevel"/>
    </xsl:template>  
    
    
    <!-- filtering templates -->
    
    <xsl:template match="@*|node()" mode="filter">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" mode="filter"/>
        </xsl:copy>
    </xsl:template>   
        
    <xsl:template match="HeaderLevel" mode="filter">  
        <!-- filter the data at headerlevel based on lookup in allowedheaders variable -->
        <xsl:if test="HeaderSort|HeaderCode = msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()" mode="filter"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template> 
    
    
    <!-- remaining templates -->
    
    <!-- all your remaining templates go here, for example: -->
    
    <xsl:template match="HeaderLevel">
        <!-- Process all nodes under HeaderLevel-->  
        <xsl:value-of select="concat(position(),'  -  ',HeaderCode,'  -  ',.)"/><br/>
    </xsl:template> 
    
    
    </xsl:stylesheet>
    

    However, you need to heed the warning given by @y.arazim: your "Main Template" stylesheet, as posted here, applies only one template.