xslt captalize each word ignore conjunctions - xslt

i've the below xml.
<?xml version="1.0" encoding="UTF-8"?>
<chapter num="A">
<title>
<content-style font-style="bold">PART 1 GENERAL PRINCIPLES</content-style>
</title>
<section level="sect1">
<section level="sect2" number-type="manual" num="1.">
<title>INTRODUCTION OF INDIA TO NEW ERA AND THE EXISTING</title>
</section>
</section>
</chapter>
by using the below xslt i'm able to captalize each word.
<xsl:element name="{concat(translate(substring(name(), 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'), substring(name(), 2))}">
and i get the below output.
Introduction Of India To New Era And The Existing
but i want as below.
Introduction of India to New Era and the Existing
i.e. i want to ignore the conjunctions. please let me know how do i do it.
Thanks

This transformation uses the strSplit-to-Words template of the FXSL library (written in pure XSLT 1.0) and doesn't require any extension functions with the exception of the de-facto standard xxx:node-set():
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="vLowercase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="vUppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vConjunctions" select=
"'|AND|THE|OF|A|AN|TO|FOR|AT|ON|IN|INTO|AMONG|FROM|'"/>
<xsl:strip-space elements="*"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="pDelims" select="'
'"/>
<xsl:template match="/">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="/"/>
<xsl:with-param name="pDelimiters"
select="$pDelims"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select=
"ext:node-set($vwordNodes)/*[normalize-space()]"/>
</xsl:template>
<xsl:template match="word">
<xsl:if test="not(position() = 1)"><xsl:text> </xsl:text></xsl:if>
<xsl:choose>
<xsl:when test="contains($vConjunctions, concat('|',.,'|'))">
<xsl:value-of select="translate(substring(., 1, 1), $vUppercase, $vLowercase)"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="substring(., 1, 1)"/></xsl:otherwise>
</xsl:choose>
<xsl:value-of select="translate(substring(., 2), $vUppercase, $vLowercase)"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<title>INTRODUCTION OF INDIA TO NEW ERA AND THE EXISTING</title>
the wanted, correct result is produced:
Introduction of India to New Era and the Existing

Piggybacking on the answer provided by #NavinRawat, here's an XSLT 1.0 variant. Note that it requires the use of the EXSLT Extension Library's node-set() function.
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pConjunctions" select="'|OF|TO|AND|THE|'"/>
<xsl:variable name="vUppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vLowercase" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:template match="/*/*/*/title">
<xsl:variable name="vTitleWords">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="delimiter" select="' '"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="exsl:node-set($vTitleWords)/*"/>
</xsl:template>
<xsl:template match="token">
<xsl:if test="position() > 1"> </xsl:if>
<xsl:choose>
<xsl:when test="contains($pConjunctions, concat('|', ., '|'))">
<xsl:value-of select="translate(., $vUppercase, $vLowercase)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of
select="concat(
substring(., 1, 1),
translate(substring(., 2), $vUppercase, $vLowercase)
)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()"/>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:choose>
<xsl:when test="contains($text,$delimiter)">
<xsl:element name="token">
<xsl:value-of select="substring-before($text,$delimiter)"/>
</xsl:element>
<xsl:call-template name="tokenize">
<xsl:with-param
name="text"
select="substring-after($text,$delimiter)"/>
<xsl:with-param
name="delimiter"
select="$delimiter"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="$text">
<xsl:element name="token">
<xsl:value-of select="$text"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
...is run against the provided XML:
<?xml version="1.0" encoding="UTF-8"?>
<chapter num="A">
<title>
<content-style font-style="bold">PART 1 GENERAL PRINCIPLES</content-style>
</title>
<section level="sect1">
<section level="sect2" number-type="manual" num="1.">
<title>INTRODUCTION OF INDIA TO NEW ERA AND THE EXISTING</title>
</section>
</section>
</chapter>
...the wanted result is produced:
Introduction of India to New Era and the Existing
Obviously, the XSLT 2.0 variant is much cleaner and doesn't require a two-pass transformation, but if you're stuck with XSLT 1.0 and can utilize EXSLT, this will get you where you want to go.

