How to get duplicate record as single record in xslt - xslt

I am struggling to get desired output result in xslt:
I want something Like I have 2 records in each records i have multiple child node I have to check if the child element values are duplicated in one record or not if yes i should only create 1 record with respect to duplicate record and remaining records for unique ones,
My input file looks like:
<Records count="2">
<Record id="parentRecord">
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field">Technology Asset Management</Field>
</Record>
</Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field1">Inadequate Information Security Practices</Field>
</Record>
</Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field1">Inadequate Information Security Practices</Field>
</Record>
</Record>
</Record>
<Record id="parentRecord">
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field">Technology Asset Management</Field>
</Record>
</Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field1">Inadequate Information Security Practices</Field>
</Record>
</Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field">Technology Asset Management</Field>
</Record>
</Record>
</Record>
</Records>
I am Expecting :
<Records>
<Record id="parentRecord">
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field">Technology Asset Management</Field>
</Record>
</Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field1">Inadequate Information Security Practices</Field>
</Record>
</Record>
</Record>
<Record id="parentRecord">
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field">Technology Asset Management</Field>
</Record>
</Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field id="childRecord2Field1">Inadequate Information Security Practices</Field>
</Record>
</Record>
</Records>
Here is my xslt:
<xsl:template match="Records">
<Records>
<xsl:for-each select="Record[id='parentRecord']">
<xsl:variable name="MatchedChildRecord2Value" select="Record/Record/Field[id='childRecord2Field']"> //I am trying to store all the field values in paraent node
<xsl:for-each select="Record/Record/Field[id=childRecord2']/">
<xsl:choose>
<xsl:when test=".=$MatchedChildRecord2Value">
//Nothing I am doing
</xsl:when>
<xsl:otherwise>
<Record>
<Record id="childRecord1">
<Record id="childRecord2">
<Field>FieldRecordvalue</Field>
</Record>
</Record>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</Records>
</xsl:template>
Could you please help me out

I've tried it this way with XSLT3. I don't get the same result as what you indicated that you wanted, but I don't understand your result as there are duplicates within your result.
I've added an additional attribute count in order to show how many duplicates were folded into the result.
I get this result:
<Records>
<Record id="parentRecord" count="2">
<Record id="childRecord1" count="6">
<Record id="childRecord2" count="6">
<Field id="childRecord2Field" count="3">Technology Asset Management</Field>
<Field id="childRecord2Field1" count="3">Inadequate Information Security Practices</Field>
</Record>
</Record>
</Record>
</Records>
using this xslt3 stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes" />
<xsl:template match="/Records">
<xsl:copy >
<xsl:call-template name="groupRecords" >
<xsl:with-param name="currentGroup" select="Record" />
</xsl:call-template >
</xsl:copy>
</xsl:template>
<xsl:template name="groupRecords" >
<xsl:param name="currentGroup" as="element()*" />
<xsl:for-each-group select="$currentGroup/self::Record" group-by="#id" >
<Record id="{current-grouping-key()}" count="{count(current-group())}" >
<xsl:call-template name="groupRecords" >
<xsl:with-param name="currentGroup" select="current-group()/*" />
</xsl:call-template>
</Record>
</xsl:for-each-group>
<xsl:for-each-group select="$currentGroup/self::Field" group-by="concat(#id, '##', text())" >
<Field id="{current-group()[1]/#id}" count="{count(current-group())}">{(current-group()/text())[1]}</Field>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Related

Design XSLT to create a comma separated value list

