Home > Net >  How to use "for-each" loop & remove empty tags from output xml with xslt?
How to use "for-each" loop & remove empty tags from output xml with xslt?

Time:08-09

I am trying to create an xslt file that will transform my xml. The file needs to use a loop so that if there are duplicate employees then there is only one parent Employee tag and not multiple. I'm able to get this part to work with my xslt file. The issue I am having are with the empty tags showing up under WorkAssingment. If there is no data for any of the WorkAssignment child fields then I'd like it so that the empty child tags are omitted.

I am a beginner with xml and xslt and do not have a coding background. I found some previous posts, however when trying out those solutions I wasn't successful. I was also a bit confused as most solutions I am finding are showing a xsl:template tag that closes before the body. I believe I need mine to close after the body as if it doesn't the loop won't work.

xml transform and remove empty tags

how to remove empty tags from output xml

Below is an example of:

  • What I am looking to transform
  • The results I am currently getting
  • The results I am trying to get
  • A copy of my xslt

<!-- This is the xml I am looking to convert -->
   <Employee>
        <XRefCode>123</XRefCode>
        <EmployeeNumber>123</EmployeeNumber>
        <FirstName>Test</FirstName>
        <LastName>Person1</LastName>
        <WorkAssignment>
            <JobXrefCode>job1</JobXrefCode>
            <DeptXrefCode>dept1</DeptXrefCode>
            <OrgXrefCode>org1</OrgXrefCode>
            <IsPrimary>1</IsPrimary>
            <EffectiveStart>2022-01-01</EffectiveStart>
            <BusinessTitle>Job1_Dept1</BusinessTitle>
        </WorkAssignment>
    </Employee>
    <Employee>
        <XRefCode>123</XRefCode>
        <EmployeeNumber>123</EmployeeNumber>
        <FirstName>Test</FirstName>
        <LastName>Person1</LastName>
        <WorkAssignment>
            <JobXrefCode>job2</JobXrefCode>
            <DeptXrefCode>dept2</DeptXrefCode>
            <OrgXrefCode>org1</OrgXrefCode>
            <IsPrimary>0</IsPrimary>
            <EffectiveStart>2022-01-01</EffectiveStart>
            <ReasonXrefCode>Reason1</ReasonXrefCode>
        </WorkAssignment>
    </Employee>
    
    
<!-- This is the output I am currently getting -->
    <Employee>
        <XRefCode>123</XRefCode>
        <EmployeeNumber>123</EmployeeNumber>
        <FirstName>Test</FirstName>
        <LastName>Person1</LastName>
        <WorkAssignment>
            <JobXrefCode>job1</JobXrefCode>
            <DeptXrefCode>dept1</DeptXrefCode>
            <OrgXrefCode>org1</OrgXrefCode>
            <IsPrimary>1</IsPrimary>
            <EffectiveStart>2022-01-01</EffectiveStart>
            <EffectiveEnd/>
            <ReasonXrefCode/>
            <Rate/>
            <FTE/>
            <PositionTermXRefCode/>
            <EmploymentIndicatorXRefCode/>
            <TipTypeGroupXrefCode/>
            <LaborPercentage/>
            <WorkLocationOverrideXrefCode/>
            <BusinessTitle>Job1_Dept1</BusinessTitle>
            <IsVirtualEmployee/>
            <LedgerCode/>
            <MultiJurisdictionAllocationPercentage/>
        </WorkAssignment>
        <WorkAssignment>
            <JobXrefCode>job2</JobXrefCode>
            <DeptXrefCode>dept2</DeptXrefCode>
            <OrgXrefCode>org1</OrgXrefCode>
            <IsPrimary>0</IsPrimary>
            <EffectiveStart>2022-01-01</EffectiveStart>
            <EffectiveEnd/>
            <ReasonXrefCode>Reason1</ReasonXrefCode>
            <Rate/>
            <FTE/>
            <PositionTermXRefCode/>
            <EmploymentIndicatorXRefCode/>
            <TipTypeGroupXrefCode/>
            <LaborPercentage/>
            <WorkLocationOverrideXrefCode/>
            <BusinessTitle/>
            <IsVirtualEmployee/>
            <LedgerCode/>
            <MultiJurisdictionAllocationPercentage/>
        </WorkAssignment>
    </Employee>
    
    
<!-- This is the output I want to get -->
    <Employee>
        <XRefCode>123</XRefCode>
        <EmployeeNumber>123</EmployeeNumber>
        <FirstName>Test</FirstName>
        <LastName>Person1</LastName>
        <WorkAssignment>
            <JobXrefCode>job1</JobXrefCode>
            <DeptXrefCode>dept1</DeptXrefCode>
            <OrgXrefCode>org1</OrgXrefCode>
            <IsPrimary>1</IsPrimary>
            <EffectiveStart>2022-01-01</EffectiveStart>
            <BusinessTitle>Job1_Dept1</BusinessTitle>
        </WorkAssignment>
        <WorkAssignment>
            <JobXrefCode>job2</JobXrefCode>
            <DeptXrefCode>dept2</DeptXrefCode>
            <OrgXrefCode>org1</OrgXrefCode>
            <IsPrimary>0</IsPrimary>
            <EffectiveStart>2022-01-01</EffectiveStart>
            <ReasonXrefCode>Reason1</ReasonXrefCode>
        </WorkAssignment>
</Employee>

BELOW IS MY XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
                  xmlns:cs="urn:cs"
