Hot questions for Handling MalformedJsonException in Retrofit

Top 10 Java Open Source / Retrofit / Handling MalformedJsonException

Retrofit2.0 gets MalformedJsonException while the json seems correct?

Question: I am using retrofit:2.0.0-beta4 for my android app.

I tried to add a user with Retrofit, the user is correctly created in Database, however I got the following error:

     com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
         at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1573)
         at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1423)
         at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:587)
         at com.google.gson.stream.JsonReader.peek(JsonReader.java:429)
         at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:202)
         at com.google.gson.TypeAdapter.fromJson(TypeAdapter.java:260)
         at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:32)
         at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:23)
         at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:213)
         at retrofit2.OkHttpCall.execute(OkHttpCall.java:177)
         at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:87)
...

The returned result from server is :

{"data":{"user_id":"48","nickname":null,"password":null,"status":null},"status":1,"msg":"OK"}

This is correct json format, I don't understand why I get such exception?

Here is my interface:

public class ApiResult<T> {
    public T data;
    public int status;
    public String msg;
}

public interface ApiUsers {
    @POST("/users/new")
    public Call<ApiResult<User>> createUser(@Body User user);
}

public class User {
    public int user_id;
    public String registration;
    public int registration_type;
    public String avatar;
    public String nickname;
    public String password;
    public String status;

}

public class Api {

    // TODO modify the value
    public static final String BASE_URL = "xxx";

    private static Api instance = new Api();

    public static Api getInstance() {
        return instance;
    }

    private Api(){}

    public Retrofit getRetrofit() {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .retryOnConnectionFailure(true)
                .connectTimeout(15, TimeUnit.SECONDS)
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        return retrofit;
    }

    public <S> S createService(Class<S> serviceClass) {
        return getRetrofit().create(serviceClass);
    }
}

The caller code is:

ApiUsers api = Api.getInstance().createService(ApiUsers.class);
Call<ApiResult<User>> call = api.createUser(user);
CreateUserMessage message = new CreateUserMessage();
callNet(call, message);

Could anyone give any clue?

Answer: there is something wrong with your POST response (there some other non json output before the json data).

Here is the response from JakeWharton regarding how to set Gson lenient mode:

make sure that you have
   compile 'com.google.code.gson:gson:2.6.1'

Gson gson = new GsonBuilder()
        .setLenient()
        .create();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();

MalformedJsonException with Retrofit API?

Question: 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.

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

        }
    });
}

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