I am not a code developer, my tool has a restriction in this case with the default XSLT. I have tried editing the default XSLT and creating a new one, but i am not getting the desired output. So, I want to write a custom XSLT and I need your help on that.
I have tried applying template and xsl:value-of select="/Records/Record/Record/Record/Field[#id='23425']" separator=", "/>, but it's not working.
Source XML:
<?xml version="1.0" encoding="UTF-16"?>
<Records count="2">
<Record contentId="490422" levelId="49" levelGuid="efcc872e-8014-4d1f-8830-6a63d0bd51d9" moduleId="71" parentId="0">
<Record contentId="490431" levelId="397" levelGuid="9a560130-79ad-4250-9901-3eafc4e0e799" moduleId="600" parentId="0">
<Record contentId="490425" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Program 1</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user1</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490425</Field>
</Record>
<Record contentId="490426" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Program 2</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user2</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490426</Field>
</Record>
<Field id="23429" guid="2bc40ece-8e0e-4f11-9e67-7d15e4021746" type="1">SErvice 4</Field>
</Record>
<Field id="120" guid="bd1c1f0e-47df-4a21-bbd7-473557d8a278" type="1">Device 1</Field>
</Record>
<Record contentId="490423" levelId="49" levelGuid="efcc872e-8014-4d1f-8830-6a63d0bd51d9" moduleId="71" parentId="0">
<Record contentId="490432" levelId="397" levelGuid="9a560130-79ad-4250-9901-3eafc4e0e799" moduleId="600" parentId="0">
<Record contentId="490427" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Copy of Program 1</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user5</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490427</Field>
</Record>
<Record contentId="490428" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Copy of Program 2</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user4</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490428</Field>
</Record>
<Record contentId="490429" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Copy of Program 3</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user5</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490429</Field>
</Record>
<Field id="23429" guid="2bc40ece-8e0e-4f11-9e67-7d15e4021746" type="1">Service 2</Field>
</Record>
<Record contentId="490431" levelId="397" levelGuid="9a560130-79ad-4250-9901-3eafc4e0e799" moduleId="600" parentId="0">
<Record contentId="490425" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Program 1</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user1</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490425</Field>
</Record>
<Record contentId="490426" levelId="398" levelGuid="2ef4fb6a-092e-471e-b481-1a6380022e10" moduleId="601" parentId="0">
<Field id="23426" guid="dd028a89-90de-4ca0-9d08-c9d2d0c47a19" type="1">Program 2</Field>
<Field id="23425" guid="0b389b7f-efc3-4933-92c3-0388e0ceb4e7" type="1">user2</Field>
<Field id="23422" guid="83c5d064-5c90-45f7-a2c7-635b42a69bdd" type="6">490426</Field>
</Record>
<Field id="23429" guid="2bc40ece-8e0e-4f11-9e67-7d15e4021746" type="1">SErvice 4</Field>
</Record>
<Field id="120" guid="bd1c1f0e-47df-4a21-bbd7-473557d8a278" type="1">Device 2</Field>
</Record>
</Records>
Needed Output/Transformed XML:
<?xml version="1.0" encoding="UTF-8"?>
<ArcherRecords xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.archer-tech.com/">
<ArcherRecord>
<Device_Name>Device 1</Device_Name>
<Programs>
<Field_id>490425</Field_id>
</Programs>
<Programs>
<Field_id>490426</Field_id>
</Programs>
<ProgExecutive>user 1,user 2<ProgExecutive>
</ArcherRecord>
<ArcherRecord>
<Device_Name>Device 2</Device_Name>
<Programs>
<Field_id>490425</Field_id>
</Programs>
<Programs>
<Field_id>490426</Field_id>
</Programs>
<Programs>
<Field_id>490427</Field_id>
</Programs>
<Programs>
<Field_id>490428</Field_id>
</Programs>
<Programs>
<Field_id>490429</Field_id>
</Programs>
<ProgExecutive>user 3,user 4,user 5, user 1, user 2<ProgExecutive>
</ArcherRecord>
</ArcherRecords>
Below XSLT is working for me
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
xmlns="http://www.archer-tech.com/">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Records">
<ArcherRecords >
<xsl:apply-templates select="Record" />
</ArcherRecords>
</xsl:template>
<xsl:template match="Record[#levelId='49']">
<ArcherRecord>
<xsl:element name="Device_Name">
<xsl:value-of select="Field[#id='120']" />
</xsl:element>
<xsl:element name="Calc_Prog_Executive">
<xsl:for-each select="Record/Record/Field[#id='23425']">
<xsl:value-of select="." />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</xsl:element>
<Programs>
<xsl:apply-templates select="Record/Record" />
</Programs>
</ArcherRecord>
</xsl:template>
<xsl:template match="Record/Record/Record">
<Tracking_Id>
<xsl:apply-templates select="#contentId" />
</Tracking_Id>
</xsl:template>
</xsl:stylesheet>

