XSLT: iteration on string with indexes - xslt

I am having a trouble with very easy XSLT transformation. Let's assume that this is the input XML document:
<root>
<myString>ABBCD</myString>
</root>
The output XML should be:
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>
Is there an easy way to split such string and increment index over it?

It's pretty easy in XSLT 2.0...
XML Input
<root>
<myString>ABBCD</myString>
</root>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="myString">
<myCharacters>
<xsl:analyze-string select="." regex=".">
<xsl:matching-substring>
<character>
<id><xsl:value-of select="position()"/></id>
<value><xsl:value-of select="."/></value>
</character>
</xsl:matching-substring>
</xsl:analyze-string>
</myCharacters>
</xsl:template>
</xsl:stylesheet>
XML Output
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>
It's not terrible in 1.0 either. You can use a recursive template. The following will produce the same output:
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="myString">
<myCharacters>
<xsl:call-template name="analyzeString">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</myCharacters>
</xsl:template>
<xsl:template name="analyzeString">
<xsl:param name="pos" select="1"/>
<xsl:param name="string"/>
<character>
<id><xsl:value-of select="$pos"/></id>
<value><xsl:value-of select="substring($string,1,1)"/></value>
</character>
<xsl:if test="string-length($string)>=2">
<xsl:call-template name="analyzeString">
<xsl:with-param name="pos" select="$pos+1"/>
<xsl:with-param name="string" select="substring($string,2)"/>
</xsl:call-template>
</xsl:if>
</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:variable name="vStyle" select="document('')"/>
<xsl:template match="myString">
<xsl:variable name="vStr" select="."/>
<root>
<myCharacters>
<xsl:for-each select=
"($vStyle//node()|$vStyle//#*|$vStyle//namespace::*)
[not(position() > string-length($vStr))]">
<character>
<id><xsl:value-of select="position()"/></id>
<value><xsl:value-of select="substring($vStr,position(),1)"/></value>
</character>
</xsl:for-each>
</myCharacters>
</root>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<root>
<myString>ABBCD</myString>
</root>
the wanted, correct result is produced:
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>
Alternatively, with FXSL one can use the str-map template like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:testmap="testmap" xmlns:f="http://fxsl.sf.net/"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="xsl f ext testmap">
<xsl:import href="str-dvc-map.xsl"/>
<testmap:testmap/>
<xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="myString">
<xsl:variable name="vrtfChars">
<xsl:call-template name="str-map">
<xsl:with-param name="pFun" select="$vTestMap"/>
<xsl:with-param name="pStr" select="."/>
</xsl:call-template>
</xsl:variable>
<myCharacters>
<xsl:apply-templates select="ext:node-set($vrtfChars)/*"/>
</myCharacters>
</xsl:template>
<xsl:template name="enumChars" match="*[namespace-uri() = 'testmap']"
mode="f:FXSL">
<xsl:param name="arg1"/>
<character>
<value><xsl:value-of select="$arg1"/></value>
</character>
</xsl:template>
<xsl:template match="character">
<character>
<id><xsl:value-of select="position()"/></id>
<xsl:copy-of select="*"/>
</character>
</xsl:template>
</xsl:stylesheet>
to produce the same correct result:
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
II. XSLT 2.0 solution -- shorter and simpler than other answers:
<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="myString">
<root>
<myCharacters>
<xsl:for-each select="string-to-codepoints(.)">
<character>
<id><xsl:value-of select="position()"/></id>
<value><xsl:value-of select="codepoints-to-string(.)"/></value>
</character>
</xsl:for-each>
</myCharacters>
</root>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document (above), produces the wanted, correct result:
<root>
<myCharacters>
<character>
<id>1</id>
<value>A</value>
</character>
<character>
<id>2</id>
<value>B</value>
</character>
<character>
<id>3</id>
<value>B</value>
</character>
<character>
<id>4</id>
<value>C</value>
</character>
<character>
<id>5</id>
<value>D</value>
</character>
</myCharacters>
</root>

Related

Multiply 2 filtered numbers and sum

