Create a list/array in XSLT - xslt

I have the following scenario. I have a list of countries (EG, KSA, UAE, AG)
I need to check an XML input if it is contained in this list or not:
<xsl:variable name="$country" select="Request/country" >
<!-- now I need to declare the list of countries here -->
<xsl:choose>
<!-- need to check if this list contains the country -->
<xsl:when test="$country='??????'">
<xsl:text>IN</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>OUT</xsl:text>
</xsl:otherwise>
</xsl:choose>
Note: I am using XSLT 1.0.

<xsl:variable name="$country" select="Request/country"/>
<xsl:variable name="countries">|EG|KSA|UAE|AG|</xsl:variable>
<xsl:when test="contains($countries,$country)">...</xsl:when>

EDIT
Upon reading your post again, I think the original version of my answer (below) is not it.
You have a list already - your variable declaration selects a node-set of all <country> nodes that are children of <Request> (a node-set is the XSLT equivalent of an array/a list):
<xsl:variable name="$country" select="Request/country" >
But the point is, you don't even need that list as a separate variable; all you need is:
<xsl:when test="Request[country=$country]"><!-- … --></xsl:when>
Where Request[country=$country] reads as "Within <Request>, look at every <country> and select it if it is equal to $country." When the expression returns a non-empty node-set, $country is in the list.
Which is, in fact, what Rubens Farias said from the start. :)
Original answer, kept for the record.
If by "list" you mean a comma-separated string of tokens:
<!-- instead of a variable, this could be a param or dynamically calculated -->
<xsl:variable name="countries" select="'EG, KSA, UAE, AG'" />
<xsl:variable name="country" select="'KSA'" />
<xsl:choose>
<!-- concat the separator to start and end to ensure unambiguous matching -->
<xsl:when test="
contains(
concat(', ', normalize-space($countries), ', ')
concat(', ', $country, ', ')
)
">
<xsl:text>IN</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>OUT</xsl:text>
</xsl:otherwise>
</xsl:choose>

Try something like, if your country list belongs on your XML input:
<xsl:when test="/yourlist[country = $country]'">
Or, if that's static, you could go with:
<xsl:when test="$country = 'EG' or $country = 'KSA' or ...">

I am modifying sam's answer,
you can create a variable which is pipe separated as a string (not a list but act as list), country codes are unique so contains method works fine
but for the similar name variables consider 'Product', 'ProductType' and 'ProductItem' present as countries parameter then it will not work ie if condition fails as contains method check string is present in the provided string,
So my suggestion is to add separators also along with the name ie concat to work contains as search criteria.
<xsl:variable name="$country" select="Request/country"/>
<xsl:variable name="countries">|EG|KSA|UAE|AG|</xsl:variable>
<xsl:when test="contains($countries,concat('|',$country,'|')">
your logic goes here....
</xsl:when>

Related

xslt in xml attribute

