Как обрабатывать динамический JSON в модернизации?
я использую эффективную модернизацию сетевой библиотеки, но мне не удалось обработать динамический JSON, который содержит один префикс responseMessage какие изменения object случайно, тот же префикс (responseMessage) изменения в строку в некоторых случаях (динамически).
JSON формат объекта responseMessage:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage формат Json динамически изменяется на тип string:
{
"applicationType":"4",
"responseMessage":"Success"
}
проблема для меня заключается в том, что retrofit имеет встроенный JSON парсинг мы нужно назначить один POJO-объект в запрос! но REST-API, к сожалению, построен с dynamic 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 имеет значение string или object типа responseMessage, поэтому мы можем создать POJO с переменной ref с тем же именем (основы java:)) поэтому я ищу то же самое решение для dynamic JSON в Ретрофите.
Я знаю, что это очень простая работа в обычных http-клиентах с асинхронной задачей, но это не лучшая практика в REST-Api JSON парсинг! глядя на спектакль критерии всегда залп или модернизация-лучший выбор, но я не справился с динамикойJSON!
возможное решение я знаю
используйте старую задачу asyc с анализом http-клиента. :(
попробуйте убедить разработчика restapi backend.
создать пользовательский клиент дооснащения:)
9 ответов:
опоздал на вечеринку, но вы можете использовать конвертер.
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);затем вы используете статический класс, который реализует конвертер retrofit:
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.
RestClient.java
import retrofit.client.Response; public interface RestClient { @GET("/api/foo") Response getYourJson(); }ваш класс.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".
любое из возможных решений будет работать. То, что вы также можете сделать, это отправить тип возврата интерфейса API Retrofit в response. С этим ответом вы получаете тело
Inputstreamкоторый вы можете преобразовать в объект JSON и читать, как вы считаете нужным.посмотрите на: http://square.github.io/retrofit/#api-declaration - под типом объекта ответа
Обновлено
Retrofit 2 вышел сейчас и с ним некоторые изменения в документации и библиотека.
посмотрите на http://square.github.io/retrofit/#restadapter-configuration есть запрос и ответ тело объекта, который может быть использован.
принятый ответ показался мне слишком сложным, я решаю его так:
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это просто логическое условие для использования соответствующей модели.
Я знаю, что очень, очень опаздываю на вечеринку. У меня была аналогичная проблема и просто решил ее так:
public class TrackerRefResponse { private String applicationType; // Changed to Object. Works fine with String and array responses. private Object responseMessage; }Я буквально только что изменил тип объекта. Я выбрал этот подход, потому что только одно поле в ответе было динамическим (для меня мой ответ был намного сложнее), поэтому использование конвертера усложнило бы жизнь. Использовать Gson для работы с объектом оттуда, в зависимости от того, если это была строка или массив.
надеюсь, это поможет кому-то искать простой ответ :).
попробуйте пользовательскую десериализацию с помощью
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); } } }Ретрофит 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.квадрат.retrofit2: retrofit: 2.3.0'
compile ' com.квадрат.retrofit2: конвертер-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; } }@Override public void onResponse(Response<TrackerRefResponse > response) { if (response.isSuccess()) { if (response.getResponseMessage() instanceof String) { handleStringResponse(); } else { handleObjectResponse(); } } }
Если бы не было возможности изменить backend API, я бы рассмотрел следующие варианты (если Gson используется для преобразования JSON).
мы можем использовать Gson адаптеры типа создать пользовательский адаптер для
ResponseMessageтип, который динамически решает, как парсить JSON и иностранных (используя что-то вродеif (reader.peek() == JsonToken.STRING)).поместите некоторую метаинформацию, описывающую тип ответа, в заголовок HTTP и используйте ее для определения типа ответа информация должна быть передана в экземпляр Gson.
В дополнение к тому, что ты сказал -
Использовать Обратный Вызов Затем вы можете получить поля с помощью обычного метода get. Для получения более подробной информации, перейдите по документации в дсын.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
Я тоже побежал этого вопроса. но я не уверен, что это был ваш случай, (я использую 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.javapublic class Error { @SerializedName("error_title") public String errorTitle; @SerializedName("error_message") public String errorMessage; }
ValidateUser.javapublic 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класс инициализируется.
Comments