Как обращаться с Dynamic JSON в Модернизации?
Я использую эффективную сетевую библиотечную модернизацию, но мне не удалось обработать Dynamic JSON, который содержит один префикс responseMessage
, который случайно изменяется на object
, тот же префикс (responseMessage
) в некоторых случаях изменяется на String (динамически).
Формат Json Объект responseMessage:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage
Формат Json динамически изменяется на строку типа:
{
"applicationType":"4",
"responseMessage":"Success"
}
Проблема для меня заключается в том, что у модификации есть встроенный синтаксический анализ JSON
, мы должны назначить один POJO на запрос! но REST-API, к сожалению, построен с динамическими ответами JSON
, префикс будет произвольно изменяться на строку на объект как в Успех (...), так и сбой (...)!
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
}
В приведенном выше коде POJO TrackerRefResponse.java префикс responseMessage установлен в строку или объект типа responseMessage, поэтому мы можем создать POJO с переменной ref с тем же именем (основы java:)), поэтому я ищу такое же решение для динамического JSON
в "Дооснащении".
Я знаю, что это очень простая работа в обычных http-клиентах с асинхронной задачей, но это не самая лучшая практика в анализе REST-Api JSON
! глядя на производительность Benchmarks, всегда Volley или Retrofit - лучший выбор, но я не справился с динамикой JSON
!
Возможное решение, которое я знаю
-
Используйте старую задачу asyc с разбором http-клиента.:(
-
Попробуйте убедить разработчика базы данных RESTapi.
-
Создать пользовательский клиент для переоснащения:)
Ответы
Ответ 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();
}
}
Я написал этот примерный конвертер, чтобы он возвращал ответ Json либо как String, Object, JsonObject или Map < Строка, Объект > . Очевидно, что не все типы возвратов будут работать для каждого Json, и есть определенная возможность для улучшения. Но он демонстрирует, как использовать конвертер для преобразования почти любого ответа на динамический Json.
Ответ 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.
Ответ 3
Любое из ваших возможных решений будет работать. То, что вы также можете сделать, это отправить возвращаемый тип интерфейса Apo Retrofit в ответ. С помощью этого ответа вы получите тело Inputstream
, которое вы можете преобразовать в объект JSON и прочитать, как вы считаете нужным.
Посмотрите: http://square.github.io/retrofit/#api-declaration - в разделе ТИП ОБЪЕКТА RESPONSE
Обновление
Retrofit 2 теперь отсутствует и с ним некоторые изменения в документации и библиотеке.
Посмотрите http://square.github.io/retrofit/#restadapter-configuration есть объект тела запроса и ответа, который можно использовать.
Ответ 4
Принятый ответ казался сложным для меня, я решаю его так:
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
является просто логической для вашего условия, чтобы использовать соответствующую модель.
Ответ 5
Попробуйте настраивать десериализацию с помощью gson-converter
как 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;
}
}
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 (пользовательский десериализатор)
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
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) {
}
});
Используемые библиотеки
compile 'com.squareup.retrofit2: retrofit: 2.3.0'
compile '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();
}
}
}
вы также можете проверить этот пост для получения дополнительной информации о динамическом разборе json
Ответ 6
Я знаю, что очень опаздываю на вечеринку. У меня была аналогичная проблема, и я просто решил ее так:
public class TrackerRefResponse {
private String applicationType;
// Changed to Object. Works fine with String and array responses.
private Object responseMessage;
}
Я буквально просто изменил тип на Object. Я выбрал этот подход, потому что только одно поле в ответе было динамическим (для меня мой ответ был более сложным), поэтому использование конвертера затруднило бы жизнь. Использовал Gson для работы с объектом оттуда, в зависимости от того, было ли это значение String или Array.
Надеюсь, это поможет кому-то найти простой ответ:).
Ответ 7
Если изменить API-интерфейс бэкэнд было невозможно, я бы рассмотрел следующие варианты (если Gson используется для преобразования JSON).
-
Мы можем использовать GSI тип адаптеров для создания настраиваемого адаптера для типа ResponseMessage
, который динамически решает, как анализировать INMONS JSON (используя что-то вроде if (reader.peek() == JsonToken.STRING)
).
-
Поместите некоторую метаинформацию, описывающую тип ответа, в HTTP-заголовок и используйте его для определения того, какая информация типа должна быть подана в экземпляр Gson.
Ответ 8
В дополнение к тому, что вы сказали -
Использовать обратный вызов
Затем вы можете получить поля, используя обычный метод get.
Для получения дополнительной информации перейдите к javadoc gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
Ответ 9
Я тоже столкнулся с этой проблемой. но я не уверен, что это ваш случай (я использую 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"
}
}
для этого я только что создал еще одну Error
POJO,
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;
}
в приведенном выше случае, если ключ result
в json содержит data1, data2, тогда инициализируется ValidateUserResult.java
. если ошибка, то класс Error.java
инициализируется.