MalformedJsonException with Retrofit API? - android

I need send a json to my webservice, json is:
{
"Sala": {
"usuario": "%#",
"adversario": "%#",
"atualizacao": "%#",
"device": "%#",
"device_tipo": "ios"
}
}
. I'm trying do it using Retrofit API 1.8.
When I try send the post throws an exception.
Exception:
com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 7 path $
I'm trying this
public class ChatObject {
private String usuario;
private String adversario;
private String atualizacao;
private String email;
private String device;
private String device_tipo;
Retrofit Interface
#POST("/WsChat/interacao.json")
public void onReceiveMessage(#Body ChatObject obj,
Callback<JsonElement> response);
Implements
public void receiveMessage(){
///{\"Sala\":{\"usuario\":\"%#\",\"adversario\":\"%#\",\"atualizacao\":\"%#\",\"device\":\"%#\",\"device_tipo\":\"ios\"}}
ChatObject chatObject = new ChatObject(BatalhaConfigs.USUARIO_EMAIL,
BatalhaConfigs.ADVERSARIO_EMAIL,
new Date().toString(),
BatalhaConfigs.USUARIO_EMAIL,
AndroidReturnId.getAndroidId(),
"android");
RestAdapter adapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setRequestInterceptor(new CustomRequestInterceptor())
.setEndpoint(END_POINT)
.build();
ChatListener listener = adapter.create(ChatListener.class);
listener.onReceiveMessage(chatObject, new Callback<JsonElement>() {
#Override
public void success(JsonElement jsonElement, retrofit.client.Response response) {
Log.i("JSON ELEMENT->", jsonElement.toString());
}
#Override
public void failure(RetrofitError error) {
Log.i("FALHOU->", error.getLocalizedMessage());
}
});
}

com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) is usually thrown when there is some character(s) that malforms the JSON. Exception message itself suggest to make the deserialization more tolerant.
But I suggest you to fix your JSON and trim it from unwanted characters.
You should extend GsonConverter and override fromBody() to make Gson read from the tolerant JsonReader. Then just set it to your RestAdapter. This will attempt to use tolerant JsonReader to deserialize and then close it, if not exception is thrown.
public class LenientGsonConverter extends GsonConverter {
private Gson mGson;
public LenientGsonConverter(Gson gson) {
super(gson);
mGson = gson;
}
public LenientGsonConverter(Gson gson, String charset) {
super(gson, charset);
mGson = gson;
}
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
boolean willCloseStream = false; // try to close the stream, if there is no exception thrown using tolerant JsonReader
try {
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.in()));
jsonReader.setLenient(true);
Object o = mGson.fromJson(jsonReader,type);
willCloseStream = true;
return o;
} catch (IOException e) {
e.printStackTrace();
}finally {
if(willCloseStream) {
closeStream(body);
}
}
return super.fromBody(body, type);
}
private void closeStream(TypedInput body){
try {
InputStream in = body.in();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

Seems its changed slightly with Retrofit 2.0
Here's how I did it:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://whatever.com")
.addConverterFactory(LenientGsonConverterFactory.create(gson))
.build();
A new lenient gson factory:
public final class LenientGsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {#link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static LenientGsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {#code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static LenientGsonConverterFactory create(Gson gson) {
return new LenientGsonConverterFactory(gson);
}
private final Gson gson;
private LenientGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new LenientGsonResponseBodyConverter<>(gson, adapter);
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new LenientGsonRequestBodyConverter<>(gson, adapter);
}
}
Lenient parsing of responses:
private class LenientGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
LenientGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
#Override
public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
jsonReader.setLenient(true);
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
Lenient creation of requests:
private class LenientGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
LenientGsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
#Override
public RequestBody convert(T value) throws IOException {
Buffer buffer = new Buffer();
Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
JsonWriter jsonWriter = gson.newJsonWriter(writer);
jsonWriter.setLenient(true);
adapter.write(jsonWriter, value);
jsonWriter.close();
return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
}
}
I just copied the Retrofit source code and added a line to the request and the response converters jsonWriter.setLenient(true);
Or even easier:
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://whatever.com")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

RestAdapter adapterRfqPost = new RestAdapter.Builder()
.setEndpoint(Constants.ENDPOINT)
`enter code here`.setConverter(new ConstantsMethods.StringConverter())
.build();
public 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();
}
}

I struggled around a day getting this error and doing what the "correct answer" of this page said so, but after all I figured out my problem, that was assigning the response from an array that was "int" (also my model class was int) to an textView which of course required me to convert it to string the int value. I didn't even required to do the solution of #Nikola Despotoski at all in my case.

