【问题标题】:Android - Retrofit Gson- How to parse JSON String to object at a JSON key in JSON response?Android - Retrofit Gson- 如何在 JSON 响应中将 JSON 字符串解析为 JSON 键处的对象?
【发布时间】:2016-09-06 07:27:09
【问题描述】:

这是我的 JSON 响应:

{
      "id": 2,
      "name": "Test",
      "content": "{\"type\": \"status\", \"text\": \"Lorem ipsum dummy text.\", \"id\": 1}"
}

这些是模型结构:

class TestModel {
    public int id;
    public String name;
    public Content content;
}

class Content {
    public int id;
    public String status;
    public String text;
}

我想使用 Retrofit 和 GsonConvertor 将内容的值直接解析到我的内容模型对象中。但目前,我将其解析为字符串值,而不是使用 Gson.fromJson() 转换为我的内容模型对象。有什么解决方案可以达到我的预期效果吗?

当我使用 GsonConverterFactory 解析它时,Retrofit 在 onFailure 方法中提供回调,但有此异常:

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 4 column 19 path $.data[0].content

【问题讨论】:

  • 在retrofit中直接传模型类
  • @ColdFire:我正在这样做,但我想将字符串值直接转换为我的内容模型。作为回应,我在内容键上得到了字符串值。
  • 为什么您的响应中的“内容”值是一个字符串?这不遵循 JSON 标准,这就是为什么将 GsonConverterFactory 添加到您的 Retrofit 实例不会给您想要的结果。正确的 JSON 格式将“内容”作为 JSON 对象返回,而不是字符串。您对 JSON 响应有任何控制权吗?
  • @MA13:和布莱恩一起去。

标签: android json gson retrofit2


【解决方案1】:

问题在于 JSON 响应,它不在 correct JSON format 中。 "content" 字段应该是一个对象,而不是字符串:

{
    "id": 2,
    "name": "Test",
    "content": {
        "type": "status",
        "text": "Lorem ipsum dummy text.",
        "id": 1
    }
}

这将允许带有GsonConverterFactorygson.fromJson(response, TestModel.class)RetroFit 将您的响应正确解析为相应的对象。


当然,这仅适用于您能够更改收到的 JSON 响应的情况。如果不是,首先确保 控制响应的人知道他们这样做错误。如果没有任何变化,那么您应该可以通过将 TestModel 中的 content 更改为 String 来解决此问题:

class TestModel {
    public int id;
    public String name;
    public String content;
}

class Content {
    public int id;
    public String type;
    public String text;
}

然后分别解析每个对象:

TestModel testModel = gson.fromJson(response, TestModel.class);
Content content = gson.fromJson(testModel.content, Content.class);

如果无法更改响应,另一种选择是为您的Content 对象创建一个TypeAdapter

public class ContentAdapter extends TypeAdapter<Content> {

    @Override
    public void write(JsonWriter out, Content value) throws IOException {
        // TODO: Writer implementation
    }

    @Override
    public Content read(JsonReader in) throws IOException {
        if(in.peek() != JsonToken.NULL) {
            return fromJson(in.nextString());
        } else {
            in.nextNull();
            return null;
        }
    }

}

然后将TypeAdapter 添加到您的 GSON 实现中:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Content.class, new ContentAdapter()).create();

【讨论】:

  • 响应正确。服务器端的人不想为Content 创建另一个表。所以,他以字符串格式存储这个值。这是主要问题,内容键的值是 JSON 字符串。现在,我正在将此字符串转换为我的 Content 对象,例如Content content = gson.fromJson(testModel.content, Content.class);,但我希望它在改造响应解析中由 GsonConverterFactory 转换。我不想使用gson.fromJson() 转换它。
  • @MA13 JSON 格式肯定正确。即使“内容”存储为string,该string 也应序列化为JSON 对象,而不是string。我不知道响应 API 是用什么语言编写的,但假设是 PHP;服务器端应该在将json_decode(content) 传递给最终被编码的数组之前调用它。
  • 我无法对响应做任何事情。我想要 Android 端的解决方案或 hack,
  • @MA13 如果无法更改响应,解决方法是我休息后发布的。您必须单独反序列化 Content 对象。您还可以使用您的Gson 对象为Content 对象注册TypeAdapter,它正在做同样的事情,但现在Gson 会自动完成。
  • @YasinKaçmaz 你是对的,它是完全有效 JSON,但这并不意味着它是正确 JSON。我要说的是“内容”字段不是 JSON 对象,它是一个字符串。对于 JSON 解析器(例如 GSON)要生成 object,它必须接收 JSON 对象,不是字符串。
【解决方案2】:

希望你使用的是改造版本 2.1.0,在这个版本中你可以选择你选择的解析器,但是在之前的版本中 Gson 是内置的解析器。我假设您需要使用 Gson。为此,您必须将converterFactory 设置为GsonConverterFactory,示例如下。

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

这会将 Gson 添加为您的 json 解析器。

查看this 教程。另请阅读文档here

不要忘记在你的 gradle 中添加这个

compile 'com.squareup.retrofit2:converter-gson:2.0.2'

【讨论】:

  • 是的,我正在使用改造版本 2.1.0 和 GsonConverterFactory。它将内容键值作为字符串返回,而不是我的内容模型对象。当我过去解析我的内容模型对象时,它会给出一个异常。我已经更新了我的问题,但有例外。顺便说一句,为什么在使用 'com.squareup.retrofit2:converter-gson:2.0.2' 时添加 'com.google.code.gson:gson:2.6.2' 作为依赖项?
  • 也粘贴你的服务器响应
  • 响应无异常还是有异常?
  • 我已经发布了正常回复。你还需要知道什么?
  • 好吧,我可以看到你的模型中有变量状态,但实际的 json 键是类型,无论如何这不是问题
【解决方案3】:
    import java.util.ArrayList;
    import java.util.List;
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;

    public class Parser {
        public static List<TestModel> JsonParser(String JsonString) {
            try {
                JSONArray array = new JSONArray(content);
                List<TestModel > arrayList = new ArrayList();
                for (int i = 0; i < array.length(); i++) {
                    JSONObject obj = array.getJSONObject(i);
                    TestModel  model = new TestModel();
                    model.setId(Integer.parseInt(obj.getString("id")));
                    model.setName(obj.getString("name"));


    JSONArray contentArray = newJSONArray(obj.getString("content")));
                        JSONObject obj1 = contentArray.getJSONObject(1);
                        Content content = new Content();

                        content.setId(Integer.parseInt(obj1.getString("id")));
                        content.setStatus(obj1.getString("status"));
                        content.setText(obj1.getString("text"));


                    model.setContent(content);
                    arrayList.add(model);
                }
                return arrayList;
            } catch (JSONException e) {
                e.printStackTrace();
                return null;
            }
        }
    }
   class TestModel {
//Generate Setter Getter for all properties
    public int id;
    public String name;
    public Content content;
}

class Content {
//Generate Setter Getter for all properties
    public int id;
    public String status;
    public String text;
}

【讨论】:

  • 我不想要这种机制。我正在使用 GsonConverterFactory 进行改造。所以,我需要一种同时使用这两个库的机制,而不是直接使用 JsonObject 或 JsonArray 类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多