Try this one:
<?xml version='1.0'?>
<xslt:stylesheet version="2.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="Conjunction">(of)|(to)|(and)|(the)</xsl:param>
<xsl:template match="chapter/section/section/title">
<xsl:call-template name="changeUpperCase">
<xsl:with-param name="Text" select="text()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="changeUpperCase">
<xsl:param name="Text"/>
<xsl:value-of select="for $EachToken in tokenize(lower-case($Text), ' ')
return
if(matches($EachToken, $Conjunction))
then
$EachToken
else
concat(upper-case(substring($EachToken, 1, 1)), substring($EachToken, 2))"/>
</xsl:template>
</xslt:stylesheet>

Related

XSLT to handle quotes and Pipe Delimited symbol

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>

How to Iterate through nodes and skip duplicate nodes with same value using a variable

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>

XSLT - extract all text but last subsection

Looking to parse out a namespace from a full class name in xml.
Data example:
<results>
<test-case name="Co.Module.Class.X">
</results>
End result (going to csv format):
,Co.Module.Class
Stylesheet:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="text" indent="yes" encoding="ISO-8859-1"/>
<xsl:param name="delim" select="','" />
<xsl:param name="quote" select="'"'" />
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
FullTestName, Namespace
<xsl:apply-templates select="//test-case" />
</xsl:template>
<xsl:template match="test-case">
<xsl:apply-templates />
<xsl:value-of select="#name" />
<xsl:value-of select="$delim" />
<xsl:value-of select="function to go here for nameWithJustNamespace" />
<xsl:value-of select="$break" />
</xsl:template>
I understand the process would need a last index of "." to be called once, yet I'm not finding XSLT to have that function. How to best accomplish this?
To do this in pure XSLT 1.0, you need to call a named recursive template, e.g.:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/results">
<xsl:call-template name="remove-last-token">
<xsl:with-param name="text" select="test-case/#name"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="remove-last-token">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'.'"/>
<xsl:value-of select="substring-before($text, $delimiter)"/>
<xsl:if test="contains(substring-after($text, $delimiter), $delimiter)">
<xsl:value-of select="$delimiter"/>
<xsl:call-template name="remove-last-token">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This pure XSLT 1.0 transformation (shorter, no conditional XSLT operations, single template):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="test-case[contains(#name, '.')]">
<xsl:param name="pDotIndex" select="0"/>
<xsl:variable name="vNextToken"
select="substring-before(substring(#name, $pDotIndex+1), '.')"/>
<xsl:value-of select="concat(substring('.', 2 - ($pDotIndex > 0)),$vNextToken)"/>
<xsl:variable name="vNewDotIndex" select="$pDotIndex+string-length($vNextToken)+1"/>
<xsl:apply-templates
select="self::node()[contains(substring(#name,$vNewDotIndex+1), '.')]">
<xsl:with-param name="pDotIndex" select="$vNewDotIndex"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<results>
<test-case name="Co.Module.Class.X"/>
</results>
produces the wanted, correct result:
Co.Module.Class
Part 2
With a slight modification the following transformation produces the complete CSV:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="test-case[contains(#name, '.')]">
<xsl:param name="pDotIndex" select="0"/>
<xsl:variable name="vNextToken"
select="substring-before(substring(#name, $pDotIndex+1), '.')"/>
<xsl:value-of select="concat(substring(',', 2 - (position() > 1)),
substring('.', 2 - ($pDotIndex > 0)), $vNextToken)"/>
<xsl:variable name="vNewDotIndex" select="$pDotIndex+string-length($vNextToken)+1"/>
<xsl:apply-templates
select="self::node()[contains(substring(#name,$vNewDotIndex+1), '.')]">
<xsl:with-param name="pDotIndex" select="$vNewDotIndex"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
When applied on this XML document:
<results>
<test-case name="Co.Module.Class.X"/>
<test-case name="Co2.Module2.Class2.Y"/>
<test-case name="Co3.Module3.Class3.Z"/>
</results>
the wanted, correct (CSV) result is produced:
Co.Module.Class,Co2.Module2.Class2,Co3.Module3.Class3

