i need to remove prefixes using xslt-3and exclude-result-prefixes - xslt-3.0

input xml
<?xml version="1.0" encoding="UTF-8" ?>
<mr:collection
xmlns:mr="http://www.lc.gov/mr2/slim"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.lc.gov/mr2/slim http://www.lc.gov/standards/mrxml/schema/mr21slim.xsd">
<mr:rc>
<mr:dtf tg="2000" i1="1" i2=" ">
<mr:sbf cd="a">Christoph Kolumbus</mr:sbf>
<mr:sbf cd="d">John Diter</mr:sbf>
<mr:sbf cd="b">Julie Nat</mr:sbf>
<mr:sbf cd="f">Darius Milhaud</mr:sbf>
<mr:sbf cd="g">Erich kleiber</mr:sbf>
<mr:sbf cd="g">Franz Ludwig Horth</mr:sbf>
</mr:dtf>
<mr:dtf tg="3000" i1="1" i2=" ">
<mr:sbf cd="a">Christoph Kolumbus</mr:sbf>
<mr:sbf cd="d">Serg</mr:sbf>
<mr:sbf cd="b">Mak</mr:sbf>
<mr:sbf cd="f">DarMil</mr:sbf>
<mr:sbf cd="g">Erikl</mr:sbf>
<mr:sbf cd="g">LudHorth</mr:sbf>
</mr:dtf>
</mr:rc>
<mr:rc>
<mr:dtf tg="2000" i1="1" i2="0">
<mr:sbf cd="a">Chris Prante</mr:sbf>
<mr:sbf cd="e">"Chris Dietz"</mr:sbf>
</mr:dtf>
</mr:rc>
</mr:collection>
with the following xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:e="https://example.com"
xmlns:dc="https://examples.com"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<e:rc>
<xsl:apply-templates/>
</e:rc>
</xsl:template>
<xsl:template match="dtf[#tg = 2000]">
<mdtd>
<rc>
<dc:title xml:lang="el">{sbf[#cd = 'a']} {sbf[#cd = 'b']}{sbf[#cd = 'e']!(':', .)} {sbf[#cd = 'f']!('/', .)}{(sbf[#cd = 'g'] => string-join(' ; '))!('', .)}</dc:title>
</rc>
</mdtd>
</xsl:template>
<xsl:template match="dtf[#tg != 2000]"/>
</xsl:stylesheet>
we get
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<e:rc xmlns:e="https://example.com">
<mdtd>
<rc>
<dc:title xmlns:dc="https://examples.com" xml:lang="el">Christoph Kolumbus Julie Nat / Darius Milhaud Erich kleiber ; Franz Ludwig Horth</dc:title>
</rc>
</mdtd>
</e:rc>
<e:rc xmlns:e="https://example.com">
<mdtd>
<rc>
<dc:title xmlns:dc="https://examples.com" xml:lang="el">Chris Prante : "Chris Dietz" </dc:title>
</rc>
</mdtd>
</e:rc>
</ListRcs>
</O-PM>
how can one get <e:rc> instead of <e:rc xmlns:e="https://example.com">?
exclude-result-prefixes should work for literal result elements we create in our XSLT code... I need to declare namespaces and i am not getting the output i want.
ie <e:rc> instead of <e:rc xmlns:e="https://example.com">
and <dc:title xml:lang="el"> instead of <dc:title xmlns:dc="https://examples.com" xml:lang="el">

If you use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:e="https://example.com"
xmlns:dc="https://examples.com"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
i.e. don't exclude the prefixes e and dc you declared for serialization you will get a root element declaring them e.g.
<O-PM xmlns:dc="https://examples.com" xmlns:e="https://example.com">
and content like
<e:rc>
<mdtd>
<rc>
<dc:title xml:lang="el">Christoph Kolumbus Julie Nat / Darius Milhaud Erich kleiber ; Franz Ludwig Horth</dc:title>
</rc>
</mdtd>
</e:rc>

Related

extract and print values only when they exist using xslt

The source xml is:
<?xml version="1.0" encoding="UTF-8" ?>
<mr:collection
xmlns:mr="http://www.lc.gov/mr2/slim"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.lc.gov/mr2/slim http://www.lc.gov/standards/mrxml/schema/mr21slim.xsd">
<mr:rc>
<mr:ctr tg="1000">311</mr:ctr>
<mr:dtf tg="12000" i1="1" i2=" ">
<mr:sbf cd="d">John Diter</mr:sbf>
</mr:dtf>
</mr:rc>
</mr:collection>
the xsl that i use:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://www.example.com"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:mr="http://www.lc.gov/mr2/slim"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<rc>
<xsl:apply-templates/>
</rc>
</xsl:template>
<xsl:template match="mr:ctr[#tg = 1000]">
<xsl:variable name="dz" as="xs:string">{tg ='1000'}!</xsl:variable><d:z xml:lang="en">{normalize-space($dz)}</d:z>
</xsl:template>
<xsl:template match="mr:dtf[#tg = 12000]">
<xsl:variable name="ds" as="xs:string">{sbf[#cd = 'a']}!</xsl:variable><d:s xml:lang="en">{normalize-space($ds)}</d:s>
<xsl:variable name="dp" as="xs:string">{sbf[#cd = 'c']}!</xsl:variable><d:p>{normalize-space($dp)}</d:p>
<xsl:variable name="dc" as="xs:string">{sbf[#cd = 'd']}!</xsl:variable><d:c>{normalize-space($dc)}</d:c>
</xsl:template>
</xsl:stylesheet>
Current output:
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<rc>
<d:z xmlns:d="http://www.example.com">false!</d:z>
<d:s xmlns:d="http://www.example.com" xml:lang="en">!</d:s>
<d:p xmlns:d="http://www.example.com">!</d:p>
<d:c xmlns:d="http://www.example.com">John Diter!</d:c>
</rc>
</ListRcs>
</O-PM>
Desired output:
<?xml version="1.0" encoding="UTF-8"?>
<O-PM>
<ListRcs>
<rc>
<d:z xmlns:d="http://www.example.com">311</d:z>
<d:c xmlns:d="http://www.example.com">John Diter!</d:c>
</rc>
</ListRcs>
</O-PM>
For starters i need to get as output the value of tg = 1000 and not false,
and secondly, how can one print only the values that exist?
In the above example only the value that matches the criterion cd = d is TRUE.
https://xsltfiddle.liberty-development.net/asoTKA/2
It seems, this time you can just match on the elements that interest you:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://www.example.com"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:mr="http://www.lc.gov/mr2/slim"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<rc>
<xsl:apply-templates/>
</rc>
</xsl:template>
<xsl:template match="mr:ctr[#tg = 1000]">
<d:z xml:lang="en">{.}</d:z>
</xsl:template>
<xsl:template match="dtf[#tg = 12000]/sbf[#cd = 'd']">
<d:c>{.}!</d:c>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/asoTKA/3
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:d="http://www.example.com"
xpath-default-namespace="http://www.lc.gov/mr2/slim"
xmlns:mr="http://www.lc.gov/mr2/slim"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="collection">
<O-PM>
<ListRcs>
<xsl:apply-templates/>
</ListRcs>
</O-PM>
</xsl:template>
<xsl:template match="rc">
<rc>
<xsl:apply-templates/>
</rc>
</xsl:template>
<xsl:template match="mr:ctr[#tg = 1000]">
<xsl:variable name="dz" as="xs:string"><xsl:value-of select="."/></xsl:variable>
<d:z xml:lang="en"><xsl:value-of select="normalize-space($dz)"/></d:z>
</xsl:template>
<xsl:template match="mr:dtf[#tg = 12000]">
<xsl:variable name="dc" as="xs:string">
<xsl:if test="mr:sbf[#cd = 'd']"><xsl:value-of select="normalize-space(.)"/> </xsl:if>
</xsl:variable>
<d:c xml:lang="en"><xsl:value-of select="normalize-space($dc)"/></d:c>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/asoTKA/5

Key Match using accumulators in XSLT3

I am looking to use accumulators in my xslt3 below and process only unmatched keys and ignore others.
I want loop through each All_Time_Offs/Time_Off and if Time_Off_Key is present in the Payroll_Input/Input_Key -> Then do not process. Else process the record
<?xml version="1.0" encoding="UTF-8"?>
<Aggregated_TimeOffs>
<All_Payroll_Inputs>
<Payroll_Input>
<RefID>PAYROLL_INPUT-6-122898</RefID>
<Emp_ID>101058</Emp_ID>
<Earning>101D</Earning>
<Start_Date/>
<Adjustment>0</Adjustment>
<Hours>4</Hours>
<Input_Key>PAYROLL_INPUT-6-122898101D4</Input_Key>
</Payroll_Input>
<Payroll_Input>
<RefID>PAYROLL_INPUT-6-122898</RefID>
<Emp_ID>101058</Emp_ID>
<Earning>101D</Earning>
<Start_Date/>
<Adjustment>0</Adjustment>
<Hours>4</Hours>
<Input_Key>PAYROLL_INPUT-6-122898101D9</Input_Key>
</Payroll_Input>
</All_Payroll_Inputs>
<All_Time_Offs>
<Time_Off>
<RefID/>
<Emp_ID>29519</Emp_ID>
<Earning/>
<Date>2020-09-10</Date>
<Adjustment/>
<Hours>7</Hours>
<Cost_Center/>
<Week>Week_2</Week>
<Time_Off_Key>PAYROLL_INPUT-6-122898101D4</Time_Off_Key>
</Time_Off>
<Time_Off>
<RefID/>
<Emp_ID>68413</Emp_ID>
<Earning/>
<Date>2020-09-09</Date>
<Adjustment/>
<Hours>8</Hours>
<Cost_Center/>
<Week>Week_2</Week>
<Time_Off_Key>INT024_PAYROLL_INPUT_2020-09-098</Time_Off_Key>
</Time_Off>
</All_Time_Offs>
</Aggregated_TimeOffs>
Below is the XSLT I am trying, but not working.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:err="http://www.w3.org/2005/xqt-errors"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ext="urn:SomeExternalSource"
xmlns:xtt="urn:com.workday/xtt"
xmlns:wd="urn:com.workday/bsvc"
xmlns:this="urn:com.workday/this"
exclude-result-prefixes="xs ext map wd xtt this"
version="3.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"></xsl:output>
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="Time_Off_Key Payroll_Input_lookup emp.id"/>
<xsl:accumulator name="Payroll_Input_lookup" as="xs:string" initial-value="''" streamable="yes">
<xsl:accumulator-rule match="Input_Key/text()" select="."/>
</xsl:accumulator>
<xsl:accumulator name="Time_Off_Key" as="map(xs:string,xs:string)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Time_Off_Key/text()" select="map:put($value, string(.), accumulator-before('Payroll_Input_lookup'))"/>
</xsl:accumulator>
<xsl:accumulator name="emp.id" streamable="yes" as="xs:string" initial-value="''">
<xsl:accumulator-rule match="Emp_ID/text()" select="."/>
</xsl:accumulator>
<xsl:template match="Aggregated_TimeOffs">
<xsl:for-each select="All_Time_Offs/Time_Off/copy-of()">
<xsl:variable name="input_exists">
<xsl:value-of select="accumulator-before('Time_Off_Key')(normalize-space(Time_Off_Key))"/>
</xsl:variable>
<xsl:if test="string-length($input_exists) < 0">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
If you only want to compare the single values Time_Off_Key occured during "earlier" parsing of the Payroll_Input/Input_Key values then a single, xs:string*, i.e. string sequence based accumulator should suffice:
<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:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:mode streamable="yes" on-no-match="shallow-skip" use-accumulators="Payroll_Input_Input_Keys"/>
<xsl:accumulator name="Payroll_Input_Input_Keys" as="xs:string*" initial-value="()" streamable="yes">
<xsl:accumulator-rule match="Payroll_Input/Input_Key/text()" select="., $value"/>
</xsl:accumulator>
<xsl:template match="Time_Off[copy-of()[not(Time_Off_Key = accumulator-before('Payroll_Input_Input_Keys'))]]">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
It might not be streamable, however, as the use of copy-of() in the predicate of the pattern breaks the motion-less requirement of streamable patterns: so we need to write
<xsl:template match="Time_Off">
<xsl:copy-of select="copy-of()[not(Time_Off_Key = accumulator-before('Payroll_Input_Input_Keys'))]"/>
</xsl:template>
instead to make that code work with streaming.

How can I handle error Exception with try/catch using xslt 3.0

How can I handle error Exception with try/catch using xslt 3.0. I am finding in xml number Element. If number Element not find in parent product then generate a file error.txt and how to write the Exception.
Input XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<product dept="WMN">
<name language="en">Fleece Pullover</name>
<colorChoices>navy black</colorChoices>
</product>
<product dept="ACC">
<number>563</number>
<name language="en">Floppy Sun Hat</name>
</product>
<product dept="ACC">
<number>443</number>
<name language="en">Deluxe Travel Bag</name>
</product>
<product dept="MEN">
<number>784</number>
<name language="en">Cotton Dress Shirt</name>
<colorChoices>white gray</colorChoices>
<desc>Our <i>favorite</i> shirt!</desc>
</product>
</catalog>
Using XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs"
version="3.0">
<xsl:template match="/">
<xsl:stream href="books.xml">
<xsl:iterate select="catalog">
<xsl:result-document href="out.xml" omit-xml-declaration="no" indent="yes">
<xsl:copy>
<xsl:for-each select="product">
<xsl:try>
<xsl:copy-of select="number"/>
<xsl:catch>
<xsl:result-document href="error.txt">
<xsl:message>Element number not given in <xsl:value-of select="product/#dept"/></xsl:message>
<error code="{$err:code}" message="{$err:description}"/>
</xsl:result-document>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</xsl:copy>
</xsl:result-document>
</xsl:iterate>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>
My requirement is how can I uses try/catch when not find the number element then make a file with error.
Try this to generate error message in log file by taking the value in variable in then check some conditon:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs"
version="3.0">
<xsl:template match="/">
<xsl:stream href="books.xml">
<xsl:iterate select="catalog">
<xsl:result-document href="out.xml" omit-xml-declaration="no" indent="yes">
<xsl:copy>
<xsl:for-each select="product">
<xsl:variable name="num" select="if (number) then (number) else ('aaaa')"/>
<xsl:try>
<number>
<xsl:value-of select="format-number($num, '############')"/>
</number>
<xsl:catch>
<xsl:result-document href="error.log" omit-xml-declaration="yes">
<xsl:value-of select="concat(' Element number not given in ', #dept)"/>
<xsl:value-of select="concat(' Error code: ', $err:code, ' and Error Desc is: ', $err:description)"/>
</xsl:result-document>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</xsl:copy>
</xsl:result-document>
</xsl:iterate>
</xsl:stream>
</xsl:template>
</xsl:stylesheet>

SharePoint Designer Data View- XSLT Count

I am working with a data view web part in SPD 2010. My xml structure is as follows:
<ProjectGroups>
<ProjectGroup>
<GroupID>1</GroupID>
<ProjectName>Project 1</ProjectName>
</ProjectGroup>
<ProjectGroup>
<GroupID>2</GroupID>
<ProjectName>Project 2</ProjectName>
</ProjectGroup>
<ProjectGroup>
<GroupID>2</GroupID>
<ProjectName>Project 3</ProjectName>
</ProjectGroup>
</ProjectGroups>
This is a rollup web part, so what I am looking to do is get a count of Projects under each Project group. For my example above, Group ID 1 has 1 project, Group ID 2 has 2. I am sure there's a way to do this, but I'm sort of learning xslt on the fly, so I'm not sure exactly what to do. Any help is appreciated. Thanks.
This style-sheet ...
<?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:key name="groups" match="ProjectGroup" use="GroupID" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="ProjectGroup[generate-id()=generate-id(key('groups',GroupID)[1])]">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:attribute name="count-of-projects">
<xsl:value-of select="count(key('groups',GroupID))" />
</xsl:attribute>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
... when applied to your input, will produce ...
<?xml version="1.0" encoding="utf-8"?>
<ProjectGroups>
<ProjectGroup count-of-projects="1">
<GroupID>1</GroupID>
<ProjectName>Project 1</ProjectName>
</ProjectGroup>
<ProjectGroup count-of-projects="2">
<GroupID>2</GroupID>
<ProjectName>Project 2</ProjectName>
</ProjectGroup>
<ProjectGroup>
<GroupID>2</GroupID>
<ProjectName>Project 3</ProjectName>
</ProjectGroup>
</ProjectGroups>

how to concatenate string with two different delimiters with xsl

I have a XML
<main>
<DATA_RECORD>
<COMPONENT_SID>100</COMPONENT_SID>
<GROUP_ID>1</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>200</COMPONENT_SID>
<GROUP_ID>1</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>400</COMPONENT_SID>
<GROUP_ID>1</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>10</COMPONENT_SID>
<GROUP_ID>2</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>20</COMPONENT_SID>
<GROUP_ID>2</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>2</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>4</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>8</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>16</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
</main>
I would like to use xsl to parse into another XML. The output would be
<comp value="100,200,400|10,20|2,4,8,16"/>
where the component_sids belonged to different group_id are separated by "|" . The component_sids belonged to the same group id should be concatenated with ",". I used the following xsl
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:element name="comp">
<xsl:attribute name="value">
<xsl:call-template name="join">
<xsl:with-param name="list" select="//DATA_RECORD[GROUP_ID=1]/COMPONENT_SID" />
<xsl:with-param name="separator" select="','" />
</xsl:call-template>
</xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template name="join">
<xsl:param name="list" />
<xsl:param name="separator"/>
<xsl:for-each select="$list">
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:value-of select="$separator" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The result is
<comp value="100,200,400"/>
But I could not figured out how to separate other groups of component_sid with "|". Can someone help me?
Thanks in advance
The XSLT 2.0 solution can be shortened further to:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vResult">
<xsl:for-each-group select="*/*" group-by="GROUP_ID">
<xsl:if test="not(position()=1)">|</xsl:if>
<xsl:value-of select="current-group()/COMPONENT_SID" separator=","/>
</xsl:for-each-group>
</xsl:variable>
<comp value="{$vResult}"/>
</xsl:template>
</xsl:stylesheet>
I. XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kRecByGrId" match="DATA_RECORD"
use="GROUP_ID"/>
<xsl:variable name="vGrIds" select=
"/*/DATA_RECORD
[generate-id()
=
generate-id(key('kRecByGrId', GROUP_ID)[1])
]
/GROUP_ID
"/>
<xsl:template match="/">
<xsl:variable name="vResult">
<xsl:for-each select="$vGrIds">
<xsl:if test="not(position()=1)">|</xsl:if>
<xsl:apply-templates select=
"key('kRecByGrId', .)/COMPONENT_SID"/>
</xsl:for-each>
</xsl:variable>
<comp value="{$vResult}"/>
</xsl:template>
<xsl:template match="COMPONENT_SID">
<xsl:if test="not(position()=1)">,</xsl:if>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<main>
<DATA_RECORD>
<COMPONENT_SID>100</COMPONENT_SID>
<GROUP_ID>1</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>200</COMPONENT_SID>
<GROUP_ID>1</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>400</COMPONENT_SID>
<GROUP_ID>1</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>10</COMPONENT_SID>
<GROUP_ID>2</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>20</COMPONENT_SID>
<GROUP_ID>2</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>2</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>4</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>8</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
<DATA_RECORD>
<COMPONENT_SID>16</COMPONENT_SID>
<GROUP_ID>3</GROUP_ID>
</DATA_RECORD>
</main>
produces the wanted, correct result:
<comp value="100,200,400|10,20|2,4,8,16"/>
Explanation:
Muenchian method for grouping -- to find all distinct values of GROUP_ID .
Simple logic to precede an item (or group) with a delimiter, whenever this item (or group) isn't the first.
II. XSLT 2.0 Solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vResult">
<xsl:for-each-group select="*/*" group-by="GROUP_ID">
<xsl:if test="not(position()=1)">|</xsl:if>
<xsl:for-each select="current-group()/COMPONENT_SID">
<xsl:if test="not(position()=1)">,</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:for-each-group>
</xsl:variable>
<comp value="{$vResult}"/>
</xsl:template>
</xsl:stylesheet>
when applied to the same XML document (above), again the wanted, correct result is produced:
<comp value="100,200,400|10,20|2,4,8,16"/>
Explanation:
Use of <xsl:for-each-group>
Use of current-group()
The same simple logic for preceding each item (or group), which isn't the first, with the respective delimiter.

Resources