You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit2+Gson发POST请求体带额外引号无法解析JSON如何解决

问题场景

在Android模拟器环境调用Java编写的RestFul API,接口支持接收纯文本、JSON格式入参,返回JSON响应;客户端采用Retrofit2搭配GsonConverterFactory发起网络请求,用Gson做字段序列化/解析,未定义POJO类,仅通过HashMap存储登录参数后转换为JSON字符串,本地打印生成的JSON格式正常:
{"password":"K16073","userid":"K16073"}

客户端实现代码如下:

/* Android code */
Gson gson = new GsonBuilder()
                .setLenient()
                .create();

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(myRFReceivingApis.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

 Map<String, String> userData = new HashMap<>();
 userData.put("userid",edtTxtUserId.getText().toString());
 userData.put("password",editTxtPassword.getText().toString());
 Gson gson = new Gson();
 System.out.println(" generated gson " +gson.toJson(userData));
 call = ApiClient.getInstance().getMyApi().callLogin(gson.toJson(userData));
 call.enqueue(new Callback<Object>() {
            @Override
            public void onResponse(Call<Object> call, Response<Object> response) {
                textViewResp.setText(response.body().toString());
      :
/* End of Android code */

实际请求到服务端时,收到的字符串被额外包裹了一层双引号,打印结果为:
"{\"password\":\"K16073\",\"userid\":\"K16073\"}"
直接解析会抛出java.lang.IllegalStateException: Not a JSON Object异常;尝试手动通过substring移除首尾引号后,又抛出com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Expected name at line 1 column 2 path $异常,始终无法拿到合法JSON结构。

服务端实现代码如下:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes({MediaType.TEXT_PLAIN, MediaType.APPLICATION_JSON})
@Path("login")
public String RFLoginNew(String jsonString) {
    String result = jsonString.substring(1, jsonString.length() - 1);
    System.out.println(" Json String  "+result);
    // tried using JsonParser  -- line 1 below
    JsonObject o = JsonParser.parseString(result).getAsJsonObject();
    // tried using Gson gson.fromJson
    Gson gson = new Gson();
    JsonElement element = gson.fromJson (result, JsonElement.class); //Converts the json string to JsonElement without POJO 
    JsonObject jsonObj = element.getAsJsonObject(); //Converting JsonElement to JsonObject
    // --line 2 below
    System.out.println("  RFLoginNew struser "+ jsonObj.get("userid").getAsString());
根因分析

问题来自双重序列化:提前把HashMap转成了JSON字符串,再把这个字符串作为参数传给Retrofit的接口方法时,GsonConverterFactory会默认将传入的String类型值再次做一次JSON序列化——把原本的JSON对象字符串转成了JSON标准的字符串值,因此才会多出外层双引号,字符串内部的双引号也被自动加上了转义符\。
手动截取首尾引号无法解决问题,因为截完的字符串里仍然保留了转义符,不符合JSON语法规范,自然会抛出解析异常。

修复方案

优先从客户端修复根源问题,二选一即可:

  • 方案1(最简便):不要提前将HashMap转为JSON字符串,直接把HashMap对象作为请求参数传给Retrofit接口,让GsonConverterFactory自动完成一次序列化即可,不会出现双重转义。
    注意Retrofit的接口方法需要给参数加上@Body注解,参数类型声明为Map<String, String>,修改请求调用代码:
    // 去掉gson.toJson转换,直接传HashMap对象
    call = ApiClient.getInstance().getMyApi().callLogin(userData);
    
  • 方案2:如果业务要求必须传提前序列化好的JSON字符串,不要让GsonConverterFactory处理该参数,将参数类型改为RequestBody,直接构造JSON格式的请求体传入,避免二次序列化:
    String jsonStr = gson.toJson(userData);
    RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonStr);
    call = ApiClient.getInstance().getMyApi().callLogin(requestBody);
    

如果暂时无法修改客户端,可在服务端做兼容处理:不要手动截取字符串,先将收到的带转义的内容反序列化为普通字符串(自动处理转义符、去掉外层引号),再解析为JsonObject:

Gson gson = new Gson();
// 第一步:将JSON字符串值反序列化为普通Java字符串,自动去除转义和外层引号
String realJson = gson.fromJson(jsonString, String.class);
// 第二步:再解析为JsonObject
JsonObject jsonObj = JsonParser.parseString(realJson).getAsJsonObject();

内容的提问来源于stack exchange,提问作者Rache

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 13:06:08