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
Related
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.
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 -> { } )
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.
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"}}