How to escape json strings in freemarker

26,401

Solution 1

You're looking for the ?js_string operator.

{
"field1" : "${response.value1?js_string}",
"field2" : "${response.value2?js_string}"
}

That will take care of escaping quotes, backslashes, et. al in order to make your JS happy.

Edit: I just saw that they introduced a ?json_string operator in Freemarker 2.3.19. See here for exactly how it works. And there was much rejoicing...

Solution 2

Use a FreeMarker macro to combine all of the answers above, while making the template more readable and maintainable:

<#macro json_string string>${string?js_string?replace("\\'", "\'")?replace("\\>", ">")}</#macro>
{
"field1" : "<@json_string "${response.value1}"/>",
"field2" : "<@json_string "${response.value2}"/>"
}

If you want to reuse the macro in multiple templates, put it in its own file and include the file instead of duplicating the macro:

<#include "/path/to/macro.ftl">
Share:
26,401

Related videos on Youtube

Skurpi
Author by

Skurpi

Updated on July 09, 2022

Comments

  • Skurpi
    Skurpi almost 2 years

    We are building a restful api using Spring MVC and freemarker as the templating language. We have chosen to build json responses in the freemarker

    Example freemarker.ftl:

    {
    "field1" : "${response.value1}",
    "field2" : "${response.value2}"
    }
    

    We get a problem when the strings in the values contain quotation marks (or any of the other characters in the JSON syntax).

    The question: How can I escape these strings using freemarker?

    We have looked at ?xml or ?html but they do not cover all relevant characters (such as \).

    EDIT: ?js_string will escape the string to comform with JavaScript. And since JSON is based on JavaScript (JavaScript Object Notation), it will work.

    EDIT2: In case a single-quote pops up, ?js_string will escape it which again leads to invalid JSON. The hotfix for it is:

    ${variable?js_string?replace("\\'", "\'")} 
    

    and if you really want to be picky:

    ${variable?js_string?replace("\\'", "\'")?replace("\\>",">")}
    

    Alternatively if you use Spring: http://www.springsurf.org/sites/1.0.0.M3/spring-webscripts/spring-webscripts-documentation/reference/html-single/index.html#js-api-index-org.springframework.extensions.webscripts.json.jsonutils

    • Stefan Haberl
      Stefan Haberl over 10 years
      +1 for the Spring alternative
  • Skurpi
    Skurpi about 13 years
    Whats the advantage to ?js_string over ?j_string in this case?
  • Waldheinz
    Waldheinz about 13 years
    @Skurpi The advantage is that js_string does the necessary escaping while j_string does not.
  • Skurpi
    Skurpi about 13 years
    @Waldheinz & @stevevls Thank you!
  • Skurpi
    Skurpi almost 13 years
    @Waldheinz @stevevls It seems it does not work entirely. See the following example: { "companyName" : "Rehabshopen AB, Göran Sjödén\'s" , "companyText" : "Vi säljer träning, sjukvård, hygienartiklar & ortoser. Uthyrning och försäljning av rullstolar &..." } If you run it through a validator it will fail, and the string has been ?js_string escaped. jsonformatter.curiousconcept.com/#jsonformatter
  • stevevls
    stevevls almost 13 years
    @Skurpi I see...it doesn't like the escaped single quote. Try this to get rid of the backslashes on the single quote : ${variable?js_string?replace('\\'', '\'')}
  • Skurpi
    Skurpi almost 13 years
    It seems risky to try to fix individual problems like that. I'd prefer an "automated" way to handle this, since there might be some other characters that it'll escape that might fail.
  • Skurpi
    Skurpi almost 13 years
    @stevevls it should be: ${variable?js_string?replace("\\'", "\'")} But excellent!
  • stevevls
    stevevls almost 13 years
    @Skurpi Agreed that a built-in would be the best way to go. Hopefully they'll put one in the next version of Freemarker!
  • Skurpi
    Skurpi over 12 years
    This is actually what we did, but we put some extra functionality in it which we had use for: <#macro hazStringContent content> <#if content?has_content && content != "">"${content?js_string?replace("\\'", "\'")?replace("\\>",">")}"<#else>null</#if> </#macro>