My requirement is to set some dynamic variables in a for loop to the datapower context something like :
<dp:set-variable name="'var://context/txn-info/appErrorInd[$i+1]'"
value="'yes'" />
The variable $i will keep on changing. The above code isn't working. Can somebody give me a solution?
Use:
<dp:set-variable name="'var:{//context/txn-info/appErrorInd[$i+1]}'"
value="'yes'" />
The above is a mechanical correction of the provided code. Most likely it contains another, more subtle error. To correct this error, too, use:
<dp:set-variable name="'var:{(//context/txn-info/appErrorInd)[$i+1]}'"
value="'yes'" />
Explanation:
Use of AVT.
The [] operator has a higher precedence than the // pseudo-operator. To override this one needs to use explicitly brackets.
Related
What is the most basic syntax in XSLT to declare a global variable for a string of text and then reference that text value into the attributes you are going to output later on the XSLT? Sounds simple - but has a specific syntax.
After two similar questions were missing the tiny detail that makes this work, I thought it might be useful to share this. Answer:
Variable declaration (near beginning of the XSL):
<xsl:variable name="defaultIconStyle" select="'Icon - Style'"/>
Note the single quotes inside the double quotes for the text string.
This method was also proposed but may be more resource/expensive:
<xsl:variable name="defaultIconStyle">Icon - Style</xsl:variable>
Calling this into an attribute value later:
(in this case, to set a character style for a tag targeted for inDesign)
<xsl:template match="note-mytype">
<xsl:copy><ph aid:cstyle="{$defaultIconStyle}"><image href="file:///myIcon.ai"/><xsl:text> </xsl:text></ph><xsl:apply-templates/></xsl:copy>
</xsl:template>
I'm trying to match a set of particular elements, but only ones which are children of another element structure (let's say it's input or select elements only somewhere inside divs with the class "special-sauce" on them). Normally, this would be easy so far as XPATH: we could parenthetically union the targeted children, like so:
div[contains(#class, 'special-sauce')//(input | select)
But this is where XSLT throws a curve ball, when we try to use this as a template match (at least in Saxon):
<xsl:template match="div[contains(#class, 'special-sauce')//(input | select)">
{"error":"The xsl file (/section-settings.xsl) could not be parsed.
Failed to compile stylesheet. 1 error
detected.","code":"TRANSFORM_ERROR","location":null,"causes":["Fatal
Error: Token \"(\" not allowed here in an XSLT pattern"]}
Basically, parentheticals aren't allowed as part of a template match at the main pathing level (they still work fine inside of conditionals/etc, obviously).
So what to do?
Well, technically, using a union can still work, but we would have to repeat the ancestor XPATH each time, since we can't parenthetically enclose the children:
<xsl:template match="div[contains(#class, 'special-sauce')//input
| div[contains(#class, 'special-sauce')//select">
This is doable (not very pretty, but sure, we can handle that! line breaks can work here to help our sanity yay) in our simple example here, but it gets problematic with more complex XPATH, especially if the parenthetical union would have been in the middle of a longer xpath, or for a lot of elements.
e.g.
div[contains(#class, 'major-mess')]/div[contains(#class, 'special-sauce')]//(dataset | optgroup | fieldset)//(button | option | label)
becomes
a crazy mess.
Ok, that quickly becomes less of an option in more complex examples. And while structuring our XSLT differently might help (intermediary matches, using modality, etc), the question remains:
How can we gracefully template match using unions of individual child elements within a larger XPATH pattern when parentheticals won't work?
An example sheet for the first example:
<div class="special-sauce">
<input class="form-control" type="text" value="" placeholder="INHERITED:" />
<select class="form-control">
<option value="INHERITED: ">INHERIT: </option>
<option value=""></option>
</select>
<div class="radio">
<label>
<input type="radio" name="param3vals" value="INHERITED: " />
INHERIT:
</label>
</div>
</div>
<div class="not-special"><input type="text" id="contact-info-include-path" size="90">
<label>contact</label>
</input></div>
<div class="sad-panda"><input type="text" id="sidenav-include-path" size="90">
<label>sidenav</label>
</input></div>
Note: this does assume that an identity transform is running as the primary method of handling the input document.
While there are other questions which could validly receive similar answers as, for example, the one I give below, I felt the context of those questions was usually more general (such that a top level union would be fine as their answer without complication), more specific in ways that didn't match, or simply too different. Hence the Q&A format.
XSLT 1.0 vs 2.0 vs 3.0
Michael Kay correctly notes in his answer below that while the original pattern attempted here doesn't work in XSLT 1.0 or 2.0, it should work in a (fully) XSLT 3.0 compatible processor. I'm currently on a system using Saxon 9.3, which is technically XSLT 2.0. I just want to call extra attention to that answer for those who are on a 3.0 system.
I looked all over and most answers to similar problems involved copying the repeated portion of the XPATH to each element and unioning it all together. But there is a better way! It's easy to forget that matching a particular element is relatively equivalent to matching that element's name within XPATH.
Use name() or local-name() instead of matching on the element directly within the template match pattern*.
Be aware of your namespace issues/needs when picking which to use. This still allows for advanced conditionals on attributes/etc of those elements.
The first match, for example, becomes:
<xsl:template match="div[contains(#class, 'special-sauce')//
element()[local-name() = ('input', 'select')]">
There's not a huge gain here in terms of space or time to write this out, but we do reduce redundancy and the associated data consistency errors that can result (all too often, especially if later making changes).
Where this really shines is the last example in the question (the mess):
<xsl:template match="div[contains(#class, 'major-mess')]/
div[contains(#class, 'special-sauce')]//
element()[local-name() = ('dataset', 'optgroup', 'fieldset')]//
element()[local-name() = ('button', 'option', 'label')]">
And since I can't remember if that's fully XSLT/XPATH 1.0 compatible by creating the element tree-fragment parenthetically for comparison, if you do need backwards compatibility the "contains() with bracketing separator tokens" (reducing chances of a false positive from another element being a substring of the full name targeted) pattern always works too:
<xsl:template match="div[contains(#class, 'major-mess')]/
div[contains(#class, 'special-sauce')]//
element()[contains('|dataset|optgroup|fieldset|'), concat('|', local-name(), '|'))]//
element()[contains('|button|option|label|', concat('|', local-name(), '|'))]">
* = "match pattern" vs "XPath"
If you're struggling with understanding why the naive approach (the first thing I attempted in the question) fails in XSLT, it helps to understand that template rules like "match" must follow XSLT patterns, which are only essentially a sub set of valid XPath expressions (which easily makes things more confusing to distinguish and remember, especially when many sources just pretend it's all XPath entirely). Note that parentheses only show up as a valid option to use as expression tokens which are only found within expressions within predicates, not within any other portion of the location path or location steps.
Final Considerations
Performance: I have no idea whether there are notable performance differences with this approach versus unioning each seperate element as a full path to each one, or whether there is even a real performance difference between addressing an element natively versus as a predicate on the anonymous element() selector. My suspicion is that while most XSLT processors can probably achieve a faster DOM tree search when a single match is written using the native path structure versus a predicate with name() function on the anonymous selector, the union cases may perform faster depending on how well the processor tries to pre-compile and optimize for logic patterns. I will leave that task for someone else to try benchmarking, because ultimately the real hurdle becomes developer sanity and maintenance issues (likelihood of incurring human errors). In complex matches, I feel that any small performance penalty will likely be easily met by the simple legibility and reduced/eliminated data redundancy of this approach.
I think that your pattern is legal in XSLT 3.0 as written. But I guess you want an XSLT 2.0 solution...
One great way that people often overlook is to use schema-aware patterns. If you want to match a choice of elements, it's quite likely that they are closely related in the schema, for example by having a common type T or by being members of a substitution group S. You can then write
div[contains(#class, 'special-sauce')//schema-element(S)
or
div[contains(#class, 'special-sauce')//element(*, T)
But I guess you want a solution that isn't schema-aware...
In that case, I don't think I can offer anything better than what you've got.
Sometimes multiple modes are the answer: for example something like
<xsl:template match="div[contains(#class, 'special-sauce')]">
<xsl:apply-templates mode="special"/>
</xsl:template>
<xsl:template match="select|input" mode="special">
Generally I think modes are greatly under-used.
Why not split this template into two or three (one for each level) with modes? Something like
<xsl:template match="div[contains(#class, 'special-sauce')">
<xsl:apply-templates select=".//select|input" mode="special-sauce"/>
</xsl:template>
<xsl:template match="select|input" mode="special-sauce">
<!-- ... -->
</xsl:template>
In my opinion this way it reads clearer.
I'm new to XSLT, and I'm carrying out a few tests using w3schools "Try it yourself" pages. I'm using the following demo:
http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=tryxsl_choose
This contains the following line:
<xsl:for-each select="catalog/cd">
I'm testing filtering the HTML rendered by position() but I'm having issues when using the < operand.
I've tried the following:
<xsl:for-each select="catalog/cd[position()=1]">
And this returns the first item from the XML data (as expected).
I then tried:
<xsl:for-each select="catalog/cd[position()<5]">
I was expecting this to return the first 4 items, but instead I get no results.
My guess is that perhaps position()=1 is doing a string comparison, which is why it returns the first item, but it cannot understand position()<5 as a string cannot be compared in this way?
Why is this happening, and what would be the correct syntax to get the results I wish to achieve?
Update: After reading #joocer's response, and testing this myself, using the > operand does work, for the opposite result:
<xsl:for-each select="catalog/cd[(position()>5)]">
It looks very much like a bug in the version of libxslt that w3schools is using.
Even inside quotes, you must type < as < so it won't be confused for the start of an element tag. I think this was done to make it easier for tolerant parsers to recover from errors and streaming parsers skip content faster. They can always look for < outside CDATA and know that is an element start or end tag.
I don't know why, but inverting the condition works, so instead of looking for less than 5, look for not more than 4
<xsl:for-each select="catalog/cd[not(position()>4)]">
I am getting an an error "Invalid CFML construct found"
iif(stImages[id][1]["seolink"] is not "", stImages[id][1]["seolink"], stImages[id][1]["url"]) />
what i am doing here wrong?
Try:
iif(stImages[id][1]["seolink"] is not "", DE(stImages[id][1]["seolink"]), DE(stImages[id][1]["url"])) />
For those readers playing from home (as it were), IIF can be an unruly beast because of the double evaluation it does. So
#IIF(myVal EQ "", "thisThing", "thatThing")#
LOOKS like it will simply return the first or second strings, but in fact it will return the content of the VARIABLES "thisThing" or "thatThing" (or throw an error that they don't exist). So say it with me: "IIF() and DE() GO TOGETHER LIKE MUTUALLY BENEFICIAL PARASITIC LIFEFORMS". "DE" as in "Delayed Evaluation". So if you want the above statement to return the first or second string, you need:
#IIF(myVal EQ "", DE("thisThing"), DE("thatThing"))#
Now, you can certainly use this feature to evaluate a field twice and NOT use "DE()", but that means you're using some kind of dynamic variable name, and it could be argued that doing that isn't best practice. Not that I haven't done that exact thing, but it should be copiously commented, because if you don't the person who maintains the code after you will probably want to kill you.
By the way, there's no mystery to "DE()". These two statements are equivalent:
#DE("thisThing")#
#"""thisThing"""#
See what's going on? "DE()" simply puts double quotes around something. So one set of quotes gets "stripped off" the first time it gets evaluated, and then the quoted string gets returned from the function. Clear as mud?
See why people don't like IIF? It's very handy in certain situations, but is a contextual mess and contributes to code that makes people go "HWUUUH??" So that's why people say to avoid it if possible.
I would avoid iif where you can,
iif(stImages[id][1]["seolink"] is not "", DE(stImages[id][1]["seolink"]), DE(stImages[id][1]["url"])) />
<cfif stImages[id][1]["seolink"] is not "">#stImages[id][1]["seolink"]#<cfelse>#stImages[id][1]["url"]#</cfif>
or if you have ColdFusion 9
<cfset stImages[id][1]["seolink"] is not "" ? #stImages[id][1]["seolink"]# : #stImages[id][1]["url"]# />
<xsl:if test="count($currentPage/..//$itemType) > 0">
I try to use the if statement with 2 param values and I get the error:
"unexpected token '$' in the expression..."
is it possible to do what I'm trying to ?
In XSLT, like in most programming languages (excluding macro languages), variables represent values, not fragments of expression text. I suspect $itemType holds an element name, and you are imagining that you can use it anywhere you could use an element name. If that's what you are trying to do, use ..//*[name()=$itemType].
This is invalid (and #Michael Kay explained it well):
//$varName
If I guess correctly what you are up to, then you may try this:
//*[name() = $varName]