Tuesday, July 22, 2008

Escaping Java Strings for use in Javascript code

What for?


In the context of a Java web application it is often necessary to generate pieces of javascript code dynamically. If you want to inject a Java String into Javascript code, you may need to escape the single-quotes that occur in the Java String, because Javascript can use single-quotes as the String delimiter. The simple way to avoid this issue is to use double quotes as String separators in Javascript, unfortunately this is not possible when the Javascript code is part of an HTLM attribute.
As in:

<a href="viewDocument?id=1234"
onclick="trackLink(this, '<%= ((Metadata) request.getAttribute("metadata")).getPageName4JS() %>')">
Document 1234
</a>

In the example above, a Javascipt error occurs if the method getPageName4JS() returns a String that contains a single quote.

How?


The following method will escape any quotes present in the input String, replacing each single quote with "\'".

public class WebUtil {
 public static String escapeSingleQuotes(String pString) {
  if (pString == null) {
   return null;
  }
  return pString.replaceAll("'", "\\\\'");
 }
}

You can then create a getter method that uses this utility method:

 public String getPageName4JS() {
  return WebUtil.escapeSingleQuotes(this.getPageName());
 }


Not very complicated, but tricky: it took me about 2 hours to write and test the escapeSingleQuotes() method. And I am still not sure I fully understand why it works. Let's look at the statement: pString.replaceAll("'", "\\\\'");
We want to replace every instance of " ' " with " \' ".
But the back-slash character in the regular expression needs to be escaped, so it becomes " \\' ". Each back-slash needs to be escaped in the Java String, so we end up with " \\\\' " in the Java code.

Unit test


Here is the JUnit test method for the escapeSingleQuotes() method:

 public void testEscapeSingleQuotes() {
  Assert.assertEquals(
   "null", null, WebUtil.escapeSingleQuotes(null));
  Assert.assertEquals(
   "empty string", "", WebUtil.escapeSingleQuotes(""));
  Assert.assertEquals(
   "Text with a 'quote'.",
   "This is a text with a \\'quote\\'.",
   WebUtil.escapeSingleQuotes("This is a text with a 'quote'."));
  Assert.assertEquals(
   "one quote", "\\'", WebUtil.escapeSingleQuotes("'"));
  Assert.assertEquals(
   "two quotes", "\\'\\'", WebUtil.escapeSingleQuotes("''"));
  Assert.assertEquals(
   "five quotes", "\\'\\'\\'\\'\\'", WebUtil.escapeSingleQuotes("'''''"));
  Assert.assertEquals(
   "mixed single quotes and back slashes",
   "\\\\\\\\'\\'\\'\\\\\\\\'\\'\\'\\\\'\\\\'\\\\'\\\\'",
   WebUtil.escapeSingleQuotes("\\\\\\'''\\\\\\'''\\'\\'\\'\\'"));
  Assert.assertEquals(
   "mixed single quotes and double quotes",
   "\"\\'\"\\'\"\\'\"\"\\'\\'",
   WebUtil.escapeSingleQuotes("\"'\"'\"'\"\"''"));
 }

No comments: