XML to Fixed Length File Using XSLT_Complex Position Logic- PART 2 - xslt-2.0

We have a requirement in which need to convert XML into Fixed Length File. First record is as header and after that we have actual records..From 2 record onwards we need to apply the logic which is mentioned below:
After length 45, consider 10 numbers 0000001000, what ever be the last digit we need to check and replace by following the below table.
Same logic we need to repeat at position 59th to 74th.
77th to 80th we need to get the value 5152 and store into one variable
I had asked the question with 1st logic and got the awesome response, now I am left with 2nd and 3rd logic
For Positive: (0000001000) - (000000100{)
{= 0
A = 1
B = 2
c = 3
D = 4
E = 5
F = 6
G = 7
H = 8
I = 9
Request anyone to help me to achieve the same, I have mentioned the XSLT which I got from previous answers.Thanks
Input:
<ZR>
<INPUT>
<I_FIL>ERES</I_FIL>
</INPUT>
<TABLES>
<T_ER>
<item>
<DATA> HEADER1111111122222222333333344456</DATA>
</item>
<item>
<DATA>778944 D4E2 EA 1234567891 2018-11-060000001000EA 0000000000000100005152D04YA30TRE0000000XXXYYY 800{ Q 2018-11-05</DATA>
</item>
<item>
<DATA>987654 D4E2 EA 1987654321 2018-11-060000002001EA 0000000000000100005152D04YA30UUU0000000XXXLRB 100{ Q 2018-11-05</DATA>
</item>
.
.
.
.
.
.
.
.
<item>
<DATA>12345678912345678934562754378909726533297TRAILER</DATA>
</item>
</T_ER>
</TABLES>
</ZR>
XSLT:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA" />
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() != 1]">
<xsl:variable name="length" select="string-length(substring(DATA,0,46))" />
<xsl:variable name="tenNumbers" select="substring(DATA, ($length + 1),
10)"/>
<xsl:variable name="charToReplace" select="translate(substring($tenNumbers, string-length($tenNumbers), 1),'0123456789','{ABCDEFGHI')" />
<xsl:value-of select="concat(substring(DATA,0,46), substring(DATA, ($length + 1), 9), $charToReplace, substring(DATA,($length+11),(string-length(DATA) + 1)))"/>
<xsl:value-of select="$break" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
OR
<xsl:template match="/">
<xsl:value-of select="
ZR/TABLES/T_ER/item[1]/DATA,
ZR/TABLES/T_ER/item[position() > 1]/DATA/concat(
substring(., 1, 54),
substring('{ABCDEFGHI', number(substring(., 55, 1)) + 1, 1),
substring(., 56)
)" separator="
" />
</xsl:template>
Expected Output:
HEADER1111111122222222333333344456
778944 D4E2 EA 1234567891 2018-11-06000000100{EA
000000000000010{005152D04YA30TRE0000000XXXYYY 800{ Q 2018-11-05
987654 D4E2 EA 1987654321 2018-11-06000000200AEA
000000000000010{005152D04YA30UUU0000000XXXLRB 100{ Q 2018-11-05
.
.
.
.
12345678912345678934562754378909726533297TRAILER

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output omit-xml-declaration="yes" />
<xsl:param name="break" select="'
'" />
<xsl:template match="/">
<xsl:value-of select="ZR/TABLES/T_ER/item[1]/DATA" />
<xsl:value-of select="$break" />
<xsl:for-each select="ZR/TABLES/T_ER/item[position() != 1]">
<xsl:variable name="length" select="string-length(substring(DATA,0,46))" />
<xsl:variable name="tenNumbers" select="substring(DATA, ($length + 1), 10)" />
<xsl:variable name="sixteenNumbers" select="substring(DATA, (string-length(substring(DATA,0,59)) + 1), 16)" />
<xsl:variable name="firstCharToReplace" select="translate(substring($tenNumbers, string-length($tenNumbers), 1),'0123456789','{ABCDEFGHI')" />
<xsl:variable name="secondCharToReplace" select="translate(substring($sixteenNumbers, string-length($sixteenNumbers), 1),'0123456789','{ABCDEFGHI')" />
<xsl:choose>
<xsl:when test="string-length($tenNumbers) != 10">
<xsl:value-of select="concat(substring(DATA,0,46), substring(DATA, ($length + 1), 9))" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(substring(DATA,0,46),
substring(DATA, ($length + 1), 9),
$firstCharToReplace,
substring(DATA,($length+11), 18),
$secondCharToReplace,
substring(DATA,($length+30),(string-length(DATA) + 1)))" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$break" />
<xsl:variable name="temp" select="substring(DATA,77,4)" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
It depends on what condition you want to replace the char.
If it is based on after EA (as per comment) the solution would be different.
For now, I have implemented it solely length based with both points 2 & 3 covered.
http://xsltfiddle.liberty-development.net/6r5Gh38/1
EDIT:
http://xsltfiddle.liberty-development.net/6r5Gh38/2
EDIT 2:
Brief about the points mentioned in comment:
1. <xsl:when test="string-length($tenNumbers) != 10"> This condition is added to remove that extra R appearing for last <item> .
It means if the 10 numbers we are taking in variable is actually having 10 numbers or not.
In the last case,
<item>
<DATA>12345678912345678934562754378909726533297TRAILER</DATA>
</item>
We don't get 10 numbers after 46th character. We get less than that. That's the reason the output has been separated out for two different cases.
2. substring(DATA,0,46) --> which will take first 45 characters to match 778944 D4E2 EA 1234567891 2018-11-06
substring(DATA, ($length + 1), 9) --> which will take 9 characters from index 46th to match 000000100
$firstCharToReplace --> This will replace 10th character as your requirement of 1st question i.e. {
substring(DATA,($length+11), 18) --> It will start remaining string from index (45+11)th to 18 characters that matches EA 000000000000010
$secondCharToReplace --> This will replace (45+11+18)= 74th character as your requirement of 2nd question i.e. {
substring(DATA,($length+30),(string-length(DATA) + 1)) --> It starts from (45+30)= 75th character to last index and gives 005152D04YA30TRE0000000XXXYYY 800{ Q 2018-11-05
Note: In your case, there are extra tabs and white-spaces exist in between the string. That's why the index number has been changed to match your expected output.

Related

How to create dynamic columns (same number of columns in header & row) using XSLT 2.0

I have an XML data source:
<ws:Report_Data xmlns:ws="urn:com.ws.report/Expense_Data">
<ws:Report_Entry>
<ws:uID>
<ws:id>1</ws:id>
</ws:uID>
<ws:Journal_Entry_Group>
<ws:Ledger_Accounts ws:Descriptor="Q1: TEST1">
<ws:ID ws:type="Ledger_Account_ID" ws:parent_type="Account_Set_ID"
ws:parent_id="Standard">Q1</ws:ID>
</ws:Ledger_Accounts>
</ws:Journal_Entry_Group>
<ws:line>
<ws:Number>000123</ws:Number>
</ws:line>
<ws:line>
<ws:Number>000124</ws:Number>
</ws:line>
</ws:Report_Entry>
<ws:Report_Entry>
<ws:uID>
<ws:id>2</ws:id>
</ws:uID>
<ws:Journal_Entry_Group>
<ws:Ledger_Accounts ws:Descriptor="Q1: TEST1">
<ws:ID ws:type="Ledger_Account_ID" ws:parent_type="Account_Set_ID"
ws:parent_id="Standard">Q1</ws:ID>
</ws:Ledger_Accounts>
<ws:Ledger_Accounts ws:Descriptor="Q2: TEST2">
<ws:ID ws:type="Ledger_Account_ID" ws:parent_type="Account_Set_ID"
ws:parent_id="Standard">Q2</ws:ID>
</ws:Ledger_Accounts>
</ws:Journal_Entry_Group>
<ws:line>
<ws:Number>000596</ws:Number> </ws:line>
</ws:Report_Entry>
</ws:Report_Data>
Based on the number of ws:ledger_accounts returned, I want to create the columns of the ledger account id and the description. The column header is generated dynamically as well (adding number _1, _2 and so on in the Ledger_Account_ID and Ledger_Account_Descheader name).
Based on the sample data source above, the second report entry has the highest number of ws:Journal_Entry_Group/ws:Ledger_Accounts returned, and so my header column should have 2 and the data row should have exact number of columns as well. The delimiter is <|> while the data row is enclosed with "".
Example expected Output:
uID<|>Ledger_Account_ID_1<|>Ledger_Account_ID_2<|>Ledger_Account_Desc_1<|>Ledger_Account_Desc_2|Number
"1"<|>"Q1"<|>""<|>"Q1: TEST1"<|>""<|>"000123"
"1"<|>"Q1"<|>""<|>"Q1: TEST1"<|>""<|>"000124"
"2"<|>"Q1"<|>"Q2"<|>"Q1: TEST1"<|>"Q2: TEST2"<|>"000596"
The XSLT code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ws="urn:com.ws.report/Expense_Data">
<xsl:output method="text"/>
<xsl:variable name="linefeed" select="'
'"/>
<xsl:variable name="delimiter"><![CDATA["<|>"]]></xsl:variable>
<xsl:variable name="delimiter1"><![CDATA[<|>]]></xsl:variable>
<xsl:template match="/ws:Report_Data">
<xsl:text>uID</xsl:text>
<xsl:value-of select="$delimiter1"/>
<xsl:value-of select="ws:Report_Entry[count(.//ws:Ledger_Accounts) = max(../ws:Report_Entry/count(.//ws:Ledger_Accounts))]//ws:Ledger_Accounts/concat('Ledger_Account_ID',$delimiter1)" separator=""/>
<xsl:value-of select="ws:Report_Entry[count(.//ws:Ledger_Accounts) = max(../ws:Report_Entry/count(.//ws:Ledger_Accounts))]//ws:Ledger_Accounts/concat('Ledger_Account_Desc',$delimiter1)" separator=""/>
<xsl:text>Number</xsl:text>
<xsl:value-of select="$linefeed"/>
<xsl:apply-templates select="/ws:Report_Data/ws:Report_Entry/ws:line"/>
</xsl:template>
<xsl:template match="/ws:Report_Data/ws:Report_Entry/ws:line ">
<xsl:text>"</xsl:text>
<xsl:value-of select="../ws:uID/ws:id"/>
<xsl:value-of select="$delimiter"/>
<!-- Ledger_Account_IDs -->
<xsl:if test="count(../ws:Journal_Entry_Group/ws:Ledger_Accounts) > 1">
<xsl:for-each select="../ws:Journal_Entry_Group/ws:Ledger_Accounts">
<xsl:value-of select="normalize-space(ws:ID[#ws:type='Ledger_Account_ID'])"/>
<xsl:if test="position()!=last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:value-of select="$delimiter"/>
<!-- Ledger_Account_Desc -->
<xsl:if test="count(../ws:Journal_Entry_Group/ws:Ledger_Accounts) > 1">
<xsl:for-each select="../ws:Journal_Entry_Group/ws:Ledger_Accounts">
<xsl:value-of select="normalize-space(#ws:Descriptor)"/>
<xsl:if test="position()!=last()">
<xsl:value-of select="$delimiter"/>
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:value-of select="$delimiter"/>
<xsl:value-of select="ws:Number"/>
<xsl:text>"</xsl:text>
<xsl:value-of select="$linefeed"/>
</xsl:template>
</xsl:stylesheet>
But the output is incorrect, the data row is not sync up with the number of columns in the header. For the first two lines it only creates 1 ledger account id column, should have at least another ""<|> before it goes to Ledger_Account_Desc column and the header can’t make it incremental.
Incorrect Output:
uID<|>Ledger_Account_ID<|>Ledger_Account_ID<|>Ledger_Account_Desc<|>Ledger_Account_Desc|Number
"1”<|>"Q1"<|>"Q1: TEST1"<|>"000123"
"1”<|>"Q1"<|>"Q1: TEST1"<|>"000124"
"2"<|>"Q1"<|>"Q2"<|>"Q1: TEST1"<|>"Q2: TEST2"<|>"000596"
I would use the separator attribute of value-of and then use a function to wrap each value in quotes; XSLT 3 would be
<?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"
xpath-default-namespace="urn:com.ws.report/Expense_Data"
xmlns:ws="urn:com.ws.report/Expense_Data"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:param name="delimiter" as="xs:string"><![CDATA[<|>]]></xsl:param>
<xsl:param name="linefeed" as="xs:string" select="'
'"/>
<xsl:output method="text"/>
<xsl:function name="mf:quote" as="xs:string">
<xsl:param name="input" as="xs:string"/>
<xsl:sequence select="'"' || $input || '"'"/>
</xsl:function>
<xsl:variable name="max-accounts" select="max(//Report_Entry/count(.//Ledger_Accounts))"/>
<xsl:template match="/">
<xsl:value-of
select="'uID',
(1 to $max-accounts)!('Ledger_Account_ID_' || .),
(1 to $max-accounts)!('Ledger_Account_Desc_1' || .),
'Number'"
separator="{$delimiter}"/>
<xsl:value-of select="$linefeed"/>
<xsl:apply-templates select="//line"/>
</xsl:template>
<xsl:template match="line">
<xsl:value-of
select="(../uID/id,
for $i in 1 to $max-accounts return string(../descendant::Ledger_Accounts[$i]/ID),
for $i in 1 to $max-accounts return string(../descendant::Ledger_Accounts[$i]/#ws:Descriptor),
Number) ! mf:quote(.)"
separator="{$delimiter}"/>
<xsl:value-of select="$linefeed"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/pNvtBGj

Group and Count data

I am working on trying to compute counts for each student assessment based on different steps in the review of test. Student test evaluation has multiple levels Application Evaluation, Written Test, Group Interview, Interview. I have find counts based on gender at each level.
xml:
<xd:StudentData xmlns:xd="urn:com.student.report/student_test">
<xd:StudentRecord>
<xd:StudentRequisition xd:Descriptor="1858 Economics"></xd:StudentRequisition>
<xd:StudentStatus xd:Descriptor="Pass"></xd:StudentStatus>
<xd:StudentApplication xd:Descriptor="1858 Economics"></xd:StudentApplication>
<xd:StudentID>S-1</xd:StudentID>
<xd:Gender xd:Descriptor="Male"></xd:Gender>
<xd:StudentDate>2020-01-01-12:00</xd:StudentDate>
<xd:Total_Score>75</xd:Total_Score>
<xd:ProfileStudentTestGroup>
<xd:StudentTest xd:Descriptor="Student Evaluation"></xd:StudentTest>
<xd:StudentTest_Status xd:Descriptor="Pass"></xd:StudentTest_Status>
<xd:StudentTest_Date>2020-01-31-12:00</xd:StudentTest_Date>
<xd:StudentTest_Result_Score>75</xd:StudentTest_Result_Score>
</xd:ProfileStudentTestGroup>
</xd:StudentRecord>
<xd:StudentRecord>
<xd:StudentRequisition xd:Descriptor="1500 Social Service"></xd:StudentRequisition>
<xd:StudentStatus xd:Descriptor="Fail"></xd:StudentStatus>
<xd:StudentApplication xd:Descriptor="1500 Social Service"></xd:StudentApplication>
<xd:StudentID>S-3</xd:StudentID>
<xd:Gender xd:Descriptor="Female"></xd:Gender>
<xd:StudentDate>2020-01-01-12:00</xd:StudentDate>
<xd:Total_Score>65</xd:Total_Score>
<xd:ProfileStudentTestGroup>
<xd:StudentTest xd:Descriptor="Student Evaluation"></xd:StudentTest>
<xd:StudentTest_Status xd:Descriptor="Pass"></xd:StudentTest_Status>
<xd:StudentTest_Date>2020-01-31-12:00</xd:StudentTest_Date>
<xd:StudentTest_Result_Score>65</xd:StudentTest_Result_Score>
</xd:ProfileStudentTestGroup>
</xd:StudentRecord>
<xd:StudentRecord>
<xd:StudentRequisition xd:Descriptor="1500 Social Service"></xd:StudentRequisition>
<xd:StudentStatus xd:Descriptor="Fail"></xd:StudentStatus>
<xd:StudentApplication xd:Descriptor="1500 Social Service"></xd:StudentApplication>
<xd:StudentID>S-4</xd:StudentID>
<xd:Gender xd:Descriptor="Female"></xd:Gender>
<xd:StudentDate>2020-01-01-12:00</xd:StudentDate>
<xd:Total_Score>67</xd:Total_Score>
<xd:ProfileStudentTestGroup>
<xd:StudentTest xd:Descriptor="Student Evaluation"></xd:StudentTest>
<xd:StudentTest_Status xd:Descriptor="Pass"></xd:StudentTest_Status>
<xd:StudentTest_Date>2020-01-31-12:00</xd:StudentTest_Date>
<xd:StudentTest_Result_Score>67</xd:StudentTest_Result_Score>
</xd:ProfileStudentTestGroup>
</xd:StudentRecord>
<xd:StudentRecord>
<xd:StudentRequisition xd:Descriptor="1858 Economics"></xd:StudentRequisition>
<xd:StudentStatus xd:Descriptor="Pass"></xd:StudentStatus>
<xd:StudentApplication xd:Descriptor="1858 Economics"></xd:StudentApplication>
<xd:StudentID>S-7</xd:StudentID>
<xd:Gender xd:Descriptor="Male"></xd:Gender>
<xd:StudentDate>2020-01-01-12:00</xd:StudentDate>
<xd:Total_Score>85</xd:Total_Score>
<xd:ProfileStudentTestGroup>
<xd:StudentTest xd:Descriptor="Student Evaluation"></xd:StudentTest>
<xd:StudentTest_Status xd:Descriptor="Pass"></xd:StudentTest_Status>
<xd:StudentTest_Date>2020-01-31-12:00</xd:StudentTest_Date>
<xd:StudentTest_Result_Score>85</xd:StudentTest_Result_Score>
</xd:ProfileStudentTestGroup>
</xd:StudentRecord>
</xd:StudentData>
xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="xsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xd="urn:com.student.report/student_test">
<xsl:iterate select="StudentData/StudentRecord">
<xsl:param name="male_pass_count" select="0.00" as="xs:integer"/>
<xsl:param name="male_fail_count" select="0.00" as="xs:integer"/>
<xsl:param name="female_pass_count" select="0.00" as="xs:integer"/>
<xsl:param name="female_fail_count" select="0.00" as="xs:integer"/>
<xsl:param name="StudentTest" select="{/xd:StudentTest/#xd:Descriptor}" as="xs:string"/>
<xsl:choose>
<xsl:when test="{/xd:Gender/#xd:Descriptor} = 'Male'">
<xsl:if test="{/xd:StudentTest_status/#xd:Descriptor} = 'Pass'">
<xsl:with-param name="male_pass_count" select="$male_pass_count + 1"/>
</xsl:if>
<xsl:if test="{/xd:StudentTest_status/#xd:Descriptor} = 'Fail'">
<xsl:with-param name="male_fail_count" select="$male_fail_count + 1"/>
</xsl:if>
</xsl:when>
<xsl:when test="{/xd:Gender/#xd:Descriptor} = 'Female'">
<xsl:if test="{/xd:StudentTest_status/#xd:Descriptor} = 'Pass'">
<xsl:with-param name="female_pass_count" select="$female_pass_count + 1"/>
</xsl:if>
<xsl:if test="{/xd:StudentTest_status/#xd:Descriptor} = 'Fail'">
<xsl:with-param name="female_fail_count" select="$female_fail_count + 1"/>
</xsl:if>
</xsl:when>
</xsl:choose>
<xsl:on-completion>
<student id="{$StudentTest}">
<male_pass_count>{$male_pass_count}</male_pass_count>
<male_fail_count>{$male_fail_count}</male_fail_count>
<female_pass_count>{$female_pass_count}</female_pass_count>
<female_fail_count>{$female_fail_count}</female_fail_count>
</student>
</xsl:on-completion>
</xsl:iterate>
output:
StudentRequisition StudentTest MalePass MaleFail FemalePass FemaleFail
1858 Economics Application evaluation 22 0 10 0
1858 Economics Interview 6 11 0 5
1858 Economics Written Test 2 0 2 0
1500 Social Service Application evaluation 10 12 10 12
1500 Social Service Interview 0 0 0 0
1500 Social Service Written Test 0 0 0 0
If you use an XSLT 3 processor (as your use it xsl:iterate indicates) you can use for-each-group with a composite grouping key on the requisition and the test I think:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="urn:com.student.report/student_test"
xmlns:xd="urn:com.student.report/student_test"
expand-text="yes"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output indent="yes" method="html" html-version="5"/>
<xsl:template match="/">
<html>
<head>
<title>Grouping example</title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="StudentData">
<table>
<thead>
<tr>
<th>Student Requisition</th>
<th>Student Test</th>
<th>Male Pass</th>
<th>Male Fail</th>
<th>Female Pass</th>
<th>Female Fail</th>
</tr>
</thead>
<tbody>
<xsl:for-each-group select="StudentRecord" composite="yes" group-by="StudentRequisition/#xd:Descriptor, ProfileStudentTestGroup/StudentTest/#xd:Descriptor">
<tr>
<td>{current-grouping-key()[1]}</td>
<td>{current-grouping-key()[2]}</td>
<td>{count(current-group()[Gender/#xd:Descriptor = 'Male'][ProfileStudentTestGroup/StudentTest_Status[#xd:Descriptor = 'Pass']])}</td>
<td>{count(current-group()[Gender/#xd:Descriptor = 'Male'][ProfileStudentTestGroup/StudentTest_Status[#xd:Descriptor = 'Fail']])}</td>
<td>{count(current-group()[Gender/#xd:Descriptor = 'Female'][ProfileStudentTestGroup/StudentTest_Status[#xd:Descriptor = 'Pass']])}</td>
<td>{count(current-group()[Gender/#xd:Descriptor = 'Female'][ProfileStudentTestGroup/StudentTest_Status[#xd:Descriptor = 'Fail']])}</td>
</tr>
</xsl:for-each-group>
</tbody>
</table>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/93dFepD
If you use an XSLT 2 processor (which doesn't support a composite grouping key) then either concat the two values or nest two for-each-group instructions.

How to check if given comma separated values is ascending numerical sequence

I would like to check if given comma separated value is ascending numeric sequence.
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<isSequence>
<seq name="a" value="1, 2, 3"/>
<seq name="b" value="3, 1, 5, 6"/>
<seq name="c" value="15, 16, 18, 0"/>
<seq name="d" value="21, 22, 23, 24, 25"/>
<seq name="e" value="A, B, C"/>
</isSequence>
</root>
I have tried below code
<?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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="/">
<xsl:for-each select="root/isSequence/seq">
<xsl:if test="subsequence(#value, 0)">
<xsl:value-of select="#name"/> is an ascending numeric sequence
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Expected Result
a is an ascending numeric sequence
d is an ascending numeric sequence
Assuming you are looking for an integer sequence then I think (in XSLT 3) this can be expressed as
<xsl:template match="seq[let $items := tokenize(#value, ',\s+')!xs:integer(.) return every $p in 1 to count($items) satisfies $items[$p] = $items[1] + ($p - 1)]">
<xsl:value-of select="#name"/><xsl:text> is an ascending numeric sequence
</xsl:text>
</xsl:template>
Full example (https://xsltfiddle.liberty-development.net/6r5Gh2H)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:template match="seq[let $items := tokenize(#value, ',\s+')!xs:integer(.) return every $p in 1 to count($items) satisfies $items[$p] = $items[1] + ($p - 1)]">
<xsl:value-of select="#name"/><xsl:text> is an ascending numeric sequence
</xsl:text>
</xsl:template>
</xsl:stylesheet>
In XSLT 2 with XPath 2 I don't think you have let to express that compactly in a match pattern but you can of course use a variable at XSLT level:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output method="text"/>
<xsl:template match="seq[every $i in tokenize(#value, ',\s+') satisfies $i castable as xs:integer]">
<xsl:variable name="items" select="for $item in tokenize(#value, ',\s+') return xs:integer($item)"/>
<xsl:if test="every $p in 1 to count($items) satisfies $items[$p] = $items[1] + ($p - 1)">
<xsl:value-of select="#name"/><xsl:text> is an ascending numeric sequence
</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.hikmatu.com/bFDb2BU

XSLT Concatenate element values

My requirement is to concatenate values coming at run time in the ref element. I could get separate values for each element and the concatenate it but still i will miss out text in between.
I have edited my questions as per suggestion made by Martin.
Here is my input XML:
<?xml version="1.0" encoding="UTF-8"?><rootelement>
<nref ref="f1"/> <nref ref="f2"/> <nref ref="f3"/>
<nref ref="f4"/> <nref ref="f5"/>
<pnotes>
<pnote id="f1">
<p>Some Text.</p>
</pnote>
<pnote id="f2">
<p><ref><year>1812</year>, <vol>3</vol><series>F. &
F.</series><pages>22</pages></ref>, at p. 27.</p>
</pnote>
<pnote id="f3">
<p><ref>[<year>1914</year>],
<vol>2</vol><series>A.B.C.</series><pages>94</pages></ref>.</p>
</pnote>
<pnote id="f4">
<p><ref><year>1955</year>,
<vol>2</vol><series>A.B.C</series><pages>509</pages></ref>.</p>
</pnote>
<pnote id="f5">
<p><ref>[<year>1805</year>], <series>A.C.</series><pages>21</pages> </ref>.</p>
</pnote>
</pnotes></rootelement>
I have tried following XSLT
<?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"
exclude-result-prefixes="xs"
version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="nref">
<xsl:variable name="ref" select="#ref"/>
<noteref>
<xsl:for-each select="//pnotes/pnote">
<xsl:if test="#id = $ref">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</noteref>
</xsl:template>
<xsl:template match="//pnotes[not(pnotes)]"/>
</xsl:stylesheet>
The result i get is not correct.
I need spaces in between the text.
Result :
<?xml version="1.0" encoding="UTF-8"?>
<rootelement>
<noteref> Some Text. </noteref>
<noteref> 1812, 3F. & F.22, at p. 27. </noteref>
<noteref> [1914], 2A.B.C.94. </noteref>
<noteref> 1955, 2A.B.C509. </noteref>
<noteref> [1805], A.C.21. </noteref>
</rootelement>
Required Result is:
<?xml version="1.0" encoding="UTF-8"?>
<rootelement>
<noteref> Some Text. </noteref>
<noteref> 1812, 3 F. & F. 22, at p. 27. </noteref>
<noteref> [1914], 2 A.B.C. 94. </noteref>
<noteref> 1955, 2 A.B.C 509. </noteref>
<noteref> [1805], A.C.21. </noteref>
</rootelement>
Can somebody help me please? Thanks in advance.
You are iterating over elemnts and anyone of them have square brackets:
When you make your first iteration, you get the element <year> and its value is 1902, not [1902]. Your square brackets are in the element <noteref>.
Change your XML to store the year as [1902] or change your for-each to print the square brackets manually.
Simply output the string value of the element to get all text, if you don't want the line breaks the also normalize the string:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ref">
<noteref>
<xsl:value-of select="normalize-space()"/>
</noteref>
</xsl:template>
</xsl:transform>
Result is
<rootelement>
<pnote>
<P>
<noteref>[1902], A.C. 12</noteref>.</P>
</pnote>
<pnote>
<P>
<noteref>1902 B.A. 98</noteref>.</P>
</pnote>
<pnote>
<P> This is a text </P>
</pnote>
<pnote>
<P>
<noteref>[1912], 1 R.B. 160</noteref>, at pp. 165, 169.</P>
</pnote>
</rootelement>
Online at http://xsltransform.hikmatu.com/b4GWV2.

Saxon XSLT2.0 Extracting Numbers from the String

I am trying to Extract Integer from a String using Xslt2.0
For Example consider the string "designa80000dd5424d" and i need the two integers inside the string i.e "8000" and "5424"
I tried using translate function as below
select="translate($term,translate($term, '0123456789', ''), '')"
But it combines both the integers and gives the output as "80005424"
i need something which separates them
I tried using translate function as below
select="translate($term,translate($term, '0123456789', ''), '')"
But it combines both the numbers and gives the output as "80005424" i
need something which separates them
I. Here is a complete XSLT 1.0 solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:variable name="vSpaces">
<xsl:call-template name="makeSpaces"/>
</xsl:variable>
<xsl:variable name="vtheNumbers"
select="normalize-space(translate(., translate(.,'0123456789',''), $vSpaces))"/>
<xsl:call-template name="tokenize">
<xsl:with-param name="pStr" select="$vtheNumbers"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="pStr"/>
<xsl:param name="pInd" select="1"/>
<xsl:if test="string-length($pStr)">
<xsl:value-of select=
"concat($pInd, ': ',substring-before(concat($pStr, ' '), ' '), '
')"/>
<xsl:call-template name="tokenize">
<xsl:with-param name="pStr" select="substring-after($pStr, ' ')"/>
<xsl:with-param name="pInd" select="$pInd +1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="makeSpaces">
<xsl:param name="pLen" select="string-length(.)"/>
<xsl:choose>
<xsl:when test="$pLen = 1">
<xsl:value-of select="' '"/>
</xsl:when>
<xsl:when test="$pLen > 1">
<xsl:variable name="vHalfLen" select="floor($pLen div 2)"/>
<xsl:call-template name="makeSpaces">
<xsl:with-param name="pLen" select="$vHalfLen"/>
</xsl:call-template>
<xsl:call-template name="makeSpaces">
<xsl:with-param name="pLen" select="$pLen -$vHalfLen"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>designa80000dd5424dan1733g122</t>
the wanted, correct result is produced:
1: 80000
2: 5424
3: 1733
4: 122
Do note:
The last argument of the outer translate() is a string having the same number of characters as that of the input string, and each of these characters is a space.
II. XPath 2.0 shorter and simpler
This XPath 2.0 expression when evaluated produces the wanted sequence of numbers:
tokenize(., '[^\d]+')[.]
Here is an XSLT - based verification:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:variable name="vNumbers"
select="tokenize(., '[^\d]+')[.]"/>
<xsl:for-each select="$vNumbers">
<xsl:value-of select="concat(position(), ': ', ., '
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document:
<t>designa80000dd5424dan1733g122</t>
the same correct result is produced:
1: 80000
2: 5424
3: 1733
4: 122
You could try it using tokenize with any non-digit sequences as the separator, i.e. using XPath 3.0 tokenize('designa80000dd5424d', '[^0-9]+')[normalize-space()]!number() or in XSLT/XPath 2.0 as for $t in tokenize('designa80000dd5424d', '[^0-9]+')[normalize-space()] return number($t) or you could use xsl:analyze-string (XSLT 2.0) or the analyze-string function (XSLT/XPath 3.0, but available with Saxon 9.7 HE).

Resources