android - ejemplo - ¿Cómo manejar Dynamic JSON en Retrofit?
retrofit gson converter (8)
Estoy utilizando la actualización eficiente de la biblioteca de red, pero no pude manejar Dynamic JSON que contiene el prefijo simple responseMessage
que cambia al object
aleatoriamente, el mismo prefijo ( responseMessage
) cambia a String en algunos casos (dinámicamente).
Formato Json Objeto de responseMessage:
{
"applicationType":"1",
"responseMessage":{
"surname":"Jhon",
"forename":" taylor",
"dob":"17081990",
"refNo":"3394909238490F",
"result":"Received"
}
}
El formato responseMessage
Json cambia dinámicamente a tipo string:
{
"applicationType":"4",
"responseMessage":"Success"
}
El problema para mí es que, dado que la actualización tiene un análisis JSON
integrado, ¡tenemos que asignar un solo POJO por solicitud! pero la REST-API desafortunadamente está construida con respuestas JSON
dinámicas, el prefijo cambiará a cadena para objetar aleatoriamente en los métodos de éxito (...) y falla (...) .
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, prefijo responseMessage se establece en cadena u objeto de tipo responseMessage, por lo que podemos crear el POJO con la variable ref con el mismo nombre (java basics :)), así que estoy buscando la misma solución para JSON
dinámico 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 análisis REST-Api JSON
! mirando el rendimiento Benchmarks siempre Volley o Retrofit es la mejor opción, ¡pero no pude manejar el JSON
dinámico!
Posible solución que yo sé
Use la antigua tarea asyc con el análisis del cliente http. :(
Intenta convencer al desarrollador de back-end de RESTapi.
Crear cliente de modificación personalizado :)
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, vaya a través del javadoc de gson.
http://google-gson.googlecode.com/svn/tags/1.2.3/docs/javadocs/com/google/gson/JsonObject.html
Cualquiera de sus posibles soluciones funcionará. Lo que también puede hacer es enviar el tipo de retorno de la interfaz de la API Retrofit a la respuesta. Con esa respuesta obtienes un cuerpo Inputstream
que puedes convertir a un Objeto JSON y leer como mejor te parezca.
Mira: http://square.github.io/retrofit/#api-declaration - en RESPONSE OBJECT TYPE
Actualizado
Retrofit 2 ya salió y con él algunos cambios en la documentación y la biblioteca.
Mire http://square.github.io/retrofit/#restadapter-configuration hay un objeto cuerpo de solicitud y respuesta que se puede usar.
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());
}
});
Esto 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.
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".
Sé que llegué 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 cambié para escribir a Object. Elegí este enfoque porque solo un campo de la respuesta era dinámico (para mí, mi respuesta era mucho más complicada), por lo que usar un convertidor me habría dificultado la vida. Usó Gson para trabajar con el objeto desde allí, dependiendo de si era un valor String o Array.
Espero que esto ayude a alguien a buscar una respuesta simple :).
Si no fuera posible cambiar la API backend, consideraría las siguientes variantes (si Gson se usa para convertir JSON).
Podemos usar adaptadores de tipo Gson para crear un adaptador personalizado para el tipo de
ResponseMessage
que dinámicamente decide cómo analizar el inoming JSON (usando algo comoif (reader.peek() == JsonToken.STRING)
).Ponga una metainformación que describa el tipo de respuesta en un encabezado HTTP y utilícela para determinar qué tipo de información se debe suministrar a la instancia de Gson.
Tarde a 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 usas una clase estática que implementa Convertidor de actualización:
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 hay margen de mejora. Pero demuestra cómo usar un convertidor para convertir casi cualquier respuesta a Json dinámico.
Pruebe la deserialización personalizada usando gson-converter
como se gson-converter
continuación (respuesta actualizada para Retrofit 2.0)
Crea 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
compile ''com.squareup.retrofit2: retrofit: 2.3.0''
compile ''com.squareup.retrofit2: converter-gson: 2.3.0''
***** Respuesta anterior (la respuesta anterior es más recomendada) *****
Cambia tu pojo como este
public class TrackerRefResponse {
private String applicationType;
private Object responseMessage;
public Object getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(Object responseMessage) {
this.responseMessage = responseMessage;
}
}
y cambiar la respuesta de on-retrofit como esta
@Override
public void onResponse(Response<TrackerRefResponse > response) {
if (response.isSuccess()) {
if (response.getResponseMessage() instanceof String)
{
handleStringResponse();
}
else
{
handleObjectResponse();
}
}
}
Consulte esta publicación en el blog para obtener más detalles sobre el análisis dinámico json