Reputation: 89
I need to remove duplicate nodes from below XML based on a condition. Can someone please help me fix the XSLT I have written? Or suggest a workaround?
My requirement: Remove entire nodes if below conditions are met.
XML File:
<?xml version="1.0" encoding="UTF-8"?>
<Workers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<File>22.0</File>
<Date>2014-05-31T16:20:07.000-07:00</Date>
<Worker_Count>2</Worker_Count>
</Header>
<Worker>
<Summary>
<Employee_ID>12345800</Employee_ID>
<Name>John Davis (12345800)</Name>
<Type>Employee</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>12345800</Employee_ID>
<Name>John Davis (12345800)</Name>
<Type>Contingent</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>32451854</Employee_ID>
<Name>Felix (32451854)</Name>
<Type>Employee</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>23471732</Employee_ID>
<Name>David (23471732)</Name>
<Type>Contingent</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>38741297</Employee_ID>
<Name>Sam Daniel (38741297)</Name>
<Type>Employee</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>38741297</Employee_ID>
<Name>Sam Daniel (38741297)</Name>
<Type>Contingent</Type>
</Summary>
</Worker>
</Workers>
Above XML need be transformed as below.
<?xml version="1.0" encoding="UTF-8"?>
<Workers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<File>22.0</File>
<Date>2014-05-31T16:20:07.000-07:00</Date>
<Worker_Count>2</Worker_Count>
</Header>
<Worker>
<Summary>
<Employee_ID>12345800</Employee_ID>
<Name>John Davis (12345800)</Name>
<Type>Employee</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>32451854</Employee_ID>
<Name>Felix (32451854)</Name>
<Type>Employee</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>23471732</Employee_ID>
<Name>David (23471732)</Name>
<Type>Contingent</Type>
</Summary>
</Worker>
<Worker>
<Summary>
<Employee_ID>38741297</Employee_ID>
<Name>Sam Daniel (38741297)</Name>
<Type>Employee</Type>
</Summary>
</Worker>
</Workers>
I have written below XSLT. Not sure how to add conditions in below XSLT to remove nodes contain duplicate employee id where in type is ‘Contingent’
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ws="urn:com.workday/workersync">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Workers/Worker[Summary/Type='Contingent']"/>
</xsl:stylesheet>
Above XSLT removes all Type which has value as ‘Contingent’. But, what I need is remove nodes which has Type as Contingent only when ‘Employee id’ has duplicate entries in the XML?
Upvotes: 0
Views: 2156
Reputation: 70598
Consider using a key to look up Worker
elements by their Employee_ID
<xsl:key name="Worker" match="Worker" use="Summary/Employee_ID" />
This then means you can write your template match to remove Worker
elements as so:
<xsl:template match="Worker[Summary/Type='Contingent'][count(key('Worker', Summary/Employee_ID)) > 1]"/>
Or maybe like so (i.e Check there is a second Worker
with the same Employee_ID
<xsl:template match="Worker[Summary/Type='Contingent'][key('Worker', Summary/Employee_ID)[2]]"/>
Try this XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ws="urn:com.workday/workersync">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="Worker" match="Worker" use="Summary/Employee_ID" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Worker[Summary/Type='Contingent'][key('Worker', Summary/Employee_ID)[2]]"/>
</xsl:stylesheet>
Note, there is no need to match on the full /Workers/Worker
path. You would only really need to do this in this case if there were Worker
elements at different levels in your XML.
Upvotes: 3
Reputation: 464
Add another predicate (here: [Summary/Employee_ID = preceding-sibling::Worker/Summary/Employee_ID or Summary/Employee_ID = following-sibling::Worker/Summary/Employee_ID]
) to your match expression for the dummy template.
The following template produces the desired output:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/Workers/Worker[Summary/Type='Contingent'][Summary/Employee_ID = preceding-sibling::Worker/Summary/Employee_ID or Summary/Employee_ID = following-sibling::Worker/Summary/Employee_ID]"/>
</xsl:stylesheet>
If predicates are chained this way, all of them have to be fulfilled for a match, as in a logical AND.
In addition to adding the extra predicate, I also changed the XSLT version to 1.0 since the template uses no version 2.0 features. Additionally, I removed the unnecessary namespace declaration.
Upvotes: 1