>

    <!-- List all paytypes under a single Export record, for each employee -->
    <xsl:output method="xml"/>

    <!-- generates a unique list of employee numbers, no duplicates. Referenced via the name 'ListEmpNumber'. The Employee numbers are
        found under Export/Record/EmployeeNumber in the xml. -->
    <xsl:key name="ListXref" match="/EmployeeImport/Employee" use="XRefCode" />

    <xsl:template match="/">
        <EmployeeImport>
            <!-- loop over the first record for an employee found in the list, matching on the EmployeeNumber -->
            <xsl:for-each select="EmployeeImport/Employee[generate-id(.) = generate-id(key('ListXref', XRefCode))]">
                <Employee>
                    <!-- store the found employee number in a variable so it can be referenced -->
                    <xsl:variable name="EmpXrefSearch" select="XRefCode"/>

                    <XRefCode>
                        <xsl:value-of select="XRefCode"/>
                    </XRefCode>
                    <EmployeeNumber>
                        <xsl:value-of select="EmployeeNumber"/>
                    </EmployeeNumber>
                    <FirstName>
                        <xsl:value-of select="FirstName"/>
                    </FirstName>
                    <LastName>
                        <xsl:value-of select="LastName"/>
                    </LastName>
                
                    <!-- add whatever else you need in order from Employee!-->


                    <!-- Find all export records for the given employee, loop and display the pay type -->
                    <xsl:for-each select="/EmployeeImport/Employee[XRefCode=$EmpXrefSearch]/WorkAssignment"> 
                        <WorkAssignment>
                            <JobXrefCode>
                                <xsl:value-of select="JobXrefCode"/>
                            </JobXrefCode>
                            <DeptXrefCode>
                                <xsl:value-of select="DeptXrefCode"/>
                            </DeptXrefCode>
                            <OrgXrefCode>
                                <xsl:value-of select="OrgXrefCode"/>
                            </OrgXrefCode>
                            <IsPrimary>
                                <xsl:value-of select="IsPrimary"/>
                            </IsPrimary>
                            <EffectiveStart>
                                <xsl:value-of select="EffectiveStart"/>
                            </EffectiveStart>
                            <EffectiveEnd>
                                <xsl:value-of select="EffectiveEnd"/>
                            </EffectiveEnd>
                            <ReasonXrefCode>
                                <xsl:value-of select="ReasonXrefCode"/>
                            </ReasonXrefCode>
                            <Rate>
                                <xsl:value-of select="Rate"/>
                            </Rate>
                            <FTE>
                                <xsl:value-of select="FTE"/>
                            </FTE>
                            <PositionTermXRefCode>
                                <xsl:value-of select="PositionTermXRefCode"/>
                            </PositionTermXRefCode>
                            <EmploymentIndicatorXRefCode>
                                <xsl:value-of select="EmploymentIndicatorXRefCode"/>
                            </EmploymentIndicatorXRefCode>
                            <TipTypeGroupXrefCode>
                                <xsl:value-of select="TipTypeGroupXrefCode"/>
                            </TipTypeGroupXrefCode>
                            <LaborPercentage>
                                <xsl:value-of select="LaborPercentage"/>
                            </LaborPercentage>
                            <WorkLocationOverrideXrefCode>
                                <xsl:value-of select="WorkLocationOverrideXrefCode"/>
                            </WorkLocationOverrideXrefCode>
                            <BusinessTitle>
                                <xsl:value-of select="BusinessTitle"/>
                            </BusinessTitle>
                            <IsVirtualEmployee>
                                <xsl:value-of select="IsVirtualEmployee"/>
                            </IsVirtualEmployee>
                            <LedgerCode>
                                <xsl:value-of select="LedgerCode"/>
                            </LedgerCode>
                            <MultiJurisdictionAllocationPercentage>
                                <xsl:value-of select="MultiJurisdictionAllocationPercentage"/>
                            </MultiJurisdictionAllocationPercentage>
                        </WorkAssignment>
                    </xsl:for-each>
                    
                    <!--<xsl:for-each select="/EmployeeImport/Employee[XRefCode=$EmpXrefSearch]/RateLevel"> -->
                        <RateLevel>
                            <JobSetXrefCode>
                                <xsl:value-of select="RateLevel/JobSetXrefCode"/>
                            </JobSetXrefCode>
                            <JobSetLevelXrefCode>
                                <xsl:value-of select="RateLevel/JobSetLevelXrefCode"/>
                            </JobSetLevelXrefCode>
                        </RateLevel>
                    <!--</xsl:for-each>-->
                    
                </Employee>
            </xsl:for-each>
        </EmployeeImport>       
    </xsl:template>
</xsl:stylesheet>

CodePudding user response:

It seems you just want to copy the elements from the input instead of explicitly creating new and then partially empty elements:

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

  <xsl:output method="xml" indent="yes"/>
  
  <xsl:strip-space elements="*"/>

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

  <xsl:key name="ListXref" match="/EmployeeImport/Employee" use="XRefCode" />
  
  <xsl:template match="Employee[generate-id() = generate-id(key('ListXref', XRefCode)[1])]">
    <xsl:copy>
      <xsl:apply-templates select="*[not(self::WorkAssignment)] | key('ListXref', XRefCode)/WorkAssignment"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Employee[not(generate-id() = generate-id(key('ListXref', XRefCode)[1]))]"/>

</xsl:stylesheet>

CodePudding user response:

AFAICT, you want to do simply:

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="ListXref" match="Employee" use="XRefCode" />

<xsl:template match="/EmployeeImport">
    <xsl:copy>
        <xsl:for-each select="Employee[generate-id(.) = generate-id(key('ListXref', XRefCode))]">
            <xsl:copy>
                <xsl:copy-of select="XRefCode|EmployeeNumber|FirstName|LastName"/>
                <xsl:copy-of select="key('ListXref', XRefCode)/WorkAssignment"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

Demo: https://xsltfiddle.liberty-development.net/nbBfrGa

For better understanding, learn about Muenchian grouping.


P.S. xsl:for-each is not a "loop".

  • Related