XSLT Group by unique parameter - xslt

I am trying to convert the Input xml value to Output xml using xslt based on for-each-group logic of RAPID_ID
Input.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Response>
<Output>
<ID>1234</ID>
<CustomerName>KUMAR</CustomerName>
<BranchName>HARBOUR</BranchName>
<SchemeName>GOLD</SchemeName>
<MobileNumber>123456789</MobileNumber>
<CustomerType>PRIMARY</CustomerType>
<DedupeFound>NO</DedupeFound>
</Output>
<Output>
<ID>1234</ID>
<CustomerName>SEAN</CustomerName>
<BranchName>HARBOUR</BranchName>
<SchemeName>GOLD</SchemeName>
<MobileNumber>123456789</MobileNumber>
<CustomerType>SECONDARY</CustomerType>
<DedupeFound>YES</DedupeFound>
</Output>
<Output>
<ID>5678</ID>
<CustomerName>MARK</CustomerName>
<BranchName>CANTONMENT</BranchName>
<SchemeName>DIAMOND</SchemeName>
<MobileNumber>123456789</MobileNumber>
<CustomerType>PRIMARY</CustomerType>
<DedupeFound>NO</DedupeFound>
</Output>
<Output>
<ID>5678</ID>
<CustomerName>STEVE</CustomerName>
<BranchName>CANTONMENT</BranchName>
<SchemeName>DIAMOND</SchemeName>
<MobileNumber>123456789</MobileNumber>
<CustomerType>SECONDARY</CustomerType>
<DedupeFound>YES</DedupeFound>
</Output>
</Response>
My Expected output is
Output.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Response>
<Output>
<ID>1234</ID>
<CustomerName>KUMAR</CustomerName>
<BranchName>HARBOUR</BranchName>
<SchemeName>GOLD</SchemeName>
<MobileNumber>123456789</MobileNumber>
<DedupeDetails>
<CustomerType>PRIMARY</CustomerType>
<CustomerName>KUMAR</CustomerName>
<DedupeFound>NO</DedupeFound>
</DedupeDetails>
<DedupeDetails>
<CustomerType>SECONDARY</CustomerType>
<CustomerName>SEAN</CustomerName>
<DedupeFound>YES</DedupeFound>
</DedupeDetails>
</Output>
<Output>
<ID>5678</ID>
<CustomerName>MARK</CustomerName>
<BranchName>CANTONMENT</BranchName>
<SchemeName>DIAMOND</SchemeName>
<MobileNumber>123456789</MobileNumber>
<DedupeDetails>
<CustomerType>PRIMARY</CustomerType>
<CustomerName>MARK</CustomerName>
<DedupeFound>NO</DedupeFound>
</DedupeDetails>
<DedupeDetails>
<CustomerType>SECONDARY</CustomerType>
<CustomerName>STEVE</CustomerName>
<DedupeFound>YES</DedupeFound>
</DedupeDetails>
</Output>
</Response>
I started with something like this but not able to proceed further.
I am trying to group ID parameters first, Inside that It start with primary customer Details.
After Primary customer Details I have to iterate each customer ( Both Primary & secondary Here )
Any Suggestions / Corrections to make this achievable.
My XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Response>
<xsl:for-each-group select="/Response/Output" group-by="ID">
<xsl:sort select="ID"/>
</xsl:for-each-group>
</Response>
</xsl:template>
</xsl:stylesheet>

I would take a different approach:
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="op" match="Output" use="ID"/>
<xsl:template match="/Response">
<xsl:copy>
<xsl:for-each select="Output[CustomerType='PRIMARY']">
<xsl:copy>
<xsl:copy-of select="ID|CustomerName|BranchName|SchemeName"/>
<xsl:for-each select="key('op', ID)">
<DedupeDetails>
<xsl:copy-of select="CustomerType|CustomerName|DedupeFound"/>
</DedupeDetails>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note that this assumes that each group has exactly one PRIMARY customer and zero or more SECONDARY customers. If the input always has one PRIMARY customer followed by one SECONDARY customer, as in your example, then this could be even simpler.

