如何在改造中处理动态 JSON?
Posted
技术标签:
【中文标题】如何在改造中处理动态 JSON?【英文标题】:How to handle Dynamic JSON in Retrofit? 【发布时间】:2014-08-08 09:02:37 【问题描述】:我正在使用改造高效网络库,但我无法处理包含单个前缀 responseMessage
的动态 JSON,该前缀随机更改为 object
,相同的前缀 (responseMessage
) 在某些情况下更改为字符串 (动态)。
responseMessage的Json格式对象:
"applicationType":"1",
"responseMessage":
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
responseMessage
Json格式动态变化为字符串类型:
"applicationType":"4",
"responseMessage":"Success"
我的问题是因为改造内置了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();
);
波乔:
public class TrackerRefResponse
private String applicationType;
private String responseMessage; //String type
//private ResponseMessage responseMessage; //Object of type ResponseMessage
//Setters and Getters
在上面的代码中,POJO TrackerRefResponse.java 前缀 responseMessage 设置为字符串或 responseMessage 类型的对象,因此我们可以使用具有相同名称的 ref 变量创建 POJO(java 基础 :))所以我正在寻找相同的动态解决方案JSON
在改造中。
我知道这在具有异步任务的普通 http 客户端中非常容易,但这不是 REST-Api JSON
解析中的最佳实践!看性能Benchmarks总是Volley或Retrofit是最好的选择,但是我处理动态JSON
失败了!
我知道的可能解决方案
在 http 客户端解析中使用旧的 asyc 任务。 :(
尝试说服 RESTapi 后端开发人员。
创建自定义 Retrofit 客户端 :)
【问题讨论】:
"尝试说服 RESTapi 后端开发人员。"为我做了伎俩!哈哈! ;)(n.b:我也是后端开发人员,我要说服自己!) 【参考方案1】:聚会迟到了,但你可以使用转换器。
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);
然后你使用一个实现改造转换器的静态类:
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();
我编写了这个示例转换器,因此它以 String、Object、JsonObject 或 Map
【讨论】:
看到RestAdapter
这个例子是针对Retrofit 1的。你将如何在Retrofit 2中实现相同的转换器?
ConversionException 在 Retrofit 2 中不可用 :(【参考方案2】:
RestClient.java
import retrofit.client.Response;
public interface RestClient
@GET("/api/foo") Response getYourJson();
YourClass.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);
你必须实现“checkResponseMessage”方法。
【讨论】:
如何在 Retrofit 2 中做同样的事情? 什么是“checkResponseMessage”?【参考方案3】:使用gson-converter
尝试自定义反序列化,如下所示(Retrofit 2.0 的更新答案)
如下图创建三个模型
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;
响应消息
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;
响应字符串
public class ResponseString extends ResponseWrapper
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);
改造 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)
);
使用的库
编译'com.squareup.retrofit2:retrofit:2.3.0'
编译'com.squareup.retrofit2:converter-gson:2.3.0'
***** 上一个答案(以上答案是比较推荐的一个) *****
像这样改变你的pojo
public class TrackerRefResponse
private String applicationType;
private Object responseMessage;
public Object getResponseMessage()
return responseMessage;
public void setResponseMessage(Object responseMessage)
this.responseMessage = responseMessage;
并像这样更改改造的 onResponse
@Override
public void onResponse(Response<TrackerRefResponse > response)
if (response.isSuccess())
if (response.getResponseMessage() instanceof String)
handleStringResponse();
else
handleObjectResponse();
您也可以查看this post for more details about dynamic json parsing
【讨论】:
ResponseWrapper 类真的有必要吗?我认为它看起来非常混乱。除了层次结构中最高的对象之外,我需要一个转换器... 如果包装类令人困惑,您可以避免使用它并尝试这个freshbytelabs.com/2018/12/…【参考方案4】:您的任何可能的解决方案都会奏效。您还可以做的是将 Retrofit api 接口返回类型发送到响应。通过该响应,您将获得一个正文 Inputstream
,您可以将其转换为 JSON 对象并按照您认为合适的方式读取。
查看:http://square.github.io/retrofit/#api-declaration - 在响应对象类型下
更新
Retrofit 2 现已推出,随之而来的是对文档和库的一些更改。
看http://square.github.io/retrofit/#restadapter-configuration有可以使用的请求和响应体对象。
【讨论】:
恐怕找不到您提供的部分,有同义词吗?【参考方案5】:接受的答案对我来说似乎过于复杂,我是这样解决的:
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());
);
这只是一个示例,旨在向您展示如何使用不同的模型。
变量isEmail
只是一个布尔值,您的条件可以使用适当的模型。
【讨论】:
您能详细说明一下吗?此代码是非描述性的。 mType 是从哪里来的? @Desdroid 我简化了代码,然后用解释扩展了 不过,如果您在拨打电话之前不知道响应类型,我相信这将无济于事,问题就是这种情况。当然你可以先拿到Response Body的InputStream,读几行,确定Body是哪个Type,然后再转换。但这并不是那么简单。 我正在寻找处理不同返回类型的最佳方法。你的回答看起来很有希望,但我不确定你从哪里知道类型。这就是我想让你详细说明的原因;) 我认为它不会起作用,因为解串器会抛出异常并到达 onFailure() 而不是 onResponse()【参考方案6】:我知道我迟到了。我有一个类似的问题,只是这样解决了:
public class TrackerRefResponse
private String applicationType;
// Changed to Object. Works fine with String and array responses.
private Object responseMessage;
我实际上只是将类型更改为 Object。我选择这种方法是因为响应中只有一个字段是动态的(对我来说,我的响应要复杂得多),所以使用转换器会让生活变得困难。使用 Gson 从那里处理对象,具体取决于它是字符串还是数组值。
希望这对寻找简单答案的人有所帮助:)。
【讨论】:
【参考方案7】:如果无法更改后端 API,我会考虑以下变体(如果使用 Gson 转换 JSON)。
我们可以使用 Gson type adapters 为 ResponseMessage
类型创建一个自定义适配器,动态决定如何解析 inoming JSON(使用类似 if (reader.peek() == JsonToken.STRING)
的东西)。
将一些描述响应类型的元信息放入 HTTP 标头中,并使用它来确定必须将哪些类型信息提供给 Gson 实例。
【讨论】:
【参考方案8】:除了你所说的-
使用回调 然后,您可以使用常规 get 方法检索字段。 更多信息,请浏览 gson 的 javadoc。
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
【讨论】:
【参考方案9】:我知道我迟到了,但我只是想分享一下我的想法。我正在研究一个正在编写方法的项目。该方法使用改造从服务器获取数据。由于我公司的其他开发人员将使用此方法,因此我无法使用 POJO
类(在您的示例中为 TrackerRefResponse
类)。所以我像这样使用JsonObject
/Object
:
接口 APIService.java
public class APIService
@FormUrlEncoded
@POST
Call<JsonObject> myPostMethod(@Url String url, @Field("input") String input);
然后在我的方法中,我写了这个:
Model1 model1 = null;
Model2 model2 = null;
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();
// then do your stuff. maybe something like this
try
model1 = new Gson().fromJson(jsonObject, Model1.class);
catch(Exception x)
try
model2 = new Gson().fromJson(jsonObject, Model2.class);
catch(Exception x)
if(model1 != null) /*handle model1 */
if(model2 != null) /*handle model2*/
// rest of the code
如果您知道某个属性会告诉您它是哪种类型的响应,您可以使用 JsonObject ,读取该属性,然后像这样转换模型:
// ... retrofit codes
@Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response)
int number = jsonObject.get("applicationType").getAsInt();
if(number == 1)
model1 = new Gson().fromJson(jsonObject, Model1.class);
// ... rest of the code
您也可以使用Object
代替“JsonObject”。稍后,当你知道它是哪种响应时,也许你可以将它转换为所需的对象。
【讨论】:
这个解决方案更适合我的问题。如果一切正常,API 响应之一为 null,并在出现问题时在简单的 Json 对象中返回成功(代码 200)和错误消息。我使用 JsonNull 作为默认响应。【参考方案10】:我也遇到过这个问题。 但我不确定这是不是你的情况,(我正在使用 Retrofit2)
就我而言,我需要处理错误和成功消息。
成功
"call_id": 1,
"status": "SUCCESS",
"status_code": "SUCCESS",
"result":
"data1":
"id": "RFP2UjW7p8ggpMXzYO9tRg==",
"name": "abcdef",
"mobile_no": "96655222",
"email": ""
,
"data2": [
"no": "12345"
,
"no": "45632"
]
出错时,
"call_id": 1,
"status": "FAILED",
"status_code": "NO_RECORDS",
"error":
"error_title": "xxx",
"error_message": "details not found"
为此,我刚刚创建了另一个 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
public class Error
@SerializedName("error_title")
public String errorTitle;
@SerializedName("error_message")
public String errorMessage;
ValidateUser.java
public class ValidateUserResult
@SerializedName("auth_check")
public String authCheck;
@SerializedName("data1")
public Data1 data1;
@SerializedName("data2")
public List<Data2> data2;
在上述情况下,如果 json 上的 result
键包含 data1,data2 则 ValidateUserResult.java
会被初始化。
如果出错则 Error.java
类被初始化。
【讨论】:
【参考方案11】:看看它对我有用的其他选项:-
json
1.
"applicationType":"1",
"responseMessage":
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
2.
"applicationType":"4",
"responseMessage":
"forename":" taylor",
"dob":"17081990",
3.
"applicationType":"5",
"responseMessage":
"refNo":"3394909238490F",
"result":"Received"
Pojo 类将是:-
public class ResponseMessage
private String surname;
private String forename;
private String dob;
private String refNo;
private String result;
public void setSurname(String surname)
this.surname = surname;
public String getSurname()
return this.surname;
public void setForename(String forename)
this.forename = forename;
public String getForename()
return this.forename;
public void setDob(String dob)
this.dob = dob;
public String getDob()
return this.dob;
public void setRefNo(String refNo)
this.refNo = refNo;
public String getRefNo()
return this.refNo;
public void setResult(String result)
this.result = result;
public String getResult()
return this.result;
public class Root
private String applicationType;
private ResponseMessage responseMessage;
public void setApplicationType(String applicationType)
this.applicationType = applicationType;
public String getApplicationType()
return this.applicationType;
public void setResponseMessage(ResponseMessage responseMessage)
this.responseMessage = responseMessage;
public ResponseMessage getResponseMessage()
return this.responseMessage;
现在是最终代码
if(responseMessage.getSurname() !=null)
---do something---
if(responseMessage.getForename !=null)
----do something
if(responseMessage.getDob() !=null)
---do something---
if(responseMessage.getRefNo() !=null)
---do something---
if(responseMessage.getResult() !=null)
---do something---
【讨论】:
以上是关于如何在改造中处理动态 JSON?的主要内容,如果未能解决你的问题,请参考以下文章
如何在改造中使用原始 json 对象调用改造 @DELETE 方法?
如何使用android studio中的改造服务在请求中传递json