MalformedJsonException with Retrofit API?
Asked Answered
C

8

17

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());

        }
    });
}
Chillon answered 15/12, 2014 at 13:36 Comment(1)
isn't your json output for ChatObject going to be {"usuario":"%@","adversario":"%@","atualizacao":"%@","email":"%@","device":"%@","device_tipo":"%@"}Feingold
S
15

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();
        }
    }
}

}

Soledadsolely answered 30/12, 2014 at 2:29 Comment(5)
Nikola,,,How to call this function means,,,, .setConverter(new LenientGsonConverter(gson))Pryor
You call this when you create your RestAdapter.Soledadsolely
Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapter(Date.class, new DateTypeAdapter()) .create(); RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(urlString) .setConverter(new LenientGsonConverter(gson)) .build(); This is not workingPryor
Yes...But i am not able to getting responsePryor
my app is not compile even i am getting same isuueCaustic
G
15

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(); 
Gustave answered 4/5, 2016 at 20:13 Comment(2)
i got this error can not resolve setLenient(), in 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'Ambulant
@ZachariaMwalwama latest version is 2.0.2 search.maven.org/#search%7Cga%7C1%7Cconverter-gsonGustave
P
1
 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();
        }
    }
Playa answered 17/3, 2016 at 12:40 Comment(1)
Can you add some explanation to this answer? How does this code solve the problem? What is it changing?Lanctot
V
0

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.

Vivienviviene answered 16/11, 2018 at 14:57 Comment(0)
G
0

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'

Garnishment answered 5/7, 2019 at 13:42 Comment(0)
F
0

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

Furniture answered 3/4, 2020 at 12:8 Comment(0)
N
0

I face with the same MalformedJsonException issue in my Kotlin project and found that none of the solutions mentioned here worked for me.

The issue was resolved by introducing Kotlin's serialization capabilities into my project. Here's what I did:

1. Add @Serializable Annotation:

For Kotlin:

@Serializable
data class MyDataClass(...)

For Java

import java.io.Serializable;
public class MyDataClass implements Serializable {}

2. Update Dependencies for Kotlin:

// build.gradle (Module)
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")

// build.gradle (Project)
kotlin("jvm") version "1.9.22" // update version
kotlin("plugin.serialization") version "1.9.22" // update version

If you're facing a similar issue and haven't yet explored Kotlin or Java serialization capabilities, I recommend giving this a try.

Niobe answered 10/1 at 20:23 Comment(0)
P
-12

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]
Pryor answered 10/3, 2015 at 8:8 Comment(1)
This does not make GSON lient.Soledadsolely

© 2022 - 2024 — McMap. All rights reserved.