Is it possible to use a variables inside xsl:key instruction? - asp.net

Relating to this question, is it possible to use variables inside xsl:key? I want to do smth like this:
<xsl:key name="ChargesKey" match="$ChargesForDisplay/charge" use="Name"/>
I'm using XSLT 1.0 with ASP.Net

I believe I can safely assume that you are referring to dynamically-generated node-set variables (as opposed to those selected from the source DOM, which are trivial), and yes it is possible to perform a key-match on the contents of a dynamically generated node-set variable (as I demonstrate for this question).
Assuming you have a variable like this:
<xsl:variable name="ChargesForDisplay">
<charge>
<Name>Name1</Name>
</charge>
<charge>
<Name>Name2</Name>
</charge>
<charge>
<Name>Name1</Name>
</charge>
<charge>
<Name>Name3</Name>
</charge>
</xsl:variable>
You would define the key like this:
<xsl:key name="ChargesKey" match="charge" use="Name"/>
And then you can apply it like this:
<xsl:template match="/">
<xsl:apply-templates select="msxsl:node-set($ChargesForDisplay)" />
</xsl:template>
<xsl:template
match="charge[generate-id(.)=generate-id(key('ChargesKey',Name)[1])]">
<xsl:variable name="matchingItems" select="key('ChargesKey', Name)" />
...
</xsl:template>
Of course, if the variable contains a selection of nodes from the source XML DOM, then it's just the same approach, except you don't need to use msxsl:node-set().
I suspect that having a key on a node-name that's also present in the source XML document or multiple dynamically-generated node-set variables may cause grouping to produce unexpected results(because the key() function would locate nodes from both the variable and the source document). For this reason, I'd suggest defining keys on nodes that would only be present in one particular variable and nowhere else.

