How to compute an href value with Diazo rules - diazo

I am trying to change the hrefs of certain <a> elements in the theme by computing the URLs using values I select from the content. However, I can't figure out how to change the href attribute at all. It seems like the attributes attribute is not understood in the <replace> rule.
Ultimately, I would like to be able to do something like:
<replace css:theme="a.languageswitcher" attributes="href">
<!-- use some XSL logic here to stitch together the new href -->
</replace>
So the following rules work, but are useless to me:
<copy attributes="href" css:theme="a.languageswitcher" css:content="#portal-logo" />
<merge attributes="href" css:theme="a.languageswitcher" css:content="#portal-logo" />
But this one already does not work, the attributes="href" makes it so this rule is ignored.
<replace attributes="href" css:theme="a.languageswitcher" css:content="#portal-logo" />
On the other hand, if I try to rebuild the <a> element from scratch, then I run into the error described by #ross-patterson in his question: Diazo - Conditionally add a class to a theme element:
<replace theme="//a[#class='languageswitcher']">
<a class="languageswitcher">
<xsl:attribute name='href'>
foo
</xsl:attribute>
</a>
</replace>
produces the error:
XSLTApplyError: xsl:attribute: Cannot add attributes to an element if children have been already added to the element.
How can this be done?

Something like this ought to work:
<replace css:theme="a.languageswitcher">
<xsl:for-each css:select="a.languageswitcher">
<xsl:variable name="link">
http://example.com
</xsl:variable>
<xsl:copy-of select="." />
</xsl:for-each>
</replace>

in a theme I used this rule
<replace css:content-children="#hptoolbar-cerca"><xsl:attribute name="href">
<xsl:value-of select="//li[#id='portaltab-catalog']//a/#href" /></xsl:attribute><xsl:apply-templates select="node()"/></replace>
to modify an href of #hptoolbar-cerca with the href from another element in the content.
Maybe this could be useful for you.
Vito

My case is similar. I am happy with my theme code, only want to "replace" its href values. With XPath copied in the Chrome inspector, the simplest rule looks like this:
<copy attributes="href"
theme="/html/body/div[2]/div[1]/div/div[1]/a"
content="//*[#id='portal-logo']" />
I expect to successfully apply this trick to all the href values, but some do not work. I find these <a> elements are in the code block manipulated by JavaScript for implementing Slide effect. I remove all its related JavaScript codes, and copy its "real" XPath again, finally everything works fine as expected. Hope this helpful.

Related

XSL Test error at least one contains instead of only the one

