处理Retrofit MalformedJsonException报错

1,807 阅读2分钟

使用Retrofit配合GsonConverter,我们能很好地将网络响应内容转换成对应的对象。比如像下面这样。

Retrofit网络接口方法

1
2
3
4
5
6
7
public interface DroidNetwork {
    @GET("/content/test.json")
    Call<DroidResponse> jsonData();

    @GET("/content/helloworld.txt")
    Call<String> plainText();
}

对应的应用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 val retrofit = Retrofit.Builder()
            .baseUrl("https://asset.droidyue.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()

    val droidNetwork = retrofit.create(DroidNetwork::class.java)

    droidNetwork.jsonData().enqueue(object : Callback<DroidResponse> {
        override fun onFailure(call: Call<DroidResponse>?, t: Throwable?) {
            t?.printStackTrace()
        }

        override fun onResponse(call: Call<DroidResponse>?, response: Response<DroidResponse>?) {
            dumpMessage("onResponse content=${response?.body()}")
        }
    })


上面的方法执行都很正常,可是执行这个方法的时候就会报错。

1
2
3
4
5
6
7
8
9
10
droidNetwork.plainText().enqueue(object : Callback<String> {
            override fun onFailure(call: Call<String>?, t: Throwable?) {
                Exception("causedByPlainText", t)?.printStackTrace()
            }

            override fun onResponse(call: Call<String>?, response: Response<String>?) {
                dumpMessage("onResponse content=${response}")
            }

        })

崩溃的信息如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 W System.err: java.lang.Exception: causedByPlainText
 W System.err:    at com.example.secoo.retrofitconvertor.MainActivity$doNetworkRequest$2.onFailure(MainActivity.kt:41)
 W System.err:    at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$2.run(ExecutorCallAdapterFactory.java:80)
 W System.err:    at android.os.Handler.handleCallback(Handler.java:891)
 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:102)
 W System.err:    at android.os.Looper.loop(Looper.java:207)
 W System.err:    at android.app.ActivityThread.main(ActivityThread.java:7470)
 W System.err:    at java.lang.reflect.Method.invoke(Native Method)
 W System.err:    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
 W System.err:    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:958)
 W System.err: Caused by: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
 W System.err:    at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1568)
 W System.err:    at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1409)
 W System.err:    at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:593)
 W System.err:    at com.google.gson.stream.JsonReader.peek(JsonReader.java:425)
 W System.err:    at com.google.gson.internal.bind.TypeAdapters$16.read(TypeAdapters.java:393)
 W System.err:    at com.google.gson.internal.bind.TypeAdapters$16.read(TypeAdapters.java:390)
 W System.err:    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
 W System.err:    at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
 W System.err:    at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:223)
 W System.err:    at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:121)
 W System.err:    at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
 W System.err:    at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
 W System.err:    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
 W System.err:    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
 W System.err:    at java.lang.Thread.run(Thread.java:784)

原因其实很简单

解决方法有两个

这里我们介绍后者的处理方法,这里我们使用ResponseBody而不是之前的String,然后按照如下代码应用即可。

接口代码

1
2
@GET("/content/helloworld.txt")
Call<ResponseBody> plainTextAsResponseBody();

应用代码

1
2
3
4
5
6
7
8
9
10
 droidNetwork.plainTextAsResponseBody().enqueue(object : Callback<ResponseBody> {
        override fun onFailure(call: Call<ResponseBody>?, t: Throwable?) {
            t?.printStackTrace()
        }

        override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>?) {
            dumpMessage("onResponse of plainTextAsResponseBody content=${response?.body()?.string()}")
        }

    })

附加内容

如果在某种情况下,我们只关心请求操作,而不关心响应内容,我们可以这样使用

1
2
@GET("/content/helloworld.txt")
Call<Void> ignoreResult();

上面的代码相对更加高效,因为这里省略了将响应内容转成内存对象的过程。