How to resolve Group and Sort withing XSLT

I have the following input message where the data needs to be grouped based on 'EmpNo' and 'Date' first. But the result should be taken from the latest date time from field 'lastDateTime'
Input XML:
<Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>3</Hours>
<EmpNo>825</EmpNo>
<lastDateTime>2019-04-12T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>4</Hours>
<EmpNo>826</EmpNo>
<lastDateTime>2019-04-12T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>5</Hours>
<EmpNo>827</EmpNo>
<lastDateTime>2019-04-12T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>3</Hours>
<EmpNo>825</EmpNo>
<lastDateTime>2019-04-14T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>4</Hours>
<EmpNo>826</EmpNo>
<lastDateTime>2019-04-10T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>5</Hours>
<EmpNo>827</EmpNo>
<lastDateTime>2019-04-12T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>3</Hours>
<EmpNo>825</EmpNo>
<lastDateTime>2019-04-10T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>4</Hours>
<EmpNo>826</EmpNo>
<lastDateTime>2019-04-11T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>5</Hours>
<EmpNo>827</EmpNo>
<lastDateTime>2019-04-16T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
</Record>
There could be many EmpNo with different Dates as well in the Input XML file. This needs to be grouped but it requires to extract only that particular node where lastDateTime is the latest.
Expected Result:
<Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>6</Hours>
<EmpNo>825</EmpNo>
<lastDateTime>2019-04-14T08:35:38.000</lastDateTime>
<Rate>1142</Rate>
<Code>13</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>4</Hours>
<EmpNo>826</EmpNo>
<lastDateTime>2019-04-12T08:35:38.000</lastDateTime>
<Rate>1140</Rate>
<Code>11</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>11</Hours>
<EmpNo>827</EmpNo>
<lastDateTime>2019-04-16T08:35:38.000</lastDateTime>
<Rate>1147</Rate>
<Code>18</Code>
</Record>
</Record>
I tried to write the following code but no luck.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf" exclude-result-prefixes="#all" version="2.0">
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="Record">
<xsl:copy>
<xsl:for-each-group select="Record" group-by="EmpNo">
<xsl:for-each-group select="current-group()" group-by="Date">
<xsl:copy>
<EmpNo>
<xsl:value-of select="EmpNo"/>
</EmpNo>
<Date>
<xsl:value-of select="Date"/>
</Date>
<lastDateTime>
<xsl:value-of select="max(current-group()/lastDateTime/xs:dateTime(.))"/>
</lastDateTime>
<Hours>
<xsl:value-of select="Hours[Date=max(current-group()/lastDateTime/xs:dateTime(.))]"/>
</Hours>
</xsl:copy>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
Based on your description I think you want
<xsl:template match="Record">
<xsl:copy>
<xsl:for-each-group select="Record" group-by="EmpNo">
<xsl:for-each-group select="current-group()" group-by="Date">
<xsl:for-each select="current-group()">
<xsl:sort select="xs:dateTime(lastDateTime)"/>
<xsl:if test="position() = last()">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:for-each-group>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
However, result at https://xsltfiddle.liberty-development.net/ncdD7mB is
<Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>3</Hours>
<EmpNo>825</EmpNo>
<lastDateTime>2019-04-14T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>4</Hours>
<EmpNo>826</EmpNo>
<lastDateTime>2019-04-12T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
<Record>
<Date>2019-04-01T00:00:00.000</Date>
<Hours>5</Hours>
<EmpNo>827</EmpNo>
<lastDateTime>2019-04-16T08:35:38.000</lastDateTime>
<Rate>1139</Rate>
<Code>102486</Code>
</Record>
</Record>
I am not sure from which item in a group you want to take the not grouped child elements.

