杰瑞科技汇

Java如何处理JSON中的特殊字符?

  1. JSON 规范中需要转义的特殊字符:这些字符在 JSON 字符串中必须有特殊的转义表示,否则会导致 JSON 格式错误。
  2. 在特定上下文中需要转义的特殊字符:当 JSON 字符串被嵌入到 HTML、XML 或 JavaScript 代码中时,JSON 字符串内的某些字符(如 <, >, &)也需要被转义。

下面我将详细讲解这两类情况,并提供 Java 中的处理方法。

Java如何处理JSON中的特殊字符?-图1
(图片来源网络,侵删)

JSON 规范中的特殊字符(JSON String Escaping)

根据 JSON 规范 (RFC 8259),在 JSON 字符串中,以下字符必须使用反斜杠 \ 进行转义:

转义序列 代表字符 说明
\" 双引号 用于包裹 JSON 字符串,字符串内部的双引号必须转义。
\\ 反斜杠 \ 反斜杠本身是转义字符,所以需要转义。
\/ 正斜杠 虽然规范中允许不转义,但为了安全,通常也进行转义。
\b 退格 Backspace
\f 换页 Form feed
\n 换行 Line feed
\r 回车 Carriage return
\t 水平制表符 Tab

如何处理?

现代的 JSON 处理库(如 Jackson, Gson)在将 Java 对象序列化为 JSON 字符串时,会自动处理这些转义,你不需要手动进行转义。

示例:

假设你有一个包含换行符和双引号的 Java 字符串。

Java如何处理JSON中的特殊字符?-图2
(图片来源网络,侵删)
String originalText = "这是一个包含\"双引号\"和\n换行符的文本。";

使用 Jackson 进行序列化:

import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        String originalText = "这是一个包含\"双引号\"和\n换行符的文本。";
        // 直接序列化,Jackson会自动处理转义
        String jsonString = mapper.writeValueAsString(originalText);
        System.out.println("原始字符串: " + originalText);
        System.out.println("JSON字符串: " + jsonString);
        // 输出:
        // 原始字符串: 这是一个包含"双引号"和
        // 换行符的文本。
        // JSON字符串: "这是一个包含\"双引号\"和\n换行符的文本。"
    }
}

使用 Gson 进行序列化:

import com.google.gson.Gson;
public class GsonExample {
    public static void main(String[] args) {
        Gson gson = new Gson();
        String originalText = "这是一个包含\"双引号\"和\n换行符的文本。";
        // 直接序列化,Gson会自动处理转义
        String jsonString = gson.toJson(originalText);
        System.out.println("原始字符串: " + originalText);
        System.out.println("JSON字符串: " + jsonString);
        // 输出与Jackson类似
    }
}

对于 JSON 规范内的转义,请相信你的 JSON 库,不要手动添加反斜杠,手动添加反而可能导致错误的转义,\"\"\"


在 Web 上下文中的特殊字符(HTML/JS Escaping)

这是一个更复杂也更常见的问题,当你将 JSON 数据(通常是作为字符串)嵌入到 HTML 页面或 JavaScript 代码中时,就需要进行额外的转义。

Java如何处理JSON中的特殊字符?-图3
(图片来源网络,侵删)

场景: 你有一个 JSON 字符串,你想把它放在一个 <script> 标签里,或者作为一个 HTML 属性的值。

问题分析

  1. JSON 字符串 vs. JavaScript 字符串:

    • JSON 字符串用双引号 包裹。
    • JavaScript 字符串可以用单引号 或双引号 包裹。
    • 如果你的 JSON 字符串被放入一个用双引号包裹的 JavaScript 字符串中,JSON 字符串内部的双引号 \" 就会和外部的双引号冲突,导致语法错误。

    错误示例:

    <script>
      var myJson = "这是一个包含"双引号"的文本"; // 语法错误!
    </script>
  2. HTML 实体编码:

    • 当 JSON 数据作为 HTML 属性值时(在 data-json 属性中),JSON 内容包含 <, >, &, , 等字符,可能会破坏 HTML 结构或导致 XSS (跨站脚本) 攻击。

如何处理?

处理这种情况的黄金法则是:逐层转义,从内到外

数据流的顺序通常是:Java 对象 -> JSON 字符串 -> HTML/JS 上下文

