Count values inside an xslt array - xslt

I'm inserting values into xslt array and I'm trying to Count the each values inside it.
Here is my XSLT style sheet.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array" exclude-result-prefixes="#all"
version="3.0">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="input"/>
<xsl:variable name="newline" select="'
'"/>
<xsl:template match="/" name="xsl:initial-template">
<root>
<xsl:variable name="a" select="A"/>
<xsl:variable name="b" select="B"/>
<xsl:variable name="c" select="C"/>
<xsl:variable name="d" select="D"/>
<xsl:variable name="array">
<xsl:value-of select="$a"/>
<xsl:value-of select="$a"/>
<xsl:value-of select="$b"/>
<xsl:value-of select="$c"/>
<xsl:value-of select="$d"/>
</xsl:variable>
<test>
<xsl:value-of select="count($array[. = 'A'])"/>
</test>
</root>
</xsl:template>
</xsl:stylesheet>
But I'm getting below error.
Engine name: Saxon-PE 9.9.1.7
Severity: fatal
Problem ID: XPDY0002
Description: The context item for axis step ./A is absent
Start location: 14:48
URL: http://www.w3.org/TR/xpath20/#ERRXPDY0002
Suggestions would be also helpful..

What is e.g. <xsl:variable name="a" select="A"/> supposed to do? Bind the value selected from any A elements to the variable a? Or do you want to bind the string 'A' there? Then you need select="'A'".
And if you have some variables and want a sequence of various values use e.g. $A, $A, $B, $C, $D e.g <xsl:variable name="sequence" select="$A, $A, $B, $C, $D"/>.

Related

how to i get the tokenize spliting function content order instead of xml order

When i use XSLT 2.0 key and tokenize function, it's return items order getting changed based on key value. in our output we required retain the same order of tokenize sequence.
Input File
<?xml version="1.0" encoding="UTF-8"?> <a> <bd id="a">a</bd> <bd id="b">b</bd> <bd id="e">e</bd> <bd id="d">d</bd> </a>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="idcollect" match="*[#id]" use="#id" />
<xsl:variable name="name" select="'d,b,e,a'"/>
<xsl:template match="/">
<xsl:for-each select="key('idcollect',tokenize($name,','))" >
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
current Output
<?xml version="1.0" encoding="UTF-8"?><bd id="a">a</bd><bd id="b">b</bd><bd id="e">e</bd><bd id="d">d</bd>
Expected output
<?xml version="1.0" encoding="UTF-8"?><bd id="d">d</bd><bd id="b">b</bd><bd id="e">e</bd><bd id="a">a</bd>
I think you want e.g.
<xsl:variable name="main-doc" select="/"/>
<xsl:for-each select="for $token in tokenize($name,',') return key('idcollect', $token, $main-doc)">
<xsl:copy-of select="."/>
</xsl:for-each>
or in XSLT 3
<xsl:variable name="main-doc" select="/"/>
<xsl:for-each select="tokenize($name,',') ! key('idcollect', ., $main-doc)">
<xsl:copy-of select="."/>
</xsl:for-each>
Of course in both cases the for-each/copy-of nesting is not needed and e.g.
<xsl:copy-of select="let $main-doc := / return tokenize($name,',') ! key('idcollect', ., $main-doc)"/>
or
<xsl:variable name="main-doc" select="/"/>
<xsl:copy-of select="for $token in tokenize($name,',') return key('idcollect', $token, $main-doc)"/>
would suffice.
Try:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="idcollect" match="*[#id]" use="#id" />
<xsl:variable name="name" select="'d,b,e,a'"/>
<xsl:template match="/">
<xsl:variable name="ids" select="tokenize($name,',')"/>
<xsl:for-each select="key('idcollect', $ids)" >
<xsl:sort select="index-of($ids, .)"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="idcollect" match="*[#id]" use="#id" />
<xsl:variable name="name" select="'d,b,e,a'"/>
<xsl:template match="/">
<xsl:variable name="xml" select="/"/>
<xsl:for-each select="tokenize($name, ',')" >
<xsl:copy-of select="key('idcollect', ., $xml)"/>
</xsl:for-each>
</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

Processing a list in XSLT

I have list of elements in variable
|ELEMENT1|ELEMENT2|ELEMENT3|ELEMENT4|ELEMENT5|
If any of request elements matches this , I should display local name and its value.
Request XML :
<Root>
<element1>Test1</element1>
<child>
<element2>222</element2>
</child>
<secondChild>
<element2>234</element2>
</secondChild>
<thirdchild>
<element3>5w2</element3>
</thirdchild>
</Root>
XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"></xsl:variable>
<xsl:variable name="list"><xsl:value-of select="'|ELEMENT1|ELEMENT2|ELEMENT3|ELEMENT4|ELEMENT5|'"/></xsl:variable>
<xsl:template match="/">
<xsl:for-each select="//*[contains(translate($list,$lower,$upper),concat('|',translate(local-name(),$lower,$upper),'|'))]">
<xsl:value-of select="concat(local-name(),':',.,'|')"></xsl:value-of>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Expected Output :
element1:Test1|element2:222|element3:5w2|
But I am getting
element1:Test1|element2:222|element2:234|element3:5w2|
This is because I have element2 in two places in XML. I should not read second element2 while processing.
Can you please help on this
Filter out any elements that have a preceding element of the same name.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:variable name="list" select="'|ELEMENT1|ELEMENT2|ELEMENT3|ELEMENT4|ELEMENT5|'" />
<xsl:template match="/">
<xsl:for-each select="//*[
contains(
concat('|', translate($list, $lower, $upper), '|'),
concat('|', translate(local-name(), $lower, $upper), '|')
)
]">
<xsl:if test="not(preceding::*[local-name() = local-name(current())])">
<xsl:value-of select="concat(local-name(), ':', ., '|')" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You can define a key
<xsl:key name="name" match="*" use="local-name()"/>
and then in your condition you check
<xsl:for-each select="//*[generate-id() = generate-id(key('name', local-name())[1])][contains(translate($list,$lower,$upper),concat('|',translate(local-name(),$lower,$upper),'|'))]">