How to access a specific node in XSL

silly question but hoping I can get some help here, I need to access specific node in XSL, the XML I am provided with looks like the below, can someone give me an idea what my XSL should look like in order to access the content of the node 'Value' I cannot no matter how i try reach that node!- any help appreciated!!!! :
<?xml version="1.0" encoding="UTF-8" ?>
<CrystalReport xmlns="urn:crystal-reports:schemas:report-detail"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:crystal-reports:schemas:report-detail http://www.businessobjects.com/products/xml/CR2008Schema.xsd">
<Group Level="1">
<Group Level="2">
<Details Level="3">
<Section SectionNumber="1">
<Field Name="Field5" FieldName="{ARTransaction.Transactions}">
<FormattedValue>0.00</FormattedValue>
<Value>0.00</Value>
</Field>
<Field Name="Field15" FieldName="{ARTransaction.PostingDate}">
<FormattedValue>8/1/2016</FormattedValue>
<Value>2016-08-01</Value>
</Field>
<Field Name="Field14" FieldName="{ARTransaction.AuditTrail}">
<FormattedValue>2016083100154</FormattedValue>
<Value>2016083100154</Value>
</Field>
<Field Name="Field13" FieldName="{ARTransaction.JobN}">
<FormattedValue>-25043</FormattedValue>
<Value>-25043</Value>
</Field>
<Field Name="Field11" FieldName="{Customer.CustomerName}">
<FormattedValue>First Church of Christ</FormattedValue>
<Value>First Church of Christ</Value>
</Field>
<Field Name="Field7" FieldName="{ARTransaction.CustomerN}">
<FormattedValue>13157</FormattedValue>
<Value>13157</Value>
</Field>
<Field Name="Field6" FieldName="{ARTransaction.InvoiceN}">
<FormattedValue>25043</FormattedValue>
<Value>25043</Value>
</Field>
<Field Name="SalesmanN1" FieldName="{ARTransaction.SalesmanN}">
<FormattedValue>22</FormattedValue>
<Value>22</Value>
</Field>
</Section>
</Details>
</Group>
</Group>
</Group>
<ReportFooter> </ReportFooter>
XSL :
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="CrystalReport">
<xsl:attribute name="xsi:schemaLocation">http://www.w3.org/2001/XMLSchema-instance</xsl:attribute>
<xsl:attribute name="version">1.2</xsl:attribute>
</xsl:element>
<xsl:element name="DR">
<xsl:value-of select="Group/Group/Details/Section/Field[#Name='Field13']/Value"/>
</xsl:element>
</xsl:template>
The main problem with your attempt is that it ignores the default namespace of the source XML.
You need to declare the namespace in your stylesheet, assign it a prefix, and use that prefix when addressing the elements in the source XML. For example, the following stylesheet:
XSL 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="urn:crystal-reports:schemas:report-detail"
exclude-result-prefixes="ns1">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/ns1:CrystalReport">
<DR>
<xsl:value-of select="ns1:Group/ns1:Group/ns1:Details/ns1:Section/ns1:Field[#Name='Field13']/ns1:Value"/>
</DR>
</xsl:template>
</xsl:stylesheet>
when applied to a well-formed input such as:
XML
<CrystalReport xmlns="urn:crystal-reports:schemas:report-detail" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:crystal-reports:schemas:report-detail http://www.businessobjects.com/products/xml/CR2008Schema.xsd">
<Group Level="1">
<Group Level="2">
<Details Level="3">
<Section SectionNumber="1">
<Field Name="Field5" FieldName="{ARTransaction.Transactions}">
<FormattedValue>0.00</FormattedValue>
<Value>0.00</Value>
</Field>
<Field Name="Field15" FieldName="{ARTransaction.PostingDate}">
<FormattedValue>8/1/2016</FormattedValue>
<Value>2016-08-01</Value>
</Field>
<Field Name="Field14" FieldName="{ARTransaction.AuditTrail}">
<FormattedValue>2016083100154</FormattedValue>
<Value>2016083100154</Value>
</Field>
<Field Name="Field13" FieldName="{ARTransaction.JobN}">
<FormattedValue>-25043</FormattedValue>
<Value>-25043</Value>
</Field>
<Field Name="Field11" FieldName="{Customer.CustomerName}">
<FormattedValue>First Church of Christ</FormattedValue>
<Value>First Church of Christ</Value>
</Field>
<Field Name="Field7" FieldName="{ARTransaction.CustomerN}">
<FormattedValue>13157</FormattedValue>
<Value>13157</Value>
</Field>
<Field Name="Field6" FieldName="{ARTransaction.InvoiceN}">
<FormattedValue>25043</FormattedValue>
<Value>25043</Value>
</Field>
<Field Name="SalesmanN1" FieldName="{ARTransaction.SalesmanN}">
<FormattedValue>22</FormattedValue>
<Value>22</Value>
</Field>
</Section>
</Details>
</Group>
</Group>
</CrystalReport>
will return:
Result
<?xml version="1.0" encoding="UTF-8"?>
<DR>-25043</DR>
Note: If you're using an XSLT 2.0 processor, you can declare a xpath-default-namespace attribute and do away with the prefix.

