Estoy usando la biblioteca de red eficiente de actualización, pero no puedo manejar Dynamic JSON que contiene un prefijo único responseMessage
que cambia object
aleatoriamente, el mismo prefijo ( responseMessage
) cambia a String en algunos casos (dinámicamente).
Formato JSON Objeto de respuesta Mensaje:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
responseMessage
El formato JSON cambia dinámicamente para escribir cadena:
{
"applicationType":"4",
"responseMessage":"Success"
}
Mi problema es que, dado que la modificación tiene un JSON
análisis integrado , ¡tengo que asignar un solo POJO por solicitud! pero la REST-API, desafortunadamente, se basa en JSON
respuestas dinámicas . ¡El prefijo cambiará de una cadena a un objeto aleatoriamente en los métodos de éxito (...) y fracaso (...) !
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
}
En el código anterior, POJO TrackerRefResponse.java prefix responseMessage se establece en una cadena u objeto de tipo responseMessage, por lo que podemos crear el POJO con la variable ref con el mismo nombre (conceptos básicos de Java :)), así que estoy buscando la misma solución para la dinámica JSON
en Retrofit. Sé que este es un trabajo muy fácil en clientes http normales con tareas asíncronas, ¡pero no es la mejor práctica en el JSON
análisis REST-Api ! mirando los puntos de referencia de rendimiento siempre Volley o Retrofit es la mejor opción, ¡pero no he logrado manejar la dinámica JSON
!
Posible solución lo sé
Utilice la tarea asyc antigua con análisis de cliente http. :(
Intente convencer al desarrollador backend de RESTapi.
Cree un cliente de Retrofit personalizado :)
Respuestas:
Tarde para la fiesta, pero puedes usar un convertidor.
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);
Luego usa una clase estática que implementa el convertidor de 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(); } }
He escrito este convertidor de muestra para que devuelva la respuesta Json como String, Object, JsonObject o Map <String, Object>. Obviamente, no todos los tipos de devolución funcionarán para todos los Json, y es seguro que hay margen de mejora. Pero demuestra cómo usar un convertidor para convertir casi cualquier respuesta a Json dinámico.
fuente
RestAdapter
este ejemplo es para Retrofit 1. ¿Cómo implementaría el mismo convertidor en Retrofit 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); }
Debe implementar el método "checkResponseMessage".
fuente
Pruebe la deserialización personalizada usando
gson-converter
lo siguiente (respuesta actualizada para Retrofit 2.0)Cree tres modelos como se muestra a continuación
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 (deserializador personalizado)
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); } } }
Implementación de 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) { } });
Bibliotecas utilizadas
***** Respuesta anterior (la respuesta anterior es más recomendada) *****
Cambia tu pojo así
public class TrackerRefResponse { private String applicationType; private Object responseMessage; public Object getResponseMessage() { return responseMessage; } public void setResponseMessage(Object responseMessage) { this.responseMessage = responseMessage; } }
y cambiar el onResponse de retrofit así
@Override public void onResponse(Response<TrackerRefResponse > response) { if (response.isSuccess()) { if (response.getResponseMessage() instanceof String) { handleStringResponse(); } else { handleObjectResponse(); } } }
también puede consultar esta publicación para obtener más detalles sobre el análisis dinámico de json
fuente
La respuesta aceptada me pareció demasiado complicada, la resuelvo de esta manera:
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()); } });
Este es solo un ejemplo en un intento de mostrarle cómo puede usar un modelo diferente.
La variable
isEmail
es solo un booleano para que su condición use el modelo apropiado.fuente
Cualquiera de sus posibles soluciones funcionará. Lo que también puede hacer es enviar el tipo de retorno de la interfaz de la API de Retrofit a la respuesta. Con esa respuesta, obtiene un cuerpo
Inputstream
que puede convertir en un objeto JSON y leerlo como mejor le parezca.Mire: http://square.github.io/retrofit/#api-declaration - bajo TIPO DE OBJETO DE RESPUESTA
Actualizado
Retrofit 2 ya está disponible y con algunos cambios en la documentación y la biblioteca.
Mire http://square.github.io/retrofit/#restadapter-configuration, hay un objeto de cuerpo de solicitud y respuesta que se puede usar.
fuente
Sé que llego muy, muy tarde a la fiesta. Tuve un problema similar y lo resolví así:
public class TrackerRefResponse { private String applicationType; // Changed to Object. Works fine with String and array responses. private Object responseMessage; }
Literalmente acabo de cambiar para escribir a Objeto. Elegí este enfoque porque solo un campo en la respuesta era dinámico (para mí, mi respuesta fue mucho más complicada), por lo que usar un convertidor me habría complicado la vida. Usé Gson para trabajar con el objeto desde allí, dependiendo de si era un valor de cadena o matriz.
Espero que esto ayude a alguien que busque una respuesta simple :).
fuente
Si no fuera posible cambiar la API de backend, consideraría las siguientes variantes (si se usa Gson para convertir JSON).
Podemos usar adaptadores de tipo Gson para crear un adaptador personalizado para el
ResponseMessage
tipo que decide dinámicamente cómo analizar el JSON inoming (usando algo comoif (reader.peek() == JsonToken.STRING)
).Coloque algo de metainformación que describa el tipo de respuesta en un encabezado HTTP y utilícelo para determinar qué tipo de información debe enviarse a la instancia de Gson.
fuente
Además de lo que dijiste -
Use Callback Luego puede recuperar los campos usando el método get normal. Para obtener más información, consulte el javadoc de gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
fuente
Yo también corrí de este problema. pero no estoy seguro de si este fue tu caso (estoy usando Retrofit2)
en mi caso, necesito manejar los mensajes de error y éxito.
Sobre el éxito
{ "call_id": 1, "status": "SUCCESS", "status_code": "SUCCESS", "result": { "data1": { "id": "RFP2UjW7p8ggpMXzYO9tRg==", "name": "abcdef", "mobile_no": "96655222", "email": "" }, "data2": [ { "no": "12345" }, { "no": "45632" } ] } }
En caso de error,
{ "call_id": 1, "status": "FAILED", "status_code": "NO_RECORDS", "error": { "error_title": "xxx", "error_message": "details not found" } }
para esto acabo de crear otro 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; }
en el caso anterior, si la
result
clave en json contiene data1, data2, entoncesValidateUserResult.java
se inicializa. si hay un error, laError.java
clase se inicializa.fuente
Sé que llego tarde, pero solo quiero compartir mi pensamiento. Estaba trabajando en un proyecto en el que estoy escribiendo un método. El método utiliza modernización para obtener datos del servidor. Dado que otros desarrolladores de mi empresa usarán este método, no podría usar una
POJO
clase (en su ejemplo, laTrackerRefResponse
clase). Entonces uséJsonObject
/Object
gusta esto:interfaz APIService.java
public class APIService{ @FormUrlEncoded @POST Call<JsonObject> myPostMethod(@Url String url, @Field("input") String input); }
Luego, en mi método, escribí esto:
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 }
También puede utilizar en
Object
lugar de 'JsonObject`. Más tarde, cuando sepa qué tipo de respuesta es, tal vez pueda convertir esto en el objeto deseado.fuente