Related

Splitting Multiline XML tag into multiple Nodes

I have a XML below, where new lines are added after each line at Note__c tag. I need to produce the XML by splitting them into multiple Note__c tags.
Input XML-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Note__c>01/15/2020
123456
DFGRTE766
6tgBFR</Note__c>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
Where Note__c contains multiple strings with new line added after each(except the last one)
Expected Output -
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c>123456</Note__c>
<Note__c>DFGRTE766</Note__c>
<Note__c>6tgBFR</Note__c>
</Notes>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
I have written this XSLT but it is missing few tags under the payload element -
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload">
<Notes>
<xsl:for-each select="tokenize(Note__c,'\n')">
<Note__c>
<xsl:value-of select="."/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
Output of this-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c> 123456</Note__c>
<Note__c> DFGRTE766</Note__c>
<Note__c> 6tgBFR</Note__c>
</Notes>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
not sure what is missing.
Thanks
Sugata
Change your XSLT to
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload/Note__c">
<Notes>
<xsl:for-each select="tokenize(.,'\n')">
<Note__c>
<xsl:value-of select="normalize-space(.)"/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

XSLT transformation with multiple nodes

I have multiple occurence nodes which need to be generated at output using XSLT transformation. Could you please help me on this.
Following XSLT code only generate one node occurrence only. Could you please help me with below XSLT code how to generate multiple nodes elements in Input XML
Input XML
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Body>
<ns1:getGenResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
<ns1:getGenReturn xsi:type="soapenc:Array" soapenc:arrayType="xsd:anyType[2]" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
</ns1:getGenReturn>
</ns1:getGenResponse>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:Gen" xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>
<name xsi:type="xsd:string">ULM</name>
<mail xsi:type="xsd:string">ulm#gmail.com</mail>
</multiRef>
<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns3:Gen" " xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">ABC</name>
<mail xsi:type="xsd:string">abc#gmail.com</mail>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
XSLT Code used for this transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" x
xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:if test="//soap:Body/multiRef">
<xsl:element name="getGenResponse">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="//name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="//mail"/></xsl:element>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- 'Copy ' node -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output from above XSLT
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
/getGenResponse>
Output expected
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
<getGenReturn>
<name>ABC</name>
<mail>abc#gmail.com<mail>
</getGenReturn>
/getGenResponse>
At you moment all you are doing is testing a multiRef element exists, and outputting only one new getGenReturn element.
All you really need to do is replace the xsl:if with xsl:for-each to select all the elements, then you will get one getGenReturn for each. And also change the xsl:value-of to use a relative path
<xsl:template match="/">
<xsl:element name="getGenResponse">
<xsl:for-each select="//soap:Body/multiRef">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="mail"/></xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
Or better still, do this, as xsl:element is not really needed here if you are using static names
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
Note, you don't actually need the identity template in this case. Try this XSLT:
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>

Gather filename information into XML node

