Android 如何在 Retrofit 中处理动态 JSON?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24279245/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to handle Dynamic JSON in Retrofit?
提问by LOG_TAG
I am using the retrofit efficient networking library, but I am unable to handle Dynamic JSON which contains single prefix responseMessage
which changes to object
randomly, the same prefix ( responseMessage
) changes to String in some cases (dynamically).
我现在用的是改装高效的网络图书馆,但我无法处理动态JSON包含单独的前缀, responseMessage
其变为object
随机,相同的前缀(responseMessage
)在某些情况下(动态)更改为字符串。
Json format Object of responseMessage:
responseMessage 的 Json 格式对象:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage
Json format dynamically changes to type string:
responseMessage
Json 格式动态更改为字符串类型:
{
"applicationType":"4",
"responseMessage":"Success"
}
My problem is since retrofit has built-in JSON
parsing, I have to assign single POJO per request! but the REST-API unfortunately, is built on dynamic JSON
responses. The prefix will change from string to object randomly in both success(...)and failure(...)methods!
我的问题是因为改造有内置JSON
解析,我必须为每个请求分配一个 POJO!但不幸的是,REST-API 是建立在动态JSON
响应之上的。在success(...)和failure(...)方法中,前缀将从字符串随机更改为对象!
void doTrackRef(Map<String, String> paramsref2) {
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint("http://192.168.100.44/RestDemo").build();
TrackerRefRequest userref = restAdapter.create(TrackerRefRequest.class);
userref.login(paramsref2,
new Callback<TrackerRefResponse>() {
@Override
public void success(
TrackerRefResponse trackdetailresponse,
Response response) {
Toast.makeText(TrackerActivity.this, "Success",
Toast.LENGTH_SHORT).show();
}
@Override
public void failure(RetrofitError retrofitError) {
Toast.makeText(TrackerActivity.this, "No internet",
Toast.LENGTH_SHORT).show();
}
});
}
Pojo:
波乔:
public class TrackerRefResponse {
private String applicationType;
private String responseMessage; //String type
//private ResponseMessage responseMessage; //Object of type ResponseMessage
//Setters and Getters
}
In above code POJO TrackerRefResponse.java prefix responseMessage is set to string or object of type responseMessage , so we can create the POJO with ref variable with same name (java basics :) ) so I'm looking for same solution for dynamic JSON
in Retrofit.
I know this is very easy job in normal http clients with async task, but it's not the best practice in the REST-Api JSON
parsing! looking at the performance Benchmarksalways Volley or Retrofit is the best choice, but I'm failed handle dynamic JSON
!
在上面的代码中,POJO TrackerRefResponse.java 前缀 responseMessage 设置为字符串或 responseMessage 类型的对象,因此我们可以创建具有相同名称的 ref 变量的 POJO(Java 基础知识 :)),因此我正在寻找与JSON
Retrofit 中的动态相同的解决方案。我知道这在具有异步任务的普通 http 客户端中非常容易,但这不是 REST-ApiJSON
解析中的最佳实践!看性能基准测试总是 Volley 或 Retrofit 是最好的选择,但我没有处理动态JSON
!
Possible solution I Know
我知道的可能解决方案
Use old asyc task with http client parsing. :(
Try to convince the RESTapi backend developer.
Create custom Retrofit client :)
将旧的 asyc 任务与 http 客户端解析一起使用。:(
尝试说服 RESTapi 后端开发人员。
创建自定义改造客户端:)
采纳答案by Oliver Hausler
Late to the party, but you can use a converter.
聚会迟到,但您可以使用转换器。
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://graph.facebook.com")
.setConverter(new DynamicJsonConverter()) // set your static class as converter here
.build();
api = restAdapter.create(FacebookApi.class);
Then you use a static class which implements retrofit's Converter:
然后你使用一个静态类来实现改造的转换器:
static class DynamicJsonConverter implements Converter {
@Override public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
try {
InputStream in = typedInput.in(); // convert the typedInput to String
String string = fromStream(in);
in.close(); // we are responsible to close the InputStream after use
if (String.class.equals(type)) {
return string;
} else {
return new Gson().fromJson(string, type); // convert to the supplied type, typically Object, JsonObject or Map<String, Object>
}
} catch (Exception e) { // a lot may happen here, whatever happens
throw new ConversionException(e); // wrap it into ConversionException so retrofit can process it
}
}
@Override public TypedOutput toBody(Object object) { // not required
return null;
}
private static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append("\r\n");
}
return out.toString();
}
}
I have written this sample converter so it returns the Json response either as String, Object, JsonObject or Map< String, Object >. Obviously not all return types will work for every Json, and there is sure room for improvement. But it demonstrates how to use a Converter to convert almost any response to dynamic Json.
我编写了这个示例转换器,因此它以 String、Object、JsonObject 或 Map<String,Object> 的形式返回 Json 响应。显然,并非所有返回类型都适用于每个 Json,并且肯定有改进的余地。但它演示了如何使用 Converter 将几乎所有响应转换为动态 Json。
回答by Yuki Yoshida
RestClient.java
客户端客户端
import retrofit.client.Response;
public interface RestClient {
@GET("/api/foo") Response getYourJson();
}
YourClass.java
你的类.java
RestClient restClient;
// create your restClient
Response response = restClient.getYourJson();
Gson gson = new Gson();
String json = response.getBody().toString();
if (checkResponseMessage(json)) {
Pojo1 pojo1 = gson.fromJson(json, Pojo1.class);
} else {
Pojo2 pojo2 = gson.fromJson(json, Pojo2.class);
}
You must implement "checkResponseMessage" method.
您必须实现“checkResponseMessage”方法。
回答by Navneet Krishna
Try custom deserialisation using gson-converter
as below(updated answer for Retrofit 2.0)
尝试使用gson-converter
如下自定义反序列化(Retrofit 2.0 的更新答案)
Create three models as shown below
创建三个模型如下图
ResponseWrapper
响应包装器
public class ResponseWrapper {
@SerializedName("applicationType")
@Expose
private String applicationType;
@SerializedName("responseMessage")
@Expose
private Object responseMessage;
public String getApplicationType() {
return applicationType;
}
public void setApplicationType(String applicationType) {
this.applicationType = applicationType;
}
public Object getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(Object responseMessage) {
this.responseMessage = responseMessage;
}
}
ResponseMessage
响应消息
public class ResponseMessage extends ResponseWrapper {
@SerializedName("surname")
@Expose
private String surname;
@SerializedName("forename")
@Expose
private String forename;
@SerializedName("dob")
@Expose
private String dob;
@SerializedName("refNo")
@Expose
private String refNo;
@SerializedName("result")
@Expose
private String result;
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getForename() {
return forename;
}
public void setForename(String forename) {
this.forename = forename;
}
public String getDob() {
return dob;
}
public void setDob(String dob) {
this.dob = dob;
}
public String getRefNo() {
return refNo;
}
public void setRefNo(String refNo) {
this.refNo = refNo;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
}
ResponseString
响应字符串
public class ResponseString extends ResponseWrapper {
}
UserResponseDeserializer(custom deserialiser)
UserResponseDeserializer(自定义反序列化器)
public class UserResponseDeserializer implements JsonDeserializer<ResponseWrapper> {
@Override
public ResponseWrapper deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (((JsonObject) json).get("responseMessage") instanceof JsonObject){
return new Gson().fromJson(json, ResponseMessage.class);
} else {
return new Gson().fromJson(json, ResponseString.class);
}
}
}
Retrofit 2.0 Implementation
改造 2.0 实施
Gson userDeserializer = new GsonBuilder().setLenient().registerTypeAdapter(ResponseWrapper.class, new UserResponseDeserializer()).create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("base_url")
.addConverterFactory(GsonConverterFactory.create(userDeserializer))
.build();
UserService request = retrofit.create(UserService.class);
Call<ResponseWrapper> call1=request.listAllUsers();
call1.enqueue(new Callback<ResponseWrapper>() {
@Override
public void onResponse(Call<ResponseWrapper> call, Response<ResponseWrapper> response) {
ResponseWrapper responseWrapper=response.body();
Log.i("DYNAMIC RESPONSE", String.valueOf(response.body().getResponseMessage()));
}
@Override
public void onFailure(Call<ResponseWrapper> call, Throwable t) {
}
});
Libraries Used
使用的库
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
编译 'com.squareup.retrofit2:retrofit:2.3.0'
编译 'com.squareup.retrofit2:converter-gson:2.3.0'
***** Previous Answer (above answer is more recommended one)*****
***** 上一个答案(以上答案比较推荐)*****
Change your pojo like this
像这样改变你的pojo
public class TrackerRefResponse {
private String applicationType;
private Object responseMessage;
public Object getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(Object responseMessage) {
this.responseMessage = responseMessage;
}
}
and change retrofit's onResponse like this
并像这样更改改造的 onResponse
@Override
public void onResponse(Response<TrackerRefResponse > response) {
if (response.isSuccess()) {
if (response.getResponseMessage() instanceof String)
{
handleStringResponse();
}
else
{
handleObjectResponse();
}
}
}
you may also check this post for more details about dynamic json parsing
回答by meda
The accepted answer seemed over complicated for me, I solve it this way:
接受的答案对我来说似乎过于复杂,我是这样解决的:
Call<ResponseBody> call = client.request(params);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response) {
if (response.isSuccess()) {
Gson gson = new Gson();
ResponseBody repsonseBody = response.body().string();
if (isEmail) {
EmailReport reports = gson.fromJson(responseBody, EmailReport.class);
} else{
PhoneReport reports = gson.fromJson(repsonseBody, PhoneReport.class);
}
}
}
@Override
public void onFailure(Throwable t) {
Log.e(LOG_TAG, "message =" + t.getMessage());
}
});
This is just an example in attempt to show you how you can use different model.
这只是一个示例,试图向您展示如何使用不同的模型。
The variable isEmail
is just a boolean for your condition to use the appropriate model.
该变量isEmail
只是一个布尔值,用于您的条件使用适当的模型。
回答by Justin Slade
Any of your possible solutions will work. What you can also do is send the Retrofit api interface return type to response. With that response you get a body Inputstream
which you can convert to a JSON Object and read as you see fit.
您的任何可能的解决方案都将起作用。您还可以做的是将 Retrofit api 接口返回类型发送到响应。通过该响应,您将获得一个正文Inputstream
,您可以将其转换为 JSON 对象并根据需要阅读。
Look at: http://square.github.io/retrofit/#api-declaration- under RESPONSE OBJECT TYPE
查看:http: //square.github.io/retrofit/#api-declaration- 在 RESPONSE OBJECT TYPE 下
Updated
更新
Retrofit 2 is out now and with it some changes to the documentation and library.
Retrofit 2 现已发布,并且对文档和库进行了一些更改。
Look at http://square.github.io/retrofit/#restadapter-configurationthere are request and response body object that can be used.
查看http://square.github.io/retrofit/#restadapter-configuration有可以使用的请求和响应主体对象。
回答by Lunchbox
I know I am very very late to the party. I had a similar issue and just solved it like this:
我知道我参加聚会已经很晚了。我有一个类似的问题,只是这样解决的:
public class TrackerRefResponse {
private String applicationType;
// Changed to Object. Works fine with String and array responses.
private Object responseMessage;
}
I literally just changed to type to Object. I chose this approach because only one field in the response was dynamic (for me, my response was way more complicated), so using a converter would have made life difficult. Used Gson to work with the Object from there, depending on if it was a String or Array value.
我实际上只是更改为输入对象。我选择这种方法是因为响应中只有一个字段是动态的(对我来说,我的响应要复杂得多),因此使用转换器会使生活变得困难。使用 Gson 从那里处理对象,具体取决于它是字符串值还是数组值。
Hope this helps someone looking for a simple answer :).
希望这可以帮助寻找简单答案的人:)。
回答by Roman Mazur
If it was not possible to change the backend API, I would consider the following variants (if Gson is used to convert JSON).
如果无法更改后端 API,我会考虑以下变体(如果使用 Gson 转换 JSON)。
We can use Gson type adaptersto create a custom adapter for
ResponseMessage
type that dynamically decides how to parse the inoming JSON (using something likeif (reader.peek() == JsonToken.STRING)
).Put some meta information describing the response type into an HTTP header and use it to determine what type information must be fed to Gson instance.
我们可以使用 Gson类型适配器为
ResponseMessage
动态决定如何解析传入 JSON 的类型创建自定义适配器(使用类似if (reader.peek() == JsonToken.STRING)
)。将一些描述响应类型的元信息放入 HTTP 标头中,并使用它来确定必须将哪些类型信息提供给 Gson 实例。
回答by Sugesh
In addition to what you told -
除了你说的——
Use Callback Then you can retrieve the fields using regular get method. For more information, go through the javadoc of gson.
使用回调然后您可以使用常规 get 方法检索字段。有关更多信息,请参阅 gson 的 javadoc。
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
回答by Akhil T Mohan
I too ran of this issue. but i am not sure if this was your case , (i am using Retrofit2)
我也跑了这个问题。但我不确定这是否是您的情况,(我正在使用 Retrofit2)
on my case i need to handle error, and success messages.
就我而言,我需要处理错误和成功消息。
On Success
成功
{
"call_id": 1,
"status": "SUCCESS",
"status_code": "SUCCESS",
"result": {
"data1": {
"id": "RFP2UjW7p8ggpMXzYO9tRg==",
"name": "abcdef",
"mobile_no": "96655222",
"email": ""
},
"data2": [
{
"no": "12345"
},
{
"no": "45632"
}
]
}
}
On Error,
出错时,
{
"call_id": 1,
"status": "FAILED",
"status_code": "NO_RECORDS",
"error": {
"error_title": "xxx",
"error_message": "details not found"
}
}
for this i just created another POJO Error
,
为此,我刚刚创建了另一个 POJO Error
,
public class ValidateUserResponse {
@SerializedName("call_id")
public String callId;
@SerializedName("status")
public String status;
@SerializedName("status_code")
public String statusCode;
@SerializedName("result")
public ValidateUserResult result;
@SerializedName("error")
public Error error;
}
Error.java
Error.java
public class Error {
@SerializedName("error_title")
public String errorTitle;
@SerializedName("error_message")
public String errorMessage;
}
ValidateUser.java
ValidateUser.java
public class ValidateUserResult {
@SerializedName("auth_check")
public String authCheck;
@SerializedName("data1")
public Data1 data1;
@SerializedName("data2")
public List<Data2> data2;
}
in the above case if the result
key on json contains data1,data2 then the ValidateUserResult.java
get initialised.
if error then the Error.java
class get initialized.
在上述情况下,如果result
json 上的键包含 data1,data2 则ValidateUserResult.java
初始化。如果出错,则Error.java
类被初始化。
回答by Qazi Fahim Farhan
I know I am late, but I just want to share my thought. I was working on a project where I am writing a method. The method uses retrofit to get data from server. Since other developers in my company will use this method, I could not use a POJO
class (in your example, the TrackerRefResponse
class). So I used JsonObject
/ Object
like this:
我知道我迟到了,但我只想分享我的想法。我正在做一个我正在编写方法的项目。该方法使用改造从服务器获取数据。由于我公司的其他开发人员将使用此方法,因此我无法使用POJO
类(在您的示例中为TrackerRefResponse
类)。所以我使用JsonObject
/Object
像这样:
interface APIService.java
接口APIService.java
public class APIService{
@FormUrlEncoded
@POST
Call<JsonObject> myPostMethod(@Url String url, @Field("input") String input);
}
Then in my method, I wrote this:
然后在我的方法中,我写了这个:
Call<JsonObject> call = RetrofitClient.getAPIService().establishUserSession(post_request_url, someParameter);
call.enqueue(new Callback<JsonObject>() {
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
JsonObject jsonObject = response.body();
String jsonString = response.body().toString();
// then do your stuff. maybe cast to object using a factory pattern
}
// rest of the code
}
You can also use Object
instead of 'JsonObject`. Later, when you will know which kind of response it is, maybe you can cast this into desired object.
您也可以使用Object
'JsonObject` 代替。稍后,当您知道它是哪种响应时,也许您可以将其转换为所需的对象。