usethe4ce
usethe4ce

Reputation: 23859

Escaping JSON in JSPX

I want to initialize a Javascript variable from some JSON (generated via Jackson) in my JSPX, something like this:

<script>
var x = <c:out value="${myJson}" />;
</script>

But the output I get looks like:

<script>
var x = {&#034;foo&#034;:&#034bar&#034};
</script>

I see what you did there, HTML-escaping the string. Obviously, I can't leave it completely unescaped because angle brackets in the data could break the page. But I don't really need all the quotes to be escaped, since I'm not putting the JSON within an attribute value, do I?

Now, this looks like it would be a perfectly valid way to write a script in HTML, just needlessly complicated (like, say, replacing spaces with &#32;). As it turns out, it works just fine in XHTML, but with an HTML content type, I get an error, both in Firefox and IE. I'm not sure of the rationale, but that's how it is.

So, what's the best approach here? Do I really want to simply escape angle brackets but not escape double quotes, or are there any other gotchas? Is there a tag out there that would replace c:out (I know there are Spring tags for escaping Javascript, but that's still not the right kind of escaping)? How do people get this to work?

BTW, yes, I could make a separate AJAX call, but an extra round trip just to work around this problem seems silly.

UPDATE

I had a lot to learn about CDATA vs. PCDATA and how HTML is different from XHTML. Here I thought JSPX would make polyglot markup easy, but it turns out to be, as someone put it, a big ball of nasty.

For HTML, the <script> element has a CDATA content model (not to be confused with CDATA sections), which means nothing can be escaped, but </ must be absolutely avoided.

In the special case of JSON, where end tags can only occur within a quoted string, this therefore means the safe way to escape is to use Javascript (rather than HTML) escaping and replace </ with <\/.

For XHTML (if you care about such things) on the other hand, you just XML-escape everything as usual (& becomes &amp;, etc.) and it all works beautifully. A compatible solution would have to use CDATA with guarding comments (<!--/*--><![CDATA[/*><!--*/ etc.) around the entire <script> body and then escape any occurrences of ]]> within the JSON; furthermore, I'd still escape </ too just to be safe. Big ball of nasty, indeed, but at least it can be automated.

Upvotes: 3

Views: 2806

Answers (2)

usethe4ce
usethe4ce

Reputation: 23859

OK, answering my own question here, after much research and no real help.

Based my "update" above, the most straightforward way targeting HTML is just:

<script>
var x = ${fn:replace(myJson, "\</", "\<\\/")};
</script>

Ugly but simple.

This will not yield valid XML or XHTML, unfortunately. If you really need that, the original c:out will work fine, though it will not yield valid HTML. And if you really need a single solution to work on both, you probably need a custom taglib (or TAGX) that will either switch from the content type or do all of the following:

  • wrap the script body in a comment-guarded CDATA section
  • replace each </ with <\/
  • replace each ]]> with ]]\>

Upvotes: 1

KV Prajapati
KV Prajapati

Reputation: 94653

set escapeXml=false

<c:out value="${myJson}" escapeXml="false"/>

Upvotes: 4

Related Questions