The match attribute of xsl:key must be a valid pattern and $x/y is not a valid pattern. So, the answer is no. Now tell us what you are trying to achieve and we can help you achieve it. (JLRishe makes some wild guesses, which s/he calls "wild assumptions", and which may well be right; but I don't know where the guesses come from).

Related

Extracting a value from XML and storing it in a variable

here is my sample XML :
<Entries>
<Entry>
<id>1</id>
.
.
</Entry>
<Entry>
<id>2</id>
<assets>
<TextAssetInfo>
<Name>Alpha</Name>
<TagName>alphaName</TagName>
<value>Harambe</value>
</TextAssetInfo>
</assets>
</Entry>
<Entries>
So my goal is simply to extract the value of Alpha and store it in a variable in my XSLT for further computation.
I am able extract and print the value. But not assign in to a variable.
Following part of XSL code works:
<T_AlphaName><xsl:value-of select="./assets/*[TagName = 'alphaName']/value"/></T_AlphaName>
However, I can't get the below to work :
<xsl:variable name="myVar" select="./assets/*[TagName = 'alphaName']/value"/>
I have tried many other combinations as well but no luck. What am I doing wrong? In my XSL I have part to test it and get a weird result
<TestVar><xsl:value-of select="$myVar"/></TestVar>
Result : <TestVar/>
Additional info :
Using XML/XSL version 1.0.
AlphaName is not the only TextAssetInfo inside the assets. There are many more.

What is cstyle in XSLT?

My XSLT is shown below.
aic is a namespace.
What is cstyle?
is it a built-in XSLT element/function?
Or an element within the expected input xml?
<xsl:stylesheet exclude-result-prefixes="aic"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aic="http://ns.adobe.com/AdobeInCopy/2.0/" >
<xsl:template match="/">
</xsl:template>
<xsl:template match="aic:cstyle[contains(#name,'bold')]">
</xsl:template>
</xsl:stylesheet>
It is an element within the expected input XML. The XPaths in an XSLT's match attributes are generally applied to contents from the input XML.
Exactly as in my answer to your previous question, aic:cstyle is a selector that matches elements whose local name is cstyle and whose namespace URI is http://ns.adobe.com/AdobeInCopy/2.0/ (the URI bound to the aic prefix in the xsl:stylesheet element). Thus
<xsl:template match="aic:cstyle[contains(#name,'bold')]">
is a template that will apply to any {http://ns.adobe.com/AdobeInCopy/2.0/}cstyle element that has a name attribute that contains the substring bold. (So, to answer your question directly: the expression in question will match elements in the input streams for which the stylesheet was written.)
As with any new programming language, I would strongly recommend that you find a decent tutorial and work through that to get comfortable with the syntax and idioms of the language through simple examples before you start trying to decode a large and complex XSLT that you've inherited from elsewhere.

How do you convert an XSLT 2.0 date duration to a string?

I am using some code to subtract one date from another using XSLT 2.0:
<xsl:template match="moveInDate">
<xsl:value-of select="current-date() - xs:date(.)"/>
</xsl:template>
This works, however it leaves me with an answer of P2243D, which I assume corresponds to a "Period of 2243 Days" (which is correct in terms of the math).
Since I only need the number of days, not the P and the D, I know I could use substring or something similar, but as a newbie to XSLT, I'm curious if there is a better, more elegant way to do this than simple string manipulation.
You could simply use fn:days-from-duration() to get the duration as a xs:integer:
days-from-duration($arg as xs:duration?) as xs:integer?
Returns an xs:integer representing the days component in the canonical lexical representation of the value of $arg. The result may be negative.
See the XQuery 1.0 and XPath 2.0 Functions and Operators specification for more information.
In your case:
<xsl:template match="moveInDate">
<xsl:value-of select="days-from-duration(current-date() - xs:date(.))"/>
</xsl:template>
Hope this helps!
EDIT: You could also do it the way you say, with substring processing. But as you point out, it's not prefered. If you for some reason would like to do something similar you need to think of the data types. The result of current-date() - xs:date(.) is returned as xs:duration which cannot be processed by the substring functions without being casted:
<xsl:template match="moveInDate">
<xsl:variable name="dur" select="(current-date() - xs:date(.)) cast as xs:string"/>
<xsl:value-of select="substring-before(substring-after($dur, 'P'), 'D')"/>
</xsl:template>

XSLT. load xml document that contains escape characters

I use XSLT to transform an XML document which I then load on to a ASP.NET website. However, if the XML contains '<' characters, the XML becomes malformed.
<title><b> < left arrows <b></title>
If I use disable-output-escaping="yes", the XML cannot be loaded and I get the error "Name cannot begin with the '' character".
If I do not disable output escaping the escaped characters are disregarded and the text appears as it is:
<title><b> < left arrows <b></title>
I want the bold tags to work, but I also want to escape the '<' character. Ideally
<b>< left arrows</b>
is what I want to achieve. Is there any solution for this?
The XML should contain the escaped sequence for the less than sign (<), not the literal < character. The XML is malformed and any XML parser must reject it.
In XSLT you could generate that sequence like this:
<xsl:text>&lt;<xsl:text>
From what I understand, the input contains HTML and literal < characters. In that case, disable-output-escaping="yes" will preserve the HTML tags but produce invalid XML and setting it to no means the HTML tags will be escaped.
What you need to do is to leave set disable-output-escaping="no" (which is the default, you don't actually have to add that) and add a XSLT rule that will copy the HTML tags. For instance:
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
I came up with a solution and was triggered by the last answer by Josh. Thanks Josh. I tried to used the match template, however I had a problem as the html tags are placed within cdata, so I had difficulties doing a match. There might be a way to do it, but I gave up on that.
What I did was to do a test="contain($text, $replace)" where the $replace is the '<' character and on top of that, I also added a condition to test if the substring after the '<' is a relevant html tag such that it is actually a <b> or </b>. So if it's just a '<' character not belonging to any html tags, I will convert '<' to ampersand, &lt;. Basically that solved my problem. Hope this is useful to anyone who encounter the same problem as me.

Cumulative Maximum, calculate for every parent record

Have anyone used the Cumulative Maximum functoid and noticed performance problems?
Abstract
If one wants to map the maximum value of a field you can use the functoid Cumulative Maximum.
Problem
After we had used it for a while we noticed degraded performance on larger files.
Examining the xslt one notices that the max calculation is made for each looping record...
One could move the calculation to the grand parent, and point out the new xslt in the Custom XSL Path, but I really like to keep the possibility to map in the mapping tool.
Any suggestions?
Kind Regards
Martin Bring
http://martinbring.blogspot.com
By removing the Cumulative Maximum and adding 3 scripting functoids, doing the calculation in another way, the problem is solved. Mapping time decreased by a factor of 40.
11 Mb, 10 000 rows, was previously mapped in 200 minutes is now mapped in 5 minutes.
Solution
One scripting functoid, "Inline XSLT Call Template" with no input or output, containing the max() portion of the library from EXSLT Math library found here. Instead of using the whole library I unzipped the file and "extracted" the max() template.
<xsl:template name="GetMax">
<xsl:param name="nodes" />
<xsl:choose>
<xsl:when test="not($nodes)">NaN</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$nodes">
<xsl:sort data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="number(.)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
One scripting functoid, "Inline XSLT Call Template" with no input or output, containing a variable which select attribute points at the template with the node set to calculate
<xsl:variable name="var:MaxValueDate">
<xsl:call-template name ="GetMax">
<xsl:with-param name ="nodes" select="Root//Parent/ValueToCalculate" />
</xsl:call-template>
</xsl:variable>
One scripting functoid, "Inline XSLT" with one output, using the variable to populate an output element with its value.
<OutputElement>
<xsl:value-of select="$var:MaxValueDate" />
</OutputElement>
Voila!

Resources