I need to sum the multiplication of 2 numbers based on this example
<test>
<stop>
<id>1</id>
<unit_id>1</unit_id>
<unit_id>2</unit_id>
</stop>
<stop>
<id>2</id>
<unit_id>1</unit_id>
<unit_id>3</unit_id>
</stop>
<unit>
<id>1</id>
<count>2</count>
<value>1</value>
</unit>
<unit>
<id>2</id>
<count>4</count>
<value>1</value>
</unit>
<unit>
<id>3</id>
<count>2</count>
<value>3</value>
</unit>
The result i want to get is the one below
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>10</sum>
</stop>
Any tips how to get it?
I tried with this example but the sum of the moltiplication doesn't work, it is ok for only the sum or the multiplication but not both
<xsl:template match="stop">
<xsl:variable name="ship_unit" select="id"/>
<xsl:value-of select="sum(following-sibling::unit[id=$ship_unit]/count*following-sibling::unit[id=$ship_unit]/value)"/>
If I am guessing correctly, you want to do something like:
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="unit" match="unit" use="id" />
<xsl:template match="/test">
<xsl:copy>
<xsl:for-each select="stop">
<xsl:variable name="unit1" select="key('unit', unit_id[1])" />
<xsl:variable name="unit2" select="key('unit', unit_id[2])" />
<xsl:copy>
<xsl:copy-of select="id"/>
<sum>
<xsl:value-of select="$unit1/count * $unit1/value + $unit2/count * $unit2/value" />
</sum>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, the result of applying this to your input example will be:
<?xml version="1.0" encoding="utf-8"?>
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>8</sum>
</stop>
</test>
and not what you posted.

Conversion for multiqual

I am trying to convert the following input XML based on grouping of qualifier but its not working and not giving me expected output.
Below is the Input XML which has to be comverted.
<document>
<item>
<gtin>1000909090</gtin>
<attrGroupMany name="foodAndBevPreparationInfo">
<row>
<attr name="preparationType">BOILING</attr>
<attrQualMany name="preparationInstructions">
<value qual="en">Prep 8</value>
<value qual="en">Prep 9</value>
<value qual="ar">Test</value>
</attrQualMany>
</row>
</attrGroupMany>
</item>
</document>
The XSLT which I am using but not giving me expected output.
XSLT:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="prepmvl" match="preparationInstructions" use="concat(generate-id(..), '|', #qual)" />
<xsl:template match="document">
<CatalogItem>
<RelationshipData>
<xsl:for-each select="item/attrGroupMany[#name ='foodAndBevPreparationInfo']/row">
<Relationship>
<RelationType>Item_Master_Food_And_Bev_Prep_MVL</RelationType>
<RelatedItems count="{count(attrQualMany[#name='preparationInstructions']/value[generate-id() = generate-id(key('prepmvl', concat(generate-id(..), '|', #qual))[1])])}">
<xsl:apply-templates select="attrQualMany[#name='preparationInstructions']/value[generate-id() = generate-id(key('prepmvl', concat(generate-id(..), '|', #qual))[1])]"/>
</RelatedItems>
</Relationship>
</xsl:for-each>
</RelationshipData>
</CatalogItem>
</xsl:template>
<xsl:template match="preparationInstructions">
<RelatedItem1 referenceKey="{concat('Food_And_Bev_Prep_MVL','-',ancestor::item/gtin,'-',attr[#name='preparationType'],'-',#qual)}"/>
</xsl:template>
</xsl:stylesheet>
And the expected output should be
<?xml version="1.0" encoding="UTF-8"?>
<CatalogItem>
<RelationshipData>
<Relationship>
<RelationType>Item_Master_Food_And_Bev_Prep_MVL</RelationType>
<RelatedItems count="2">
<RelatedItem1 referenceKey="Food_And_Bev_Prep_MVL-1000909090-BOILING-en" />
<RelatedItem1 referenceKey="Food_And_Bev_Prep_MVL-1000909090-BOILING-ar" />
</RelatedItems>
</Relationship>
</RelationshipData>
</CatalogItem>
you need to change
<xsl:key name="prepmvl" match="preparationInstructions" use="concat(generate-id(..), '|', #qual)" />
to
<xsl:key name="prepmvl" match="value" use="concat(generate-id(..), '|', #qual)" />
and
<xsl:template match="preparationInstructions">
<RelatedItem1 referenceKey="{concat('Food_And_Bev_Prep_MVL','-',ancestor::item/gtin,'-',attr[#name='preparationType'],'-',#qual)}"/>
</xsl:template>
to
<xsl:template match="value">
<RelatedItem1 referenceKey="{concat('Food_And_Bev_Prep_MVL','-',ancestor::item/gtin,'-',../preceding-sibling::attr[#name='preparationType'],'-',#qual)}"/>
</xsl:template>

XSLT for XML Filtering the repeating group

