I've searched far and wide for an example of this, and have so far had no luck in applying a sum template to make my XSTL work.
This is the XML (number of lines varies on each planfeature)
<PlanFeatures>
<PlanFeature name="Line0001">
<CoordGeom>
<Line>
<Start pntRef="7540">5605 8950 1020</Start>
<End pntRef="7541">5605 8951 1019</End>
</Line>
<Line>
<Start pntRef="7541">5605 8951 1019</Start>
<End pntRef="7542">5605 8947 1019</End>
</Line>
<Line>
<Start pntRef="7542">5605 8947 1019</Start>
<End pntRef="7543">5605 8940 1011</End>
</Line>
<Line>
<Start pntRef="7543">5605 8940 1011</Start>
<End pntRef="7544">5605 8931 1020</End>
</Line>
</CoordGeom>
</PlanFeature>
</PlanFeatures>
This is where I'm at with the XSL, which uses a recursive call template to calculate the distance of each line segment.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:landxml="http://www.landxml.org/schema/LandXML-1.2" xmlns:hexagon="http://xml.hexagon.com/schema/HeXML-1.5" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" version="1.0" encoding="UTF-16" indent="no" omit-xml-declaration="yes"/>
<xsl:variable name="XML" select="/"/>
<xsl:variable name="fileExt" select="'txt'"/>
<xsl:variable name="fileDesc" select="'line distance report'"/>
<xsl:template match="/">
<xsl:for-each select="$XML">
<xsl:for-each select="landxml:LandXML/landxml:PlanFeatures/landxml:PlanFeature">
<xsl:value-of select="#name"/><xsl:text>::</xsl:text>
<xsl:for-each select="landxml:CoordGeom/landxml:Line">
<xsl:value-of select="landxml:Start/#pntRef"/><xsl:text>-</xsl:text>
<xsl:variable name="lista" select="landxml:Start"/>
<xsl:variable name="x1" select="substring-before($lista,' ')"/>
<xsl:variable name="yt1" select="substring-after($lista,' ')"/>
<xsl:variable name="y1" select="substring-before($yt1,' ')"/>
<xsl:variable name="z1" select="substring-after($yt1,' ')"/>
<xsl:variable name="listb" select="landxml:End"/>
<xsl:value-of select="landxml:End/#pntRef"/><xsl:text>: </xsl:text>
<xsl:variable name="x2" select="substring-before($listb,' ')"/>
<xsl:variable name="yt2" select="substring-after($listb,' ')"/>
<xsl:variable name="y2" select="substring-before($yt2,' ')"/>
<xsl:variable name="z2" select="substring-after($yt2,' ')"/>
<xsl:variable name="seg" select= "((($x2 - $x1)*($x2 - $x1))+(($y2 - $y1)*($y2 - $y1))+(($z2 - $z1)*($z2 - $z1)))"/>
<xsl:call-template name="root">
<xsl:with-param name="X" select="$seg"/>
</xsl:call-template>
<xsl:text>, </xsl:text>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="root">
<xsl:param name="X"/>
<xsl:param name="xn" select="0"/>
<xsl:param name="xn_1" select="($X+1) div 2"/>
<xsl:choose>
<xsl:when test="string(number($X)) = 'NaN'">
<xsl:value-of select=" ' ' "/>
</xsl:when>
<xsl:when test="($xn_1 - $xn) * ($xn_1 - $xn) < 0.00000001">
<xsl:value-of select='format-number($xn_1, "#.000")'/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="root">
<xsl:with-param name="X" select="$X"/>
<xsl:with-param name="xn" select="$xn_1"/>
<xsl:with-param name="xn_1" select="($xn_1 + ($X div $xn_1)) div 2"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I need to sum the value of X (distance) from the root call template, to create a value which represents the sum of each line segment. I think I need to use a match template, but so far it hand even got close to working.
Currently exporting LINEID::StartPt-EndPt: dist, StartPt-EndPt: dist, etc. I need the sum of the 'dist' to be shown at the end of each line as well. As below
Line0001::7540-7541: 1.414, 7541-7542: 2.000, 7542-7543: 12.042, 7543-7544: 12.720
but I would like
Line0001::7540-7541: 1.414, 7541-7542: 2.000, 7542-7543: 12.042, 7543-7544: 12.728 -- 28.184
Any help would be appreciated... the examples on this site have helped me so much already, but I just can't seem to get through this roadblock.
Cheers,
Chris
You can accomplish this with some relatively simple recursion and parameter passing. Try replacing your first template with these four templates:
<xsl:template match="/">
<xsl:for-each select="$XML">
<xsl:apply-templates
select="landxml:LandXML/landxml:PlanFeatures/landxml:PlanFeature" />
</xsl:for-each>
</xsl:template>
<xsl:template match ="landxml:PlanFeature">
<xsl:value-of select="concat(#name, '::')" />
<xsl:apply-templates select="landxml:CoordGeom/landxml:Line[1]" />
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="landxml:Line">
<xsl:param name="total" select="0" />
<xsl:value-of
select="concat(landxml:Start/#pntRef, '-', landxml:End/#pntRef, ': ')"/>
<xsl:variable name="len">
<xsl:call-template name="root">
<xsl:with-param name="X">
<xsl:call-template name="seg" />
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$len"/>
<xsl:variable name="next" select="following-sibling::landxml:Line[1]" />
<xsl:variable name="newTot" select="$total + $len" />
<xsl:choose>
<xsl:when test="$next">
<xsl:text>, </xsl:text>
<xsl:apply-templates select="$next">
<xsl:with-param name="total" select="$newTot" />
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(' -- ', format-number($newTot, '#.000'))" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="seg">
<xsl:variable name="lista" select="landxml:Start"/>
<xsl:variable name="x1" select="substring-before($lista,' ')"/>
<xsl:variable name="yt1" select="substring-after($lista,' ')"/>
<xsl:variable name="y1" select="substring-before($yt1,' ')"/>
<xsl:variable name="z1" select="substring-after($yt1,' ')"/>
<xsl:variable name="listb" select="landxml:End"/>
<xsl:variable name="x2" select="substring-before($listb,' ')"/>
<xsl:variable name="yt2" select="substring-after($listb,' ')"/>
<xsl:variable name="y2" select="substring-before($yt2,' ')"/>
<xsl:variable name="z2" select="substring-after($yt2,' ')"/>
<xsl:value-of select= "($x2 - $x1)*($x2 - $x1)+
($y2 - $y1)*($y2 - $y1)+
($z2 - $z1)*($z2 - $z1)"/>
</xsl:template>
When run on your sample input (after adjusting it to match the paths in your XSLT), the result is:
Line0001::7540-7541: 1.414, 7541-7542: 4.000, 7542-7543: 10.630, 7543-7544: 12.728 -- 28.772
Related
In continuation with what is asked in below link regarding capitalising first character
Convert First character of each word to upper case
The above link assumes that there is space between each characters. How can I dynamically identify any non-alphanumeric character in between and then capitalise the following letters
For e.g. O'connel derrick should return as O'Connel Derrick
and
Adrian-merriel james should return as Adrian-Merriel James
I used below code and it works fine for string with space
<xsl:variable name='text' select='"dInEsh sAchdeV kApil Muk"' />
<xsl:variable name='lowers' select='"abcdefghijklmnopqrstuvwxyz"' />
<xsl:variable name='uppers' select='"ABCDEFGHIJKLMNOPQRSTUVWXYZ"' />
<xsl:template match="/">
<xsl:for-each select='str:split($text, " ")'>
<xsl:value-of select='concat(
translate(substring(., 1, 1), $lowers, $uppers),
translate(substring(., 2), $uppers, $lowers),
" "
)' />
</xsl:for-each>
</xsl:template>
Any help is highly appreciated.
For an XSLT 1.0 solution, you could setup a recursive template call that walks over each of the characters and tracks whether or not it has seen a alpha-numeric value, capitalizing the first that it sees, and then resetting when it encounters a non-alpha-numeric value:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:variable name='text' select='"dInEsh sAchdeV kApil Muk"' />
<xsl:variable name='lowers' select='"abcdefghijklmnopqrstuvwxyz"' />
<xsl:variable name='uppers' select='"ABCDEFGHIJKLMNOPQRSTUVWXYZ"' />
<xsl:variable name='numeric' select='0123456789'/>
<xsl:variable name='alpha-numeric' select="concat($lowers,$uppers,$numeric)"/>
<xsl:template match="/">
<xsl:call-template name="capitalize">
<xsl:with-param name="val" select="$text"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="capitalize">
<xsl:param name="val"/>
<xsl:param name="alphanumeric-seen" select="false()" />
<xsl:variable name="head" select="substring($val, 1, 1)"/>
<xsl:if test="$head">
<xsl:variable name="is-alpha-numeric" select="not(translate($head, $alpha-numeric, ''))"/>
<xsl:variable name="tail" select="substring($val, 2)"/>
<xsl:choose>
<xsl:when test="$is-alpha-numeric">
<xsl:choose>
<xsl:when test="$alphanumeric-seen">
<xsl:value-of select="translate($head, $uppers, $lowers)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate($head, $lowers, $uppers)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="capitalize">
<xsl:with-param name="val" select="$tail"/>
<xsl:with-param name="alphanumeric-seen" select="true()"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$head"/>
<xsl:call-template name="capitalize">
<xsl:with-param name="val" select="$tail"/>
<xsl:with-param name="alphanumeric-seen" select="false()"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
With XSLT 2.0 you could use xsl:analyze-string and regex:
<xsl:stylesheet exclude-result-prefixes="#all" version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:variable name='text' select='"dInEsh sAchdeV kApil Muk"' />
<xsl:variable name='lowers' select='"abcdefghijklmnopqrstuvwxyz"' />
<xsl:variable name='uppers' select='"ABCDEFGHIJKLMNOPQRSTUVWXYZ"' />
<xsl:template match="/">
<xsl:analyze-string select="$text" regex="[a-zA-Z0-9]+">
<xsl:matching-substring>
<xsl:value-of select="upper-case(substring(., 1, 1)), lower-case(substring(., 2))" separator=""/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
I would also use a recursive named template - but instead of going over each character in text I would iterate on delimiters only:
XSLT 1.0 + EXSLT str:split() extension function
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="text">dInEsh sAchdeV kApil. muk O'connel derrick, Adrian-merriel james</xsl:param>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:variable name="punc" select="translate($text, concat($lower, upper), '')" />
<xsl:template match="/">
<output>
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="translate($text, $upper, $lower)"/>
<xsl:with-param name="delimiters" select="translate($text, concat($lower, $upper), '')"/>
</xsl:call-template>
</output>
</xsl:template>
<xsl:template name="capitalize">
<xsl:param name="text"/>
<xsl:param name="delimiters"/>
<xsl:choose>
<xsl:when test="$delimiters">
<xsl:variable name="delimiter" select="substring($delimiters, 1, 1)"/>
<xsl:call-template name="capitalize">
<xsl:with-param name="text">
<xsl:for-each select="str:split($text, $delimiter)">
<xsl:value-of select="translate(substring(., 1, 1), $lower, $upper)"/>
<xsl:value-of select="substring(., 2)"/>
<xsl:if test="position()!=last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
</xsl:with-param>
<xsl:with-param name="delimiters" select="translate($delimiters, $delimiter, '')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="translate(substring($text, 1, 1), $lower, $upper)"/>
<xsl:value-of select="substring($text, 2)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>Dinesh Sachdev Kapil. Muk O'Connel Derrick, Adrian-Merriel James</output>
However, this is not perfect: a sequence of consecutive delimiter character of the same kind will be reduced to a single character - e.g. alpha---bravo ==> Alpha-Bravo.
I started learning XSLT, kind of got stuck while writing xslt functions for converting from lower-case to upper-case and upper-case to lower-case in xslt
I have tried a lot by writing different xslt functions but I think some where I'm doing mistake in my code
<xsl:template name="ConvertXmlStyleToCamelCase">
<xsl:param name="occupation"/>
<xsl:template match="node()"/>
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:param name="delimiter" select='/'/>
<xsl:param name="delimiter2" select= "' '"/>
<xsl:if test="not($occupation = '')" >
<xsl:choose>
<xsl:when test="contains($occupation, $delimiter)">
<xsl:variable name="word" select="substring-before(concat($occupation, $delimiter), $delimiter)"></xsl:variable>
<xsl:if test="$word">
<xsl:value-of select="translate(substring($word, 1, 1), $lowercase, $uppercase)"/>
<xsl:value-of select="translate(substring($word,2), $uppercase, $lowercase)"/>
</xsl:if>
</xsl:when>
<xsl:when test="contains( $occupation, $delimiter)">
<xsl:value-of select="$delimiter"/>
<!-- Recursive call to template to translate the text after delimeter -->
<xsl:call-template name="ConvertXmlStyleToCamelCase">
<xsl:with-param name="occupation" select="substring-after($occupation, $delimiter)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($occupation, $delimiter2)">
<xsl:variable name="word2" select="substring-before(concat($occupation, $delimiter2), $delimiter2)"></xsl:variable>
<xsl:if test="$word2">
<xsl:value-of select="translate(substring($word2, 1, 1), $lowercase, $uppercase)"></xsl:value-of>
<xsl:value-of select="translate(substring($word2, 2), $uppercase, $lowercase)"/>
</xsl:if>
</xsl:when>
<xsl:when test="contains($occupation, $delimiter2)">
<xsl:value-of select="$delimiter2"/>
<!-- Recursive call to template to translate the text after delimeter2 -->
<xsl:call-template name="ConvertXmlStyleToCamelCase">
<xsl:with-param name="occupation" select="substring-after($occupation, $delimiter2)"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:if>
<xsl:if test="not($occupation = $delimiter and $delimiter2)">
<xsl:value-of select="substring(occupation, 1, 1)"/>
<xsl:value-of select="translate(substring(occupation, 2), $uppercase, $lowercase)"/>
</xsl:if>
</xsl:template>
input will be any one value from the below
1.SELF/EMPLOYED
2.SKILL TRADE
3.GOVERNMENT
Expected output as below
Self/Employed
Skill Trade
Government
But the actual outcome is
SelfSelf employed
Skill/trade
Government
As I mentioned in a comment, your code is not reproducible. From the results you report it is clear that your 2nd delimiter is not applied. AFAICT, it is because you check first for the existence of the 1st delimiter - and if you find it, you do not bother to test if the 2nd delimiter exists before the 1st one.
Consider the following example (adapted from Converting first letter of a string to capital in xslt):
XML
<input>
<item>Self/Employed</item>
<item>Skill Trade</item>
<item>Government</item>
<item>a combi/na/tion of various de/limi/ters</item>
</input>
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="*"/>
<xsl:template match="/input">
<output>
<xsl:for-each select="item">
<caps>
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</caps>
</xsl:for-each>
</output>
</xsl:template>
<xsl:template name="capitalize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:variable name="upper-case" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="lower-case" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="word" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:choose>
<xsl:when test="$delimiter=' '">
<!-- tokenize word by 2nd delimiter -->
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="$word"/>
<xsl:with-param name="delimiter" select="'/'"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- capitalize word -->
<xsl:value-of select="translate(substring($word, 1, 1), $lower-case, $upper-case)"/>
<xsl:value-of select="translate(substring($word, 2), $upper-case, $lower-case)"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="contains($text, $delimiter)">
<xsl:value-of select="$delimiter"/>
<!-- recursive call -->
<xsl:call-template name="capitalize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<caps>Self/Employed</caps>
<caps>Skill Trade</caps>
<caps>Government</caps>
<caps>A Combi/Na/Tion Of Various De/Limi/Ters</caps>
</output>
Could you have more than two delimiters in the future? If so, try this XSLT, which can be readily extended to have more (single character) delimiters. Just change the delimiters parameters in the ConvertXmlStyleToCamelCase template.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<item>
<xsl:call-template name="ConvertXmlStyleToCamelCase" />
</item>
</xsl:template>
<xsl:template name="ConvertXmlStyleToCamelCase">
<xsl:param name="text" select="."/>
<xsl:param name="delimiters" select="' /'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="nextDelimiter" select="substring(translate($text, translate($text, $delimiters, ''), ''), 1, 1)" />
<xsl:variable name="string" select="substring-before(concat($text, ' '), substring(concat($nextDelimiter, ' '), 1, 1))" />
<xsl:message terminate="no">Next delimiter is <xsl:value-of select="$nextDelimiter" /></xsl:message>
<xsl:value-of select="translate(substring($string, 1, 1), $lower, $upper)"/>
<xsl:value-of select="translate(substring($string, 2), $upper, $lower)"/>
<xsl:if test="$nextDelimiter">
<xsl:value-of select="$nextDelimiter" />
<xsl:call-template name="ConvertXmlStyleToCamelCase">
<xsl:with-param name="text" select="substring-after($text, $nextDelimiter)"/>
<xsl:with-param name="delimiters" select="$delimiters"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltfiddle.liberty-development.net/gWvjQeR where I have used a third delimiters as an example.
With reference to the double-translate, the purpose of this is to find the next delimiter in the string. To do this (in XSLT 1.0) you need to remove all the characters that are not delimiters. Doing translate($text, $delimiters, '') removes all delimiters, and so returns all characters that are not delimiters. If you then apply this result to the original string, you are left with just the delimiters present. The first character will then be the next delimiter.
I'm using XSLT to convert XML to certain file format. I'm excluding reversal transactions matching two fields (OrigTxnId and TxnId). The problem is the count I'm doing in the header for the number of transactions still include the Transactions which have been removed.
Herewith input XML exsample:
<XML>
<Record><GroupId>10028</GroupId><Id>1</Id><User>CHRISVI</User><TxnId>264-10028-1-516739-2</TxnId><Date>30-Sep-2014</Date><Time>12:21:24</Time><Account>12440531</Account><Amount>217090</Amount><AllowableMOP>0</AllowableMOP><BankBranchCode>280071</BankBranchCode><ChequeAccNo>62247628681</ChequeAccNo><ChequeNo>000040</ChequeNo></Record>
<Record><GroupId>10028</GroupId><Id>1</Id><User>CHRISVI</User><TxnId>264-10028-1-516743-2</TxnId><Date>30-Sep-2014</Date><Time>12:21:52</Time><Account>10895388</Account><Amount>150000</Amount><AllowableMOP>1</AllowableMOP></Record>
<Record><GroupId>10028</GroupId><Id>1</Id><User>CHRISVI</User><TxnId>264-10028-1-516748-1</TxnId><Date>30-Sep-2014</Date><Time>12:22:26</Time><OrigTxnId>264-10028-1-516743-2</OrigTxnId><Account>10895388</Account><Amount>150000</Amount><AllowableMOP>1</AllowableMOP></Record>
<Record><GroupId>10028</GroupId><Id>1</Id><User>CHRISVI</User><TxnId>264-10028-1-516756-1</TxnId><Date>30-Sep-2014</Date><Time>12:23:01</Time><Account>10895388</Account><Amount>10000</Amount><AllowableMOP>1</AllowableMOP></Record>
<Record><GroupId>10028</GroupId><Id>1</Id><User>CHRISVI</User><TxnId>264-10028-1-516760-2</TxnId><Date>30-Sep-2014</Date><Time>12:23:24</Time><Account>10605762</Account><Amount>15000</Amount><AllowableMOP>1</AllowableMOP></Record>
</XML>
The XSLT code to convert XML:
]]>
<xsl:key name="original" match="/XML/Record" use="TxnId" />
<xsl:key name="copy" match="/XML/Record" use="OrigTxnId" />
<xsl:template match="/">
<?Header Starts?>
<xsl:value-of select="user:IncrementBatchNo('Batchcow','C:\WebRiposte\Agents\Configuration\Configurations.xml')"/>
<?RECORDTYPE?>
<xsl:text>H</xsl:text>
<?FILETYPE?>
<xsl:text>PNP</xsl:text>
<?COMPANYCODE?>
<xsl:text>WHK</xsl:text>
<?COMPANYNAME?>
<xsl:text> Windhoek Municipality</xsl:text>
<?ACTIONDATETIME?>
<xsl:value-of select="user:getdatetime()"/>
<?PAYMENTBATCHNO?>
<xsl:value-of select="format-number(user:GetBatchNo('Batchcow','C:\WebRiposte\Agents\Configuration\Configurations.xml'),'000000')"/>
<?RECORDSIZE?>
<xsl:text>000256</xsl:text>
<?NUMRECORDS?>
<xsl:value-of select="format-number(count(//SessionId),'000000')"/>
<?TESTLIVE?>
<xsl:text>L</xsl:text>
<?FILLER?>
<xsl:call-template name="pad-some-space">
<xsl:with-param name="currentlength" select="1"/>
<xsl:with-param name="newlength" select="177"/>
</xsl:call-template>
<?Line Feed?>
<xsl:text>
</xsl:text>
<?Header Ends ?>
<?Body Starts?>
<xsl:for-each select="XML/Record[not(key('original', OrigTxnId) or key('copy', TxnId))]">
<?Record Type - 1 - Fixed value “D”(etail)?>
<xsl:text>D</xsl:text>
<?PAYMENTBATCHNO?>
<xsl:value-of select="format-number(user:GetBatchNo('Batchcow','C:\WebRiposte\Agents\Configuration\Configurations.xml'),'000000')"/>
<?SeqNo - 6 - Right justified, zero padded?>
<xsl:value-of select="format-number(count(preceding-sibling::Record)+1, '000000')"/>
<?CompanyCode - 3 - Leave Blank, Space padded?>
<xsl:text>WHK</xsl:text>
<?CustAccountNo - 20 - Right justified, zero padded?>
<xsl:value-of select="format-number(Account, '00000000000000000000')"/>
<?Invoice No and Ref no?>
<xsl:text>0000000000000000000000000</xsl:text>
<?Create Group id Variable?>
<xsl:variable name="GroupId" select="GroupId"/>
<?NamPostBranch - 50 - ?>
<xsl:call-template name="reformat-string-length">
<xsl:with-param name="value" select="user:GetPostOfficeName(string($GroupId),'C:\WebRiposte\Agents\Configuration\Configurations.xml')"/>
<xsl:with-param name="str-len" select="50"/>
</xsl:call-template>
<?NamPostReceiptNo - 16 - Group-Node-Sequence No?>
<xsl:call-template name="reformat-string-length">
<xsl:with-param name="value" select="substring(SessionId,5,16)"/>
<xsl:with-param name="str-len" select="16"/>
<xsl:with-param name="alignment" select=" 'right' "/>
</xsl:call-template>
<?MOPCheck?>
<xsl:choose>
<xsl:when test="ChequeNo > 0">
<xsl:text>1</xsl:text>
<?BankBranchCode - 6 - space padded?>
<xsl:call-template name="reformat-string-length">
<xsl:with-param name="value" select="BankBranchCode"/>
<xsl:with-param name="str-len" select="6"/>
</xsl:call-template>
<?ChequeAccountNo -15- Left justified, space padded?>
<xsl:call-template name="reformat-string-length">
<xsl:with-param name="value" select="ChequeAccNo"/>
<xsl:with-param name="str-len" select="15"/>
</xsl:call-template>
<?ChequeNo - 6 - Right justified, zero padded?>
<xsl:value-of select="format-number(ChequeNo, '000000')"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>2</xsl:text>
<?BankBranchCode - 6 - space padded?>
<xsl:text> </xsl:text>
<?ChequeAccountNo -15- Left justified, space padded?>
<xsl:text> </xsl:text>
<?ChequeNo - 6 - Right justified, zero padded?>
<xsl:value-of select="format-number(0, '000000')"/>
</xsl:otherwise>
</xsl:choose>
<?PayAmountCents - 9 - Right justified, zero padded?>
<xsl:value-of select="format-number(Amount, '000000000')"/>
<?PaymentDateTime (YYYYMMDDHHMMSS)?>
<xsl:value-of select="substring(Date,8,4)"/>
<xsl:call-template name="format-month-3letter-to-number">
<xsl:with-param name="month-3letter" select="substring(Date,4,3)"/>
</xsl:call-template>
<xsl:value-of select="substring(Date,1,2)"/>
<xsl:value-of select="substring(Time,1,2)"/>
<xsl:value-of select="substring(Time,4,2)"/>
<xsl:value-of select="substring(Time,7,2)"/>
<?Entry Mode?>
<xsl:text>M</xsl:text>
<?AmountSign - 1- ?>
<xsl:text>D</xsl:text>
<?Filler?>
<xsl:call-template name="pad-some-space">
<xsl:with-param name="currentlength" select="1"/>
<xsl:with-param name="newlength" select="77"/>
</xsl:call-template>
<?Line?>
<xsl:text>
</xsl:text>
</xsl:for-each>
<?Body Ends?>
<?Trailer Starts?>
<?RECORDTYPE?>
<xsl:text>T</xsl:text>
<?COMPANYCODE?>
<xsl:text>WHK</xsl:text>
<?PAYMENTBATCHNO?>
<xsl:value-of select="format-number(user:GetBatchNo('Batchcow','C:\WebRiposte\Agents\Configuration\Configurations.xml'),'000000')"/>
<?TOTALAMOUNTCENTS?>
<xsl:value-of select="format-number(sum(//Amount), '00000000000')"/>
<?AMOUNTSIGN?>
<xsl:text>D</xsl:text>
<?FILLER?>
<xsl:call-template name="pad-some-space">
<xsl:with-param name="currentlength" select="1"/>
<xsl:with-param name="newlength" select="235"/>
</xsl:call-template>
<?Trailer Ends?>
</xsl:template>
<?Support functions ------------------- ?>
<?Date time format?>
<xsl:template name="format-date-time">
<xsl:param name="currdatetime"/>
<xsl:value-of select="concat(substring($currdatetime,1,4),substring($currdatetime,6,2),substring($currdatetime,9,2),substring($currdatetime,12,2),substring($currdatetime,15,2),substring($currdatetime,18,2))"/>
</xsl:template>
<?Convert month from text to number?>
<xsl:template name="format-month-3letter-to-number">
<xsl:param name="month-3letter"/>
<xsl:variable name="MonthName" select="'Jan01Feb02Mar03Apr04May05Jun06Jul07Aug08Sep09Oct10Nov11Dec12'"/>
<xsl:value-of select="substring(concat(substring-after($MonthName,$month-3letter),'00'),1,2)"/>
</xsl:template>
<?Pad space?>
<xsl:template name="pad-some-space">
<xsl:param name="currentlength"/>
<xsl:param name="newlength"/>
<xsl:if test="number($currentlength) < number($newlength)">
<xsl:text> </xsl:text>
<xsl:call-template name="pad-some-space">
<xsl:with-param name="currentlength" select="number($currentlength)+1"/>
<xsl:with-param name="newlength" select="$newlength"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<?Evaluate-string-length?>
<xsl:template name="reformat-string-length">
<xsl:param name="value"/>
<xsl:param name="str-len"/>
<xsl:choose>
<xsl:when test="string-length($value) > number($str-len)">
<xsl:value-of select="substring($value,1,$str-len)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$value"/>
<xsl:call-template name="pad-some-space">
<xsl:with-param name="currentlength" select="string-length($value)"/>
<xsl:with-param name="newlength" select="number($str-len)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Simplifying problems is always good. Although it involved some guess work, I simplified your problem to this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="original" match="/XML/Record" use="TxnId"/>
<xsl:key name="copy" match="/XML/Record" use="OrigTxnId"/>
<xsl:template match="/">
<xsl:value-of select="format-number(count(//GroupId),'000000')"/>
<xsl:text>
</xsl:text>
<xsl:for-each select="XML/Record[not(key('original', OrigTxnId) or key('copy', TxnId))]">
<xsl:value-of select="format-number(Account, '00000000000000000000')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
(Note, as mentioned in my comments, I have replaced <xsl:value-of select="format-number(count(//Session),'000000')"/> with <xsl:value-of select="format-number(count(//GroupId),'000000')"/>)
This outputs the following
000005
00000000000012440531
00000000000010895388
00000000000010605762
So, the first count says 5 items, but there are only 3 rows.
Now, in your code, you have this xsl:for-each
<xsl:for-each
select="XML/Record[not(key('original', OrigTxnId) or key('copy', TxnId))]">
So, all you need to do is add the condition in you xsl:for-each to your count statement.
<xsl:value-of
select="format-number(count(XML/Record[not(key('original', OrigTxnId) or key('copy', TxnId))]/GroupId),'000000')"/>
It might be better to make use of a variable to remove the repetitive coding.
As an example, try this XSLT as a basis
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="original" match="/XML/Record" use="TxnId"/>
<xsl:key name="copy" match="/XML/Record" use="OrigTxnId"/>
<xsl:template match="/">
<xsl:variable name="records" select="XML/Record[not(key('original', OrigTxnId) or key('copy', TxnId))]" />
<xsl:value-of select="format-number(count($records/GroupId),'000000')"/>
<xsl:text>
</xsl:text>
<xsl:for-each select="$records">
<xsl:value-of select="format-number(Account, '00000000000000000000')"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This outputs the following:
000003
00000000000012440531
00000000000010895388
00000000000010605762
There is XML document:
<data>how;many;i;can;tell;you</data>
Need to get XML using XSLT version 1:
<manydata>
<onedata>how</onedata>
<onedata>many</onedata>
<onedata>i</onedata>
<onedata>can</onedata>
<onedata>tell</onedata>
<onedata>you</onedata>
</manydata>
How I can do it?
UPDATE:
Output format must be XML.
This recursive solution is probably one of the shortest possible:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="data">
<manydata><xsl:apply-templates/></manydata>
</xsl:template>
<xsl:template match="text()" name="tokenize">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText)">
<onedata>
<xsl:value-of select=
"substring-before(concat($pText,';'),';')"/>
</onedata>
<xsl:call-template name="tokenize">
<xsl:with-param name="pText" select=
"substring-after($pText,';')"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document;
<data>how;many;i;can;tell;you</data>
the wanted, correct result is produced:
<manydata>
<onedata>how</onedata>
<onedata>many</onedata>
<onedata>i</onedata>
<onedata>can</onedata>
<onedata>tell</onedata>
<onedata>you</onedata>
</manydata>
<xsl:template match="data">
<manydata>
<!--
empty <manydata/> will be generated,
if <data/> without child text()
-->
<xsl:apply-templates select="text()"/>
</manydata>
</xsl:template>
<xsl:template match="data/text()">
<!-- start point for recursion -->
<xsl:call-template name="tokenize-string">
<xsl:with-param name="separator" select="';'"/>
<xsl:with-param name="string" select="text()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize-string">
<xsl:param name="separator"/>
<xsl:param name="string"/>
<xsl:variable name="string-before-separator"
select="substring-before( $string, $separator )"/>
<onedata>
<xsl:choose>
<!-- if $separator presents in $string take first piece -->
<xsl:when test="$string-before-separator">
<xsl:value-of select="$string-before-separator"/>
</xsl:when>
<!-- take whole $string, if no $separator in the $string -->
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</onedata>
<!-- recursive call, if separator was found -->
<xsl:if test="$string-before-separator">
<xsl:call-template name="tokenize-string">
<xsl:with-param name="separator" select="$separator"/>
<xsl:with-param name="string"
select="substring-after( $string, $separator )"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Try this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:element name="manydata">
<xsl:call-template name="splitsemicolons">
<xsl:with-param name="text" select="text()" />
</xsl:call-template>
</xsl:element>
</xsl:template>
<xsl:template name="splitsemicolons">
<xsl:param name="text" />
<xsl:choose>
<xsl:when test="contains($text,';')">
<xsl:element name="onedata">
<xsl:value-of select="substring-before($text,';')" />
</xsl:element>
<xsl:call-template name="splitsemicolons">
<xsl:with-param name="text" select="substring-after($text,';')" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="onedata">
<xsl:value-of select="$text" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This uses a named template that is called recursively, each time outputting what's before the first ;, and calling itself with everything after the first ;. If there isn't a ;, it just outputs whatever's left as-is.
You can use the XSLT extension library to get the tokenize function. Here is how the final code will look like:
<xsl:template match="/">
<manydata>
<xsl:for-each select="str:tokenize(data, ';')">
<xsl:value-of select="." />
</xsl:for-each>
</manydata>
</xsl:template>
</xsl:stylesheet>
Please note you will have to import the extension library into you XSLT using:
<xsl:import href="path/str.xsl" />
before you use the library functions.
How can I limit a string's word count in XSLT 1.0?
How about something like:
<xsl:template match="data"> <!-- your data element or whatever -->
<xsl:call-template name="firstWords">
<xsl:with-param name="value" select="."/>
<xsl:with-param name="count" select="4"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="firstWords">
<xsl:param name="value"/>
<xsl:param name="count"/>
<xsl:if test="number($count) >= 1">
<xsl:value-of select="concat(substring-before($value,' '),' ')"/>
</xsl:if>
<xsl:if test="number($count) > 1">
<xsl:variable name="remaining" select="substring-after($value,' ')"/>
<xsl:if test="string-length($remaining) > 0">
<xsl:call-template name="firstWords">
<xsl:with-param name="value" select="$remaining"/>
<xsl:with-param name="count" select="number($count)-1"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
This is an XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<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="',
()-'"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="strTakeWords">
<xsl:with-param name="pN" select="10"/>
<xsl:with-param name="pText" select="/*"/>
<xsl:with-param name="pWords"
select="ext:node-set($vwordNodes)/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="word" priority="10">
<xsl:value-of select="concat(position(), ' ', ., '
')"/>
</xsl:template>
<xsl:template name="strTakeWords">
<xsl:param name="pN" select="10"/>
<xsl:param name="pText"/>
<xsl:param name="pWords"/>
<xsl:param name="pResult"/>
<xsl:choose>
<xsl:when test="not($pN > 0)">
<xsl:value-of select="$pResult"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vWord" select="$pWords[1]"/>
<xsl:variable name="vprecDelims" select=
"substring-before($pText,$pWords[1])"/>
<xsl:variable name="vnewText" select=
"concat($vprecDelims, $vWord)"/>
<xsl:call-template name="strTakeWords">
<xsl:with-param name="pN" select="$pN -1"/>
<xsl:with-param name="pText" select=
"substring-after($pText, $vnewText)"/>
<xsl:with-param name="pWords" select=
"$pWords[position() > 1]"/>
<xsl:with-param name="pResult" select=
"concat($pResult, $vnewText)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<t>
(CNN) -- Behind closed doors in recent days,
senior White House aides have been saying that
measuring President Obama's first 100 days
is the journalistic equivalent of a Hallmark holiday.
</t>
the wanted result is returned:
(CNN) -- Behind closed doors in recent days,
senior White House
Do note:
The str-split-to-words template from FXSL is used for tokenization.
This template accepts a parameter pDelimiters which is a string consisting of all characters that should be treated as delimiters. Thus, in contrast with other solutions, it is possible to specify every delimiter (and not just a "space") -- in this case 8 of them.
The named template strTakeWords calls itself recursively to accumulate the text before and including every word from the wordlist produced by the tokenization, until the specified number of words has been processed.