With several xml documents containing topic elements and only one file containing a topic element with attribute product="OPTION".
I'd like to write OPTION if the attribute is set for the topic and TOTO if no attribute exists for the topic.
The problem is that actally if at least one file contains the attribute product="OPTION" OPTION is written on every generated pdf page.
<xsl:template name="insertBodyEvenHeader">
<xsl:param as="xs:string" name="flow-name" select="'even-body-header'" />
<fo:static-content flow-name="{$flow-name}">
<fo:block xsl:use-attribute-sets="__body__even__header">
<xsl:choose>
<xsl:when test="topic/#product='OPTION'">
<fo:inline xsl:use-attribute-sets="title__option">
<xsl:text>OPTION</xsl:text>
</fo:inline>
</xsl:when>
<xsl:otherwise>TOTO</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:static-content>
</xsl:template>
Would you be so kind to help me dealing with that test? thansks, best regards
It is likely that your context for this template is something above topic (which makes sense because you test on topic/#product). That is why if you have 10 topic elements and one has #product='OPTION' then the WHEN is true.
If that is true and you want to test on current topic being output, then you would use retrieve-marker in the header and then create markers in the topic template and pull those markers into header.
Your choose would then become #product (because you are in the topic template) and you would just drop an fo:marker that would have your choose inside, then use fo:retrieve-marker and some retrieve-position depending on how you want them to be processed. If a topic always starts a page then you would use first-including-carryover.

XSLT: Dynamic node name insertion based on template parameter

I am trying to generate the following structure in an XSLT template.
<ns:e1>
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</ns:e1>
or
<ns:e2>
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</ns:e2>
or other elements ns:e3 etc (although finite), based on a template parameter (say type). Typically I could use an xls:choose construct. In such a case, I would be duplicating the child elements (whose values are also template parameters).
Is there a way in XSLT to dynamically assume the element name ns:e1 or ns:e2 so that I can put the child elements once in its parent. I could save maintenance effort later if I have change the child elements or values (change once in one place and avoid bugs due to human errors).
Thanks for your help in advance.
Yes, you may use the xsl:element instruction to do that.
Assuming you always want to have <child1>some value<child1><child2>some value<child2><child3>some value<child3> as children for your parent element, you could rewrite your code like so:
<xsl:variable name="elementName">
<!-- compute the element name here ... -->
</xsl:variable>
<!-- Here we create an element having the name computed in variable elementName -->
<xsl:element name="{$elementName}" namespace="http://www.anamespace.com/and/so/on">
<child1>some value<child1>
<child2>some value<child2>
<child3>some value<child3>
</xsl:element>

xsl:template match not working

I have an image (in the content) with a src attribute
http://myPage/rss.gif
I wan't to change the attribute. The xsl:template match expression doesn't work..
<replace css:content="#content" css:theme=".content" />
<xsl:template match="img/#src[contains(., 'rss.gif')]">
<xsl:attribute name="src">/++theme++myPackage/images/<xsl:value-of select="." /></xsl:attribute>
</xsl:template>
What am I doing wrong?
In your example you select an img-tag containing the string rss.gif in its src-attribute, this string is probably http://myPage/rss.gif.
Then you add an attribute src to the img-tag beeing its value the concatenation of the strings /++theme++myPackage/images/ and the original value http://myPage/rss.gif.
The original value is added by <xsl:value-of select="." /></xsl:attribute>. This selects the whole value of the src-attribute!
Thus you get something like:
<img id="content" src="/++theme++myPackage/images/http://myPage/rss.gif" />
For test purposes you could first add an img-element whith the expected src-attribute to your theme.html file and check if the image file is accessed. Please check also that the value of src in your content file only contains the string that you want to append to /++theme++myPackage/images/.
BTW, in your example you replace the theme element by class: css:theme=".content". Remind that this will replace all elements with the class content. Consider using id or a narrower selector e.g. div.content or better div#someid.content.

Call XSLT template and save all output to variable

I want to call and template and save the output to a variable. I want to save ALL of the output including HTML markup, but that's not what happens.
For example, take this simple XSLT:
<xsl:call-template name="demonstration">
<xsl:template name="demonstration">
<p>Just a test</p>
</xsl:template>
This simple template will output <p>Just a test</p> to the HTML page. If I view the page source, I see that (of course). Now take this example using the same template, but instead of just outputting to HTML I want to save the output to a variable:
<xsl:variable name="test">
<xsl:call-template name="demonstration">
</xsl:variable>
<xsl:value-of select="$test"/>
<xsl:template name="demonstration">
<p>Just a test</p>
</xsl:template>
Viewing the variable shows that the only output now is Just a test.
Where did the HTML markup go? I'd like to be able to save the output of a call-template to a variable, but I need the HTML markup too.
Is there a way of avoiding the loss of HTML tags when calling a template like this? Is there a different way of writing my template? Maybe a setting I'm missing? I've tried disable-escape-encoding, but that makes no difference (in Safari at least).
I prefer to use the template for both needs: I'd like to be able to just call it and have the output in an HTML page for viewing. I'd also like to be able to wrap the call in a variable, but it's important that both call methods including all of the HTML tags/markup as specificed in the template.
EDIT:
I've tried both of the posted answers so far, but copy-of gives me the same result as value-of. Actually, I'm not using value-of, I was only showing how to duplicate the problem. Here is a more thorough explanation of what I'm trying to do.
Have a stylesheet that is used to transform a rather large XML received from a REST response. The stylesheet has its output method set for html.
One of the templates in this stylesheet does a lot of decision-making on how to display as many as 4 rows of data in a table. There are 2 columns, one is for a label, the other is for data.
The decision-making that's done includes what the text of the label is and what class - sometimes the label is green, sometimes its red, etc. The data column can contain text and/or numbers. Some of the text might be bold, some might be colorized. There are a bunch of prerequisites that determine these attributes.
If I were displaying a details of a single item, I'd be done with this template, but one of these items can have a multitude of attributes chosen. For example, there might be a size and a color. Depending on which size or color a user chooses, the price can be different, the particular item might be out of stock, if it has a different price then it will have a different savings to the user. Different items might have free shipping, or might be available for preorder only. There are LOTS of attributes.
And so I have a template that anylizes all of these prerequisites and constructs a <tr><td></td><td></td></tr> filled with firly simple text and data derived by some not-so-simple logic.
I've modularized this template so that I can call it for any one of the items. It's paramertized so I can specify the label text, data, class, and a couple of other things.
When the web page loads, the default/main item is displayed along with general info - like the range of prices and a range of savings, etc.
But when a user selects a color or size, these table rows need to be updated with the correct data. I've already processed the data from the XML and it would be very costly to make another server request, so what I have done is created a JSON string array containing all of the data for all of the different kinds of items. I save this JSON in the value attribute of a hidden input control. There's other ways of doing it, yes, but this works and it seems manageable to me.
Since the logic to create these table rows is in the stylesheet on the server, I perform all this logic on all items and then pass the calculated strings to the client. Here is an example of what this looks like:
<input id="hfData" type="hidden" value="[
{"ProductID": "00001", "Color": "Beige", "Size": "14"},
{"ProductID": "00002", "Color": "Black", "Size": "14"},
{"ProductID": "00003", "Color": "Blue", "Size": "10"},
{"ProductID": "00004", "Color": "Pink", "Size": "10"},
{"ProductID": "00005", "Color": "Yellow", "Size": "10"}
]"
/>
I then have a small JQuery script that is triggered any time the user changes a dropdownlist selection, or changes an attribute. The script parses the above JSON and determines what the currently configured ProductID is.
Note: Since the value attribute of the input control would not be valid if I had double quotes scattered through it like this, they are actually all ". I'm showing double quotes here to make it easy to look at.
Once the ProductID is determined, the page gets updated with lots of different details as described earlier in this edit. These details come from yet another JSON object I've created - again, since all of the product details are known in the XSLT Stylesheet, that is where I am creating the JSON string array.
<input id="hfDetails" type="hidden" value="[
{"ProductID": "00001", "ListPrice": "<tr class="collapsed"><td class="datalabel">List Price:</td><td class="datainfo"></td></tr>", "YourPrice": "<tr><td class="datalabel">Price:</td><td class="datainfo">$23.99 & is elegible for <span class="freeshipping">FREE</span> shipping Details</td></tr>"},
... etc, etc, etc ...
]"
/>
As you can see, the hidden input just above has a value attribute containing a JSON object which contains HTML markup. I've seen this work, but in order to get JSON to work correctly I have to escape all of the tags. Ampersands have to be &, then there's <, > and also escaping all quotes with a backslash. I've coded it all that way and it works - albeit nasty to look at, it works and it prevents me from having to make round trips to the server - it prevents me from putting all the logic to create these strings on the client side.
None of this has anything to do with the problem I'm having, but I'm hoping to get rid of all the comments from JSON and XSL/XML professors who challenge me for having JSON and XSLT (or HTML) in a single sentence...
Now, I hope I can show (very simply) the precise problem I am having. To start, it has really VERY LITTLE to do with JSON. It has virtually nothing to do with using xsl:value-of over xsl:copy-of.
Here is where I create a hidden input field with the value attribute containing a JSON string:
<input>
<xsl:attribute name="id">
<xsl:value-of select="$id"/>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>hidden</xsl:text>
</xsl:attribute>
<xsl:attribute name="runat">
<xsl:text>server</xsl:text>
</xsl:attribute>
<xsl:attribute name="value">
<xsl:text>[</xsl:text>
<xsl:for-each select="Items/Item">
<xsl:call-template name="JSONItemDetails">
<xsl:with-param name="offerNode" select="Offers"/>
<xsl:with-param name="lastitem" select="position() = last()"/>
</xsl:call-template>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:attribute>
</input>
<xsl:template name="JSONItemDetails">
<xsl:param name="offerNode" select="."/>
<xsl:param name="attributesNode" select="ItemAttributes"/>
<xsl:param name="listprice" select="0"/>
<xsl:param name="lastitem" select="false()"/>
<!-- Product ID -->
<xsl:text>{</xsl:text>
<xsl:text>"ProductID": </xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="./ProductID"/>
<xsl:text>",</xsl:text>
<xsl:for-each select="msxml:node-set($offerNode)">
<!-- Title -->
<xsl:text>"Title": </xsl:text>
<xsl:text>"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText">
<xsl:call-template name="title">
<xsl:with-param name="node" select="$attributesNode" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<xsl:text>",</xsl:text>
<!-- List Price -->
<xsl:text>"ListPrice": </xsl:text>
<xsl:text>"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText">
<xsl:call-template name="DataTableRow">
<xsl:with-param name="label" select="'List Price:'" />
<xsl:with-param name="data" select="./Price/FormattedPrice" />
<xsl:with-param name="dataid" select="'listprice'" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:for-each>
<xsl:text>}</xsl:text>
<xsl:if test="$lastitem != true()">
<xsl:text>,</xsl:text>
</xsl:if>
The DataTableRow template does a lot of things and which mostly provide consistency, but also clean up all the stray HTML tags used to create rows and columns. Here is that template.
<xsl:template name="DataTableRow">
<xsl:param name="label" select="''"/>
<xsl:param name="data" select="''"/>
<xsl:param name="dataid" select="''"/>
<xsl:param name="concat" select="''"/>
<xsl:param name="class" select="''"/>
<tr>
<xsl:choose>
<xsl:when test="$data = not(string(.))">
<xsl:attribute name="style">
<xsl:text>display:none</xsl:text>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="style">
<xsl:text>display:block</xsl:text>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<!-- Label Column -->
<td>
<div>
<xsl:choose>
<xsl:when test="$class = 'bigmaroon'">
<xsl:attribute name="class">
<xsl:text>datalabel maroon</xsl:text>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="class">
<xsl:text>datalabel</xsl:text>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$label"/>
</div>
</td>
<!-- Data Column -->
<td class="datainfo">
<xsl:attribute name="id">
<xsl:value-of select="$dataid"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="$class = 'strike'">
<strike>
<xsl:value-of select="$data" />
</strike>
<xsl:value-of select="$concat"/>
</xsl:when>
<xsl:when test="$class = 'maroon'">
<span class="maroon">
<xsl:value-of select="$data" />
</span>
<xsl:value-of select="$concat"/>
</xsl:when>
<xsl:when test="$class = 'bigmaroon'">
<span class="bigmaroon">
<xsl:value-of select="$data" />
</span>
<xsl:value-of select="$concat"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$data" />
<xsl:value-of select="$concat" />
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
The DataTableRow template has a bit of logic that would be messy to do everytime I needed to write one of these rows. First off, All of these rows have to exist in the HTML. If there is no data, then they are set with a style of display:none. This is important since I won't be able to fill them in with data from my JQuery script unless there is a valid selector...
Problem is, after all of this, DataTableRow works just fine when it is called directly like this:
<xsl:call-template name="DataTableRow"/>
In the above call, the HTML tags are all output to my web page. They look fine, their classes and styles are correct. I can call it like this from anywhere in my template and it works just fine. I'm sure there is a bug here or there, and I might see the light on a better/more efficient way of coding this, but it basically works and works well for HTML.
THE PROBLEM: I am unable to get any of the HTML tags when I build my hidden input fields above. The only value that gets stored in my JSON strings are the innerhtml. DataTableRow is called from JSONItemDetails template and instead of getting a result of:
<tr><td>Some Label</td><td>Some Data</td></tr>
I get a result of
Some LabelSome Data
This was an awfult lot of information to ask such a simple question, but I've been getting responses that seem to hint I'm not doing things right if I'm dealing with JSON within XSLT/HTML.
Can anyone help with my problem? Why are the HTML tags being stripped from the output of DataTableRow when I call it in JSONItemDetails to create my JSON string?
EDIT #2:
I've got several things going on with my code that have been causing this problem of HTML tags being stripped. Am finally getting a handle on what's been causing the problem and now I've been trying to figure out how to resolve the issue. Here are additional notes.
Am able to confirm, xsl:value-of is stripping my HTML tags from the output of a template call. xsl:copy-of shows me the tags I expect. This is a big help for me in understanding where the problem is at, but it also allows me to identify other problems.
xsl-copy cannot be used to output under the tag's value= attribute. I understand why, though I'm clueless of how to handle this problem if I proceed in the direction I was going.
Even if I figure out a solution to #1, I have to figure out how to escape the double quotes in my HTML markup. Ie: A double quote isn't valid within the value= attribute. I understand that an apostrophe would work, but these double quotes are created from using xsl:attribute under certain tags to specify a class or occassional style. I have a template that escapes double quotes with a backslash, but calling it also strips out my HTML tags and I can't see why - so I don't know how to fix it. I'll post the code for this template below.
If I had a workaround for the above 2 problems I could proceed in the direction I've been going, but I'm open to hearing advice on why I shouldn't be mixing data with display in my JSON.
I mix the two because I don't like the idea of putting the display logic in my JQuery script. I'd like my script to remain ignorant of this logic. It's simple in JQuery using these JSON objects to drill down to a ProductID and replace a table row like this:
var details = $.parseJSON($("#[id*='hfDetails']").val());
var filteredDetails = $.grep(details, function (n) {
return n.ProductID == ProductID;
});
if (filteredDetails.length > 0) {
var product = filteredDetails[0];
$("div#title").html(product.Title);
$("td#listprice").parent().html(product.ListPrice);
$("td#price").parent().html(product.Price);
$("td#yousave").parent().html(product.YouSave);
}
If I remove display from my JSON strings as Dimitre is advising, all the sudden I have to put a lot of logic in my jquery script to not only provide <tr> and <td> formatting (classes), but also logic to wrap the actual data, like <strike, <strong>, color specifications and font sizes or even whether a particular table row is a display:block or a display:none. I absolutely do not want to code any of that logic in JQuery script on the client.
It's a very simple xsl:for-each in my XSLT template to create these strings on the server and to stuff the results in my JSON object so that I can use the above script. Admittingly, it's ugly data to look at.
Another note, no matter if I seperate display from data or not, the current XSLT template I use to process my XML will still be needed in the event there is only one item with a single possibility of display. In the event there is only a single product, JSON doesn't come into the picture since there are no controls displayed on the page to change the product's attributes (color, size, etc).
I definitely want to code this "right", so I certainly appreciate hearing from experience. I just don't think anyone can effectively advise me for or against the end result without knowing all of the considerations made that got me there
Here is (was) the template I used to escape double quotes:
<!--
Escape quotes with \ for JSON strings
-->
<xsl:template name="escapeQuote">
<xsl:param name="pText" select="."/>
<xsl:if test="string-length($pText) >0">
<xsl:copy-of select="substring-before(concat($pText, '"'), '"')"/>
<xsl:if test="contains($pText, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '"')"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
This template strips HTML tags from the input string. I call it like this:
<xsl:variable name="output3">
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText">
<xsl:call-template name="DataTableRow">
... with-params ...
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
Immediately doing:
<xsl:copy-of select="$output3"/>
Shows that HTML tags no longer exist and therefore, no double quotes exist either. However, doing this reveals all of my HTML tags:
<xsl:variable name="output3">
<xsl:call-template name="DataTableRow">
... with-params ...
</xsl:call-template>
</xsl:variable>
<xsl:copy-of select="$output3"/>
Thanks for reading all of this - and btw, I see other posts where XSL code is formatted nicely and colorized. It's a LOT easier to read, but I can't get it to work with the code I'm posting. Everything is black text and left-justified. I've tried correcting that, but it's not working for me.
EDIT #3:
Dimitre commented that a quote could be used literally in the value attribute of a hidden input field, so I did some experimenting with that in mind. There seems to be a gotcha everywhere I look.
<input value='something containing " literally'/>
To get my value= contents wrapped by apostrophes instead of double quotes, I tried creating my hidden <input> like this:
<input>
<xsl:attribute name="id">
<xsl:value-of select="$id"/>
</xsl:attribute>
<xsl:attribute name="type">
<xsl:text>hidden</xsl:text>
</xsl:attribute>
<xsl:attribute name="runat">
<xsl:text>server</xsl:text>
</xsl:attribute>
<xsl:text> value='[</xsl:text>
<xsl:copy-of select="$output"/>
<xsl:text>]'</xsl:text>
</input>
This doesn't work because the <input...> tag gets rendered with the value= attribute (and it's contents) AFTER the closing </input>.
So I removed the xsl:attribute elements and formed it with xsl:text. Unfortunately, using a < inside a xsl:text isn't valid and generates an error. So I changed the < to < as well as the corresponding >. This didn't generate any errors, but causes the entire <input> and its contents to be displayed as a string when the page is rendered. (I'm viewing the page in Firefox).
I haven't tried coding this with CDATA because I'm pretty sure that's not going to work either and I'll get the same results as the latter - everything being displayed as a string.
shrug
EDIT #4:
OJay recommended dropping the idea of putting my JSON in the value= attribute of a hidden field. I've considered this all along, but wasn't sure how much I'd have to change my code. When he showed examples of how little I'd have to change, I decided to go for it.
Unfortunately, there's another gotcha.
<script type="text/javascript">
var hfDetails = [{ProductID: '000001',Title: '<h3>American Apparel Sheer Jersey Chemise X-Small-Asphalt</h3>',Condition: 'New',ListPrice: '<tr style="display:block">
<td>
<div class="datalabel">0List Price:</div>
</td>
<td class="datainfo" id="listprice">$24.99</td>
</tr>',Price: '<tr style="display:block">
<td>
<div class="datalabel maroon">Sale:</div>
</td>
<td class="datainfo" id="price"><span class="bigmaroon">$15.49</span></td>
</tr>',YouSave: '<tr style="display:block">
<td>
<div class="datalabel">You Save:</div>
</td>
<td class="datainfo" id="yousave"><span class="maroon">$9.50 (38%)</span></td>
</tr>'},
....
]
</script>
This is a paste from the rendered web page. I'm getting an error of "Unterminated string literal" and it's pointing to the apostrophe right before the very first <tr> - on the second line.
I'm assuming this is because the string has whitespace spreading it over several lines. The whitespace is as-is from making the call-template. I'm not sure how to disable this whitespacing or if that'll even make the difference. Am going to do some googling on this, but does anyone know if there is a way to turn whitespace off for a call-template?
Try <xsl:copy-of select="$test"> instead of <xsl:value-of ... />
Also note, <xsl:value-of /> (and <xsl:copy-of /> for that matter), should be inside a template, not at the root level of the <xsl:stylesheet >....</xsl:stylesheet> - throws an error in my XSLT debugger
so test that worked for me was
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="test">
<xsl:call-template name="demonstration" />
</xsl:variable>
<xsl:template match="/">
<xsl:copy-of select="$test"/>
</xsl:template>
<xsl:template name="demonstration">
<p>Just a test</p>
</xsl:template>
</xsl:stylesheet>
Value-of would be getting the value of the variable, and becuase we are in XML the value would be the same as the value of a node, i.e the data between the tags. Copy of would output exactly that a complete copy , tags and all
EDIT
As per my started comment, what I was meaning was something like this:
out from the XSLT
<script type="text/javascript">
var hfDetails = [{"ProductID": "00001", "ListPrice": '<tr class="collapsed"><td class="datalabel">List Price:</td><td class="datainfo"></td></tr>', "YourPrice": '<tr><td class="datalabel">Price:</td><td class="datainfo">$23.99 & is elegible for <span class="freeshipping">FREE</span> shipping Details</td></tr>'},
... etc, etc, etc ...
];
</script>
instead of the hidden input field. What this will do is create a global javascript array object.
in essences jumps the step required by
var details = $.parseJSON($("#[id*='hfDetails']").val());
you could then just do
var details = hfDetails;
and no more code needs to change.
If you are concerned about the escaping of apostrophe and quotes. Either can be used to define a string in javascript, and then the other can safely be used in the string i.e.
"ListPrice" : '<tr class="collapsed"><td class="datalabel">List Price:</td><td class="datainfo"></td></tr>'
is valid and so is
"ListPrice" : "<tr class='collapsed'><td class='datalabel'>List Price:</td><td class='datainfo'></td></tr>"
also note, you don't have to enclose the properties of the object in quotes i.e.
"ProductID" : "00001"
can just be
ProductID : "00001"
as long as you don't have spaces in your property names
<xsl:value-of select="$test"/>
You must use xsl:copy-of (or xsl:sequence is recommended in XSLT 2.0) -- instead of xsl:value-of.
By definition, xsl:value-of outputs the string value of the result of evaluating the expression in its select attribute.
<xsl:copy-of> outputs (in document order) all the nodes of the node-set specified in its select attribute. Or, to quote the W3C XSLT 1.0 specification again:
"The xsl:copy-of element can be used to copy a node-set over to the
result tree without converting it to a string"
In XSLT 2.0 xsl:sequence may be used to output the nodes of a node-set in any desired order.

Using a variable as part of a XPath selection

I'm looking to use a variable as part of an XPath expression.
My problem might be the msxsl node-set function... not sure. But don't let that cloud your judgement... read on...
I'm using a .NET function to load up the content file, which is passed in via bespoke XML content. The #file results in an XML file.
The bespoke XML the sits on the page looks like :
<control name="import" file="information.xml" node="r:container/r:group[#id='set01']/r:item[1]" />
The XSL looks like :
<xsl:variable name="document">
<xsl:copy-of select="ext:getIncludedContent(#file)" />
</xsl:variable>
I'm then translating this to a node-set so I can query the document
<xsl:variable name="preset-xml" select="msxsl:node-set($document)" />
The source file I am loading in looks like :
<container>
<group id="set01">
<item>value01</item>
<item>value02</item>
<item>value03</item>
</group>
<group id="set02">
<item>value04</item>
<item>value05</item>
</group>
</container>
It works up until this point. I can see the source file being brought thru and output as XML.
My problem comes in when I am trying to query the source file with an XPath expression fed in from the content.
I've tried :
<xsl:value-of select="$preset-xml/#node" />
Clearly that doesn't work as it looks for #node as a direct child of the loaded in XML.
I've tried :
<xsl:variable name="node" select="#node" />
<xsl:value-of select="$preset-xml/$node" />
But it doesn't like the second variable.
I've tried concat($preset-xml,'/',$node), but that still draws out the entire document in the result.
The only way I can get this working at the moment is to write out the full expression in the template :
<xsl:value-of select="$preset-xml/r:container/r:group[#id='set01']/r:item[1]" />
Which correctly brings thru value01
But that is then a hard coded solution.
I want to develop a solution which can be manipulated from the content to suit any sort of imported content, with the parameters declared in the content file.
p.s. I'm using XSLT 1.0 because the tech admin won't change the Microsoft parser to support later versions of XSL, so any solution would need to be written with that in mind.
There is no feature similar to reflection in XSLT. MS XSLT engine, in particular, compiles XPath queries when the XSLT was loaded, not when it was applied to your XML document. There is a reason to this: it separates the code (XSLT) from the data (input XML).
What are you trying to achieve by mixing up code and data; are you sure you really need to execute an arbitrary XPath expression from the input XML? Think, for example, what if the user will pass in the #name equal to e.g. //*, and then you'll send to the output the content of the entire input XML document, which might contain some sensitive data.
If you really need to do the thing you described, you may write your own XSLT extension object, which will accept the document root and the XPath query and will return the latter applied to the former. Then you'll be able to call it like that:
<xsl:value-of select="myExtensionNs:apply-xpath($preset-xml, #node)"/>
As for your original attempt with concat($preset-xml,'/',$node), concat is the string concatenating function. You supply some arguments to it, and it returns the concatenation of its string representations. So, concat($preset-xml,'/',$node) will return the string value of $preset-xml, concatenated with /, concatenated with the string value of $node. If you were trying to write a needed XPath query, concat('$preset-xml','/',$node) would seem more logical (note the quotes); still, there is no way to evaluate the resulted string (e.g. $preset-xml/r:container/r:group[#id='set01']/r:item[1]) to the value of corresponging XPath query; it could only be sent to output.
One possible trick is to call an parametrized xsl:template which carries out the crucial select comparison part and evaluate it's output as string.
I had a similar problem (count all nodes of an arbitrary attribute value) which I solved this way (btw: $sd = 'document($filename)' so I load a secondary xml files content):
<xsl:template match="/">
<xsl:variable name="string_cntsuccess">
<xsl:for-each select="./Testcase">
<xsl:variable name="tcname" select="#location"/>
<xsl:for-each select="$sd//TestCase[#state='Passed']">
<xsl:call-template name="producedots">
<xsl:with-param name="name" select="$tcname"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="cntsuccess" select="string-length($string_cntsuccess)"/>
</xsl:template>
<xsl:template name="producedots">
<xsl:param name="name"/>
<xsl:variable name="ename" select="#name"/>
<xsl:if test="$name = $ename">
<xsl:text>.</xsl:text>
</xsl:if>
</xsl:template>