Below code worked for me
Gson gson = new GsonBuilder()
.setLenient()
.create();
final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(endPoint)
.setConverter(new GsonConverter(gson))
.build();
For using ".setLenient()", need to add below line into app's gradle file.
implementation 'com.google.code.gson:gson:2.7'

if you are using PHP as API please check whether it echoes
only JSON encoded objects otherwise it will throw this type of exception

You should help this code :
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://192.168.10.115/test.php")
.setConverter(new GsonConverter(new Gson()))
.build();
Put jar file :
[gson-2.2.2.jar][1]

Related

Retrofit change string chars during the request

I built the request like following:
public static ApiBuilder newInstance(){
GsonBuilder gsonBuilder = new GsonBuilder()
.registerTypeAdapter(Boolean.class, booleanAsIntAdapter)
.registerTypeAdapter(boolean.class, booleanAsIntAdapter)
.registerTypeAdapter(Date.class, (JsonDeserializer<Date>) (json, typeOfT, context)
-> new Date(Long.valueOf(json.getAsString())));
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(180, TimeUnit.SECONDS);
client.setReadTimeout(240, TimeUnit.SECONDS);
String url = MyApplicationConfiguration.getInstance().get("urlofservice");
RestAdapter restAdapter = new RestAdapter.Builder()
.setRequestInterceptor(
request -> {
request.addHeader(deviceIdHeader, MyApplication.getDeviceId());
}
).setConverter(new GsonConverter(gsonBuilder.create()))
.setEndpoint(url)
.setClient(new OkClient(client)).build();
restAdapter.setLogLevel(RestAdapter.LogLevel.FULL);
return restAdapter.create(ApiExtendedValidation.class);
}
private static final TypeAdapter<Boolean> booleanAsIntAdapter = new TypeAdapter<Boolean>() {
#Override
public void write(JsonWriter out, Boolean value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value);
}
}
#Override
public Boolean read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
switch (peek) {
case BOOLEAN:
return in.nextBoolean();
case NULL:
in.nextNull();
return null;
case NUMBER:
return in.nextInt() != 0;
case STRING:
return Boolean.parseBoolean(in.nextString());
default:
throw new IllegalStateException("Expected BOOLEAN or NUMBER but was " + peek);
}
}
};
The API interface is:
#Headers("Content-Type: application/json")
#POST("/validate")
Observable<QRValidateResponse> validateQRCode(#Body QRRequest request);
The QRRequest is an object, that contains a String field with a Base64 ecnoded string
Encoding example
byte[] data = rawResult.getText().getBytes(StandardCharsets.ISO_8859_1);
String encodedResult = Base64.encodeToString(data, Base64.DEFAULT);
object example
request{
"lang": "EN",
"content": "SGVsbG8gV29ybGQh="
}
When Retrofit execute the request, the request.content changes like follwing:
SGVsbG8gV29ybGQh\u003d
The Server doesn't recognize the content, so its response in an error.
Why during the request the content changes? Is there a way to avoid it?
SOLUTION
There was an escaping error linked to the use of Gson.
According to this post, solved adding this disableHtmlEscaping() to the GsonBuilder.

Remove unwanted string from retrofit response

I want to remove string from my retorfit2 POST request response.
This is my response:
/*-secure-{"response":{"response":{"response":{"token":"95a2c5a8","email":"xxx#sample.in","name":"xxx"},"status":true,"code":0.0},"status":200},"status":200}*/
Due to some security reason my server automatically adding the strings
/*-secure- and */
beacuse of this , on converting to json I am getting below error:
com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 2 path $
My custom gson converter class
final class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
private Pattern pattern = Pattern.compile("^\\/\\*-secure-\\W(.*)\\*\\/$");
CustomGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
#Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
System.out.println(response);
Matcher matcher = pattern.matcher(response);
JsonReader jsonReader = gson.newJsonReader(new StringReader(matcher.group(1)));
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
Gson gson = new GsonBuilder()
.setLenient()
.build();
// ...
.addConverterFactory(GsonConverterFactory.create(gson)
Please see this

Getting simple JSON object response using Retrofit library

I have a web query with JSON response as:
{
"status":true,
"result":
{
"id":"1",
"name":"ABC 1",
"email":"info#ABc.dcom",
"password":"123456",
"status":false,
"created":"0000-00-00 00:00:00"
},
"message":"Login successfully"
}
I am using the following code for:
#GET("/stockers/login")
public void login(
#Query("email") String email,
#Query("password") String password,
Callback<JSONObject> callback);
In Debugger the query made by the Retrofit library is correct, however I get an empty JSON in response.
ApiManager.getInstance().mUrlManager.login(
email.getText().toString().trim(),
password.getText().toString().trim(),
new Callback<JSONObject>()
{
#Override
public void success(JSONObject jsonObj, Response response)
{
mDialog.dismiss();
Simply use JsonElement insted of JSONobject. Like:
#GET("/stockers/login")
Call<JsonElement> getLogin(
#Query("email") String email,
#Query("password") String password
);
The answers seam kinda old and for Retrofit 1, if you are using Retrofit 2 and don't want to use a converter you have to use ResponseBody.
#GET("/stockers/login")
public void login(
#Query("email") String email,
#Query("password") String password,
Callback<ResponseBody> callback);
And then in your callback in the onResponse method call string on the body and create a JSONObject from it.
if(response.isSuccessful())
JSONObject json = new JSONObject(response.body().string());
Instead of Callback with JSONObject class, you could use the Retrofit basic callback which use the Response class and then, once you get the response, you had to create the JSONObject from it.
See this:
https://stackoverflow.com/a/30870326/2037304
Otherwise you can create your own model class to handle the response.
First the Result class:
public class Result {
public int id;
public String name;
public String email;
public String password;
public boolean status;
public Date created;
}
And then your response class to use with Retrofit
public class MyResponse {
public boolean status;
public Result result;
public String message;
}
Now you can call:
#GET("/stockers/login")
public void login(
#Query("email") String email,
#Query("password") String password,
Callback<MyResponse> callback);
You can create custom factory like belowe or copy it from here :
https://github.com/marcinOz/Retrofit2JSONConverterFactory
public class JSONConverterFactory extends Converter.Factory {
public static JSONConverterFactory create() {
return new JSONConverterFactory();
}
private JSONConverterFactory() {
}
#Override public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (type == JSONObject.class
|| type == JSONArray.class) {
return JSONRequestBodyConverter.INSTANCE;
}
return null;
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
if (type == JSONObject.class) {
return JSONResponseBodyConverters.JSONObjectResponseBodyConverter.INSTANCE;
}
if (type == JSONArray.class) {
return JSONResponseBodyConverters.JSONArrayResponseBodyConverter.INSTANCE;
}
return null;
}
}
public class JSONRequestBodyConverter<T> implements Converter<T, RequestBody> {
static final JSONRequestBodyConverter<Object> INSTANCE = new JSONRequestBodyConverter<>();
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain; charset=UTF-8");
private JSONRequestBodyConverter() {
}
#Override public RequestBody convert(T value) throws IOException {
return RequestBody.create(MEDIA_TYPE, String.valueOf(value));
}
}
public class JSONResponseBodyConverters {
private JSONResponseBodyConverters() {}
static final class JSONObjectResponseBodyConverter implements Converter<ResponseBody, JSONObject> {
static final JSONObjectResponseBodyConverter INSTANCE = new JSONObjectResponseBodyConverter();
#Override public JSONObject convert(ResponseBody value) throws IOException {
try {
return new JSONObject(value.string());
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
static final class JSONArrayResponseBodyConverter implements Converter<ResponseBody, JSONArray> {
static final JSONArrayResponseBodyConverter INSTANCE = new JSONArrayResponseBodyConverter();
#Override public JSONArray convert(ResponseBody value) throws IOException {
try {
return new JSONArray(value.string());
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
}
try this instead :
#GET("/stockers/login")
public void login(
#Query("email") String email,
#Query("password") String password,
Callback<Response> callback); // set the callback generic parameter to Response
ApiManager.getInstance().mUrlManager.login(
email.getText().toString().trim(),
password.getText().toString().trim(),
new Callback<Response>()
{
#Override
public void success(Response response, Response response1)
{
String json = response.getBody();
try {
JSONObject jsonObj = new JSONObject(json);
} catch(JSONException e) {
}
alog.dismiss();
Just define the type of the object you want to get as a String as com.google.gson.JsonObject instead of String and call .toString() on that object to get the JSON string itself.
I`m using this site to create my classes (POJO) from JSON.
http://www.jsonschema2pojo.org/
just be sure to set to JSON insted of JSON Schema and check GSON, because retrofit is using GSON as well for parsing.
your retrofit code looks fine.
Use JacksonConverterFactory instead of GsonConverterFactory while setting up Retrofit. Now you can directly work with JsonObject responses.
compile 'com.squareup.retrofit2:converter-jackson:2.1.0'

Gson deserializer with Retrofit converter: just need inner JSON for all responses

I am working with an API that always responds like so:
{
"stuff_i_need": [
{
"title": "Hello"
},
{
"title": "World!"
}
],
"status": "success"
}
and
{
"other_things_key":
{
"version": "208"
},
"status": "success"
}
There are always two elements, and I just need the one that is not "status."
I want to do this with one GsonBuilder, as well.
I tried:
new GsonConverter(new GsonBuilder()
.registerTypeAdapter(List.class, new JsonDeserializer<List>() {
#Override
public List deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
final JsonElement element = entry.getValue();
if (element.isJsonArray()) {
return new Gson().fromJson(element.getAsJsonArray(),
new TypeToken<List>(){}.getType());
}
}
return null;
}
)
but I don't think that is right, and it doens't satisfy the broader conditions.
Try this
public class ItemTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.has("data") && jsonObject.get("data").isJsonObject())
{
jsonElement = jsonObject.get("data");
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}}
Next, you have to add it to the Gson object in your RestClient.
public class RestClient
{
private static final String BASE_URL = "your base url";
private ApiService apiService;
public RestClient()
{
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(new ItemTypeAdapterFactory()) // This is the important line ;)
.setDateFormat("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'")
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint(BASE_URL)
.setConverter(new GsonConverter(gson))
.setRequestInterceptor(new SessionRequestInterceptor())
.build();
apiService = restAdapter.create(ApiService.class);
}
public ApiService getApiService()
{
return apiService;
}
}
Hope it helps...
With some help from Gowtham, I ended up doing the following:
private static class ItemTypeAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
#Override
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = elementAdapter.read(in);
if (jsonElement.isJsonObject()) {
JsonObject jsonObject = jsonElement.getAsJsonObject();
if (jsonObject.entrySet().size() == 2) {
jsonObject.remove("status");
jsonElement = jsonObject.entrySet().iterator().next().getValue();
}
}
return delegate.fromJsonTree(jsonElement);
}
}.nullSafe();
}
}
and this is set on the RestAdapter.Builder:
.setConverter(new GsonConverter(new GsonBuilder()
.registerTypeAdapterFactory(new ItemTypeAdapterFactory())
.create()))
I really just ended up removing the "status" JsonObject.

How can I return String or JSONObject from asynchronous callback using Retrofit?

For example, calling
api.getUserName(userId, new Callback<String>() {...});
cause:
retrofit.RetrofitError: retrofit.converter.ConversionException:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected a string but was BEGIN_OBJECT at line 1 column 2
I think I must disable gson parsing into POJOs but can't figure out how to do it.
I figured it out. It's embarrassing but it was very simple... Temporary solution may be like this:
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();
}
}
But if you want to get directly Callback the Better way is to use 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()));
}
}
}
If you know how to improve this code - please feel free to write about it.
A possible solution would be to use JsonElement as the Callback type (Callback<JsonElement>). In your original example:
api.getUserName(userId, new Callback<JsonElement>() {...});
In the success method you can convert the JsonElement to either a String or a JsonObject.
JsonObject jsonObj = element.getAsJsonObject();
String strObj = element.toString();
Retrofit 2.0.0-beta3 adds a converter-scalars module provides a
Converter.Factory for converting String, the 8 primitive types,
and the 8 boxed primitive types as text/plain bodies. Install this
before your normal converter to avoid passing these simple scalars
through, for example, a JSON converter.
So, first add converter-scalars module to build.gradle file for your application.
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'
}
Then, create your Retrofit instance like this:
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);
Now you can use API declaration like this:
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);
}
The answer may be much shorter than already mentioned and doesn't require any additional libraries:
In declaration use Response as follows:
... Callback<Response> callback);
And while handling response:
#Override
public void success(Response s, Response response) {
new JSONObject(new String(((TypedByteArray) response.getBody()).getBytes()))
}
When #lordmegamax answer completely work there is much nicer solution which is come from
Okio is a new library that complements java.io and java.nio
other squares project which already tight with retrofit and therefore you don't need to add any new dependency and it's have to be reliable:
ByteString.read(body.in(), (int) body.length()).utf8();
ByteString is an immutable sequence of bytes. For character data, String is fundamental. ByteString is String's long-lost brother, making it easy to treat binary data as a value. This class is ergonomic: it knows how to encode and decode itself as hex, base64, and UTF-8.
Full example:
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);
}
}
To get Call JSONObject or JSONArray
you can create custom factory or copy it from here : https://github.com/marcinOz/Retrofit2JSONConverterFactory

Categories

Resources