I have the input XML as
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<document>
<item>
<functionalName lang="en">Filte</functionalName>
<functionalName lang="hin">test1</functionalName>
<functionalName lang="chi">Filters2</functionalName>
<functionalName lang="hin">Filters3</functionalName>
</item>
<item>
<functionalName lang="en">Filte</functionalName>
<functionalName lang="chi">Filters</functionalName>
<functionalName lang="en">Filters1</functionalName>
</item>
And the desired output after parsing through the XSLT is mentioned below
XSLT is mentioned at the end of the query
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<CatalogItem>
<RelationshipData>
<Relationship>
<RelationType>Descriptions_for_Item</RelationType>
<RelatedItems count="3">
<RelatedItem1 referenceKey="ITEM_DESCRIPTION-functionalName-en" />
<RelatedItem1 referenceKey="ITEM_DESCRIPTION-functionalName-hin" />
<RelatedItem1 referenceKey="ITEM_DESCRIPTION-functionalName-chi" />
</RelatedItems>
</Relationship>
</RelationshipData>
<RelationshipData>
<Relationship>
<RelationType>Descriptions_for_Item</RelationType>
<RelatedItems count="2">
<RelatedItem1 referenceKey="ITEM_DESCRIPTION-functionalName-en" />
<RelatedItem1 referenceKey="ITEM_DESCRIPTION-functionalName-chi" />
</RelatedItems>
</Relationship>
</RelationshipData>
XSLT which I am using is not giving me the desired response. Please help
<xsl:output method="xml" indent="yes" />
<xsl:key name="functional" match="functionalName" use="#lang" />
<xsl:template match="document">
<CatalogItem>
<xsl:for-each select="item">
<RelationshipData>
<Relationship>
<RelationType>Descriptions_for_Item</RelationType>
<RelatedItems>
<xsl:attribute name="count">
<xsl:value-of select="count(functionalName[generate-id(.)=generate-id(key('functional',#lang)[1])])"/>
</xsl:attribute>
<xsl:apply-templates select="functionalName[generate-id(.)=generate-id(key('functional',#lang)[1])]"/>
</RelatedItems>
</Relationship>
</RelationshipData>
</xsl:for-each>
</CatalogItem>
</xsl:template>
<xsl:template match="functionalName">
<xsl:for-each value="{#lang}">
<xsl:variable name="language" select="../#lang" />
<RelatedItem1>
<xsl:attribute name="referenceKey">
<xsl:value-of select="concat('ITEM_DESCRIPTION','-','functionalName','-',../#lang)"/>
</xsl:attribute>
</RelatedItem1>
</xsl:for-each>
</xsl:template>
Change your key from
<xsl:key name="functional" match="functionalName" use="#lang" />
to
<xsl:key name="functional" match="functionalName" use="concat(generate-id(..), '|', #lang)" />
then you can use
<RelatedItems count="{count(functionalName[generate-id() = generate-id(key('functional', concat(generate-id(..), '|', #lang))[1])])}">
and
<xsl:apply-templates select="functionalName[generate-id() = generate-id(key('functional', concat(generate-id(..), '|', #lang))[1])]"/>
and finally you can simplify the template for functionaName to
<xsl:template match="functionalName">
<RelatedItem1 referenceKey="{concat('ITEM_DESCRIPTION','-','functionalName','-', #lang)}"/>
</xsl:template>
So the complete samples is
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="functional" match="functionalName" use="concat(generate-id(..), '|', #lang)" />
<xsl:template match="document">
<CatalogItem>
<xsl:for-each select="item">
<RelationshipData>
<Relationship>
<RelationType>Descriptions_for_Item</RelationType>
<RelatedItems count="{count(functionalName[generate-id() = generate-id(key('functional', concat(generate-id(..), '|', #lang))[1])])}">
<xsl:apply-templates select="functionalName[generate-id() = generate-id(key('functional', concat(generate-id(..), '|', #lang))[1])]"/>
</RelatedItems>
</Relationship>
</RelationshipData>
</xsl:for-each>
</CatalogItem>
</xsl:template>
<xsl:template match="functionalName">
<RelatedItem1 referenceKey="{concat('ITEM_DESCRIPTION','-','functionalName','-', #lang)}"/>
</xsl:template>
</xsl:stylesheet>

xslt: how to normalize numerical element

