Saturday, June 5, 2010

Apostrophes in JSPs

I recently came across an interesting problem trying to c:out Strings containing apostrophes in a JSP generated web page.

Let's say you had a call to a JavaScript function -- similar to the one below -- that was called when an html component was clicked and the parameters were populated by JSP c:out tags:

onclick = "doSomething('<c:out value="${text}">')"

As you can see, when the variable text contains an apostrophe, it will close the parameter String for doSomething early, causing the rest of characters in text to be read as uncompilable code.

Now I don't claim to be a JSP/Javascript/HTML ninja, so there may be a more obvious solution then the one I eventually arrived to. But due to the number of people I found asking for solutions to problems like this online, and the lack of finding the solution I eventually arrived at, I thought this was worth sharing.

The solution that I found on numerous sites was to use escape characters to display the apostrophe. In my Tag on the server side I replaced the apostrophe in the text variable with something like this:

text = text.replaceAll("'", "\\\\'");

That's one escape character to tell JSP to display the apostrophe instead of using it to close the sting, another escape character to put in front of that escape character to tell Java that you want to pass an escape character, and another escape character in front of each of those escape characters because replaceAll uses regular expressions to find and replace your substrings. This worked fine, but even as I released it it felt flimsy to me, and in when tested on another environment, was still throwing an error unless another escape character was added. I couldn't find the discrepancy between the two environments, but this was proof enough for me that it was not a solid enough fix. It seemed to me that if on a different environment our string went through some additional protocol that uses backslash as a character escape, then it would mess up the whole code.

The solution I found was to substitute HTML codes for the escape character and the apostrophe:

text = text.replaceAll("'", "&#092;&#039;'");

where &#092 is the HTML code for the "\" and &#039; is the HTML code for an apostrophe. Using these character codes, we can bypass any code that might process our escape characters and break our code before it gets to our page.

However, by default JSP will convert our HTML codes, process the character break and display an apostrophe, thus defeating our original purpose in replacing the apostrophe. This can be disabled per c:out tag by setting the escapeXML tag to false

onclick = "doSomething('<c:out value="${text}" escapeXML = "{false}">')"

If you view the source, you will wee the HTML codes used in your String instead of an apostrophe. If you want the apostrophe to display, just don't add the escapeXML = "{false}" parameter.