Create a recursive string function for adding a sequence

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>

how to insert xml nodet at a defined points

xslt have function(like substring vice versa) or how to solve it? I had xml:
<document>
<Line>
<Line-Item>
<LineNumber>10</LineNumber>
<EAN>111</EAN>
<BIC>123123</BIC>
<SIC>AVD091</SIC>
</Line-Item>
</Line>
<Line>
<Line-Item>
<LineNumber>20</LineNumber>
<EAN>22222</EAN>
<BIC>3232332</BIC>
<SIC>AVD25482</SIC>
</Line-Item>
</Line>
</document>
needed output:
10 111 123123 AVD091
20 22222 3232332 AVD25482
Field line number start from 1 column position, EAN start from 11 column position, BIC start from 19 and SIC from 31.
Try this XSLT 1.0 style-sheet. The pad template is an XSLT 1.0 version of Martin's mf:pad function.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="document/Line/Line-Item"/>
</xsl:template>
<xsl:template name="pad">
<xsl:param name="value" />
<xsl:param name="width" />
<xsl:variable name="col-max" select="' '"/>
<xsl:value-of select="substring( concat($value,$col-max), 1, $width)" />
</xsl:template>
<xsl:template match="Line-Item" >
<xsl:call-template name="pad" >
<xsl:with-param name="value" select="LineNumber"/>
<xsl:with-param name="width" select="10" />
</xsl:call-template>
<xsl:call-template name="pad" >
<xsl:with-param name="value" select="EAN"/>
<xsl:with-param name="width" select="8" />
</xsl:call-template>
<xsl:call-template name="pad" >
<xsl:with-param name="value" select="BIC"/>
<xsl:with-param name="width" select="12" />
</xsl:call-template>
<xsl:value-of select="SIC" />
<xsl:value-of select="'
'" />
</xsl:template>
<xsl:template match="*" />
</xsl:stylesheet>
This short and generic transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<my:fields>
<fieldset name="LineNumber" width="10"/>
<fieldset name="EAN" width="8"/>
<fieldset name="BIC" width="12"/>
</my:fields>
<xsl:variable name="vSpaces" select="' '"/>
<xsl:variable name="vFields" select="document('')/*/my:fields/*"/>
<xsl:template match="Line-Item">
<xsl:text>
</xsl:text>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="Line-Item/*">
<xsl:value-of select=
"concat(.,
substring($vSpaces,
1,
$vFields[#name = name(current())]/#width
-
string-length()
)
)"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<document>
<Line>
<Line-Item>
<LineNumber>10</LineNumber>
<EAN>111</EAN>
<BIC>123123</BIC>
<SIC>AVD091</SIC>
</Line-Item>
</Line>
<Line>
<Line-Item>
<LineNumber>20</LineNumber>
<EAN>22222</EAN>
<BIC>3232332</BIC>
<SIC>AVD25482</SIC>
</Line-Item>
</Line>
</document>
produces the wanted, correct result:
10 111 123123 AVD091
20 22222 3232332 AVD25482
Do note:
The element my:fields can be put in its own XML document. Thus, no modifications would be required to the XSLT code if some fields widths need to be modified.
Here is a sample stylesheet (XSLT 2.0, sorry, started writing before your comment indicated a request for 1.0):
<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:param name="col-max" as="xs:string" select="' '"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:function name="mf:pad" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<xsl:param name="col-length" as="xs:integer"/>
<xsl:sequence select="concat($input, substring($col-max, 1, $col-length - string-length($input)))"/>
</xsl:function>
<xsl:template match="Line">
<xsl:if test="position() > 1">
<xsl:text>
</xsl:text>
</xsl:if>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="LineNumber">
<xsl:sequence select="mf:pad(., 10)"/>
</xsl:template>
<xsl:template match="EAN">
<xsl:sequence select="mf:pad(., 9)"/>
</xsl:template>
<xsl:template match="BIC">
<xsl:sequence select="mf:pad(., 12)"/>
</xsl:template>
<xsl:template match="SIC">
<xsl:sequence select="mf:pad(., string-length())"/>
</xsl:template>
</xsl:stylesheet>