I would like to add an element to existing XML file with normalized value of existing element in the XML.
Any help would be extremely appreciated.
regards
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>no</a>
<b>8</b>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>no</a>
<b>6</b>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<c>32</c>
<d>34</d>
</Results>
</top>
In the sample input xml above, I would like to add "b_nom" elements to each of the results where the value is (b)/(minimum of 'b' grouped with a). The expected output is as below
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<b_nom>1.66</b_nom>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>no</a>
<b>8</b>
<b_nom>1.33</b_nom>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>no</a>
<b>6</b>
<b_nom>1</b_nom>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<b_nom>1</b_nom>
<c>32</c>
<d>34</d>
</Results>
</top>
I think you want
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="k1" match="Results" use="a"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:call-template name="identity"/>
<xsl:variable name="min">
<xsl:for-each select="key('k1', ../a)">
<xsl:sort select="b" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="b"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<b_nom>
<xsl:value-of select="format-number(. div $min, '0.##')"/>
</b_nom>
</xsl:template>
</xsl:stylesheet>
Here is a changed version of the stylesheet that takes a couple of values identifying a group into account:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:data="http:://example.com/data"
exclude-result-prefixes="data">
<data:data xmlns="">
<group>
<key>no</key>
<values>
<value>no</value>
<value>n</value>
<value>0</value>
</values>
</group>
<group>
<key>yes</key>
<values>
<value>yes</value>
<value>y</value>
<value>1</value>
</values>
</group>
</data:data>
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="main-input" select="/"/>
<xsl:variable name="groups" select="document('')/xsl:stylesheet/data:data/group"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:call-template name="identity"/>
<xsl:variable name="this" select="."/>
<xsl:variable name="min">
<xsl:for-each select="$main-input//Results[a = $groups/values[value = $this/../a]/value]">
<xsl:sort select="b" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="b"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<b_nom>
<xsl:value-of select="format-number(. div $min, '0.##')"/>
</b_nom>
</xsl:template>
</xsl:stylesheet>
That transforms the input
<?xml version="1.0" encoding="UTF-8"?>
<top>
<Results>
<a>no</a>
<b>10</b>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>n</a>
<b>8</b>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>0</a>
<b>6</b>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<c>32</c>
<d>34</d>
</Results>
</top>
into the output
<top>
<Results>
<a>no</a>
<b>10</b>
<b_nom>1.67</b_nom>
<c>12</c>
<d>9</d>
</Results>
<Results>
<a>n</a>
<b>8</b>
<b_nom>1.33</b_nom>
<c>50</c>
<d>12</d>
</Results>
<Results>
<a>0</a>
<b>6</b>
<b_nom>1</b_nom>
<c>55</c>
<d>56</d>
</Results>
<Results>
<a>yes</a>
<b>23</b>
<b_nom>1</b_nom>
<c>32</c>
<d>34</d>
</Results>
</top>

Get distinct values from xml

My sample xml looks below: I need to get the distinct states from xml. I am using xslt 1.0 in vs 2010 editor.
<?xml version="1.0" encoding="utf-8" ?>
<states>
<node>
<value>2</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>NJ</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
</states>
My xslt looks like below:
<?xml version="1.0" encoding="utf-8"?>
<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:user="urn:my-scripts">
<xsl:output method="text" indent="yes"/>
<xsl:key name="st" match="//states/node/state" use="." />
<xsl:variable name="disst">
<xsl:for-each select="//states/node[contains(value,1)]/state[generate-id()=generate-id(key('st',.)[1])]" >
<xsl:choose>
<xsl:when test="(position() != 1)">
<xsl:value-of select="concat(', ',.)" disable-output-escaping="yes"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/" >
<xsl:value-of disable-output-escaping="yes" select="$disst"/>
</xsl:template>
</xsl:stylesheet>
Output: DE,NJ,NY
My above xml looks good for the above test xml.
If I change the xml as below:
<?xml version="1.0" encoding="utf-8" ?>
<states>
<node>
<value>2</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>DE</state>
</node>
<node>
<value>1</value>
<state>NJ</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
<node>
<value>1</value>
<state>NY</state>
</node>
</states>
It in not picking the state DE. Can any one suggest the suitable solution.Thanks in advance.
I need to find out the distinct states from the xml.
The problem here is your use of a predicate in your Muenchian grouping XPath:
[contains(value,1)]
This will often make Muenchian grouping fail to find all of the available distinct values. Instead, you should add the predicate to the key:
<xsl:key name="st" match="//states/node[contains(value, 1)]/state" use="." />
Alternatively, you can apply the predicate inside the grouping statement:
<xsl:apply-templates
select="//states/node
/state[generate-id() =
generate-id(key('st',.)[contains(../value, 1)][1])]" />
Full XSLT (with some improvements):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:user="urn:my-scripts">
<xsl:output method="text" indent="yes"/>
<xsl:key name="st" match="//states/node/state" use="." />
<xsl:variable name="a" select="1" />
<xsl:variable name="disst">
<xsl:apply-templates
select="//states/node
/state[generate-id() =
generate-id(key('st',.)[contains(../value, $a)][1])]" />
</xsl:variable>
<xsl:template match="state">
<xsl:if test="position() > 1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:value-of select ="." disable-output-escaping="yes" />
</xsl:template>
<xsl:template match="/" >
<xsl:value-of disable-output-escaping="yes" select="$disst"/>
</xsl:template>
</xsl:stylesheet>
Result when run on your sample XML:
DE,NJ,NY