XSL iterate over attribute and ignore some elements - xslt-1.0

I need to do rather simple thing of iterating over elements in my XML(more explanation under the XML)
<form>
<field index="1" name="field_X_1" group="firstGroup" type="String">
<value>Value of Field X 1 of first group</value>
</field>
<field index="1" name="field_Y_1" group="firstGroup" type="String">
<value>Value of Field Y 1 of first group</value>
</field>
<field index="2" name="field_X_2" group="firstGroup" type="String">
<value>Value of Field X 2 of first group</value>
</field>
<field index="2" name="field_Y_2" group="firstGroup" type="String">
<value>Value of Field Y 2 of first group</value>
</field>
<field index="1" name="field_A_1" group="secondGroup" type="String">
<value>Value of Field A 1 of second group</value>
</field>
<field index="1" name="field_B_1" group="secondGroup" type="String">
<value>Value of Field B 1 of second group</value>
</field>
<field index="2" name="field_A_2" group="secondGroup" type="String">
<value>Value of Field A 2 of second group</value>
</field>
<field index="2" name="field_B_2" group="secondGroup" type="String">
<value>Value of Field B 2 of second group</value>
</field>
</form>
Right now I am iterating over ALL fields of firstGroup and everytime I found that index has changed(by comparing index to index of sibbling) I add new line character).
Problem is I need only to iterate over index (but still keep distiction between firstGroup and secondGroup).
My output should look like
Value of Field X 1 of first group, Value of Field Y 1 of first group
Value of Field X 2 of first group, Value of Field Y 2 of first group
Value of Field A 1 of second group, Value of Field B 1 of second group
Value of Field A 2 of second group, Value of Field B 2 of second group
etc
My XSL now looks like
<xsl:for-each select='/form/field[#group="firstGroup"]'>
<xsl:sort select="#index" order="ascending"></xsl:sort>
<xsl:if test='preceding-sibling::*[#group="firstGroup"][1]/#index != #index and count(preceding-sibling::*) != 0 '>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:value-of select="//field[#name=concat('field_X_', #index,')]/value"/>
<xsl:value-of select="//field[#name=concat('field_Y_', #index,')]/value"/>
</xsl:for-each>
<!-- Differente iteration for second group-->
<xsl:text>
</xsl:text>
<xsl:for-each select='/form/field[#group="firstGroup"]'>
<xsl:sort select="#index" order="ascending"></xsl:sort>
<xsl:if test='preceding-sibling::*[#group="firstGroup"][1]/#index != #index and count(preceding-sibling::*) != 0 '>
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:value-of select="//field[#name=concat('field_A_', #index,')]/value"/>
<xsl:value-of select="//field[#name=concat('field_B_', #index,')]/value"/>
</xsl:for-each>