I have several XML files, as follows:
file : 1.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val1</info1>
<info2>val2</info2>
</info>
<info>
<info1>val3</info1>
<info2>val4</info2>
</info>
</config>
file : 2.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val5</info1>
<info2>val6</info2>
</info>
<info>
<info1>val7</info1>
<info2>val8</info2>
</info>
</config>
file: 3.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val9</info1>
<info2>val10</info2>
</info>
<info>
<info1>val11</info1>
<info2>val12</info2>
</info>
</config>
using XSLT2.0 (saxon), I would like to merge them and also add to each node:
<info3>XXX</info3>
and also
<file>filename.xml</file>
filename.xml was the file from which the info has been copied.
The output should look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val1</info1>
<info2>val2</info2>
<info3>XXX</info3>
<file>1.xml</file>
</info>
<info>
<info1>val3</info1>
<info2>val4</info2>
<info3>XXX</info3>
<file>1.xml</file>
</info>
<info>
<info1>val5</info1>
<info2>val6</info2>
<info3>XXX</info3>
<file>2.xml</file>
</info>
<info>
<info1>val7</info1>
<info2>val8</info2>
<info3>XXX</info3>
<file>2.xml</file>
</info>
<info>
<info1>val9</info1>
<info2>val10</info2>
<info3>XXX</info3>
<file>3.xml</file>
</info>
<info>
<info1>val11</info1>
<info2>val12</info2>
<info3>XXX</info3>
<file>3.xml</file>
</info>
</config>
So far I have been able to merge the file by creating an XML file that lists the file I want to merge (merge.xml):
<mergeData newRoot="config">
<filelist>
<fileItem>1.xml</fileItem>
<fileItem>2.xml</fileItem>
<fileItem>3.xml</fileItem>
</filelist>
</mergeData>
using the following XSL (merge.xsl):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:param name="new">
<info>XXX</info>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{mergeData/#newRoot}">
<xsl:apply-templates select="mergeData/fileList/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(translate(., '\', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
<xsl:copy>
<xsl:copy-of select="node()"/>
<xsl:copy-of select="$new"/>
</xsl:copy>
<file><xsl:value-of select="tokenize(document-uri(.), '/')[last()]"/></file>
</xsl:template>
How should I modify the XSL to get the filename into each info at the same time.
Really the only thing you should have to do is move file inside of the xsl:copy.
Example (with a couple of other minor mods):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:output indent="yes"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{mergeData/#newRoot}">
<xsl:apply-templates select="mergeData/filelist/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(translate(., '\', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
<xsl:copy>
<xsl:copy-of select="node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/), '/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You could also do this using collection() instead of creating the separate mergeData.xml file:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="newRoot" select="'config'"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{$newRoot}">
<xsl:apply-templates select="collection('file:///C:/some/path?select=[0-9]*.xml')/*/info"/>
</xsl:element>
</xsl:template>
<xsl:template match="info">
<xsl:copy>
<xsl:copy-of select="#*|node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
One additional alternative, since you're using Saxon, is to use saxon:discard-document() along with your mergeData.xml input. If you have a lot of files listed in mergeData.xml, this can help with memory consumption. (It does require Saxon PE or EE or an older version of Saxon that allows the extension functions.)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/mergeData">
<xsl:element name="{#newRoot}">
<xsl:apply-templates select="filelist/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(.)/saxon:discard-document(.)/*/*" xmlns:saxon="http://saxon.sf.net/"/>
</xsl:template>
<xsl:template match="info">
<xsl:copy>
<xsl:copy-of select="#*|node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The following XSLT yields your required result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:param name="new">
<info>XXX</info>
</xsl:param>
<xsl:template match="/mergeData">
<config>
<xsl:for-each select="filelist/fileItem">
<xsl:variable name="filename" select="text()"/>
<xsl:for-each select="document($filename)/config/info">
<info>
<xsl:copy-of select="./*"/>
<xsl:element name="info{count(*)+1}">
<xsl:value-of select="$new"/>
</xsl:element>
<file><xsl:value-of select="$filename"/></file>
</info>
</xsl:for-each>
</xsl:for-each>
</config>
</xsl:template>
</xsl:stylesheet>
Notes:
This already works with XSLT 1.0, hence I changed the XSLT declaration.
Since this approach is top down with a predefined sub structure it does not use the attribute newRoot of your input file anymore.
The answer does not extract the basename of your input files but uses the full path supplied in the merge configuration. You may want to revert this simplification. Of course, using tokenize pushes it back to XSLT 2.0 or extended functions.

Grouping of same tags under an element in xslt

I am working XSLT where the source looks like this.
Source:
<Data>
<AB>all</AB>
<AB>all2</AB>
<CD>hhhhhh</CD>
<DE>hhhshhh</DE>
</Data>
Need to write XSLT to get output as
<Info>
<XXX>
<TTT value="all"/>
<TTT value="all2"/>
</XXX>
<!-- ....-->
<!-- ..to het all the elements.. -->
</Info>
I have to write xslt to match tag.
<xsl:template match="AB">
</xsl:template>
I can do it by matching Data tag.
<?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="Data">
<info>
<XXX>
<xsl:for-each select="AB">
<TTT>
<xsl:attribute name="value">
<xsl:value-of select="."/>
</xsl:attribute>
</TTT>
</xsl:for-each>
</XXX>
</info>
</xsl:template>
</xsl:stylesheet>
Can any one help me out how to do it by matching AB tag
<xsl:template match="AB">
</xsl:template>
Thank you.
I think you are asking how do you use xsl:apply-templates. If so, your XSLT would look like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:apply-templates select="AB"/>
</XXX>
</info>
</xsl:template>
<xsl:template match="AB">
<TTT value="{.}"/>
</xsl:template>
</xsl:stylesheet>
Do also note the use of Attribute Value Templates in the AB template to simplify the XSLT.
If you also require the other non-AB elements to be output unchanged, you would make use of the identity transform in your XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:apply-templates select="AB"/>
</XXX>
<xsl:apply-templates select="node()[not(self::AB)]" />
</info>
</xsl:template>
<xsl:template match="AB">
<TTT value="{.}"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This would also match all non-AB elements, outputing following the XXX element. In your case, it would output the following:
<info>
<XXX>
<TTT value="all" />
<TTT value="all2" />
</XXX>
<CD>hhhhhh</CD>
<DE>hhhshhh</DE>
</info>
Of course, there is no reason you couldn't have other templates matching elements like CD or DE to transform those too.

Removing duplicates in xsl

I have the following XML and I want to process it so that I do not get duplicates in the result set. I have simplified the problem to make it easier to understand. Also how can I use the results of one template as part of the input for another?
This:
<LINES xmlns:set="http://exslt.org/sets">
<STDINSTRSEQ>0</STDINSTRSEQ>
<STDINSTRSEQ>1</STDINSTRSEQ>
<STDINSTRSEQ>2</STDINSTRSEQ>
</LINES>
is the desired result. <STDINSTRSEQ> is the name of the field that should be treated as the key. I have provided the sample data here.
<PURCHASEORDER>
<POHEADER>
<REQUISITIONNO>1103025T12 000 000</REQUISITIONNO>
<REQLDATE>2004-10-26</REQLDATE>
<MTLREQDT></MTLREQDT>
<CREATEDT>2005-03-16</CREATEDT>
<VNDRLONGNM>DORI FOODS, INC.</VNDRLONGNM>
</POHEADER>
<LINES>
<LINE>
<POLINENBR>12</POLINENBR>
<STDINSTRDESC>NOTE: THIS PURCHASE ORDER SERVES AS CONFIRMATION</STDINSTRDESC>
<STDINSTRSEQ>0</STDINSTRSEQ>
</LINE>
<LINE>
<POLINENBR>11</POLINENBR>
<STDINSTRDESC>NOTE: THIS PURCHASE ORDER SERVES AS CONFIRMATION</STDINSTRDESC>
<STDINSTRSEQ>0</STDINSTRSEQ>
</LINE>
<LINE>
<POLINENBR>11</POLINENBR>
<STDINSTRDESC>THAT WE ACCEPT THE TERMS AND CONDITIONS OUTLINED IN</STDINSTRDESC>
<STDINSTRSEQ>1</STDINSTRSEQ>
</LINE>
<LINE>
<POLINENBR>12</POLINENBR>
<STDINSTRDESC>THAT WE ACCEPT THE TERMS AND CONDITIONS OUTLINED IN</STDINSTRDESC>
<STDINSTRSEQ>1</STDINSTRSEQ>
</LINE>
<LINE>
<POLINENBR>23</POLINENBR>
<STDINSTRDESC>YOUR CONTRACT DATED 02/16/2007 FOR THE FOLLOWING</STDINSTRDESC>
<STDINSTRSEQ>2</STDINSTRSEQ>
</LINE>
<LINE>
<POLINENBR>22</POLINENBR>
<STDINSTRDESC>YOUR CONTRACT DATED 02/16/2007 FOR THE FOLLOWING</STDINSTRDESC>
<STDINSTRSEQ>2</STDINSTRSEQ>
</LINE>
</LINES>
</PURCHASEORDER>
If I understand correctly what you are asking for, then given your sample XML this would work
<?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" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates select="/PURCHASEORDER/LINES" />
</xsl:template>
<xsl:template match="LINES">
<LINES xmlns:set="http://exslt.org/sets">
<xsl:apply-templates select="LINE/STDINSTRSEQ[not(. = preceding::STDINSTRSEQ)]"/>
</LINES>
</xsl:template>
<xsl:template match="STDINSTRSEQ">
<xsl:copy> <xsl:apply-templates/> </xsl:copy>
</xsl:template>
</xsl:stylesheet>
An optimized version that uses <xsl:key> to filter and provide unique results:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:key name="STDIN" match="STDINSTRSEQ" use="./text()" />
<xsl:template match="/">
<xsl:apply-templates select="/PURCHASEORDER/LINES" />
</xsl:template>
<xsl:template match="LINES">
<LINES xmlns:set="http://exslt.org/sets">
<xsl:apply-templates select="LINE/STDINSTRSEQ[generate-id(.) = generate-id(key('STDIN', .))]"/>
</LINES>
</xsl:template>
<xsl:template match="STDINSTRSEQ">
<xsl:copy> <xsl:apply-templates/> </xsl:copy>
</xsl:template>
</xsl:stylesheet>
I'm not sure about the apply-templates. I don't understand how they work. So for a XML that was like that:
<xml>
<line>209</line>
<line>209</line>
<line>209</line>
<line>100</line>
<line>101</line>
<line>101</line>
<line>100</line>
<line>102</line>
<line>209</line>
<line>101</line>
<line>101</line>
<line>101</line>
<line>101</line>
<line>209</line>
<line>100</line>
</xml>
to remove the repetitions, leaving only unique values:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="no" />
<xsl:template match="/">
<Output>
<xsl:for-each select="//line">
<xsl:if test="not(. = preceding::line)">
<line>
<xsl:value-of select="string(.)" disable-output-escaping="no" />
</line>
</xsl:if>
</xsl:for-each>
</Output>
</xsl:template>
</xsl:stylesheet>
And the result was:
<Output>
<line>100</line>
<line>101</line>
<line>102</line>
<line>209</line>
</Output>
And it was much quicker than the solution provided by Mads Hansen.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:key name="kSTDINSTRSEQ" match="STDINSTRSEQ" use="." />
<xsl:template match="PURCHASEORDER">
<xsl:apply-templates select="LINES" />
</xsl:template>
<xsl:template match="LINES">
<LINES xmlns:set="http://exslt.org/sets">
<xsl:copy-of select="
LINE/STDINSTRSEQ[
generate-id() = generate-id(key('kSTDINSTRSEQ', .)[1])
]
" />
</LINES>
</xsl:template>
</xsl:stylesheet>
produces
<LINES xmlns:set="http://exslt.org/sets">
<STDINSTRSEQ>0</STDINSTRSEQ>
<STDINSTRSEQ>1</STDINSTRSEQ>
<STDINSTRSEQ>2</STDINSTRSEQ>
</LINES>
To access this from another XSL template, write it to a temporary file and use the document() function. Alternatively, just do the grouping in the other XSLT file.