7

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 &quot;. 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 <a href="#">Details</a></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 &amp;, then there's &lt;, &gt; 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>&quot;ProductID&quot;: </xsl:text>
    <xsl:text>&quot;</xsl:text>
    <xsl:value-of select="./ProductID"/>
    <xsl:text>&quot;,</xsl:text>

    <xsl:for-each select="msxml:node-set($offerNode)">

        <!-- Title -->
        <xsl:text>&quot;Title&quot;: </xsl:text>
        <xsl:text>&quot;</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>&quot;,</xsl:text>

        <!-- List Price -->
        <xsl:text>&quot;ListPrice&quot;: </xsl:text>
        <xsl:text>&quot;</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>&quot;</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.

  1. 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.
  2. 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, '&quot;'), '&quot;')"/>

    <xsl:if test="contains($pText, '&quot;')">
      <xsl:text>\"</xsl:text>

      <xsl:call-template name="escapeQuote">
        <xsl:with-param name="pText" select="substring-after($pText, '&quot;')"/>
      </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 &lt; 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?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
rwkiii
  • 5,716
  • 18
  • 65
  • 114
  • rwkiii: Your main problem is that you are mixing data with presentation. I strongly recommend to have only the data in the second JSON object and to create the HTML markup on the client. In this way the problem is completely avoided. – Dimitre Novatchev Jun 25 '12 at 13:30
  • @DimitreNovatchev a) The system shows no history of *any* deleted comments on this post (except for your previous one, which I just deleted) as of the time of this writing b) moderators are not re-elected. – casperOne Jun 25 '12 at 13:52
  • @casperOne: Yesterday there was a long discussion thread on this answer -- rwkiii and I had at least two comments each in it. I was asking for more details and the OP was replying that everything is very complicated and he preferred not to show more detail. Then I recommended using something like KO for generating the markup on the client. Now all this is gone. Has there been a server malfunctioning/blackout at SO? – Dimitre Novatchev Jun 25 '12 at 14:28
  • @DimitreNovatchev I'll file a bug and have someone look into it. – casperOne Jun 25 '12 at 14:34
  • @Dimitre: I suspect you were remembering the discussion here: http://stackoverflow.com/questions/11182057/create-json-containing-html-from-within-xslt (a previous question from rwkii, which he deleted). – Shog9 Jun 25 '12 at 19:24
  • @rwkiii, you should be a bit careful about deleting questions, particularly when someone's been trying to help you with them - it can be confusing (as seen here) and is generally seen as rude. – Shog9 Jun 25 '12 at 19:29
  • @Shog9: Thank you for clarifying this mysterious disappearance. – Dimitre Novatchev Jun 25 '12 at 19:39
  • @rwkiii, `xsl:copy-of` produces the same result (with the same `select` attribute) as `xsl:value-of`, only if the expression in the `select` attribute evaluates to a scalar (non-node) or to a text node, or if the transformation specifies ``. You can easily correct the reason for this, then using `xsl:copy-of` solves the problem. – Dimitre Novatchev Jun 25 '12 at 19:44
  • Dimitre and Shog9, I deleted that post out of frustration that it was going nowhere. I felt I was confusing readers with my question. But, the only responder was Dimitre and his last comment recommended Knock Out. I apologize for wasting anyone's time. All I was trying to do was get rid of the misleading post so I could simplify my question. I will be more considerate. Also, I will be following up shortly to the comments here about my question in this post. ;) – rwkiii Jun 25 '12 at 20:11
  • Dimitre, as a test I output my data with `xsl:copy-of` and then with `xsl:value-of`. I can confirm, `xsl:copy-of` will output the HTML tags I expected. Problem with that is, `xsl:copy-of` cannot be used to output data under my tag's value= attribute - and, I understand that... I can't output a node tree element under an attribute. That makes sense to me. So, I'm still stuck with this. I tried outputting the `xsl:copy-of` as a string to a new variable and then outputting THAT with `xsl:value-of`, but it just won't work that way. (I will continue this comment in an EDIT of my question). – rwkiii Jun 25 '12 at 20:20
  • Dimitre and Shog9, please see my "EDIT #2:" in the original question. Thank you for helping me. – rwkiii Jun 25 '12 at 21:26
  • Dimitre, I've looked at your very first comment several times. Just keep looking at it and I keep noticing the double quote in the middle of the value attribute ``. Are you telling my that a double quote can be in the value attribute so long as the entire string is surrounded by an apostrohpe instead of double quotes? This would seem to resolve the problem so long as it's cross browser compatible AND my JSON strings don't ever contain an apostrophe? If I have to escape apostrophes though, I'm back to the original problem... – rwkiii Jun 25 '12 at 21:43

2 Answers2

13

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 <a href="#">Details</a></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

OJay
  • 4,763
  • 3
  • 26
  • 47
  • The template I am calling is only a small portion of a much larger XSLT stylesheet. The output method on the stylesheet is html. Might that be a difference in our results? I do understand the difference between value-of and copy-of, but in the called template, the HTML tags are not output with a value-of and I'm not sure how I would output them with copy-of... I used a variable to wrap the call-template just to make it easier to see my dilemna. I'm actually making the call-template within an `xsl:attribute name="value"` of a hidden input control. HTML tags never get output... – rwkiii Jun 25 '12 at 04:27
  • rwkii - after reading all the code and comments, and the principlals of what you are tring to achieve, would it be simpler to output the jSON directly to the page with script tags. Instead of storing what is in essence a encode string in a hidden fields alue attribute. what I mean, something like: – OJay Jun 25 '12 at 23:13
  • @rwkiii - sorry ran out of time and characters, I'll post as an edit to my answer – OJay Jun 25 '12 at 23:20
  • user, I've definitely considered this. I've got other code in my XSLT that outputs code between script tags. Not only for the reason that I'm kinda stuck, but also because it seems it might be a more correct way of doing it. The biggest thing stopping me is that I just don't know JQuery/Javascript very well at all. I'm fortunate to have gotten as far as I have using a hidden input field. Also, I'm not sure that I'd be able to include a doublt quote inside a JSON object between script tags any more than with a hidden field. Is pretty much a limitation in most languages. – rwkiii Jun 25 '12 at 23:23
  • How about if I wrap the strings in apostrophes, then I get a string with an apostrophe in its title, like "Men's". What is required to escape them? – rwkiii Jun 26 '12 at 00:23
  • stick a backslash in front of it i.e. 'Men\'s' or encoded character perhaps (' for single apostrophe) – OJay Jun 26 '12 at 00:50
  • Ojay, I like this. So I made a few changes to see if it would work for me. Guess what? Lol, another gotcha. Please see EDIT #4 in my question. I think it's too much data to post in this comment. Thanks. – rwkiii Jun 26 '12 at 01:12
  • Yeah Javascript doesn't like it when you split strings across lines, and XML respects all whitespace. Is it possible to get all of the listprice property values on one line OR javascript accepts a \ character as a string literal continuation – OJay Jun 26 '12 at 01:47
2
<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.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431