Я использую модифицированную эффективную сетевую библиотеку, но я не могу обрабатывать динамический 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();
}
});
}
Поджо:
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
модификации в Retrofit. Я знаю, что это очень простая работа в обычных http-клиентах с асинхронной задачей, но это не лучшая практика при JSON
разборе REST-Api ! Всегда смотрю на производительность. Тесты производительности: Volley или Retrofit - лучший выбор, но мне не удалось справиться с динамикой JSON
!
Возможное решение я знаю
Используйте старую задачу asyc с синтаксическим анализом http-клиента. :(
Попробуйте убедить бэкэнд-разработчика RESTapi.
Создайте собственный клиент модернизации :)
Ответы:
Поздно на вечеринку, но можно использовать конвертер.
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 <String, Object>. Очевидно, что не все возвращаемые типы будут работать для каждого Json, и есть определенные возможности для улучшения. Но он демонстрирует, как использовать Converter для преобразования практически любого ответа в динамический Json.
источник
RestAdapter
этот пример для Модернизированного 1. Как бы реализовать один и тот же преобразователь в Модернизированном 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.
источник
Попробуйте пользовательскую десериализацию,
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) { } });
Используемые библиотеки
***** Предыдущий ответ (приведенный выше ответ является более рекомендуемым) *****
Измените свое 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.
источник
Принятый ответ показался мне слишком сложным, я решаю его так:
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
- это просто логическое значение для вашего условия использования соответствующей модели.источник
Любое из ваших возможных решений будет работать. Что вы также можете сделать, так это отправить в ответ тип возврата интерфейса Retrofit api. С этим ответом вы получите тело,
Inputstream
которое вы можете преобразовать в объект JSON и читать по своему усмотрению.Посмотрите: http://square.github.io/retrofit/#api-declaration - в разделе ТИП ОБЪЕКТА ОТВЕТА
Обновлено
Retrofit 2 уже вышел, а вместе с ним и некоторые изменения в документации и библиотеке.
Посмотрите http://square.github.io/retrofit/#restadapter-configuration, есть объекты тела запроса и ответа, которые можно использовать.
источник
Я знаю, что очень опаздываю на вечеринку. У меня была аналогичная проблема, и я решил ее так:
public class TrackerRefResponse { private String applicationType; // Changed to Object. Works fine with String and array responses. private Object responseMessage; }
Я буквально просто перешел на тип Object. Я выбрал этот подход, потому что только одно поле в ответе было динамическим (для меня мой ответ был намного сложнее), поэтому использование конвертера усложнило бы жизнь. Использовал Gson для работы с объектом оттуда, в зависимости от того, было ли это значение String или Array.
Надеюсь, это поможет кому-то, кто ищет простой ответ :).
источник
Если бы не было возможности изменить backend API, я бы рассмотрел следующие варианты (если Gson используется для преобразования JSON).
Мы можем использовать адаптеры типа Gson для создания настраиваемого адаптера для
ResponseMessage
типа, который динамически решает, как анализировать входящий JSON (используя что-то вродеif (reader.peek() == JsonToken.STRING)
).Поместите некоторую метаинформацию, описывающую тип ответа, в заголовок HTTP и используйте ее, чтобы определить, какая информация о типе должна быть передана экземпляру Gson.
источник
В дополнение к тому, что вы сказали -
Использование обратного вызова. Затем вы можете получить поля, используя обычный метод get. Для получения дополнительной информации просмотрите javadoc gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
источник
Я знаю, что опаздываю, но я просто хочу поделиться своей мыслью. Я работал над проектом, в котором пишу метод. Метод использует дооснащение для получения данных с сервера. Поскольку другие разработчики в моей компании будут использовать этот метод, я не мог использовать
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. Позже, когда вы узнаете, какой это ответ, возможно, вы сможете преобразовать его в желаемый объект.источник
Я тоже столкнулся с этой проблемой. но я не уверен, был ли это ваш случай (я использую 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; }
в приведенном выше случае, если
result
ключ на json содержит data1, data2, тогдаValidateUserResult.java
get инициализируется. в случае ошибкиError.java
класс инициализируется.источник