I have below two variables which have multiple commas separated string values in different XSLT variable
Variable_01 : 88888,777777
Variable_02 : abc,xyz
Now I am looking for below output
[{"Group":"88888", "Name":"abc"},{"Group":"777777", "Name":"xyz"}]
Could you please help me what is correct XSLT code for the above output.
Here's one way you could look at it:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="Variable_01">88888,777777</xsl:variable>
<xsl:variable name="Variable_02">abc,xyz</xsl:variable>
<xsl:template match="/">
<xsl:text>[</xsl:text>
<xsl:for-each select="tokenize($Variable_01, ',')">
<xsl:variable name="i" select="position()"/>
<xsl:text>{"Group":"</xsl:text>
<xsl:value-of select="." />
<xsl:text>", "Name":"</xsl:text>
<xsl:value-of select="tokenize($Variable_02, ',')[$i]" />
<xsl:text>"}</xsl:text>
<xsl:if test="position()!=last()">
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:template>
</xsl:stylesheet>
Result
[{"Group":"88888", "Name":"abc"},{"Group":"777777", "Name":"xyz"}]
Demo: http://xsltransform.hikmatu.com/jyH9rLV
In XSLT 3 with higher-order function for-each-pair and JSON map and array support this would be as easy as
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="json" indent="yes"/>
<xsl:param name="Variable_01" as="xs:string">88888,777777</xsl:param>
<xsl:param name="Variable_02" as="xs:string">abc,xyz</xsl:param>
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence
select="array {
for-each-pair(
tokenize($Variable_01, ','),
tokenize($Variable_02, ','),
function($a, $b) { map { 'Group' : $a, 'Name' : $b } }
)
}"/>
</xsl:template>
</xsl:stylesheet>
In XSLT 3 without higher-order function support for for-each-pair you could implement your own function along the lines of the defintion:
<?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:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="json" indent="yes"/>
<xsl:function name="mf:object-for-each-pair" as="map(xs:string, xs:string)*">
<xsl:param name="seq1"/>
<xsl:param name="seq2"/>
<xsl:param name="action"/>
<xsl:if test="exists($seq1) and exists($seq2)">
<xsl:sequence select="map { 'Group' : head($seq1), 'Name' : head($seq2) }"/>
<xsl:sequence select="mf:object-for-each-pair(tail($seq1), tail($seq2))"/>
</xsl:if>
</xsl:function>
<xsl:param name="Variable_01" as="xs:string">88888,777777</xsl:param>
<xsl:param name="Variable_02" as="xs:string">abc,xyz</xsl:param>
<xsl:template match="/" name="xsl:initial-template">
<xsl:sequence select="array { mf:object-for-each-pair(tokenize($Variable_01, ','), tokenize($Variable_02, ',')) }"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qVRKxk
Finally in XSLT 2 without JSON array and map support you could use the same approach to create text output instead:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="2.0">
<xsl:output method="text"/>
<xsl:function name="mf:object-for-each-pair" as="xs:string*">
<xsl:param name="seq1"/>
<xsl:param name="seq2"/>
<xsl:if test="exists($seq1) and exists($seq2)">
<xsl:sequence select="concat('{ "Group" : "', $seq1[1], '", "Name" : "', $seq2[1], '" }')"/>
<xsl:sequence select="mf:object-for-each-pair(subsequence($seq1, 2), subsequence($seq2, 2))"/>
</xsl:if>
</xsl:function>
<xsl:param name="Variable_01" as="xs:string">88888,777777</xsl:param>
<xsl:param name="Variable_02" as="xs:string">abc,xyz</xsl:param>
<xsl:template match="/">
<xsl:text>[ </xsl:text>
<xsl:value-of select="mf:object-for-each-pair(tokenize($Variable_01, ','), tokenize($Variable_02, ','))" separator=", "/>
<xsl:text> ]</xsl:text>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.hikmatu.com/nc4NzPX
This is somewhat crude. But it gets the job done.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="Variable_01">88888,777777</xsl:variable>
<xsl:variable name="Variable_02">abc,xyz</xsl:variable>
<xsl:template match="/">
<xsl:text>[</xsl:text>
<xsl:call-template name="tokenizeString">
<!-- store anything left in another variable -->
<xsl:with-param name="list" select="$Variable_01"/>
<xsl:with-param name="list2" select="$Variable_02"/>
<xsl:with-param name="delimiter" select="','"/>
</xsl:call-template>
<xsl:text>]</xsl:text>
</xsl:template>
<xsl:template name="tokenizeString">
<!--passed template parameter -->
<xsl:param name="list"/>
<xsl:param name="list2"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<!-- get everything in front of the first delimiter -->
<xsl:text>{"Group":"</xsl:text>
<xsl:value-of select="substring-before($list,$delimiter)"/>
<xsl:text>", "Name":"</xsl:text>
<xsl:call-template name="tokenizeAnother">
<xsl:with-param name="list2" select="$list2"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
<xsl:text>"},</xsl:text>
<xsl:call-template name="tokenizeString">
<!-- store anything left in another variable -->
<xsl:with-param name="list" select="substring-after($list,$delimiter)"/>
<xsl:with-param name="list2" select="substring-after($list2,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:text>{"Group":"</xsl:text>
<xsl:value-of select="$list"/>
<xsl:text>", "Name":"</xsl:text>
<xsl:call-template name="tokenizeAnother">
<xsl:with-param name="list2" select="$list2"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
<xsl:text>"}</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="tokenizeAnother">
<xsl:param name="list2"/>
<xsl:param name="delimiter"/>
<xsl:choose>
<xsl:when test="contains($list2, $delimiter)">
<!-- get everything in front of the first delimiter -->
<xsl:value-of select="substring-before($list2,$delimiter)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$list2"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
See it in action here.
For completion, here's a purely XSLT 1.0 solution that does not require any extension functions:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:variable name="Variable_01">88888,777777</xsl:variable>
<xsl:variable name="Variable_02">abc,xyz</xsl:variable>
<xsl:template match="/">
<xsl:text>[</xsl:text>
<xsl:call-template name="tokenize-to-pairs">
<xsl:with-param name="left-text" select="$Variable_01"/>
<xsl:with-param name="right-text" select="$Variable_02"/>
</xsl:call-template>
<xsl:text>]</xsl:text>
</xsl:template>
<xsl:template name="tokenize-to-pairs">
<xsl:param name="left-text"/>
<xsl:param name="right-text"/>
<xsl:param name="delimiter" select="','"/>
<!-- output -->
<xsl:text>{"Group":"</xsl:text>
<xsl:value-of select="substring-before(concat($left-text, $delimiter), $delimiter)" />
<xsl:text>", "Name":"</xsl:text>
<xsl:value-of select="substring-before(concat($right-text, $delimiter), $delimiter)" />
<xsl:text>"}</xsl:text>
<!-- recursive call -->
<xsl:if test="contains($left-text, $delimiter)">
<xsl:text>,</xsl:text>
<xsl:call-template name="tokenize-to-pairs">
<xsl:with-param name="left-text" select="substring-after($left-text, $delimiter)"/>
<xsl:with-param name="right-text" select="substring-after($right-text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Related
This is my template (a simplification of a real situation):
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args/*">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
I call it like this:
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args"/>
<a><xsl:value-of select="./#one"/></a>
<a><xsl:value-of select="./#two"/></a>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Basically, I create artificial nodes <a/>, which looks ugly to me. Is there a better way?
My example would use functions but I have done both:
<?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:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:function name="mf:f1" as="text()">
<xsl:param name="items" as="item()*"/>
<xsl:value-of select="$items"/>
</xsl:function>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="foo">
<function-example>
<xsl:sequence select="mf:f1((#one, #two))"/>
</function-example>
<template-example1>
<xsl:call-template name="h">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</template-example1>
<template-example2>
<xsl:call-template name="i">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</template-example2>
</xsl:template>
<xsl:template name="h">
<xsl:param name="args"/>
<xsl:value-of select="$args"/>
</xsl:template>
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
transforms
<foo one="1" two="2"/>
into
<function-example>1 2</function-example>
<template-example1>1 2</template-example1>
<template-example2>12</template-example2>
https://xsltfiddle.liberty-development.net/jyfAiD9
Of course, for the sole xsl:value-of use, if you don't want to have the default space separator between the args values, use xsl:value-of separator="".
Consider the following simple example:
XML
<f one="alpha" two="bravo"/>
template call
<xsl:template match="f">
<xsl:call-template name="i">
<xsl:with-param name="args" select="#one, #two"/>
</xsl:call-template>
</xsl:template>
template execution
<xsl:template name="i">
<xsl:param name="args"/>
<xsl:for-each select="$args">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
Demo: https://xsltfiddle.liberty-development.net/93PXKr2
Experts, i need to write XSLT 1.0 code to remove the quotes for multiple conditions.
CASE1: Remove the double quotes
CASE2: Remove the double quotes + delete the PIPE symbol inside that double quotes (IF exist)
CASE3: Remove Single quote " from the input field.
Input:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns0:Accounting xmlns:ns0="http://sample.com">
<Record>
<DRCR>"DR"</DRCR>
<GLREFERENCE>"TEST|CASE"</GLREFERENCE>
<GLVALUEDATE>EXAM"PLE</GLVALUEDATE>
<GLACCOUNTNUMBER>"1160</GLACCOUNTNUMBER>
<GLEXAMPLE>123</GLEXAMPLE>
<GLEXAMPLE1>EXTRACT|2021-06-16|2853|1308026.7500|1176</GLEXAMPLE1>
</Record>
</ns0:Accounting>
** Desired Output:**
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ns0:Accounting xmlns:ns0="http://sample.com">
<Record>
<DRCR>DR</DRCR>
<GLREFERENCE>TEST CASE</GLREFERENCE>
<GLVALUEDATE>EXAMPLE</GLVALUEDATE>
<GLACCOUNTNUMBER>1160</GLACCOUNTNUMBER>
<GLEXAMPLE>123</GLEXAMPLE>
<GLEXAMPLE1>EXTRACT|2021-06-16|2853|1308026.7500|1176</GLEXAMPLE1>
</Record>
</ns0:Accounting>
** XSLT I tried:**
<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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="process">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template name="process">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '"')">
<xsl:value-of select="substring-before($text, '"')"/>
<xsl:value-of select="translate(substring-before(substring-after($text, '"'), '"'), '|', '')"/>
<xsl:call-template name="process">
<xsl:with-param name="text" select="substring-after(substring-after($text, '"'), '"')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This XSLT not handling case 3, which has single quote in the input field. Please assist here..
Maybe something like this could work for you:
XSLT 1.0 (+ EXSLT node-set function)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:choose>
<xsl:when test="contains(., '"')">
<xsl:variable name="tokens">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:for-each select="exsl:node-set($tokens)/token">
<xsl:choose>
<xsl:when test="(position()=1 or position()=last()) and last() > 1">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(., '|', '')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'"'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<token>
<xsl:value-of select="$token"/>
</token>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Note that this does not check the parity of the quotation marks. Any vertical bar character that is both preceded and followed by a quotation mark will be removed. For example, an input of:
<EXAMPLE>abc|123"def|456"ghi|789"jkl|012</EXAMPLE>
will be transformed to:
<EXAMPLE>abc|123def456ghi789jkl|012</EXAMPLE>
I am trying to separate the map output values by tag. Right now I am getting all the values in one (BOM) tag. I want the output separated by each BOMTransactionType(Deleted/Added). I am using XSLT Transformation to separate it. Could anyone please let me know how can I do that. Thanks.
XSLT Transformation Logic in DataProcess Shape:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<PLMData>
<ChangeOrders>
<AffectedItems>
<BOM>
<xsl:apply-templates/>
</BOM>
</AffectedItems>
</ChangeOrders>
</PLMData>
</xsl:template>
//Split the ItemNumber
<xsl:template match="ItemNumber/text()" name="split">
<xsl:param name="pText" select="."/>
<xsl:if test="$pText">
<xsl:element name="ItemNumber">
<xsl:value-of select= "substring-before(concat($pText, ','), ',')"/>
</xsl:element>
<xsl:call-template name="split">
<xsl:with-param name="pText" select="substring-after($pText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
//Split the BOMTransactionType
<xsl:template match="BOM/BOMTransactionType/text()" name="split1">
<xsl:param name="bText" select="."/>
<xsl:param name="bOrd" select="1"/>
<xsl:if test="$bText">
<xsl:element name="BOMTransactionType">
<xsl:value-of select= "substring-before(concat($bText, ','), ',')"/>
</xsl:element>
<xsl:call-template name="split1">
<xsl:with-param name="bText" select="substring-after($bText, ',')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Input:
<PLMData>
<ChangeOrders>
<AffectedItems>
<BOM>
<ItemNumber>P00001,020-00003-01</ItemNumber>
<BOMTransactionType>Added,Deleted</BOMTransactionType>
</BOM>
</AffectedItems>
</ChangeOrders>
</PLMData>
Output:
<PLMData>
<ChangeOrders>
<AffectedItems>
<BOM>
<ItemNumber>P00001</ItemNumber>
<ItemNumber>020-00003-01</ItemNumber>
<BOMTransactionType>Added</BOMTransactionType>
<BOMTransactionType>Deleted</BOMTransactionType>
</BOM>
</AffectedItems>
</ChangeOrders>
</PLMData>
Expected Output:
<PLMData>
<ChangeOrders>
<AffectedItems>
<BOM>
<ItemNumber>P00001</ItemNumber>
<BOMTransactionType>Added</BOMTransactionType>
</BOM>
<BOM>
<ItemNumber>020-00003-01</ItemNumber>
<BOMTransactionType>Deleted</BOMTransactionType>
</BOM>
</AffectedItems>
</ChangeOrders>
</PLMData>
I would do it this way:
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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="BOM">
<xsl:call-template name="tokenize">
<xsl:with-param name="item-numbers" select="ItemNumber"/>
<xsl:with-param name="transaction-types" select="BOMTransactionType"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="item-numbers"/>
<xsl:param name="transaction-types"/>
<xsl:param name="delimiter" select="','"/>
<BOM>
<ItemNumber>
<xsl:value-of select="substring-before(concat($item-numbers, $delimiter), $delimiter)" />
</ItemNumber>
<BOMTransactionType>
<xsl:value-of select="substring-before(concat($transaction-types, $delimiter), $delimiter)" />
</BOMTransactionType>
</BOM>
<xsl:if test="contains($item-numbers, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="item-numbers" select="substring-after($item-numbers, $delimiter)"/>
<xsl:with-param name="transaction-types" select="substring-after($transaction-types, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
i have a xml like,
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>xxx</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">ABC/DEF/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>yyy</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">LMN/OPQ/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>mmm</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">XYZ/GHY/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>nnn</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">AJK/UTL/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
My xslt,
<xsl:template name="substring-after-last">
<xsl:param name="string" />
<xsl:param name="delimiter" />
<xsl:choose>
<xsl:when test="contains($string, $delimiter)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string"
select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:for-each select="select="//DESIGN-FUNCTION-PROTOTYPE/ea:TYPE-TREF[#TYPE='DESIGN-FUNCTION-TYPE']">
<xsl:variable name="myVar" select="current()"/>
<xsl:variable name="taskName" select="../ea:SHORT-NAME"/>
<xsl:variable name="Var7">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="$myVar" />
<xsl:with-param name="delimiter" select="'/'" />
</xsl:call-template>
</xsl:variable>
<varoutput>
<xsl:value-of select="$Var7"/>
</varoutput>
</xsl:for-each>
My intention here is to iterate all the 'DESIGN-FUNCTION-PROTOTYPE' elements and display the sub-string of 'TYPE-TREF' value, but if a sub-string of 'TYPE-TREF' value has already been read..i must skip that element.
Expected output,
123
456
And Not,
123
123
456
456
In general I should consider only the first occurrence and skip the rest.
To do this in pure XSLT 1.0, without relying on processor-specific extensions, you could do:
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="k1" match="DESIGN-FUNCTION-PROTOTYPE" use="substring-after(substring-after(TYPE-TREF, '/'), '/')"/>
<xsl:template match="/Root">
<root>
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE[count(. | key('k1', substring-after(substring-after(TYPE-TREF, '/'), '/'))[1]) = 1]">
<varoutput>
<xsl:value-of select="substring-after(substring-after(TYPE-TREF, '/'), '/')" />
</varoutput>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/bFN1y9s
This is of course assuming that the value you're after is always the third "token" in TYPE-TREF. Otherwise you would have to do something similar to your attempt:
XSLT 1.0 + EXSLT node-set() function
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl" >
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="k1" match="value" use="."/>
<xsl:template match="/Root">
<!-- EXTRACT VALUES -->
<xsl:variable name="values">
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE">
<value>
<xsl:call-template name="last-token">
<xsl:with-param name="text" select="TYPE-TREF"/>
</xsl:call-template>
</value>
</xsl:for-each>
</xsl:variable>
<!-- OUTPUT -->
<root>
<xsl:for-each select="exsl:node-set($values)/value[count(. | key('k1', .)[1]) = 1]">
<varoutput>
<xsl:value-of select="." />
</varoutput>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template name="last-token">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'/'"/>
<xsl:choose>
<xsl:when test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="last-token">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/bFN1y9s/1
Assuming you use Xalan you should have access to the EXSLT str:split function (http://xalan.apache.org/xalan-j/apidocs/org/apache/xalan/lib/ExsltStrings.html#split(java.lang.String,%20java.lang.String):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings" exclude-result-prefixes="str" version="1.0">
<xsl:key name="group" match="DESIGN-FUNCTION-PROTOTYPE/TYPE-TREF"
use="str:split(., '/')[last()]"/>
<xsl:template match="Root">
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE/TYPE-TREF[generate-id() = generate-id(key('group', str:split(., '/')[last()])[1])]">
<varoutput>
<xsl:value-of select="str:split(., '/')[last()]"/>
</varoutput>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Transforms
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>xxx</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">ABC/DEF/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>yyy</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">LMN/OPQ/123</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>mmm</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">XYZ/GHY/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
<DESIGN-FUNCTION-PROTOTYPE>
<SHORT-NAME>nnn</SHORT-NAME>
<TYPE-TREF TYPE="DESIGN-FUNCTION-PROTOTYPE">AJK/UTL/456</TYPE-TREF>
</DESIGN-FUNCTION-PROTOTYPE>
</Root>
into
<?xml version="1.0" encoding="UTF-8"?><varoutput>123</varoutput><varoutput>456</varoutput>
with Xalan Java and Xalan Java XSLTC.
Or, as suggested in a comment, if you simply want to find the distinct values you can use set:distinct e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings"
xmlns:set="http://exslt.org/sets"
exclude-result-prefixes="exsl str set"
version="1.0">
<xsl:template match="Root">
<xsl:variable name="split-values">
<xsl:for-each select="DESIGN-FUNCTION-PROTOTYPE/TYPE-TREF">
<xsl:copy-of select="str:split(., '/')[last()]"/>
</xsl:for-each>
</xsl:variable>
<xsl:copy-of select="set:distinct(exsl:node-set($split-values)/node())"/>
</xsl:template>
</xsl:stylesheet>
I've a challenging problem and so far I wasn't able to solve.
Within my xlst I have variable which contains a string.
I need to add the following sequence [eol] to this string.
On a fix position namely every 65 characters
I thought to use a function or template to recursive add this charackter.
The reason is that the string length can variate in length.
<xsl:function name="funct:insert-eol" as="xs:string" >
<xsl:param name="originalString" as="xs:string?"/>
<xsl:variable name="length">
<xsl:value-of select="string-length($originalString)"/>
</xsl:variable>
<xsl:variable name="start" as="xs:integer">
<xsl:value-of select="1"/>
</xsl:variable>
<xsl:variable name="eol" as="xs:integer">
<xsl:value-of select="65"/>
</xsl:variable>
<xsl:variable name="newLines">
<xsl:value-of select="$length idiv number('65')"/>
</xsl:variable>
<xsl:for-each select="1 to $newLines">
<xsl:value-of select="substring($originalString, $start, $eol)" />
</xsl:for-each>
</xsl:function>
The more I write code the more variables I need to introduce. This is still my lack on understanding.
For example we want every 5 chars an [eol]
aaaaaaabbbbbbccccccccc
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]cc
Hope someone has a starting point for me..
Regards Dirk
Rather straight-forward and short -- no recursion is necessary (and can even be specified as a single XPath expression):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pLLength" select="5"/>
<xsl:template match="/*">
<xsl:variable name="vText" select="string()"/>
<xsl:for-each select="1 to string-length($vText) idiv $pLLength +1">
<xsl:value-of select="substring($vText, $pLLength*(position()-1)+1, $pLLength)"/>
<xsl:if test=
"not(position() eq last()
or position() eq last() and string-length($vText) mod $pLLength)">[eol]</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<t>aaaaaaabbbbbbccccccccc</t>
the wanted, correct result is produced:
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]cc
When this XML document is processed:
<t>aaaaaaabbbbbbcccccccccddd</t>
again the wanted, correct result is produced:
aaaaa[eol]aabbb[eol]bbbcc[eol]ccccc[eol]ccddd[eol]
You can treat it as a grouping problem, using for-each-group:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:function name="mf:insert-eol" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="chunk-size" as="xs:integer"/>
<xsl:value-of>
<xsl:for-each-group select="string-to-codepoints($input)" group-by="(position() - 1) idiv $chunk-size">
<xsl:if test="position() gt 1"><xsl:sequence select="'eol'"/></xsl:if>
<xsl:sequence select="codepoints-to-string(current-group())"/>
</xsl:for-each-group>
</xsl:value-of>
</xsl:function>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text">
<xsl:copy>
<xsl:sequence select="mf:insert-eol(., 5)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
That stylesheet transforms
<root>
<text>aaaaaaabbbbbbccccccccc</text>
</root>
into
<root>
<text>aaaaaeolaabbbeolbbbcceolccccceolcc</text>
</root>
Try this one:
<?xml version='1.0' ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="TextToChange" select="'aaaaaaabbbbbbccccccccc'"/>
<xsl:param name="RequiredLength" select="xs:integer(5)"/>
<xsl:template match="/">
<xsl:call-template name="AddText"/>
</xsl:template>
<xsl:template name="AddText">
<xsl:param name="Text" select="$TextToChange"/>
<xsl:param name="TextLength" select="string-length($TextToChange)"/>
<xsl:param name="start" select="xs:integer(1)"/>
<xsl:param name="end" select="$RequiredLength"/>
<xsl:choose>
<xsl:when test="$TextLength gt $RequiredLength">
<xsl:value-of select="substring($Text,$start,$end)"/>
<xsl:text>[eol]</xsl:text>
<xsl:call-template name="AddText">
<xsl:with-param name="Text" select="substring-after($Text, substring($Text,$start,$end))"/>
<xsl:with-param name="TextLength"
select="string-length(substring-after($Text, substring($Text,$start,$end)))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>