I am trying to serialize the request before sending it to the retrofit for webservice calls.
as i am serializing the request , i need to pass json string to retrofit calls in #Body paramenter and due to that
the generated json string results into following json string with the " (Double quotes in front and end ).
"{\"access_token\":\"d80fa6bd6f78cc704104d61146c599bc94b82ca225349ee68762fc6c70d2dcf0\",\"fitness\":[{\"_id\":\"1d051bfe-df30-4fa0-808b-9d7300a608ab\",\"activity_id\":\"877284d3-4f36-4ec0-a536-11563207dc4d\",\"calories\":600.0,\"distance\":40.0,\"intensity\":\"100\",\"timestamp\":\"2018-07-18T12:56:43+00:00\",\"type\":\"Running\",\"utc_offset\":\"+05:30\"},{\"_id\":\"2004ff72-707d-489a-927e-4cdeed410095\",\"activity_id\":\"5ed7c90f-805e-4763-aa62-7f8126c84f06\",\"calories\":600.0,\"distance\":40.0,\"intensity\":\"100\",\"timestamp\":\"2018-07-18T12:56:43+00:00\",\"type\":\"Running\",\"utc_offset\":\"+05:30\"}]}"
as there are double quotes the third party api is unable to parse it successfully.
here is my reqeust serializer code
public class RequestSerializer implements JsonSerializer<Request<?>> {
#Override
public JsonElement serialize(Request<?> request, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new GsonBuilder().create().toJsonTree(request,Request.class).getAsJsonObject();
JsonElement requestList = jsonObject.get("requestList");
jsonObject.remove("requestList");
jsonObject.add("fitness",requestList);
return jsonObject;
}
}
code to call retrofit webservice
GsonBuilder builder = new GsonBuilder();
builder.excludeFieldsWithoutExposeAnnotation();
builder.registerTypeAdapter(Request.class, new RequestSerializer());
Gson gson = builder.create();
String data = gson.toJson(request);
Flowable<Response> fitnessFlowable = new WebRequest().getRemoteClient().create(FitnessApi.class).postFitnessData("5b238abb4d3590001d9b94a8",data);
Using objects eliminates string quoting request in POST #Body,
example as my working code:
class Data{
#SerializedName("access_token")
#Expose
private String access_token;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
// Other field defined here
}
#Keep
class Result{
#SerializedName("rc") int rc;
}
#Keep
interface APIFitness{
#Headers("Content-Type: application/json")
#POST("api/save/")
Observable<Result> Save(#Body Data data);
//More methods..
}
using
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("url")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
APIFitness service = retrofit.create(APIFitness.class);
Data data = new Data();
authCode.setAccess_token(token);
service.Save(data)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.newThread())
.subscribe( data -> { } )
Related
I'm trying to send the following java model as form encoded body WITHOUT the wrapping {}. I've tried everything I can find to send a Model NOT as JSON but as form encoded data using Retrofit 2.
// Sends as JSON
#Headers("Content-Type:application/x-www-form-urlencoded")
#POST(SERVICES + USERS)
Observable<UserInfoResponse> signupUser(#Body SignUpParams params);
// Works
#FormUrlEncoded
#POST(SERVICES + USERS)
Observable<UserInfoResponse> signupUser(
#Field("approve") boolean approve,
#Field("daily_newsletter") int newsletter,
#Field("include_order_info") boolean includeOrderInfo,
#Field("is_21") int is21,
#Field("is_guest") int isGuest,
#Field("method") String method,
#Field("email") String email,
#Field("password") String password,
#Field("oauth_token") String oauthToken
);
Here's our setup if it helps
// Dagger Provider
#Provides
#Singleton
#Named(JT_API)
Retrofit provideJTSecureApiRetrofit(OkHttpClient okHttpClient, Gson gson) {
Retrofit retrofit = new Retrofit.Builder().client(okHttpClient)
.baseUrl(jtBaseUrl)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit;
}
#Provides
#Singleton
OkHttpClient provideOkHttpClient(JTApp app) {
Interceptor addUrlParams = chain -> {
Request request = chain.request();
HttpUrl url = request.url()
.newBuilder()
.addQueryParameter("app_version", BuildConfig.VERSION_NAME)
.build();
request = request.newBuilder()
.url(url)
.build();
return chain.proceed(request);
};
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder.addInterceptor(addUrlParams);
// this doesn't seem to do anything…
okHttpClientBuilder.addInterceptor(chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.addHeader("Content-Type", "application/x-www-form-urlencoded");
Request request = requestBuilder.build();
return chain.proceed(request);
});
okHttpClientBuilder.readTimeout(JTApp.HTTP_TIMEOUT, TimeUnit.SECONDS)
.connectTimeout(JTApp.HTTP_TIMEOUT, TimeUnit.SECONDS);
return okHttpClientBuilder.build();
}
If i am not mistaken
For application/x-www-form-urlencoded, the body of the HTTP message
sent to the server is essentially one giant query string -- name/value
pairs are separated by the ampersand (&), and names are separated from
values by the equals symbol (=).
How to send Form data in retrofit2 android
Turns out I had to create my own key value pair converter which extends the Retrofit2 Converter.Factory
/**
* Retrofit 2 Key Value Pair Form Data Encoder
*
* This is a copy over of {#link GsonConverterFactory}. This class sends the outgoing response as
* form data vs the gson converter which sends it as JSON. The response is proxied through the
* gson converter factory just the same though
*
* Created by marius on 11/17/16.
*/
public class RF2_KeyValuePairConverter extends Converter.Factory {
private final GsonConverterFactory gsonConverter;
/**
* Create an instance using a default {#link Gson} instance for conversion. Encoding to form data and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static RF2_KeyValuePairConverter create() {
return create(new Gson());
}
/**
* Create an instance using {#code gson} for conversion. Encoding to Form data and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static RF2_KeyValuePairConverter create(Gson gson) {
return new RF2_KeyValuePairConverter(gson);
}
private final Gson gson;
private RF2_KeyValuePairConverter(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
this.gsonConverter = GsonConverterFactory.create(gson);
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
return gsonConverter.responseBodyConverter(type, annotations, retrofit);
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
return new KeyValueBodyConverter<>(gson);
}
}
And here's our KeyValueBody
public class KeyValuePairConverter extends retrofit2.Converter.Factory implements Converter {
private final Gson gson;
public KeyValuePairConverter(Gson gson) {
this.gson = gson;
}
// Taken from retrofit's GsonConverter
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
String charset = MimeUtil.parseCharset(body.mimeType());
InputStreamReader isr = null;
try {
isr = new InputStreamReader(body.in(), charset);
return gson.fromJson(isr, type);
} catch (IOException e) {
throw new ConversionException(e);
} catch (JsonParseException e) {
throw new ConversionException(e);
} finally {
if (isr != null) {
try {
isr.close();
} catch (IOException ignored) {
}
}
}
}
#Override
public TypedOutput toBody(Object object) {
String json = gson.toJson(object);
//Log.d( "RETROFIT", json );
Type type = new TypeToken<Map<String, Object>>() { } .getType();
// this converts any int values to doubles so we are fixing them back in pojoToTypedOutput
Map<String, Object> map = gson.fromJson(json, type);
String body = pojoToTypedOutput(map, null);
// removes the initial ampersand
return new TypedString(body.substring(1));
}
/**
* Converts object to list of query parameters
* (works with nested objects)
*
* #todo
* query parameter encoding
*
* #param map this is the object map
* #param parentKey this is the parent key for lists/arrays
* #return
*/
public String pojoToTypedOutput(Map<String, Object> map, String parentKey) {
StringBuffer sb = new StringBuffer();
if (map != null && map.size() > 0) {
for (String key : map.keySet()) {
// recursive call for nested objects
if (map.get(key).getClass().equals(LinkedTreeMap.class)) {
sb.append(pojoToTypedOutput((Map<String, Object>) map.get(key), key));
} else {
// parent key for nested objects
Object objectValue = map.get(key);
// converts any doubles that really could be ints to integers (0.0 to 0)
if (objectValue.getClass().equals(Double.class)) {
Double doubleValue = (Double) objectValue;
if ((doubleValue == Math.floor(doubleValue)) && !Double.isInfinite(doubleValue)) {
objectValue = ((Double) objectValue).intValue();
}
}
if (parentKey != null && parentKey.length() != 0) {
sb.append("&").append(key).append("=").append(objectValue);
} else {
sb.append("&").append(parentKey + "[" + key + "]").append("=").append(objectValue);
}
}
}
}
return sb.toString();
}
}
In your Retrofit builder add .addConverterFactory(RF2_KeyValuePairConverter.create(gson)) and this will convert your responses to key/value pairs
Hi i have a json to send to the server (POST METHORD){"country":"india","devicetype":"android"} it is in form data model
like the key for this json is data ie is the server accept it like
data={"country":"india","devicetype":"android"} am using retrofit i use Multipart like this
#Multipart
#POST("initiate")
#Headers({
"Content-Type: application/json",
"Cache-Control: no-cache"
})
Call<UserInfoServerResponse> getUserInfoRequest(#Part(value="data") UserInfo mUserInfo);
here UserInfo is the json but am getting fail message from server after that i used FormUrlEncoded methord
#FormUrlEncoded
#POST("initiate")
#Headers({
"Content-Type: application/json",
"Cache-Control: no-cache"
})
Call<UserInfoServerResponse> getUserInfoRequest(#Field(value="data",encoded = false) String mUserInfo);
its out put is also same failure result from server, but the data sending to the server is in the formate
data=%7B%22country%22%3A%22india%22%2C%22devicetype%22%3A%22%22%7D
My UserInfo.class
public class UserInfo {
public String country;
public String devicetype;
public UserInfo( String country,String devicetype) {
this.country=country;
this.devicetype=devicetype;
}
}
My adaptor class
RemoteRetrofitInterfaces mService;
Retrofit mRetrofit;
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS).addInterceptor(interceptor)
.build();
mRetrofit = new Retrofit.Builder()
.baseUrl(AppConstant.HOST).addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
mService = mRetrofit.create(RemoteRetrofitInterfaces.class);
Call<UserInfoServerResponse> api = mService.getUserInfoRequest(new Gson().toJson(mUserInfo));
api.enqueue(new Callback<UserInfoServerResponse>() {
#Override
public void onResponse(Call<UserInfoServerResponse> responseCall, Response<UserInfoServerResponse> response) {
if (response.body().status != null) {
if (response.body().status.equals("success")) {
Log.d(TAG, "success---");
}
} else {
Log.d(TAG, "Failed---");
}
}
#Override
public void onFailure(Call<UserInfoServerResponse> responseCall, Throwable t) {
t.printStackTrace();
}
});
so how can i send the json to server using retrofit successfully i gone through the retofit document and follow couple of steps but i dont get any result. can any one help me in this
Thank you
finally i found the solution hope this will help some other
i achieve the solution by using FieldMap
of retrofit.
#POST("initiate")
#FormUrlEncoded
Call<UserInfoServerResponse> getUserInfoRequest(#FieldMap Map<String,String> params);
and in the Rest Adaptor section i changed request data from string to Hashmap form like following
Log.d(TAG, "sendUserInfo called");
UserInfo mInfo = new UserInfo("countyname","android");
String request = new Gson().toJson(mUserInfo);
// Here the json data is add to a hash map with key data
Map<String,String> params = new HashMap<String, String>();
params.put("data", request);
Call<UserInfoServerResponse> api = mService.getUserInfoRequest(params);
api.enqueue(new Callback<UserInfoServerResponse>() {
#Override
public void onResponse(Call<UserInfoServerResponse> responseCall, Response<UserInfoServerResponse> response) {
if (response.body().status != null) {
if (response.body().status.equals("success")) {
Log.d(TAG, "success---" + response.body());
}
} else {
Log.d(TAG, "Failed---");
}
}
#Override
public void onFailure(Call<UserInfoServerResponse> responseCall, Throwable t) {
t.printStackTrace();
}
});
Basilcally what I used #FormUrlEncoded for form data and #FieldMap to put my request JSON as a key value. i got solution by following this method, hope this will help some one :)
The above solution works but is cumbersome to use , a better solution will be to use a converter for #Multipart formData
Please use the bellow code for proceeding with #Multipart FormData
This is because
"" is added to your posting strings
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
/**
* Created by kural on 10/27/17.
*/
public class StringConverterFactory extends Converter.Factory {
private static final MediaType MEDIA_TYPE = MediaType.parse("text/plain");
public static StringConverterFactory create() {
return new StringConverterFactory();
}
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<ResponseBody, String>() {
#Override
public String convert(ResponseBody value) throws IOException {
return value.string();
}
};
}
return null;
}
#Override
public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
if (String.class.equals(type)) {
return new Converter<String, RequestBody>() {
#Override
public RequestBody convert(String value) throws IOException {
return RequestBody.create(MEDIA_TYPE, value);
}
};
}
return null;
}
}
and in your retrofit client add this line
.addConverterFactory(StringConverterFactory.create())
public class RetroFitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl) {
if (retrofit==null) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
/*retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();*/
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(client)
.addConverterFactory(StringConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
This works fine for me and return a json to obtain new valid Microsoft Azure Token :
My end point :
#PostMapping(value = "/get-new-token", consumes = {"application/JSON"}, produces = {"application/JSON"})
#Timed
public ResponseEntity<String> getNewToken(#RequestBody String refreshToken) throws IOException {
JSONObject json = tokenService.getNewTokenByRefreshToken(refreshToken);
return new ResponseEntity<>(json.toString(), HttpStatus.OK);
}
My getGraphRepository :
public GraphRepository getGraphRepository() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor).build();
// Create and configure the Retrofit object
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(" https://login.microsoftonline.com")
.client(client)
.addConverterFactory(JacksonConverterFactory.create())
.build();
// Generate the graph repo
return retrofit.create(GraphRepository.class);
}
My Token Service :
public JSONObject getNewTokenByRefreshToken(String refreshToken) throws IOException {
GraphRepository graphRepository = getGraphRepository();
// My list of -> Key : Value
Map<String,String> params = new HashMap<String, String>();
params.put("grant_type", "refresh_token");
params.put("client_id", this.client_id);
params.put("client_secret", client_secret);
params.put("refresh_token", refreshToken);
RefreshToken data = graphRepository.getRefreshToken(tenantId, params).execute().body();
JSONObject json = new JSONObject(data);
return json;
}
My GraphRepository :
#POST("/{tenant_id}/oauth2/v2.0/token")
#FormUrlEncoded
Call<RefreshToken> getRefreshToken(
#Path("tenant_id") String tenant_id,
#FieldMap Map<String, String> params
);
I hope this can help someone.
I am working on Retrofit v2 library for network calls.I am using following dependencies in build.gradle file.
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
1.User.java
public class User {
#SerializedName("email")
String email;
#SerializedName("password")
String password;
public User(String email, String password) {
this.email = email;
this.password = password;
}
}
2.MyAPI.java
public interface MyAPI {
#GET("{roomID}")
Call<List<Message>>loadMessages(#Path("roomID") String roomID);
#POST("almabay_oauth/authorize")
Call<User>login(#Body User user);
}
3.MainActivity.java
public class MainActivity extends AppCompatActivity {
String roomID = "548b737c0eadfb00eb93891bb28242e5";
MyAdapter adapter;
ListView lv;
List<Message> items;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv = (ListView) findViewById(R.id.lv);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://pms.vebific.com:81/chat/index/")
.addConverterFactory(GsonConverterFactory.create())
.build();
MyAPI myAPI = retrofit.create(MyAPI.class);
Call<List<Message>> call = myAPI.loadMessages(roomID);
call.enqueue(new Callback<List<Message>>() {
#Override
public void onResponse(Response<List<Message>> response, Retrofit retrofit) {
// Log.e("ResponseP", String.valueOf(response.body()));
List<Message> items = response.body();
Iterator iterator = items.iterator();
while (iterator.hasNext()) {
Message message = (Message) iterator.next();
String user = message.getUser();
//Log.e("User", user);
}
int statusCode = response.code();
//Log.e("StatusCode", String.valueOf(statusCode));
adapter = new MyAdapter(getApplicationContext(), items);
lv.setAdapter(adapter);
}
#Override
public void onFailure(Throwable t) {
}
});
//--------------POST-------------
Retrofit retrofit1 = new Retrofit.Builder()
.baseUrl("http://phpstack-11819-25991-62288.cloudwaysapps.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
MyAPI myPostApi = retrofit1.create(MyAPI.class);
User user = new User("j#yahoo.com", "Admin123#");
Call<User> call1 = myPostApi.login(user);
call1.enqueue(new Callback<User>() {
#Override
public void onResponse(Response<User> response, Retrofit retrofit) {
Log.e("Response code ", String.valueOf(response.code()));
User user1 = response.body();
Log.e("Response Message",response.message());
}
#Override
public void onFailure(Throwable t) {
}
});
}
}
I have succesfully handled GET request method using this library but i am not able to understand how to read the response after posting data to server using this library.Here i am sending email ID and password to server using Retrofit library.I am getting the status code as 200 here.It means everything is working fine.I know after posting data to server successfully,i am getting some response string .But here i am unable to view the response.Please help.
as you said you got response code 200 it means there is no issue with your request and response.
use OkHttpLoggingIntercepter class.
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient httpClient = new OkHttpClient();
httpClient.interceptors().add(logging);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient)
.build();
you will get detailed log of your response and all the errors if any.
one more thing Gson Converter only parse json data which has json object at root. not JsonArray.
so check your response string as well in log by writing above code.
try to send this POST request via Postman plugin for Chrome, maybe your User class is wrong
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]
I'm using Retrofit to send a POST request to my server:
#POST("/login")
void login( #Body User user ,Callback<User> callback);
Where my user object has only email and password fields.
Checking the logs, I can see that my parameters are sent with this format:
D/Retrofit﹕{"email":"example#test.com","password":"asdfasdf"}
What I need to do to my parameters be sent like this?
{"user" : {"email":"example#test.com","password":"asdfasdf"} }
EDIT: Making the right way, using a custom JsonSerializer:
public class CustomGsonAdapter {
public static class UserAdapter implements JsonSerializer<User> {
public JsonElement serialize(User user, Type typeOfSrc,
JsonSerializationContext context) {
Gson gson = new Gson();
JsonElement je = gson.toJsonTree(user);
JsonObject jo = new JsonObject();
jo.add("user", je);
return jo;
}
}
}
And then, on your API Client builder:
public static RestApiClient buildApiService() {
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(User.class, new CustomGsonAdapter.UserAdapter())
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(API_URL)
.setConverter(new GsonConverter(gson))
.build();
return restAdapter.create(MudamosApi.class);
}
The simplest mode to solve your problem is creating a RequestPOJO class, for example:
File User.java:
public class User{
public String email;
public String password;
}
File LoginRequestPojo.java:
public class LoginRequestPojo{
public User user;
public LoginRequestPojo(User user){
this.user = user;
}
}
And, in your retrofit 2 request:
#POST("/login")
void login( #Body LoginRequestPojo requestPojo, Callback<User> callback);
Finally, your request body:
{"user":{"email":"someone#something.com","password":"123123"}}