JSON 字符串反斜杠问题
一、问题背景
在项目中,我们从 TCP 通道收到字符串消息,再通过 MQTT 转发到云端。
然而,日志中发现:
-
初始几次发送正常;
-
随后出现大量
\\
; -
云端解析报错:
Invalid url parameter
。
示例
第一次发送:
{"command":"/api/robot_status","extraData":"{\"channel\":\"rcs-app-executor\"}"}几轮之后:
{"command":"{\"command\":\"{\\\"command\\\":\\\"..."} // 出现多层转义
问题:JSON 被多次序列化,出现“字符串套字符串”。
二、JSON 与 String 的区别
类型 | 本质 | 表现形式 | 是否需要转义 |
---|---|---|---|
JSON 对象 | 结构化数据 | { "a":1 } | 否 |
JSON 字符串 | 纯文本 | "{"a":1}" | ✅ 是,需要 \" 保护内部引号 |
普通字符串 | 任意字符 | "hello" | 否 |
三、三个常见方法的区别(重点)
方法 | 含义 | 输入 | 输出 | 是否会增加反斜杠 |
---|---|---|---|---|
gson.fromJson(text, Type) | JSON → 对象 | JSON 文本 | 对象 | ❌ 否 |
gson.toJson(obj) | 对象 → JSON | 对象 | JSON 文本 | ❌ 否 |
JsonParser.parseString(text).toString() | 解析→规范化 | JSON 文本 | 干净 JSON | ❌ 否 |
🚫 gson.toJson(text) | ❌ 错误:把 JSON 当普通字符串 | JSON 文本 | "{"a":"b"}" → "{\"a\":\"b\"}" | ✅ 会! |
四、错误原因剖析
错误写法:
GlobalMqttManager.publish(ctx, topic, gson.toJson(responseMessage))
-
responseMessage
已经是"{"a":"b"}"
形式的 JSON 字符串; -
再
toJson()
一次,就相当于在外面再包一层引号; -
每一轮都套一层 → 反斜杠越来越多 → 云端无法解析。
五、正确写法(推荐两种)
✅ 方法1:JsonParser规范化(最简单)
val payload = JsonParser.parseString(responseMessage).toString() GlobalMqttManager.publish(ctx, topic, payload)
特点:
-
不再多一层引号;
-
保证传递的是合法 JSON 文本;
-
无需定义数据类。
✅ 方法2:先解析对象,再一次性序列化(标准写法)
val gson = Gson() val env = gson.fromJson(responseMessage, Envelope::class.java) val payload = gson.toJson(env) GlobalMqttManager.publish(ctx, topic, payload)
特点:
-
结构化访问字段;
-
能对内容做校验、加工;
-
fromJson → toJson
各一次,永远不会“变花”。
六、记忆口诀
fromJson
:解析文本 → 对象
toJson
:序列化对象 → 文本
parseString → toString
:清洗/规范化文本✅ 对对象用
toJson
✅ 对 JSON 字符串用
parseString().toString()
🚫 千万别对 JSON 字符串再
toJson(String)
七、总结
场景 | 正确做法 |
---|---|
发送对象 | gson.toJson(obj) |
收到 JSON 文本想解析 | gson.fromJson(text, Class) |
收到 JSON 文本想原样转发 | JsonParser.parseString(text).toString() |
❌ 千万不要 | gson.toJson(jsonText) |
八、“决策口令”
对象 → 文本:
gson.toJson(obj)
文本 → 对象:
gson.fromJson(text, Type)
文本 → 干净文本:
JsonParser.parseString(text).toString()
已经是 JSON 的 String 想转发:可以直接发;更稳的是parse→toString 后再发;绝不要
gson.toJson(text)
再发。