步骤 1:生成 JSON 字符串(由 Jackson/Gson 完成) 如前所述,Jackson/Gson 会生成一个符合 JSON 规范的字符串,其中包含了必要的转义(如 \", \n)。

步骤 2:将 JSON 字符串放入 HTML/JS 上下文(需要手动处理)

你需要根据 JSON 字符串将要放置的位置,选择正确的转义方法。

情况 A:JSON 作为 JavaScript 字符串

最安全的方法是使用 JavaScript 的 JSON.stringify() 函数,这个函数会自动处理所有必要的转义,包括 JSON 规范的转义以及 JavaScript 上下文需要的转义。

推荐做法: 不要在 Java 中拼接 JSON 字符串到 HTML 中,而是让服务器端直接输出一个 JavaScript 对象。

<!-- 错误且危险的做法 -->
<script>
  var data = <%-- 直接输出JSON字符串 --%>;
</script>
<!-- 正确且安全的做法 -->
<script>
  // 服务器端渲染时,直接输出一个合法的JS对象
  var data = {"name": "张三", "bio": "他说:\"你好!\" \n 欢迎来到我的页面。"};
</script>

如果必须从 Java 字符串拼接,那么你需要对字符串进行 JavaScript 转义。

手动转义(不推荐,但为了理解): 你需要对以下字符进行转义:

  • \ -> \\
  • -> \"
  • -> \'
  • -> \/
  • ` -> \`
  • 以及控制字符(如换行符 \n)最好替换为 \n 或其他转义序列。

使用 Apache Commons Lang 的 StringEscapeUtils(推荐手动转义时使用):

import org.apache.commons.text.StringEscapeUtils;
// 假设这是由 Jackson/Gson 生成的标准 JSON 字符串
//  "{\"name\": \"O'Reilly\", \"msg\": \"Say \"\"Hello\"\"\"}"
String jsonString = "{\"name\": \"O'Reilly\", \"msg\": \"Say \\\"Hello\\\"\"}";
// 将 JSON 字符串转义为可以在 JS 字符串中使用的形式
String jsEscapedJson = StringEscapeUtils.escapeEcmaScript(jsonString);
// 在 JSP 或 Thymeleaf 中使用
// <script>
//   var data = <%= jsEscapedJson %>;
// </script>
// 输出: var data = "{\"name\": \"O'Reilly\", \"msg\": \"Say \\\"Hello\\\"\"}";

注意: StringEscapeUtils.escapeEcmaScript 会处理 和 等字符,使其安全地嵌入 JS 字符串中。

情况 B:JSON 作为 HTML 属性值

当 JSON 作为 HTML 属性(如 <div data-json='...'>)的值时,你需要对 HTML 实体进行转义。

需要转义的字符:

  • & -> &amp;
  • < -> &lt;
  • > -> &gt;
  • -> &quot;
  • -> &#39;&apos;

使用 Apache Commons Lang 的 StringEscapeUtils

import org.apache.commons.text.StringEscapeUtils;
String jsonString = "{\"name\": \"Smith & Jones\", \"bio\": \"<b>CEO</b>\"}";
// 将 JSON 字符串转义为可以在 HTML 属性中使用的形式
// 使用单引号包裹属性,所以需要转义单引号
String htmlEscapedJson = StringEscapeUtils.escapeHtml4(jsonString);
// 在 JSP 或 Thymeleaf 中使用
// <div data-json='<%= htmlEscapedJson %>'></div>
// 输出: <div data-json='{"name": "Smith &amp; Jones", "bio": "&lt;b&gt;CEO&lt;/b&gt;"}'></div>

总结与最佳实践

场景 处理方法 工具/库
Java 对象 -> JSON 字符串 自动处理,JSON 库会处理所有 JSON 规范要求的转义(, \, \n 等)。 Jackson, Gson
JSON 字符串 -> JavaScript 字符串 最佳实践:在服务端直接输出 JS 对象,而不是 JSON 字符串。
备选方案:如果必须拼接,对 JSON 字符串进行 JavaScript 转义。
JSON.stringify() (前端), StringEscapeUtils.escapeEcmaScript() (后端)
JSON 字符串 -> HTML 属性值 对 JSON 字符串进行 HTML 实体转义。 StringEscapeUtils.escapeHtml4() (后端)

核心原则:

  1. 不要手动处理 JSON 内部的转义:让 Jackson/Gson 这样的库来做,它们已经为你做好了。
  2. 关注数据输出的最终位置:问题的根源通常不是 JSON 本身,而是 JSON 被嵌入的上下文(HTML 或 JavaScript)。
  3. 使用成熟的工具库:对于 HTML/JS 转义,优先使用像 org.apache.commons.text.StringEscapeUtils 或 OWASP Java Encoder 这样的库,而不是自己实现,以避免遗漏和安全漏洞。
  4. 防御性编程:始终假设任何来自用户或外部系统的数据都是不安全的,并在输出到任何上下文(Web 页面、API 等)之前进行适当的转义或净化。
分享:
扫描分享到社交APP
上一篇
下一篇