android - usando - ¿Cómo puedo devolver String o JSONObject desde la devolución de llamada asíncrona utilizando Retrofit?
retrofit rest android (7)
Por ejemplo, llamando
api.getUserName(userId, new Callback<String>() {...});
porque:
retrofit.RetrofitError: retrofit.converter.ConversionException:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected a string but was BEGIN_OBJECT at line 1 column 2
Creo que debo deshabilitar el análisis de gson en POJOs pero no puedo encontrar la manera de hacerlo.
Retrofit 2.0.0-beta3 agrega un módulo
converter-scalars
que proporciona unConverter.Factory
para convertirString
, los 8 tipos primitivos y los 8 tipos primitivos en caja comotext/plain
cuerpos sintext/plain
. Instale esto antes de su convertidor normal para evitar pasar estos simples escalares a través de, por ejemplo, un convertidor JSON.
Por lo tanto, primero agregue el módulo converter-scalars
build.gradle
archivo build.gradle
para su aplicación.
dependencies {
...
// use your Retrofit version (requires at minimum 2.0.0-beta3) instead of 2.0.0
// also do not forget to add other Retrofit module you needed
compile ''com.squareup.retrofit2:converter-scalars:2.0.0''
}
Luego, crea tu instancia de Retrofit
esta manera:
new Retrofit.Builder()
.baseUrl(BASE_URL)
// add the converter-scalars for coverting String
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(Service.class);
Ahora puedes usar la declaración API como esta:
interface Service {
@GET("/users/{id}/name")
Call<String> userName(@Path("userId") String userId);
// RxJava version
@GET("/users/{id}/name")
Observable<String> userName(@Path("userId") String userId);
}
Cuando la respuesta de @lordmegamax funciona completamente, hay una solución mucho mejor que proviene de
Okio es una nueva biblioteca que complementa java.io y java.nio
proyecto de otros cuadrados que ya está ajustado con la retrofit
y, por lo tanto, no es necesario agregar ninguna nueva dependencia y tiene que ser confiable:
ByteString.read(body.in(), (int) body.length()).utf8();
ByteString es una secuencia inmutable de bytes. Para datos de personajes, String es fundamental. ByteString es el hermano perdido de String, lo que facilita el tratamiento de los datos binarios como un valor. Esta clase es ergonómica: sabe cómo codificarse y decodificarse como hexadecimal, base64 y UTF-8.
Ejemplo completo:
public class StringConverter implements Converter {
@Override public Object fromBody(TypedInput body, Type type) throws ConversionException {
try {
return ByteString.read(body.in(), (int) body.length()).utf8();
} catch (IOException e) {
throw new ConversionException("Problem when convert string", e);
}
}
@Override public TypedOutput toBody(Object object) {
return new TypedString((String) object);
}
}
Esto es lo que hice, después de hurgar en el depurador. Nota: esto es para realmente obtenerlo dentro de una devolución de llamada de error, no la devolución de llamada correcta.
Verá que el tipo de éxito se encuentra llamando a retrofitError.getSuccessType()
y devuelve un objeto de tipo Type
Luego puedes llamar a retrofitError.getBodyAs(YourType.class)
que es todo lo que necesitaba hacer porque para mí es siempre la clase que espero que sea.
Aquí está la respuesta de una sola línea:
retrofitError.getBodyAs(retrofitError.getSuccessType())
Ahora, me doy cuenta de que no tengo que hacer nada como esto con respecto a la devolución de llamada exitosa porque ya está funcionando mágicamente.
La respuesta puede ser mucho más corta de lo que ya se mencionó y no requiere ninguna biblioteca adicional:
En la declaración, use la Response
siguiente manera:
... Callback<Response> callback);
Y mientras se maneja la respuesta:
@Override
public void success(Response s, Response response) {
new JSONObject(new String(((TypedByteArray) response.getBody()).getBytes()))
}
Me lo imaginé. Es vergonzoso, pero fue muy simple ... La solución temporal puede ser así:
public void success(Response response, Response ignored) {
TypedInput body = response.getBody();
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(body.in()));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
// Prints the correct String representation of body.
System.out.println(out);
} catch (IOException e) {
e.printStackTrace();
}
}
Pero si desea obtener directamente Callback, la mejor manera es utilizar Converter .
public class Main {
public interface ApiService {
@GET("/api/")
public void getJson(Callback<String> callback);
}
public static void main(String[] args) {
RestAdapter restAdapter = new RestAdapter.Builder()
.setClient(new MockClient())
.setConverter(new StringConverter())
.setEndpoint("http://www.example.com").build();
ApiService service = restAdapter.create(ApiService.class);
service.getJson(new Callback<String>() {
@Override
public void success(String str, Response ignored) {
// Prints the correct String representation of body.
System.out.println(str);
}
@Override
public void failure(RetrofitError retrofitError) {
System.out.println("Failure, retrofitError" + retrofitError);
}
});
}
static class StringConverter implements Converter {
@Override
public Object fromBody(TypedInput typedInput, Type type) throws ConversionException {
String text = null;
try {
text = fromStream(typedInput.in());
} catch (IOException ignored) {/*NOP*/ }
return text;
}
@Override
public TypedOutput toBody(Object o) {
return null;
}
public static String fromStream(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder out = new StringBuilder();
String newLine = System.getProperty("line.separator");
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(newLine);
}
return out.toString();
}
}
public static class MockClient implements Client {
@Override
public Response execute(Request request) throws IOException {
URI uri = URI.create(request.getUrl());
String responseString = "";
if (uri.getPath().equals("/api/")) {
responseString = "{result:/"ok/"}";
} else {
responseString = "{result:/"error/"}";
}
return new Response(request.getUrl(), 200, "nothing", Collections.EMPTY_LIST,
new TypedByteArray("application/json", responseString.getBytes()));
}
}
}
Si sabe cómo mejorar este código, no dude en escribir sobre él.
Para obtener Call JSONObject o JSONArray
puede crear una fábrica personalizada o copiarla desde aquí: https://github.com/marcinOz/Retrofit2JSONConverterFactory
Una posible solución sería utilizar JsonElement
como el tipo de Callback<JsonElement>
( Callback<JsonElement>
). En su ejemplo original:
api.getUserName(userId, new Callback<JsonElement>() {...});
En el método de éxito, puede convertir JsonElement
en una String
o un JsonObject
.
JsonObject jsonObj = element.getAsJsonObject();
String strObj = element.toString();