I have xml element tyle boleean.
<testelement>0</testelement>
I use xslt to transform value to no/yes depending on 0/1 value and it works great
<xsl:choose>
<xsl:when test="./text()='0'">
<xsl:text>No</xsl:text>
</xsl:when>
<xsl:when test="./text()='1'">
<xsl:text>Yes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">The Yes/No value to be translated did not match expected input</xsl:message>
</xsl:otherwise>
</xsl:choose>
The same I try to do with attribute type boolean. Element has maxOcc unbounded.
<element attribute="0">
...
</element>
<element attribute="1">
...
</element>
In xlts:
<xsl:choose>
<xsl:when test="//#attribute='0'">
<xsl:text>No</xsl:text>
</xsl:when>
<xsl:when test="//#attribute='1'">
<xsl:text>Yes</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="no">The Yes/No value to be translated did not match expected input</xsl:message>
</xsl:otherwise>
</xsl:choose>
But after i use this code all values are Yes or all values are No depending what is value in first node element. EG if 0 is in first element all values are No and it doesnd matter that in second is 1.
How to transform it properly?
Thanks
all values are Yes or all values are No
depending what is value in first node element
Yes, of course they are. That's because your test:
<xsl:when test="//#attribute='0'">
selects all the attributes in the XML document, and in XSLT 1.0 (which I assume you're using) only the first one's value will be used.
You need first to be in the context of element, then test that specific element's attribute by:
<xsl:when test="#attribute='0'">
Here's a better way to do it:
<xsl:template match="node()[.='0'] | #*[.='0']" mode="toYesNo"/>No</xsl:template>
<xsl:template match="node()[.='1'] | #*[.='1']" mode="toYesNo"/>Yes</xsl:template>
<xsl:template match="node()|#*" mode="toYesNo"/>
<xsl:message terminate="no">The Yes/No value to be translated did not match expected input</xsl:message>
</xsl:template>
and then you can xsl:apply-templates (with mode="toYesNo") selecting any element, attribute, or text node to get the appropriate conversion.
In XSLT 3.0 you can replace the patterns withm for example, match=".[.='0']" to match any kind of node.
Try to avoid using ./text() because it goes wrong when there are comments in your XML. You can nearly always replace it with ..
And of course you error with the attributes was the leading //. You need to be very clear about the difference between absolute path expressions (which start with / and select from the root of the tree) and relative path expressions (which select from the node you are current processing).

XSLT 2.0 how to test for position() in tokenize() on output

In XSLT 2.0 I have a parameter than comes in as a delimited string of document names like:
ms609_0080.xml~ms609_0176.xml~ms609_0210.xml~ms609_0418.xml
I tokenize() this string and cycle through it with xsl:for-each to pass each document to a key. The results from the key I then assemble into a comma-delimited string to output to screen.
<xsl:variable name="list_of_corresp_events">
<xsl:variable name ="tokenparam" select="tokenize($paramCorrespdocs,'~')"/>
<xsl:for-each select="$tokenparam">
<xsl:choose>
<xsl:when test=".[position() != last()]">
<xsl:value-of select="document(concat($paramSaxondatapath, .))/(key('correspkey',$correspid))/#xml:id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(document(concat($paramSaxondatapath, .))/(key('correspkey',$correspid))/#xml:id, ', ')"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
Everything works fine except that when I output the variable $list_of_corresp_events it looks like the following, with an unexpected trailing comma:
ms609-0080-2, ms609-0176-1, ms609-0210-1, ms609-0418-1,
Ordinarily the last comma should not appear based on test=".[position() != last()]" ? Possibly positions don't work for tokenized data? I didn't see a way to apply string-join() to this.
Many thanks.
Improving on the solution from #zx485, try
<xsl:for-each select="$tokenparam">
<xsl:if test="position()!=1">, </xsl:if>
<xsl:value-of select="document(concat($paramSaxondatapath, .))/(key('correspkey',$correspid))/#xml:id"/>
</xsl:for-each>
Two things here:
(a) you don't need to repeat the same code in both conditional branches
(b) it's more efficient to output the comma separator before every item except the first, rather than after every item except the last. That's because evaluating last() involves an expensive look-ahead.
Change
<xsl:when test=".[position() != last()]">
to
<xsl:when test="position() != last()">
Then it should all work as desired.
It seems you can simplify this to
<xsl:variable name="list_of_corresp_events">
<xsl:value-of select="for $t in tokenize($paramCorrespdocs,'~') document(concat($paramSaxondatapath, $))/(key('correspkey',$correspid))/#xml:id" separator=", "/>
</xsl:variable>
or with string-join
<xsl:variable name="list_of_corresp_events" select="string-join(for $t in tokenize($paramCorrespdocs,'~') document(concat($paramSaxondatapath, $))/(key('correspkey',$correspid))/#xml:id, ', ')"/>

XSLT starts with not 'E' or 'Z'

I am very new to XSLT. I am trying to build a custom list view in SharePoint 2010.
Dependant on what the user wishes to see, will depend on what items are returned.
If say they want to see view 1: it will get all ref thats starts with E.
If view 2: all ref start with Z.
If view 3: all ref starts with every other letter.
I have the following code:
<xsl:variable name="dvt_StyleName">Table</xsl:variable>
<xsl:variable name="RowLimit" select="30" />
<xsl:variable name="query_string" select="substring-after($current_url,'sheet=')"/>
<xsl:variable name="query_sheet" select="substring-before($query_string,'&acYear=')"/>
<xsl:variable name="query_year" select="substring-after($query_string,'acYear=')"/>
<!--sheet query assignment-->
<xsl:variable name="qOptions">
<xsl:choose>
<xsl:when test="$query_sheet = '2'">E</xsl:when>
<xsl:when test="$query_sheet = '3'">Z</xsl:when>
<xsl:otherwise>A|B|C|D|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!--debugging--->
<xsl:value-of select="$qOptions"/>
<!-- end of debugging--->
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[starts-with(#Title, $qOptions)]"/>
The oitherwise statement currently does not work because i am trying to get multiple conditions into the starts-with statement. I know i am doing this very wrong so any help would be greatly appreciated. If you could try and be a little more hand-holdy with me that would be great cause the syntax of this language really confuses me!
Thanks in advance
As you dealing with single letters, you could define your "otherwise" like this...
<xsl:otherwise>ABCDFGHIJKLMNOPQRSTUVWXY</xsl:otherwise>
Then your Rows variable could be re-written like so
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[contains($qOptions, substring(#Title, 1, 1))]"/>
i.e Does the $qOptions variable contain the first letter of the Title attribute
EDIT: If, you want to match "everything else" rather than "every other letter", then consider re-writing it to this, slightly more long winded way....
<xsl:variable name="qOptions">
<xsl:choose>
<xsl:when test="$query_sheet = '2'">E</xsl:when>
<xsl:when test="$query_sheet = '3'">Z</xsl:when>
<xsl:otherwise>!EZ</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[(not(starts-with($qOptions, '!')) and contains($qOptions, substring(#Title, 1, 1))) or (starts-with($qOptions, '!') and not(contains($qOptions, substring(#Title, 1, 1))))]"/>

xslt comma separate list of values

I am creating a summary view of Microsoft InfoPath form(s) using a custom XSLT Stylesheet.
I have a selection of the radio buttons on the form which are clicked "Yes", "No"
e.g.
What is the primary construction of the building?
Steel Yes
No
Timber Yes
NO
etc....
In the stylesheet I have
<tr>
<td>
The primary contruction of the building is
<xsl:choose>
<xsl:when test="/my:myFields/my:BrickPrimaryConstruction= 'true'">
Brick
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="/my:myFields/my:TimberPrimaryConstruction = 'true'">
Timber Framed
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="/my:myFields/my:ConcretePrimaryConstruction = 'true'">
Concrete Framed
</xsl:when>
</xsl:choose>
etc....
What I want to achieve in the final HTML output is something like:
The primary construction of the building is Brick, Concrete Framed, Prefabricated
I have not got much experience of XSLT, but what is the best way of achieving this?
Avoiding this:
, Concrete Framed, Prefabricated,
or
Brick, , Prefabricated
Normally in C# I would assign to a string and check if it is empty before appending a comma and then trim commas on the ends however I know that I cannot assign variables in xslt.
EDIT
I also mentioned that I wanted to be able to reuse the function in other situations such as
<xsl:value-of select="/my:myFields/my:Road"/>,
<xsl:value-of select="/my:myFields/my:District"/>,
<xsl:value-of select="/my:myFields/my:City"/>,
<xsl:value-of select="/my:myFields/my:County"/>,
<xsl:value-of select="/my:myFields/my:Postcode"/>
where these would be separated by comma or a new line character, but there is the possibility that the "District" for example might be blank resulting in "Road, , City" etc.
Try this:
<tr>
<td>
The primary contruction of the building is
<xsl:for-each select="/my:myFields/my:*[ends-with(local-name(), 'PrimaryConstruction') and (.='true')]">
<xsl:if test="position()!=1" xml:space="preserve">, </xsl:if>
<xsl:choose>
<xsl:when test="self::my:BrickPrimaryConstruction">Brick</xsl:when>
<xsl:when test="self::my:TimberPrimaryConstruction">Timber Framed</xsl:when>
<xsl:when test="self::my:ConcretePrimaryConstruction">Concrete Framed</xsl:when>
etc...
</xsl:choose>
</xsl:for-each>
Basically, the for-each loops over all the relevant fields, so that you can check their position and only emit the comma if you're not on the first item (XSLT has a 1-based index). Since the for-each already filters only the relevant entries, we then only have to check the type, not whether the value is true or not.
Note that you can extend the same principle to emit an "and" for the last item instead of a comma if you like to do so.
Maybe look at Implode.
Then, you could do
<xsl:variable name="theItems">
<xsl:if test="/my:myFields/my:BrickPrimaryConstruction= 'true'">
<foo>Brick</foo>
</xsl:if>
</xsl:variable>
<xsl:call-template name="implode">
<xsl:with-param name="items" select="exsl:node-set($theItems)" />
</xsl:call-template>
You have to convert $theItems, which is a xml fragment, to a node-set. This can only be archived using non-standart methods. XML.com shows some methods to archive it using various XML processors.

XSl:Variable - Condition to check whether value exist

Using XSLT 1.0, how do I check whether the value in the variable exists or not?
I am assigning the value to the variable initially from my XML data and then need to check whether it exits or not:
<xsl:variable name="DOC_TYPE">
<xsl:value-of select="name(./RootTag/*[1])"/>
</xsl:variable>
<xsl:if test="string($DOC_TYPE) = ''">
<xsl:variable name="DOC_TYPE">
<xsl:value-of select="name(./*[1])"/>
</xsl:variable>
</xsl:if>
The above is not working as expected. What I need is if <RootTag> exists in my data then the variable should contain the child node below the <RootTag>. If <RootTag> does not exist then the DOC_TYPE should be the first Tag in my XML data.
Thanks for your response.
You can't re-assign variables in XSLT. Variables a immutable, you can't change their value. Ever.
This means you must decide within the variable declaration what value it is going to have:
<xsl:variable name="DOC_TYPE">
<xsl:choose>
<xsl:when test="RootTag">
<xsl:value-of select="name(RootTag/*[1])" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name(*[1])" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
A few other notes:
this: './RootTag' is redundant. Every XPath you don't start with a slash is relative by default, so saying 'RootTag' is enough
this: <xsl:value-of select="name(*[1])"/> already results in a string (names are strings by definition), so there is no need to do <xsl:if test="string($DOC_TYPE) = ''"> , a simple <xsl:if test="$DOC_TYPE = ''"> suffices
to check if a node exists simply select it via XPath in a test="..." expression - any non-empty node-set evaluates to true
XSLT has strict scoping rules. Variables are valid within their parent elements only. Your second variable (the one within the <xsl:if>) would go out of scope immediately(meaning right at the </xsl:if>).
Try this
<xsl:variable name="DOC_TYPE">
<xsl:choose>
<xsl:when test="/RootTag"><xsl:value-of select="name(/RootTag/*[1])"></xsl:value-of></xsl:when>
<xsl:otherwise><xsl:value-of select="name(/*[1])"/></xsl:otherwise>
</xsl:choose>
</xsl:variable>
It only exists if you have assigned it. There's no reason to test it for existence.
See also here