More XSL Grouping - how to remove parent nodes with duplicate child attributes?

Yes, this is another XSLT grouping/duplicates questions, but I was unable to find any answers that I could successfully apply to my situation.
This is the original XML:
<data jsxid="jsxroot">
<record jsxid="10" groupNum="1319" item="q123" total="1"/>
<record jsxid="20" groupNum="1319" item="w123" total="1"/>
<record jsxid="30" groupNum="1319" item="" total="0"/>
<record jsxid="40" groupNum="1322" item="z123" total="1"/>
<record jsxid="50" groupNum="1322" item="x123" total="1"/>
<record jsxid="60" groupNum="1322" item="c123" total="1"/>
<record jsxid="70" groupNum="1322" item="" total="0"/>
<record jsxid="80" groupNum="1323" item="x123" total="1"/>
<record jsxid="90" groupNum="1323" item="c123" total="1"/>
<record jsxid="100" groupNum="1323" item="z123" total="1"/>
<record jsxid="110" groupNum="1323" item="" total="0"/>
</data>
First, I need it grouped by attribute "groupNum" and wrapped in a parent element jsxid that increments with each group, so that the output looks like this:
<data jsxid="jsxroot">
<record jsxid="1">
<record jsxid="10" groupNum="1319" item="q123" total="1"/>
<record jsxid="20" groupNum="1319" item="w123" total="1"/>
<record jsxid="30" groupNum="1319" item="" total="0"/>
</record>
<record jsxid="2">
<record jsxid="40" groupNum="1322" item="z123" total="1"/>
<record jsxid="50" groupNum="1322" item="x123" total="1"/>
<record jsxid="60" groupNum="1322" item="c123" total="1"/>
<record jsxid="70" groupNum="1322" item="" total="0"/>
</record>
<record jsxid="3">
<record jsxid="80" groupNum="1323" item="x123" total="1"/>
<record jsxid="90" groupNum="1323" item="c123" total="1"/>
<record jsxid="100" groupNum="1323" item="z123" total="1"/>
<record jsxid="110" groupNum="1323" item="" total="0"/>
</record>
</data>
I was able to accomplish that with this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="groupNum" match="data/*" use="#groupNum" />
<xsl:template match="data">
<data>
<xsl:apply-templates select="*[generate-id(.)=generate-id(key('groupNum',#groupNum)[1])]"/>
</data>
</xsl:template>
<xsl:template match="*">
<record jsxid="{position()}" >
<xsl:copy-of select="key('groupNum', #groupNum)" />
</record>
</xsl:template>
</xsl:stylesheet>
Now I need to remove any grouping that contains the exact same items as another grouping for records where total = "1". If you look at the groupings of jsxid = 2 and jsxid = 3 above, you'll see that they both contain the following item attributes, though not in the same order:
item="x123"
item="c123"
item="z123"
So I want to remove the entire jsxid = 3 grouping and have the final output look like this:
<data jsxid="jsxroot">
<record jsxid="1">
<record jsxid="1" groupNum="1319" item="q123" total="1"/>
<record jsxid="2" groupNum="1319" item="w123" total="1"/>
<record jsxid="3" groupNum="1319" item="" total="0"/>
</record>
<record jsxid="2">
<record jsxid="4" groupNum="1322" item="z123" total="1"/>
<record jsxid="5" groupNum="1322" item="x123" total="1"/>
<record jsxid="6" groupNum="1322" item="c123" total="1"/>
<record jsxid="7" groupNum="1322" item="" total="0"/>
</record>
</data>
EDIT: Instead of removing the duplicate grouping, what if I wanted to add in a new attribute as a "grouping identifier." I'd call it something like "groupType" so the output for the above situation where the last two groupings are the same, they would have the same grouptype:
<data jsxid="jsxroot">
<record jsxid="1">
<record jsxid="10" groupNum="1319" item="q123" groupType = "type1" total="1"/>
<record jsxid="20" groupNum="1319" item="w123" groupType = "type1" total="1"/>
<record jsxid="30" groupNum="1319" item="" groupType = "type1" total="0"/>
</record>
<record jsxid="2">
<record jsxid="40" groupNum="1322" item="z123" groupType = "type2" total="1"/>
<record jsxid="50" groupNum="1322" item="x123" groupType = "type2" total="1"/>
<record jsxid="60" groupNum="1322" item="c123" groupType = "type2" total="1"/>
<record jsxid="70" groupNum="1322" item="" groupType = "type2" total="0"/>
</record>
<record jsxid="3">
<record jsxid="80" groupNum="1323" item="x123" groupType = "type2" total="1"/>
<record jsxid="90" groupNum="1323" item="c123" groupType = "type2" total="1"/>
<record jsxid="100" groupNum="1323" item="z123" groupType = "type2" total="1"/>
<record jsxid="110" groupNum="1323" item="" groupType = "type2" total="0"/>
</record>
</data>
Any help is greatly appreciated.
This is not going to be simple, so hang onto your seat:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="root" select="/" />
<xsl:key name="rec-by-group" match="record" use="#groupNum" />
<xsl:key name="group-by-items" match="group" use="#items" />
<xsl:template match="/">
<!-- first pass -->
<xsl:variable name="groups">
<!-- for each unique groupNum ... -->
<xsl:for-each select="data/record[count(. | key('rec-by-group', #groupNum)[1]) = 1]">
<xsl:variable name="groupNum" select="#groupNum" />
<!-- ... create a group ... -->
<group groupNum="{$groupNum}">
<!-- ... and concatenate all its items - in known order(!) - into a single attribute. -->
<xsl:attribute name="items">
<!-- switch context back to document in order to use key -->
<xsl:for-each select="$root">
<xsl:for-each select="key('rec-by-group', $groupNum)[#total=1]">
<xsl:sort select="#item" data-type="text" order="ascending"/>
<xsl:value-of select="#item"/>
<xsl:text>|</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:attribute>
</group>
</xsl:for-each>
</xsl:variable>
<!-- final pass -->
<data jsxid="jsxroot">
<!-- for each unique group (unique by its #items attribute) ... -->
<xsl:for-each select="exsl:node-set($groups)/group[count(. | key('group-by-items', #items)[1]) = 1]">
<!-- ... create a wrapper record ... -->
<record jsxid="{position()}">
<xsl:variable name="groupNum" select="#groupNum" />
<!-- switch context back to document in order to use key -->
<xsl:for-each select="$root">
<!-- ... and list the records in this group. -->
<xsl:for-each select="key('rec-by-group', $groupNum)">
<record>
<xsl:attribute name="jsxid">
<xsl:number/>
</xsl:attribute>
<xsl:copy-of select="#groupNum | #item | #total"/>
</record>
</xsl:for-each>
</xsl:for-each>
</record>
</xsl:for-each>
</data>
</xsl:template>
</xsl:stylesheet>
Edit:
in response to your edit:
If you can live with groupType being an arbitrary (though unique) number, you can change the "final pass" part to something like:
<!-- final pass -->
<data jsxid="jsxroot">
<!-- for each group ... -->
<xsl:for-each select="exsl:node-set($groups)/group">
<!-- ... create a wrapper record ... -->
<record jsxid="{position()}">
<xsl:variable name="groupNum" select="#groupNum" />
<xsl:variable name="groupType" select="key('group-by-items', #items)[1]/#groupNum" />
<!-- switch context back to document in order to use key -->
<xsl:for-each select="$root">
<!-- ... and list the records in this group. -->
<xsl:for-each select="key('rec-by-group', $groupNum)">
<record groupType="{$groupType}">
<xsl:attribute name="jsxid">
<xsl:number/>
</xsl:attribute>
<xsl:copy-of select="#groupNum | #item | #total"/>
</record>
</xsl:for-each>
</xsl:for-each>
</record>
</xsl:for-each>
</data>
Using your input example, this would return:
<?xml version="1.0" encoding="UTF-8"?>
<data jsxid="jsxroot">
<record jsxid="1">
<record groupType="1319" jsxid="1" groupNum="1319" item="q123" total="1"/>
<record groupType="1319" jsxid="2" groupNum="1319" item="w123" total="1"/>
<record groupType="1319" jsxid="3" groupNum="1319" item="" total="0"/>
</record>
<record jsxid="2">
<record groupType="1322" jsxid="4" groupNum="1322" item="z123" total="1"/>
<record groupType="1322" jsxid="5" groupNum="1322" item="x123" total="1"/>
<record groupType="1322" jsxid="6" groupNum="1322" item="c123" total="1"/>
<record groupType="1322" jsxid="7" groupNum="1322" item="" total="0"/>
</record>
<record jsxid="3">
<record groupType="1322" jsxid="8" groupNum="1323" item="x123" total="1"/>
<record groupType="1322" jsxid="9" groupNum="1323" item="c123" total="1"/>
<record groupType="1322" jsxid="10" groupNum="1323" item="z123" total="1"/>
<record groupType="1322" jsxid="11" groupNum="1323" item="" total="0"/>
</record>
</data>
As you can see, it uses the groupNum of the first group of the current type as the groupType value. Alternatively, you could also use the auto-generated id of that group:
<xsl:variable name="groupType" select="generate-id(key('group-by-items', #items)[1])" />.
To only copy the first instance, When generating each item, enclose it in a condition using count(preceding-sibling::*[]) = 0.
<xsl:template match="record">
<xsl:variable name="item" select="item"/>
<record jsxid="{position()}" >
<xsl:if test="count (preceding-sibling::*[item=$item])=0">
<xsl:copy-of select="key('groupNum', #groupNum)" />
</xsl:if>
</record>
</xsl:template>

Implementing bit tricky logic in xslt 1.0 transformation

I am finding bit difficulty to implement following logic while doing transformation using xslt 1.0.
Here is my requirement:
I need to define following logic on hours worked by an employee.
1. anything over 8 will be OT else Regular in a day.
2. if worked continuously for 3 days then 3rd day hours would be double time.
Here is an example:
Input:
<Input>
<Record>
<EmpId>1</EmpId>
<Date></Date>
<Hours></Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/1/2012</Date>
<Hours>6</Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/1/2012</Date>
<Hours>4</Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/2/2012</Date>
<Hours>4</Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/2/2012</Date>
<Hours></Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/3/2012</Date>
<Hours>3</Hours>
</Record>
<Record>
<EmpId>2</EmpId>
<Date>12/1/2012</Date>
<Hours>4</Hours>
</Record>
<Record>
<EmpId>2</EmpId>
<Date>12/1/2012</Date>
<Hours></Hours>
</Record>
</Input>
Output:
<Output>
<Record>
<EmployeeId>1</EmployeeId>
<Detail>
<Date>12/1/2012</Date>
<RegHours>8</Reghours>
<OTHours>2</OTHours>
</Detail>
<Detail>
<Date>12/2/2012</Date>
<RegHours>4</Reghours>
</Detail>
<Detail>
<Date>12/3/2012</Date>
<DTHours>3</DThours>
</Detail>
</Record>
<Record>
<EmployeeId>2</EmployeeId>
<Detail>
<Date>12/1/2012</Date>
<RegHours>4</Reghours>
</Detail>
<Detail>
</Record>
</Output>
Would appreciate for any help.
Thanks..
Abhi
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kRecByEmpId" match="Record[Date/node() and Hours/node()]"
use="EmpId"/>
<xsl:key name="kRecByEmpIdDate" match="Record[Date/node() and Hours/node()]"
use="concat(EmpId,'+',Date)"/>
<xsl:template match=
"Record[generate-id()=generate-id(key('kRecByEmpId',EmpId)[1])]">
<Record>
<EmployeeId>
<xsl:value-of select="EmpId"/>
</EmployeeId>
<xsl:apply-templates mode="datetime" select=
"key('kRecByEmpId', EmpId)
[generate-id()
=
generate-id(key('kRecByEmpIdDate',
concat(EmpId,'+',Date)
)[1]
)
]
"/>
</Record>
</xsl:template>
<xsl:template match="Record" mode="datetime">
<Detail>
<xsl:copy-of select="Date"/>
<xsl:variable name="vTotal" select=
"sum(key('kRecByEmpIdDate', concat(EmpId,'+',Date))/Hours)"/>
<RegHours>
<xsl:value-of select="8*($vTotal >= 8) + $vTotal*(not($vTotal >= 8))"/>
</RegHours>
<xsl:if test="$vTotal > 8">
<OTHours>
<xsl:value-of select="$vTotal -8"/>
</OTHours>
</xsl:if>
</Detail>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided XML document:
<Input>
<Record>
<EmpId>1</EmpId>
<Date></Date>
<Hours></Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/1/2012</Date>
<Hours>6</Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/1/2012</Date>
<Hours>4</Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/2/2012</Date>
<Hours>4</Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/2/2012</Date>
<Hours></Hours>
</Record>
<Record>
<EmpId>1</EmpId>
<Date>12/3/2012</Date>
<Hours>3</Hours>
</Record>
<Record>
<EmpId>2</EmpId>
<Date>12/1/2012</Date>
<Hours>4</Hours>
</Record>
<Record>
<EmpId>2</EmpId>
<Date>12/1/2012</Date>
<Hours></Hours>
</Record>
</Input>
produces the wanted, correct result:
<Record>
<EmployeeId>1</EmployeeId>
<Detail>
<Date>12/1/2012</Date>
<RegHours>8</RegHours>
<OTHours>2</OTHours>
</Detail>
<Detail>
<Date>12/2/2012</Date>
<RegHours>4</RegHours>
</Detail>
<Detail>
<Date>12/3/2012</Date>
<RegHours>3</RegHours>
</Detail>
</Record>
<Record>
<EmployeeId>2</EmployeeId>
<Detail>
<Date>12/1/2012</Date>
<RegHours>4</RegHours>
</Detail>
</Record>