<xsl:output method="text" />
<xsl:template match="form">
<!--* find all the groups *-->
<xsl:variable name="groups">
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="field" />
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="process-all-groups">
<xsl:with-param name="group-names" select="$groups" />
</xsl:call-template>
</xsl:template>
<xsl:template name="get-group-names">
<xsl:param name="nodes" />
<xsl:param name="result-so-far" />
<xsl:choose>
<xsl:when test="not($nodes)">
<xsl:value-of select="$result-so-far" />
</xsl:when>
<xsl:when test="contains(
concat(' ', $result-so-far),
concat(' ', $nodes[1]/#group, ' '))" >
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="result-so-far" select="$result-so-far" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="get-group-names">
<xsl:with-param name="nodes" select="$nodes[position() > 1]" />
<xsl:with-param name="result-so-far"
select="concat($result-so-far, $nodes[1]/#group, ' ')" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="process-all-groups">
<xsl:param name="group-names" />
<xsl:variable name="group" select="substring-before($group-names, ' ')"/>
<xsl:if test="not(string-length($group) = 0)">
<xsl:call-template name="index">
<xsl:with-param name="n" select="1" />
<xsl:with-param name="group" select="$group" />
</xsl:call-template>
<xsl:call-template name="process-all-groups">
<xsl:with-param name="group-names"
select="substring-after($group-names, ' ')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="index">
<xsl:param name="n" select="1" />
<xsl:param name="group" />
<xsl:variable name="with-n"
select="/form/field[#group = $group][#index = $n]" />
<xsl:if test="$with-n">
<xsl:for-each select="$with-n">
<xsl:sort use="#index" order="ascending" />
<xsl:value-of select="value" />
<xsl:if test="not(position() = last())">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:call-template name="index">
<xsl:with-param name="n" select="$n + 1" />
<xsl:with-param name="group" select="$group" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="field"></xsl:template>

Related

XSLT 1.0 Json Array [duplicate]

How would you convert JSON to XML?
Consider:
<sampleTag>
{
"Order": {
"InvestmentAccount": { "AccountNumber": "10" },
"Parcel": {
"Limit": "0",
"ExpiryDate": "1900-01-01T00:00:00",
"Asset": [
{
"Open": "25.15",
"High": "25.15",
"Low": "25.11",
"Close": "25.87"
}
{
"Open": "25.15",
"High": "25.15",
"Low": "25.11",
"Close": "25.87"
}]
},
"OrderDate": "2012-10-11T21:46:03.6489906+11:00",
}
}
</sampleTag>
After transformation, the document is as follows:
<Order>
<InvestmentAccount>
<AccountNumber>10</AccountNumber>
</InvestmentAccount>
<Parcel>
<Limit>0</Limit>
<ExpiryDate>1900-01-01T00:00:00</ExpiryDate>
<Asset>
<Open>25.15</Open>
<High>25.15</High>
<Low>25.11</Low>
<Close>25.87</Close>
</Asset>
<Asset>
<Open>25.15</Open>
<High>25.15</High>
<Low>25.11</Low>
<Close>25.87</Close>
</Asset>
</Parcel>
<OrderDate>2012-10-11T21:46:03.6489906+11:00</OrderDate>
</Order>
My work on JSON parsing doesn't cover the full JSON grammar.
And the task of "translating" any JSON document to an XML document doesn't have a solution. There are JSON constructs, which cannot be translated to XML without defining additional conventions and introducing additional elements -- so the final XML structure isn't a true and natural representation of the original JSON object.
In XSLT 3.0 there is a function to parse any JSON object -- parse-json() -- to a map -- a new data type introduced in XSLT 3.0. Read about this here:
http://www.w3.org/TR/xslt-30/#json
Actually, it is not that hard. The way to approach it is to examine the syntax of jason, and view each production like it was a template. I was just about to write a solution, when I considered the possibility that the OP forgot to google for pre-existing solutions. I searched and lo and behold ....
http://dnovatchev.wordpress.com/2007/07/05/transforming-json/
UPDATE
Here is a JSon to XML converter. But it only works on a subset of json. Hopefully, the subset is broad enough for your particular needs. In particular the limitations are:
The only simple type supported is string. No integer, boolean or null.
Json object names must be valid xml element names.
No escape codes permitted inside string values. This means that you cant transport values that include, for instance, the " character (without rolling your own encoding layer).
This XSLT 1.0 style-sheet...*
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx"
xmlns:exsl="http://exslt.org/common"
xmlns:so="http://stackoverflow.com/questions/13007280"
exclude-result-prefixes="xsl xs json so exsl">
<xsl:output indent="yes" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="quot" select="'"'" />
<xsl:template match="/*">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($t1)/so:output/*" mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
<xsl:template name="field">
<!-- Input like: "Open": "25.15" bla -->
<!-- output like: <so:output><Open>25.15</Open></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:variable name="field-name" select="substring-before(substring-after($json-in,$quot),$quot)" />
<xsl:variable name="remainder" select="substring-after($json-in,':')" />
<xsl:call-template name="value">
<xsl:with-param name="json-in" select="$remainder" />
<xsl:with-param name="parent-ele" select="$field-name" />
</xsl:call-template>
</xsl:template>
<xsl:template name="fields">
<!-- Input like: "Open": "25.15" , "High": "25.15" } bla -->
<!-- output like: <so:output><Open>25.15</Open><High>25.15</High></so:output> <so:extra>} bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:variable name="n" select="normalize-space($json-in)" />
<xsl:choose>
<xsl:when test="substring($n,1,1) = $quot">
<xsl:variable name="t1">
<xsl:call-template name="field">
<xsl:with-param name="json-in" select="$n" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) " />
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)=','">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="substring-after($t2,',')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra><xsl:value-of select="$t2" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*" />
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra" />
</xsl:when>
<xsl:when test="$n">
<so:extra><xsl:value-of select="$n" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="object">
<!-- Input like: { X } bla -->
<!-- output like: <so:output>fields(X)</so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" select="''" />
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'{'))" />
<xsl:variable name="t2">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="$t1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3" select="normalize-space(substring-after( exsl:node-set($t2)/so:extra, '}'))" />
<so:output>
<xsl:choose>
<xsl:when test="$parent-ele">
<xsl:element name="{$parent-ele}">
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()" />
</xsl:otherwise>
</xsl:choose>
</so:output>
<xsl:if test="$t3">
<so:extra><xsl:value-of select="$t3" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="objects">
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="n" select="normalize-space($json-in)" />
<xsl:choose>
<xsl:when test="substring($n,1,1) = '{'">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$n" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) " />
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t2" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra><xsl:value-of select="$t2" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*" />
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra" />
</xsl:when>
<xsl:when test="$n">
<so:extra><xsl:value-of select="$n" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="array">
<!-- Input like: [ X1 X2 ] bla -->
<!-- output like: <so:output><Y>X1</Y><Y>X2</Y></so:output> <so:extra>}bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'['))" />
<xsl:variable name="t2">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t1" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3" select="normalize-space(substring-after( exsl:node-set($t2)/so:extra, ']'))" />
<xsl:copy-of select="exsl:node-set($t2)/so:output" />
<xsl:if test="$t3">
<so:extra><xsl:value-of select="$t3" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="value">
<!-- Input like either array, object or string -->
<!-- output like either array, object or string -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="first-letter" select="substring(normalize-space($json-in),1,1)" />
<xsl:choose>
<xsl:when test="$first-letter='{'">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter='['">
<xsl:call-template name="array">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter=$quot">
<xsl:call-template name="string">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<so:output>ERROR</so:output>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="string">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value" select="substring-before(substring-after($json-in,$quot),$quot)" />
<xsl:variable name="remainder" select="normalize-space(substring-after(substring-after($json-in,$quot),$quot))" />
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
...applied to this input (modified from OP supplied to remove an extraneous comma)...
<sampleTag>
{
"Order": {
"InvestmentAccount": { "AccountNumber": "10" },
"Parcel": {
"Limit": "0",
"ExpiryDate": "1900-01-01T00:00:00",
"Asset": [
{
"Open": "25.15",
"High": "25.15",
"Low": "25.11",
"Close": "25.87"
}
{
"Open": "25.15",
"High": "25.15",
"Low": "25.11",
"Close": "25.87"
}]
},
"OrderDate": "2012-10-11T21:46:03.6489906+11:00"
}
}
</sampleTag>
..yields...
<Order>
<InvestmentAccount>
<AccountNumber>10</AccountNumber>
</InvestmentAccount>
<Parcel>
<Limit>0</Limit>
<ExpiryDate>1900-01-01T00:00:00</ExpiryDate>
<Asset>
<Open>25.15</Open>
<High>25.15</High>
<Low>25.11</Low>
<Close>25.87</Close>
</Asset>
<Asset>
<Open>25.15</Open>
<High>25.15</High>
<Low>25.11</Low>
<Close>25.87</Close>
</Asset>
</Parcel>
<OrderDate>2012-10-11T21:46:03.6489906+11:00</OrderDate>
</Order>
I tweaked Sean B. Durkin's template a bit and thought I'd share.
Updates include:
Support for numbers
Support for booleans
Fix for object array elements separated by a comma (per JSON spec)
Non-update changes:
Array elements are displayed in their own XML elements with the element name as the object key suffixed with _element
Still not supported:
Escaped characters (quotes) in strings
Here's the template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx"
xmlns:exsl="http://exslt.org/common"
xmlns:so="http://stackoverflow.com/questions/13007280"
exclude-result-prefixes="xsl xs json so exsl">
<xsl:output indent="yes" encoding="UTF-8" />
<xsl:strip-space elements="*" />
<xsl:variable name="quot" select="'"'" />
<xsl:variable name="numbers" select="'0123456789'"/>
<xsl:variable name="booleans" select="'tf'"/>
<xsl:template match="/*">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($t1)/so:output/*" mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
<xsl:template name="field">
<!-- Input like: "Open": "25.15" bla -->
<!-- output like: <so:output><Open>25.15</Open></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:variable name="field-name" select="substring-before(substring-after($json-in,$quot),$quot)" />
<xsl:variable name="remainder" select="substring-after($json-in,':')" />
<xsl:call-template name="value">
<xsl:with-param name="json-in" select="$remainder" />
<xsl:with-param name="parent-ele" select="$field-name" />
</xsl:call-template>
</xsl:template>
<xsl:template name="fields">
<!-- Input like: "Open": "25.15" , "High": "25.15" } bla -->
<!-- output like: <so:output><Open>25.15</Open><High>25.15</High></so:output> <so:extra>} bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:variable name="n" select="normalize-space($json-in)" />
<xsl:choose>
<xsl:when test="substring($n,1,1) = $quot">
<xsl:variable name="t1">
<xsl:call-template name="field">
<xsl:with-param name="json-in" select="$n" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) " />
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)=','">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="substring-after($t2,',')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra><xsl:value-of select="$t2" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*" />
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra" />
</xsl:when>
<xsl:when test="$n">
<so:extra><xsl:value-of select="$n" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="object">
<!-- Input like: { X } bla -->
<!-- output like: <so:output>fields(X)</so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" select="''" />
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'{'))" />
<xsl:variable name="t2">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="$t1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3" select="normalize-space(substring-after( exsl:node-set($t2)/so:extra, '}'))" />
<so:output>
<xsl:choose>
<xsl:when test="$parent-ele">
<xsl:element name="{$parent-ele}">
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()" />
</xsl:otherwise>
</xsl:choose>
</so:output>
<xsl:if test="$t3">
<so:extra><xsl:value-of select="$t3" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="objects">
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="n" select="normalize-space($json-in)" />
<xsl:choose>
<xsl:when test="substring($n,1,1) = '{'">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$n" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) " />
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t2" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="substring($t2,1,1)=',' and substring(normalize-space(substring-after($t2,',')),1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="normalize-space(substring-after($t2,','))" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra><xsl:value-of select="$t2" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*" />
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra" />
</xsl:when>
<xsl:when test="$n">
<so:extra><xsl:value-of select="$n" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="array">
<!-- Input like: [ X1 X2 ] bla -->
<!-- output like: <so:output><Y>X1</Y><Y>X2</Y></so:output> <so:extra>}bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'['))" />
<xsl:variable name="t2">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t1" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="contains(substring-before(exsl:node-set($t2)/so:extra,']'),',')">
<xsl:value-of select="normalize-space(substring-after(exsl:node-set($t2)/so:extra,','))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-after(exsl:node-set($t2)/so:extra,']'))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="t4">
<xsl:element name="{$parent-ele}">
<xsl:for-each select="$t2/so:output/*[local-name(.)=$parent-ele]">
<xsl:variable name="self" select="."/>
<xsl:variable name="tempResult">
<xsl:element name="{concat($parent-ele,'_element')}">
<xsl:copy-of select="exsl:node-set($self/*)" />
</xsl:element>
</xsl:variable>
<xsl:copy-of select="exsl:node-set($tempResult)"/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:variable name="t5" select="exsl:node-set($t4)"/>
<so:output>
<xsl:copy-of select="$t5"/>
</so:output>
<xsl:if test="$t3">
<so:extra><xsl:value-of select="$t3" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="value">
<!-- Input like either array, object or string -->
<!-- output like either array, object or string -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="first-letter" select="substring(normalize-space($json-in),1,1)" />
<xsl:choose>
<xsl:when test="$first-letter='{'">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter='['">
<xsl:call-template name="array">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter=$quot">
<xsl:call-template name="string">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($numbers,$first-letter)">
<xsl:call-template name="number">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($booleans,$first-letter)">
<xsl:call-template name="boolean">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<so:output>ERROR</so:output>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="string">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value" select="substring-before(substring-after($json-in,$quot),$quot)" />
<xsl:variable name="remainder" select="normalize-space(substring-after(substring-after($json-in,$quot),$quot))" />
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="number">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="normalize-space(substring-before($json-in,'}'))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="normalize-space(substring-before($json-in,']'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($json-in,','))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="remainder">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="concat('}',normalize-space(substring-after($json-in,'}')))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="concat(']',normalize-space(substring-after($json-in,']')))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(',',normalize-space(substring-after($json-in,',')))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="boolean">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="normalize-space(substring-before($json-in,'}'))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="normalize-space(substring-before($json-in,']'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($json-in,','))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="remainder">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="concat('}',normalize-space(substring-after($json-in,'}')))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="concat(']',normalize-space(substring-after($json-in,']')))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(',',normalize-space(substring-after($json-in,',')))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
So with this (adjusted) input:
<?xml version="1.0" encoding="UTF-8"?>
<sampleTag><![CDATA[
{
"Order": {
"InvestmentAccount": { "AccountNumber": "10" },
"Parcel": {
"Limit": 0,
"ExpiryDate": "1900-01-01T00:00:00",
"valid": true,
"Asset": [
{
"Open": 25.15,
"High": 25.15,
"Low": 25.11,
"Close": 25.87
},
{
"Open": 25.15,
"High": 25.15,
"Low": 25.11,
"Close": 25.87
}
]
},
"OrderDate": "2012-10-11T21:46:03.6489906+11:00"
}
}
]]></sampleTag>
I get this output:
<?xml version="1.0" encoding="UTF-8"?>
<Order>
<InvestmentAccount>
<AccountNumber>10</AccountNumber>
</InvestmentAccount>
<Parcel>
<Limit>0</Limit>
<ExpiryDate>1900-01-01T00:00:00</ExpiryDate>
<valid>true</valid>
<Asset>
<Asset_element>
<Open>25.15</Open>
<High>25.15</High>
<Low>25.11</Low>
<Close>25.87</Close>
</Asset_element>
<Asset_element>
<Open>25.15</Open>
<High>25.15</High>
<Low>25.11</Low>
<Close>25.87</Close>
</Asset_element>
</Asset>
</Parcel>
<OrderDate>2012-10-11T21:46:03.6489906+11:00</OrderDate>
</Order>
Updating Samuel Murphy answer.
Updates include:
Support for null
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:json="http://www.ibm.com/xmlns/prod/2009/jsonx"
xmlns:exsl="http://exslt.org/common"
xmlns:so="http://stackoverflow.com/questions/13007280"
exclude-result-prefixes="xsl xs json so exsl">
<xsl:output indent="yes" encoding="UTF-8" />
<xsl:strip-space elements="*" />
<xsl:variable name="quot" select="'"'" />
<xsl:variable name="numbers" select="'0123456789'"/>
<xsl:variable name="booleans" select="'tf'"/>
<xsl:variable name="nulls" select="'n'"/>
<xsl:template match="/*">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($t1)/so:output/*" mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
<xsl:template name="field">
<!-- Input like: "Open": "25.15" bla -->
<!-- output like: <so:output><Open>25.15</Open></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:variable name="field-name" select="substring-before(substring-after($json-in,$quot),$quot)" />
<xsl:variable name="remainder" select="substring-after($json-in,':')" />
<xsl:call-template name="value">
<xsl:with-param name="json-in" select="$remainder" />
<xsl:with-param name="parent-ele" select="$field-name" />
</xsl:call-template>
</xsl:template>
<xsl:template name="fields">
<!-- Input like: "Open": "25.15" , "High": "25.15" } bla -->
<!-- output like: <so:output><Open>25.15</Open><High>25.15</High></so:output> <so:extra>} bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:variable name="n" select="normalize-space($json-in)" />
<xsl:choose>
<xsl:when test="substring($n,1,1) = $quot">
<xsl:variable name="t1">
<xsl:call-template name="field">
<xsl:with-param name="json-in" select="$n" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) " />
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)=','">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="substring-after($t2,',')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra><xsl:value-of select="$t2" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*" />
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra" />
</xsl:when>
<xsl:when test="$n">
<so:extra><xsl:value-of select="$n" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="object">
<!-- Input like: { X } bla -->
<!-- output like: <so:output>fields(X)</so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" select="''" />
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'{'))" />
<xsl:variable name="t2">
<xsl:call-template name="fields">
<xsl:with-param name="json-in" select="$t1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3" select="normalize-space(substring-after( exsl:node-set($t2)/so:extra, '}'))" />
<so:output>
<xsl:choose>
<xsl:when test="$parent-ele">
<xsl:element name="{$parent-ele}">
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="exsl:node-set($t2)/so:output/node()" />
</xsl:otherwise>
</xsl:choose>
</so:output>
<xsl:if test="$t3">
<so:extra><xsl:value-of select="$t3" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="objects">
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="n" select="normalize-space($json-in)" />
<xsl:choose>
<xsl:when test="substring($n,1,1) = '{'">
<xsl:variable name="t1">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$n" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t2" select="normalize-space( exsl:node-set($t1)/so:extra) " />
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="substring($t2,1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t2" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="substring($t2,1,1)=',' and substring(normalize-space(substring-after($t2,',')),1,1)='{'">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="normalize-space(substring-after($t2,','))" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$t2">
<so:extra><xsl:value-of select="$t2" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:copy-of select="exsl:node-set($t1)/so:output/* | exsl:node-set($t3)/so:output/*" />
</so:output>
<xsl:copy-of select="exsl:node-set($t3)/so:extra" />
</xsl:when>
<xsl:when test="$n">
<so:extra><xsl:value-of select="$n" /></so:extra>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="array">
<!-- Input like: [ X1 X2 ] bla -->
<!-- output like: <so:output><Y>X1</Y><Y>X2</Y></so:output> <so:extra>}bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="t1" select="normalize-space(substring-after($json-in,'['))" />
<xsl:variable name="t2">
<xsl:call-template name="objects">
<xsl:with-param name="json-in" select="$t1" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="t3">
<xsl:choose>
<xsl:when test="contains(substring-before(exsl:node-set($t2)/so:extra,']'),',')">
<xsl:value-of select="normalize-space(substring-after(exsl:node-set($t2)/so:extra,','))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-after(exsl:node-set($t2)/so:extra,']'))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="t4">
<xsl:element name="{$parent-ele}">
<xsl:for-each select="$t2/so:output/*[local-name(.)=$parent-ele]">
<xsl:variable name="self" select="."/>
<xsl:variable name="tempResult">
<xsl:element name="{concat($parent-ele,'_element')}">
<xsl:copy-of select="exsl:node-set($self/*)" />
</xsl:element>
</xsl:variable>
<xsl:copy-of select="exsl:node-set($tempResult)"/>
</xsl:for-each>
</xsl:element>
</xsl:variable>
<xsl:variable name="t5" select="exsl:node-set($t4)"/>
<so:output>
<xsl:copy-of select="$t5"/>
</so:output>
<xsl:if test="$t3">
<so:extra><xsl:value-of select="$t3" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="value">
<!-- Input like either array, object or string -->
<!-- output like either array, object or string -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="first-letter" select="substring(normalize-space($json-in),1,1)" />
<xsl:choose>
<xsl:when test="$first-letter='{'">
<xsl:call-template name="object">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter='['">
<xsl:call-template name="array">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="$first-letter=$quot">
<xsl:call-template name="string">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele" />
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($numbers,$first-letter)">
<xsl:call-template name="number">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($booleans,$first-letter)">
<xsl:call-template name="boolean">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($nulls,$first-letter)">
<xsl:call-template name="boolean">
<xsl:with-param name="json-in" select="$json-in" />
<xsl:with-param name="parent-ele" select="$parent-ele"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<so:output>ERROR</so:output>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="string">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value" select="substring-before(substring-after($json-in,$quot),$quot)" />
<xsl:variable name="remainder" select="normalize-space(substring-after(substring-after($json-in,$quot),$quot))" />
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="number">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="normalize-space(substring-before($json-in,'}'))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="normalize-space(substring-before($json-in,']'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($json-in,','))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="remainder">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="concat('}',normalize-space(substring-after($json-in,'}')))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="concat(']',normalize-space(substring-after($json-in,']')))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(',',normalize-space(substring-after($json-in,',')))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
<xsl:template name="boolean">
<!-- Input like: "X" bla -->
<!-- output like: <so:output><Y>X</Y></so:output> <so:extra>bla</so:extra> -->
<xsl:param name="json-in" />
<xsl:param name="parent-ele" />
<xsl:variable name="value">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="normalize-space(substring-before($json-in,'}'))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="normalize-space(substring-before($json-in,']'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="normalize-space(substring-before($json-in,','))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="remainder">
<xsl:choose>
<xsl:when test="contains(substring-before($json-in,','),'}')">
<xsl:value-of select="concat('}',normalize-space(substring-after($json-in,'}')))"/>
</xsl:when>
<xsl:when test="contains(substring-before($json-in,','),']')">
<xsl:value-of select="concat(']',normalize-space(substring-after($json-in,']')))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(',',normalize-space(substring-after($json-in,',')))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<so:output>
<xsl:element name="{$parent-ele}">
<xsl:value-of select="$value" />
</xsl:element>
</so:output>
<xsl:if test="$remainder">
<so:extra><xsl:value-of select="$remainder" /></so:extra>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The question is tagged for XSLT 1.0, so I do not know if this answer will help the original question. But if you can use XSLT 3.0, the function json-to-xml does exactly what you need.
https://www.w3.org/TR/xslt-30/#func-json-to-xml
As well as Dimitre's XSLT parsing framework, there is also Gunther Rademacher's Rex parser generator, which also includes JSON as one of its sample grammars:
http://www.bottlecaps.de/rex/
Try this lib:
https://github.com/bramstein/xsltjson
Looks very nice.
Its a 2.0 XSLT solution, although he also points to 1.0 version.
XSLT has many strengths and a few big weaknesses. Text processing is its weakness at least version 1.0
Although it would be technically possible to process that text with XSLT 1.0, I can't think of any situation where it would be a very good idea, and where it wouldn't be a very fragile conversion. The code you would have to produce would be very unwieldy.
Is there no other language available to you to do the processing?

How to remove duplicate characters in string?

I have a string that looks like this: some, , , , text
I need it to look like this: some, text
There is unknown number of such commas, it may be 2, it may be 10
I tried recursive approach but it did not work as expected:
<xsl:template name="remove-duplicate">
<xsl:param name="text" />
<xsl:param name="remove" />
<xsl:value-of select="$text" />
<xsl:if test="contains($text, $remove)">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="replace" select="$remove" />
<xsl:with-param name="by" select="''" />
</xsl:call-template>
<xsl:call-template name="remove-duplicate">
<xsl:with-param name="text" select="$text" />
<xsl:with-param name="remove" select="$remove" />
</xsl:call-template>
</xsl:if>
</xsl:template>
Calling the template:
<xsl:call-template name="remove-duplicate">
<xsl:with-param name="text" select="string(.)" />
<xsl:with-param name="remove" select="' ,'" />
</xsl:call-template>
The string-replace-all was stolen from this question: XSLT string replace
How about:
<xsl:value-of select="translate(normalize-space(translate($yourString, ', ', ' …')), ' …', ', ')"/>
Note that this assumes your string does not contain any whitespace characters other than spaces.
If this assumption is not true, try the solution posted here: https://stackoverflow.com/a/38177946/3016153

How can I eliminate a duplicate result in XSLT 1.0?

I am using the Altova mapping tool and I cannot find an option on how to eliminate a duplicate value so I am trying to update the XSLT file directly and I cannot figure out how to do this. Below is the XSLT file, the problem is in the Detail06 section.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <xsl:template match="/">
<AsyncBarcode>
<xsl:variable name="var1_instance" select="." />
<xsl:for-each select="$var1_instance/n:SyncReceiveDelivery">
<xsl:variable name="var2_SyncReceiveDelivery" select="." />
<Prefix>
<xsl:for-each select="n:DataArea/n:Sync/n:AccountingEntityID">
<CompanyID>
<xsl:value-of select="string(.)" />
</CompanyID>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:Sync/n:AccountingEntityID">
<ExternalPartnerID>
<xsl:value-of select="string(.)" />
</ExternalPartnerID>
</xsl:for-each>
<DocumentType>
<xsl:value-of select="'AsyncBarcode'" />
</DocumentType>
<xsl:for-each select="n:ApplicationArea/n:BODID">
<DocumentNumber>
<xsl:value-of select="substring-after(substring-before(string(.), ':1?'), 'Infor:')" />
</DocumentNumber>
</xsl:for-each>
<TransactionDirection>
<xsl:value-of select="'I'" />
</TransactionDirection>
<DateStamp>
<xsl:value-of select="substring-before(string(n:ApplicationArea/n:CreationDateTime), 'T')" />
</DateStamp>
<TimeStamp>
<xsl:value-of select="substring-before(substring-after(string(n:ApplicationArea/n:CreationDateTime), 'T'), 'Z')" />
</TimeStamp>
<xsl:for-each select="n:ApplicationArea/n:BODID">
<ControlNumber>
<xsl:value-of select="substring-after(substring-before(string(.), ':1?'), 'Infor:')" />
</ControlNumber>
</xsl:for-each>
</Prefix>
<Header00>
<TransactionDefinitionKey>
<xsl:value-of select="'25'" />
</TransactionDefinitionKey>
<xsl:for-each select="n:DataArea/n:Sync/n:AccountingEntityID">
<CompanyID0>
<xsl:value-of select="string(.)" />
</CompanyID0>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryHeader/n:DocumentReference/n:DocumentID">
<xsl:variable name="var14_DocumentID" select="." />
<xsl:if test="$var14_DocumentID/n:ID/#location">
<BranchID>
<xsl:value-of select="substring-before(string(n:ID/#location), '-B')" />
</BranchID>
</xsl:if>
</xsl:for-each>
<UserID>
<xsl:value-of select="'WMUser'" />
</UserID>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryHeader/n:WarehouseLocation/n:ID">
<WarehouseID>
<xsl:value-of select="substring-before(string(.), '-W')" />
</WarehouseID>
</xsl:for-each>
</Header00>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryItem">
<Detail01>
<xsl:for-each select="$var2_SyncReceiveDelivery/n:DataArea/n:Sync/n:AccountingEntityID">
<Scanneddata1>
<xsl:value-of select="string(.)" />
</Scanneddata1>
</xsl:for-each>
</Detail01>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery">
<xsl:variable name="var22_ReceiveDelivery" select="." />
<xsl:for-each select="n:ReceiveDeliveryItem">
<Detail02>
<xsl:for-each select="$var22_ReceiveDelivery/n:ReceiveDeliveryHeader/n:DocumentReference/n:DocumentID">
<xsl:variable name="var26_DocumentID" select="." />
<xsl:if test="$var26_DocumentID/n:ID/#location">
<Scanneddata2>
<xsl:value-of select="substring-before(string(n:ID/#location), '-B')" />
</Scanneddata2>
</xsl:if>
</xsl:for-each>
</Detail02>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery">
<xsl:variable name="var28_ReceiveDelivery" select="." />
<xsl:for-each select="n:ReceiveDeliveryItem">
<Detail03>
<xsl:for-each select="$var28_ReceiveDelivery/n:ReceiveDeliveryHeader/n:DocumentReference/n:DocumentID">
<Scanneddata3>
<xsl:value-of select="string(n:ID)" />
</Scanneddata3>
</xsl:for-each>
</Detail03>
</xsl:for-each>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryItem">
<Detail04>
<xsl:for-each select="n:LineNumber">
<Scanneddata4>
<xsl:value-of select="string(.)" />
</Scanneddata4>
</xsl:for-each>
</Detail04>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryItem">
<xsl:variable name="var38_ReceiveDeliveryItem" select="." />
<Detail05>
<xsl:variable name="var40_map_select_SerializedLot">
<xsl:if test="string((n:SerializedLot/n:Lot/n:LotIDs/n:ID) = (n:SerializedLot/n:Lot/n:LotIDs/n:ID)) != 'false'">
<xsl:value-of select="'1'" />
</xsl:if>
</xsl:variable>
<xsl:variable name="var48_">
<xsl:choose>
<xsl:when test="string(boolean(string($var40_map_select_SerializedLot))) != 'false'">
<xsl:variable name="var45_map_select_SerializedLot">
<xsl:for-each select="n:SerializedLot/n:Lot/n:LotIDs/n:ID">
<xsl:value-of select="string(.)" />
</xsl:for-each>
</xsl:variable>
<xsl:variable name="var41_map_select_SerializedLot">
<xsl:if test="string((string($var45_map_select_SerializedLot)) = (string($var45_map_select_SerializedLot))) != 'false'">
<xsl:value-of select="'1'" />
</xsl:if>
</xsl:variable>
<xsl:if test="string(boolean(string($var41_map_select_SerializedLot))) != 'false'">
<xsl:variable name="var42_map_select_SerializedLot">
<xsl:for-each select="n:SerializedLot/n:Lot/n:LotIDs/n:ID">
<xsl:value-of select="string(.)" />
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="string($var42_map_select_SerializedLot)" />
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="' '" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<Scanneddata5>
<xsl:copy-of select="$var48_" />
</Scanneddata5>
</Detail05>
</xsl:for-each>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryItem">
<Detail06>
<xsl:for-each select="n:HoldCodes/n:Code">
<Scanneddata6>
<xsl:choose>
<xsl:when test="string((' ' != string(.))) != 'false'">
<xsl:value-of select="'Hold'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'Inventory'" />
</xsl:otherwise>
</xsl:choose>
</Scanneddata6>
</xsl:for-each>
</Detail06>
</xsl:for-each>
<Detail07>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryHeader/n:ReceivedDateTime">
<Scanneddata7>
<xsl:value-of select="string(.)" />
</Scanneddata7>
</xsl:for-each>
</Detail07>
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryItem">
<Detail11>
<xsl:for-each select="n:ReceivedQuantity">
<Scanneddata11>
<xsl:value-of select="number(string(.))" />
</Scanneddata11>
</xsl:for-each>
</Detail11>
</xsl:for-each>
</xsl:for-each>
</AsyncBarcode>
</xsl:template>
</xsl:stylesheet>
Below is the input I am receiving:
<SyncReceiveDelivery xmlns="http://schema.infor.com/InforOAGIS/2" releaseID="9.2" versionID="2.8.0">
<ApplicationArea>
<Sender>
<LogicalID>lid://</LogicalID>
<ComponentID>Warehouse Management</ComponentID>
<ReferenceID accountingEntity="01" location="RS01-W">0000058141</ReferenceID>
</Sender>
<CreationDateTime>2015-03-31T20:08:16Z</CreationDateTime>
<BODID>infor-nid:infor:01:RS01-W:0000002445:62883?ReceiveDelivery&verb=Sync</BODID>
</ApplicationArea>
<DataArea>
<Sync>
<TenantID>infor</TenantID>
<AccountingEntityID>01</AccountingEntityID>
<LocationID accountingEntity="01">RS01-W</LocationID>
<ActionCriteria>
<ActionExpression actionCode="Add" />
</ActionCriteria>
</Sync>
<ReceiveDelivery>
<ReceiveDeliveryHeader>
<DocumentID>
<ID accountingEntity="01" location="RS01-W" variationID="62883">0000002445</ID>
</DocumentID>
<LastModificationDateTime>2015-03-31T20:08:12Z</LastModificationDateTime>
<DocumentDateTime>2015-03-31T20:08:12Z</DocumentDateTime>
<DocumentReference type="CustomerReturn">
<DocumentID>
<ID accountingEntity="01" location="1323-B">930131</ID>
</DocumentID>
</DocumentReference>
<Status>
<Code listID="ReceiveDeliveryStatus">Received</Code>
</Status>
<WarehouseLocation>
<ID accountingEntity="01">RS01-W</ID>
<Name languageID="en-US">Power Packaging</Name>
<Address>
<AddressLine sequence="1">401 N. Main</AddressLine>
<CityName>Rosendale</CityName>
<CountrySubDivisionCode>WI</CountrySubDivisionCode>
<PostalCode listID="PostalCode">54974</PostalCode>
</Address>
</WarehouseLocation>
<ActualDeliveryDateTime>2015-03-31T17:31:46Z</ActualDeliveryDateTime>
<GrossWeightMeasure unitCode="LB">8120.4147</GrossWeightMeasure>
<TotalVolumeMeasure unitCode="CF">0</TotalVolumeMeasure>
<ShipFromParty>
<Location>
<ID>30155</ID>
<Name languageID="en-US">RS-IFP-USFS HOUSTON</Name>
<Address>
<AddressLine sequence="1">USFS HOUSTON</AddressLine>
<AddressLine sequence="2">111 ALIANT DRIVE</AddressLine>
<CityName>HOUSTON</CityName>
<CountrySubDivisionCode>TX</CountrySubDivisionCode>
<CountryCode>USA</CountryCode>
<PostalCode listID="PostalCode">77032</PostalCode>
</Address>
</Location>
</ShipFromParty>
<ReceivedDateTime>2015-03-31T20:08:12Z</ReceivedDateTime>
<DeliverToParty>
<Location>
<ID accountingEntity="01">RS01-W</ID>
<Name languageID="en-US">Power Packaging</Name>
<Address>
<AddressLine sequence="1">401 N. Main</AddressLine>
<CityName>Rosendale</CityName>
<CountrySubDivisionCode>WI</CountrySubDivisionCode>
<PostalCode listID="PostalCode">54974</PostalCode>
</Address>
</Location>
</DeliverToParty>
<ASNReference>
<DocumentID>
<ID accountingEntity="01" location="RS01-W">0000002445</ID>
</DocumentID>
</ASNReference>
</ReceiveDeliveryHeader>
<ReceiveDeliveryItem>
<ItemID>
<ID accountingEntity="01">200135-100250</ID>
</ItemID>
<ServiceIndicator>false</ServiceIndicator>
<Description languageID="en-US">Orchard Splash 12/25 fl oz Orange Gold 100</Description>
<Note languageID="en-US">1</Note>
<DocumentReference type="CustomerReturn">
<DocumentID>
<ID accountingEntity="01" location="1323-B">930131</ID>
</DocumentID>
<LineNumber>1</LineNumber>
</DocumentReference>
<PackingSlipQuantity unitCode="CS">0.0</PackingSlipQuantity>
<PackingSlipBaseUOMQuantity unitCode="CS">0.0</PackingSlipBaseUOMQuantity>
<ReceivedQuantity unitCode="CS">90.0</ReceivedQuantity>
<ReceivedBaseUOMQuantity unitCode="CS">90.0</ReceivedBaseUOMQuantity>
<ReturnedQuantity unitCode="CS">0.0</ReturnedQuantity>
<ReturnedBaseUOMQuantity unitCode="CS">0.0</ReturnedBaseUOMQuantity>
<SerializedLot>
<ItemQuantity unitCode="CS">90.0</ItemQuantity>
<ItemBaseUOMQuantity unitCode="CS">90.0</ItemBaseUOMQuantity>
<Lot>
<LotIDs>
<ID>RS1412107</ID>
</LotIDs>
<Quantity unitCode="CS">90.0</Quantity>
<BaseUOMQuantity unitCode="CS">90.0</BaseUOMQuantity>
</Lot>
</SerializedLot>
<LineNumber>1</LineNumber>
<HoldCodes>
<Code listID="Hold Reason Codes">HOLD</Code>
</HoldCodes>
<HoldCodes>
<Code listID="Hold Reason Codes">QCREQ</Code>
</HoldCodes>
<CountSequence>1</CountSequence>
</ReceiveDeliveryItem>
<ReceiveDeliveryItem>
<ItemID>
<ID accountingEntity="01">200135-100252</ID>
</ItemID>
<ServiceIndicator>false</ServiceIndicator>
<Description languageID="en-US">Orchard Hills 12/25 fl oz Orange 100</Description>
<Note languageID="en-US">2</Note>
<DocumentReference type="CustomerReturn">
<DocumentID>
<ID accountingEntity="01" location="1323-B">930131</ID>
</DocumentID>
<LineNumber>2</LineNumber>
</DocumentReference>
<PackingSlipQuantity unitCode="CS">0.0</PackingSlipQuantity>
<PackingSlipBaseUOMQuantity unitCode="CS">0.0</PackingSlipBaseUOMQuantity>
<ReceivedQuantity unitCode="CS">90.0</ReceivedQuantity>
<ReceivedBaseUOMQuantity unitCode="CS">90.0</ReceivedBaseUOMQuantity>
<ReturnedQuantity unitCode="CS">0.0</ReturnedQuantity>
<ReturnedBaseUOMQuantity unitCode="CS">0.0</ReturnedBaseUOMQuantity>
<SerializedLot>
<ItemQuantity unitCode="CS">90.0</ItemQuantity>
<ItemBaseUOMQuantity unitCode="CS">90.0</ItemBaseUOMQuantity>
<Lot>
<LotIDs>
<ID>RS141112</ID>
</LotIDs>
<Quantity unitCode="CS">90.0</Quantity>
<BaseUOMQuantity unitCode="CS">90.0</BaseUOMQuantity>
</Lot>
</SerializedLot>
<LineNumber>2</LineNumber>
<HoldCodes>
<Code listID="Hold Reason Codes">HOLD</Code>
</HoldCodes>
<HoldCodes>
<Code listID="Hold Reason Codes">QCREQ</Code>
</HoldCodes>
<CountSequence>1</CountSequence>
</ReceiveDeliveryItem>
<ReceiveDeliveryItem>
<ItemID>
<ID accountingEntity="01">200135-100252</ID>
</ItemID>
<ServiceIndicator>false</ServiceIndicator>
<Description languageID="en-US">Orchard Hills 12/25 fl oz Orange 100</Description>
<Note languageID="en-US">3</Note>
<DocumentReference type="CustomerReturn">
<DocumentID>
<ID accountingEntity="01" location="1323-B">930131</ID>
</DocumentID>
<LineNumber>3</LineNumber>
</DocumentReference>
<PackingSlipQuantity unitCode="CS">0.0</PackingSlipQuantity>
<PackingSlipBaseUOMQuantity unitCode="CS">0.0</PackingSlipBaseUOMQuantity>
<ReceivedQuantity unitCode="CS">90.0</ReceivedQuantity>
<ReceivedBaseUOMQuantity unitCode="CS">90.0</ReceivedBaseUOMQuantity>
<ReturnedQuantity unitCode="CS">0.0</ReturnedQuantity>
<ReturnedBaseUOMQuantity unitCode="CS">0.0</ReturnedBaseUOMQuantity>
<SerializedLot>
<ItemQuantity unitCode="CS">90.0</ItemQuantity>
<ItemBaseUOMQuantity unitCode="CS">90.0</ItemBaseUOMQuantity>
<Lot>
<LotIDs>
<ID>RS1412030</ID>
</LotIDs>
<Quantity unitCode="CS">90.0</Quantity>
<BaseUOMQuantity unitCode="CS">90.0</BaseUOMQuantity>
</Lot>
</SerializedLot>
<LineNumber>3</LineNumber>
<HoldCodes>
<Code listID="Hold Reason Codes">HOLD</Code>
</HoldCodes>
<HoldCodes>
<Code listID="Hold Reason Codes">QCREQ</Code>
</HoldCodes>
<CountSequence>1</CountSequence>
</ReceiveDeliveryItem>
</ReceiveDelivery>
</DataArea>
</SyncReceiveDelivery>
Finally, this is what I am expecting however I am getting duplicates values in Scannedata6 for the Detail06 section:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE AsyncBarcode SYSTEM "C:/InboundBODS/AsyncBarcode_Inbound.dtd">
<AsyncBarcode>
<Prefix>
<CompanyID>01</CompanyID>
<ExternalPartnerID>01</ExternalPartnerID>
<DocumentType>AsyncBarcode</DocumentType>
<DocumentNumber/>
<TransactionDirection>I</TransactionDirection>
<DateStamp>2015-03-31</DateStamp>
<TimeStamp>20:08:16</TimeStamp>
<ControlNumber/>
</Prefix>
<Header00>
<TransactionDefinitionKey>25</TransactionDefinitionKey>
<CompanyID0>01</CompanyID0>
<BranchID>1323</BranchID>
<UserID>WMUser</UserID>
<WarehouseID>RS01</WarehouseID>
</Header00>
<Detail01>
<Scanneddata1>01</Scanneddata1>
</Detail01>
<Detail01>
<Scanneddata1>01</Scanneddata1>
</Detail01>
<Detail01>
<Scanneddata1>01</Scanneddata1>
</Detail01>
<Detail02>
<Scanneddata2>1323</Scanneddata2>
</Detail02>
<Detail02>
<Scanneddata2>1323</Scanneddata2>
</Detail02>
<Detail02>
<Scanneddata2>1323</Scanneddata2>
</Detail02>
<Detail03>
<Scanneddata3>930131</Scanneddata3>
</Detail03>
<Detail03>
<Scanneddata3>930131</Scanneddata3>
</Detail03>
<Detail03>
<Scanneddata3>930131</Scanneddata3>
</Detail03>
<Detail04>
<Scanneddata4>1</Scanneddata4>
</Detail04>
<Detail04>
<Scanneddata4>2</Scanneddata4>
</Detail04>
<Detail04>
<Scanneddata4>3</Scanneddata4>
</Detail04>
<Detail05>
<Scanneddata5>RS1412107</Scanneddata5>
</Detail05>
<Detail05>
<Scanneddata5>RS141112</Scanneddata5>
</Detail05>
<Detail05>
<Scanneddata5>RS1412030</Scanneddata5>
</Detail05>
<Detail06>
<Scanneddata6>Hold</Scanneddata6>
</Detail06>
<Detail06>
<Scanneddata6>Hold</Scanneddata6>
</Detail06>
<Detail06>
<Scanneddata6>Hold</Scanneddata6>
</Detail06>
<Detail07>
<Scanneddata7>2015-03-31T20:08:12Z</Scanneddata7>
</Detail07>
<Detail11>
<Scanneddata11>90</Scanneddata11>
</Detail11>
<Detail11>
<Scanneddata11>90</Scanneddata11>
</Detail11>
<Detail11>
<Scanneddata11>90</Scanneddata11>
</Detail11>
</AsyncBarcode>
Was able to figure out what I needed to change. Change shown below:
<xsl:for-each select="n:DataArea/n:ReceiveDelivery/n:ReceiveDeliveryItem/n:HoldCodes[1]">
<Detail06>
<xsl:for-each select="n:Code[1]">
<Scanneddata6>
<xsl:choose>
<xsl:when test="string(('' != string(.))) != 'false'">
<xsl:value-of select="'Hold'" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'Inventory'" />
</xsl:otherwise>
</xsl:choose>
</Scanneddata6>
</xsl:for-each>
</Detail06>
</xsl:for-each>

XSLT 1.0 Substring from right side of string

I have a value in an element as 433554567643.
I would like to change it to 43 35545 67643. The grouping should start from right side of the value.
Is it possible to use subtring from end to start of the value?
Thanks.
You could do this with a recursive template.
<xsl:template name="add-spaces">
<xsl:param name="group" select="5" />
<xsl:param name="text" />
<xsl:if test="string-length($text) > $group">
<xsl:call-template name="add-spaces">
<xsl:with-param name="group" select="$group" />
<xsl:with-param name="text"
select="substring($text, 1, string-length($text) - $group)" />
</xsl:call-template>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:value-of select="substring($text, string-length($text) - $group + 1)" />
</xsl:template>
You would call this when required using
<xsl:call-template name="add-spaces">
<xsl:with-param name="text" select="'433554567643'" />
<!-- or select="path/to/element" as appropriate -->
</xsl:call-template>
If your value is always a number, you could use format-number() with a pattern that groups the numbers by 5 digits and then translate() the "," into " ":
<xsl:value-of select="translate(format-number('433554567643', '#,#####'),
',', ' ')" />

XSLT: Looping and setting a variable when condition met at least once

I am trying to loop through a node, which in itselt is not a problem. I want to check to see if the selectedLetter matches the first letter of the node, which is working as intended (see code below). The difficulties arise in that I would like to present some alternative text if the conditional "" is not met at least once during the loop, along the lines of "Sorry, no documents begin with that letter". I was thinking about setting a variable i.e. flag in the conditional and check later on if this variable has been set, but from what I have read here, the scope of the variable is restricted and only available within the loop? Can anyone offer any assistance?
Cheers,
Taff
<xsl:param name="selectedLetter" select="''"/>
<xsl:key name="docLetter" match="docs" use="substring(text()),1,1)"/>
<xsl:for-each select="//ad-documents/item">
<xsl:sort select="documentName"/>
<xsl:variable name="firstLetter" select="upper-case(substring(documentName, 1, 1))"/>
<xsl:choose>
<xsl:when test="$firstLetter = $selectedLetter">
<div class="doc-date">
<xsl:if test="upload-date/day < 10">0</xsl:if>
<xsl:value-of select="upload-date/day"/>
<xsl:text>.</xsl:text>
<xsl:if test="upload-date/month < 10">0</xsl:if>
<xsl:value-of select="upload-date/month"/>
<xsl:text>.</xsl:text>
<xsl:value-of select="upload-date/year"/>
</div>
<div class="doc-link">
<a>
<xsl:attribute name="href">
<xsl:choose>
<xsl:when test="isEditableFormDoc/box = 'true'">
#
</xsl:when>
<xsl:otherwise>
<xsl:text>/img/ejbfile/</xsl:text>
<xsl:value-of select="documents/document/#documentName"/>
<xsl:text>?id=</xsl:text>
<xsl:value-of select="documents/document/#src"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:if test="isEditableFormDoc/box = 'true'">
<xsl:attribute name="target">
<xsl:text>_blank</xsl:text>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="documentName"/>
</a>
</div>
<div class="doc-short-desc">
<xsl:apply-templates select="document-short-desc" mode="format"/>
</div>
<!--<xsl:apply-templates select="//ad-documents/item" mode="filteredDocuments"/>-->
</xsl:when>
</xsl:choose>
</xsl:for-each>
EDIT:
XML Example
<ad-documents name="ad-documents" label="Dokumente (limitiert auf 20 pro Seite)">
<item description="Doc%203" id="1" timestamp="1328525592205">
<documentName name="documentName" description="Doc%203">Doc 3</documentName>
<optionalLetter name="optionalLetter" description="c">c</optionalLetter>
<document-short-desc name="document-short-desc" description="">
<p>yxf</p>
</document-short-desc>
<upload-date name="upload-date" description="">
<day>24</day>
<month>2</month>
<year>2012</year>
</upload-date>
<isEditableFormDoc name="isEditableFormDoc" description="">
<box/>
</isEditableFormDoc>
<documents name="documents" description=""/>
</item>
<item description="Doc%204" id="2" timestamp="1328525624889">
<documentName name="documentName" description="Doc%204">Doc 4</documentName>
<optionalLetter name="optionalLetter" description="z%2Ci%2Cg">z,i,g</optionalLetter>
<document-short-desc name="document-short-desc" description="">
<p>asff</p>
</document-short-desc>
<upload-date name="upload-date" description="">
<day>25</day>
<month>2</month>
<year>2012</year>
</upload-date>
<isEditableFormDoc name="isEditableFormDoc" description="">
<box/>
</isEditableFormDoc>
<documents name="documents" description=""/>
</item>
<item description="Doc%201" id="1" timestamp="1328523551639">
<documentName name="documentName" description="Doc%201">Doc 1</documentName>
<optionalLetter name="optionalLetter" description="b%2Cc">b,c</optionalLetter>
<document-short-desc name="document-short-desc" description="">
<p>Short Desc 1</p>
</document-short-desc>
<upload-date name="upload-date" description="">
<day>9</day>
<month>2</month>
<year>2012</year>
</upload-date>
<isEditableFormDoc name="isEditableFormDoc" description="">
<box/>
</isEditableFormDoc>
<documents name="documents" description=""/>
</item>
</ad-documents>
The solution is simple:
Just replace:
<xsl:for-each select="//ad-documents/item">
with
<xsl:for-each select=
"//ad-documents/item
[$selectedLetter eq upper-case(substring(documentName, 1, 1) ]">
You can add after this xsl:for-each
<xsl:sequence select=
"'YourErrorMessage'
[not(//ad-documents/item
[$selectedLetter eq upper-case(substring(documentName, 1, 1) ]
)
]"
/>
Thanks to Dimitre, I managed to solve the problem with count()
<xsl:variable name="foundDocs" select="count(//ad-documents/item[$selectedLetter eq upper-case(substring(documentName, 1, 1))])"/>
<xsl:if test="$foundDocs = 0">
<div class="no-documents">
<xsl:text>Leider keine Dokumente gefunden</xsl:text>
</div>
</xsl:if>