How to assign formated value to other variable in xslt

<xsl:value-of select="substring-before($temp1,';')" disable-output-escaping="yes"/>
where temp1="fassdf sdf; asdf &dfsdfsdf;fsdfsf;"
The above code I am using to split value using ";". The problem is temp1 having &, so it splits this value by the escaped sequence character ;. So i am getting wrong output. But if I use the disable-output-escaping="yes" then the "&" is converted to &.
How to get the formatted value from the string? So if i split the string i will not get any issue. Because I will get string with & instead of &
Lets assume a sample XML for your/our convenience ..
<?xml version="1.0" encoding="utf-8"?>
<root>
<child>sharepoint; R&D;Department;</child>
</root>
XSLT code to output the desired one:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="text" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="child">
<xsl:call-template name="SplitString">
<xsl:with-param name="StringVal" select="concat(.,';')"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="SplitString">
<xsl:param name="StringVal"/>
<xsl:variable name="first" select="substring-before($StringVal, ';')" />
<xsl:variable name="remaining" select="substring-after($StringVal, ';')" />
<xsl:value-of select="normalize-space($first)" disable-output-escaping="yes" />
<xsl:if test="$remaining">
<xsl:value-of select="'
'"/>
<xsl:call-template name="SplitString">
<xsl:with-param name="StringVal" select="$remaining" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
This is the Output you get:
sharepoint
R&D
Department

How can I achieve SPLIT function in XSLT 1.0

I have a following scenario. I have a TEAM-MEMBERS node which has name in the format last name, first name
<TEAM-MEMBER><LONG-NAME>Last Name, First Name</LONG-NAME></TEAM-MEMBER>
I want to transform this to
<CONTACT><FIRSTNAME>First Name</FIRSTNAME><LASTNAME>Last Name</LASTNAME></CONTACT>
Basically i want to split the <LONG-NAME> node's value by ,
How can I achieve this using XSLT 1.0
This XSLT will be consumed by BizTalk Server hence i am looking for some XSLT 1.0 solutions only
Thanks
Karthik
Here is a complete XSLT 1.0 solution that uses a general "split" template that splits a string into multiple substrings, provided a delimiter to designate the boundary between substrings:
<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:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="TEAM-MEMBER">
<xsl:variable name="vrtfSplitWords">
<xsl:call-template name="split">
<xsl:with-param name="pText" select="."/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vSplitWords"
select="ext:node-set($vrtfSplitWords)/*"/>
<CONTACT>
<FIRSTNAME><xsl:value-of select="$vSplitWords[2]"/></FIRSTNAME>
<LASTNAME><xsl:value-of select="$vSplitWords[1]"/></LASTNAME>
</CONTACT>
</xsl:template>
<xsl:template name="split">
<xsl:param name="pText" select="."/>
<xsl:param name="pDelim" select="', '"/>
<xsl:param name="pElemName" select="'word'"/>
<xsl:if test="string-length($pText)">
<xsl:element name="{$pElemName}">
<xsl:value-of select=
"substring-before(concat($pText,$pDelim),
$pDelim
)
"/>
</xsl:element>
<xsl:call-template name="split">
<xsl:with-param name="pText" select=
"substring-after($pText,$pDelim)"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
<xsl:with-param name="pElemName" select="$pElemName"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<TEAM-MEMBER><LONG-NAME>Last Name, First Name</LONG-NAME></TEAM-MEMBER>
the wanted, correct result is produced:
<CONTACT>
<FIRSTNAME>First Name</FIRSTNAME>
<LASTNAME>Last Name</LASTNAME>
</CONTACT>
II. Solution using FXSL
This transformation uses the str-split-to-words template from FXSL:
<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:strip-space elements="*"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="pmaxLines" select="10"/>
<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:apply-templates select=
"ext:node-set($vwordNodes)/*[normalize-space()]"/>
</xsl:template>
<xsl:template match="word[normalize-space()][1]">
<FIRSTNAME>
<xsl:value-of select="normalize-space()"/>
</FIRSTNAME>
</xsl:template>
<xsl:template match="word[normalize-space()][2]">
<MIDNAME>
<xsl:value-of select="normalize-space()"/>
</MIDNAME>
</xsl:template>
<xsl:template match="word[normalize-space()][last()]">
<LASTNAME>
<xsl:value-of select="normalize-space(.)"/>
</LASTNAME>
</xsl:template>
</xsl:stylesheet>
when applied to this XML document (made quite more complex):
<TEAM-MEMBER><LONG-NAME>First Name, (Jr.), Last Name</LONG-NAME></TEAM-MEMBER>
the wanted, correct result is produced:
<FIRSTNAME>First Name</FIRSTNAME>
<MIDNAME>Jr.</MIDNAME>
<LASTNAME>Last Name</LASTNAME>
Do Note:
The str-split-to-words template accepts multiple delimiters. Thus in this transformation the delimiters used are: ',', '(' and ')'
You need a recursive named template. Fortunately it's already been written: look for str:tokenize at http://www.exslt.org.
used substring-after() and substring-before() to get around the split