如何在Freemarker中转义JSON字符串

22
我们正在使用Spring MVC和Freemarker作为模板语言构建一个RESTful API。我们选择在Freemarker中构建JSON响应。
示例freemarker.ftl:
{
"field1" : "${response.value1}",
"field2" : "${response.value2}"
}

当值中的字符串包含引号(或者JSON语法中的任何其他字符)时,我们会遇到问题。

问题是:如何使用freemarker转义这些字符串?

我们已经尝试过?xml?html,但它们并不涵盖所有相关字符(比如\)。

编辑:?js_string可以将字符串转义为符合JavaScript标准的形式。由于JSON基于JavaScript(JavaScript对象表示法)开发,因此它也能够解决该问题。

编辑2:如果出现单引号,?js_string会对其进行转义,从而导致JSON无效。解决方法是:

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

如果你真的很挑剔:

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

如果您使用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

3个回答

33

您在寻找 ?js_string 操作符。

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

这将处理转义引号、反斜杠等内容,以满足JS的需要。

编辑: 我刚看到他们在Freemarker 2.3.19中引入了一个?json_string运算符。请查看此处,了解其确切工作方式。大家都很高兴...


@Waldheinz @stevevls 看起来它并没有完全工作。请看以下示例: { "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 &..." } 如果您通过验证器运行它,它将失败,并且该字符串已被?js_string转义。http://jsonformatter.curiousconcept.com/#jsonformatter - Skurpi
3
@Skurpi 我明白了……它似乎不支持转义单引号。尝试使用以下代码消除单引号的反斜杠:${variable?js_string?replace('\\'', '\'')} - stevevls
2
尝试修复单个问题似乎有风险。我更喜欢一种“自动化”的方式来处理这个问题,因为可能会有其他字符需要转义,否则可能会失败。 - Skurpi
1
@stevevls 应该是:${variable?js_string?replace("\'", "'")} 但非常好! - Skurpi
1
@Skurpi 同意使用内置功能是最好的方法。希望在下一版本的Freemarker中能加入一个! - stevevls
显示剩余2条评论

2
使用FreeMarker宏将上面所有答案组合在一起,同时使模板更易读和易维护。
<#macro json_string string>${string?js_string?replace("\\'", "\'")?replace("\\>", ">")}</#macro>
{
"field1" : "<@json_string "${response.value1}"/>",
"field2" : "<@json_string "${response.value2}"/>"
}

如果您想在多个模板中重复使用宏,请将其放在单独的文件中,并包含该文件,而不是复制宏:

<#include "/path/to/macro.ftl">

这实际上是我们所做的事情,但我们还在其中添加了一些额外的功能,这是我们需要使用的: <#macro hazStringContent content> <#if content?has_content && content != "">"${content?js_string?replace("\'", "'")?replace("\>",">")}"<#else>null</#if> </#macro> - Skurpi

0

从FreeMarker 2.3.32开始,现在更容易了,因为?c现在可以引用字符串:

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

请注意,${...}周围没有引号!这就是关键所在;如果值是字符串(而不是数字或布尔值),?c 将它们放在那里。
在2.3.32之前,上述代码将因为 ?c 不允许字符串输入而终止,并报错。
从2.3.32开始,可以使用 ?cn 代替 ?c,它代表 "?c可空"。然后,如果值缺失/null,它将输出 null,而没有引号。所以您不必通过有条件地打印引号来复杂化模板。
此外,将 c_format 设置为 "JSON""JavaScript或JSON" 是一个好主意。(或者,如果您无论如何都将 incompatible_improvements 配置设置为2.3.32或更高版本,则后者将成为默认设置。)

我无法在2.3.32 Java客户端中使用?cn,它显示为未识别的内置命令。 - Krittam Kothari
@KrittamKothari ?cn 在 2.3.32 版本中肯定是支持的。${.version} 输出什么? - ddekany
你说得对,我的依赖关系出了问题;不知怎么回事,我正在使用2.3.31。一旦我修复了依赖关系,它就正常工作了。 - Krittam Kothari

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接