当前位置: 首页 > 知识库问答 >
问题:

Android Retrifit 2多转换器(Gson和SimpleXML)错误

齐健柏
2023-03-14
@Provides
@Singleton
public Gson providesGson() {
    return new GsonBuilder().create();
}

@Provides
@Singleton
public Retrofit providesRetrofit(@NonNull OkHttpClient okHttpClient, @NonNull Gson gson) {
    return new Retrofit.Builder()
            .baseUrl(ConstantsManager.BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(SimpleXmlConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create(gson))
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
}
D/OkHttp: {"date":"20.11.2016","bank":"PB","baseCurrency":980,"baseCurrencyLit":"UAH","exchangeRate":[{"baseCurrency":"UAH","currency":"AUD","saleRateNB":19.4452740,"purchaseRateNB":19.4452740},{"baseCurrency":"UAH","currency":"CAD","saleRateNB":19.4047320,"purchaseRateNB":19.4047320},{"baseCurrency":"UAH","currency":"CZK","saleRateNB":1.0322170,"purchaseRateNB":1.0322170,"saleRate":1.0800000,"purchaseRate":0.9800000},{"baseCurrency":"UAH","currency":"DKK","saleRateNB":3.7519280,"purchaseRateNB":3.7519280},{"baseCurrency":"UAH","currency":"HUF","saleRateNB":0.0902556,"purchaseRateNB":0.0902556},{"baseCurrency":"UAH","currency":"ILS","saleRateNB":6.7524710,"purchaseRateNB":6.7524710,"saleRate":7.0000000,"purchaseRate":6.3000000},{"baseCurrency":"UAH","currency":"JPY","saleRateNB":0.2384005,"purchaseRateNB":0.2384005,"saleRate":0.2500000,"purchaseRate":0.2200000},{"baseCurrency":"UAH","currency":"LVL","saleRateNB":0.2384005,"purchaseRateNB":0.2384005},{"baseCurrency":"UAH","currency":"LTL","saleRateNB":0.2384005,"purchaseRateNB":0.2384005},{"baseCurrency":"UAH","currency":"NOK","saleRateNB":3.0724120,"purchaseRateNB":3.0724120,"saleRate":3.2000000,"purchaseRate":2.9000000},{"baseCurrency":"UAH","currency":"SKK","saleRateNB":3.0724120,"purchaseRateNB":3.0724120},{"baseCurrency":"UAH","currency":"SEK","saleRateNB":2.8384710,"purchaseRateNB":2.8384710},{"baseCurrency":"UAH","currency":"CHF","saleRateNB":26.0049080,"purchaseRateNB":26.0049080,"saleRate":27.5000000,"purchaseRate":25.0000000},{"baseCurrency":"UAH","currency":"RUB","saleRateNB":0.4013400,"purchaseRateNB":0.4013400,"saleRate":0.4200000,"purchaseRate":0.4000000},{"baseCurrency":"UAH","currency":"GBP","saleRateNB":32.4460750,"purchaseRateNB":32.4460750,"saleRate":34.0000000,"purchaseRate":31.0000000},{"baseCurrency":"UAH","currency":"USD","saleRateNB":26.0534380,"purchaseRateNB":26.0534380,"saleRate":27.0000000,"purchaseRate":26.6000000},{"baseCurrency":"UAH","currency":"BYR","saleRateNB":26.0534380,"purchaseRateNB":26.0534380},{"baseCurrency":"UAH","currency":"EUR","saleRateNB":27.9214700,"purchaseRateNB":27.9214700,"saleRate":28.6000000,"purchaseRate":28.2000000},{"baseCurrency":"UAH","currency":"GEL","saleRateNB":10.5921530,"purchaseRateNB":10.5921530},{"baseCurrency":"UAH","currency":"PLZ","saleRateNB":6.2818280,"purchaseRateNB":6.2818280,"saleRate":6.6000000,"purchaseRate":5.9000000}]}
D/OkHttp: <-- END HTTP (2377-byte body)
E/DateCurrencyService: Error while loading data occurred!
                   java.lang.RuntimeException: org.xmlpull.v1.XmlPullParserException: Unexpected token (position:TEXT {"date":"20.11.2...@1:2378 in java.io.InputStreamReader@2ec8965) 
                       at retrofit2.converter.simplexml.SimpleXmlResponseBodyConverter.convert(SimpleXmlResponseBodyConverter.java:44)
                       at retrofit2.converter.simplexml.SimpleXmlResponseBodyConverter.convert(SimpleXmlResponseBodyConverter.java:23)
                       at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
                       at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
                       at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
                       at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171)
                       at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:80)
                       at rx.Subscriber.setProducer(Subscriber.java:209)
                       at rx.Subscriber.setProducer(Subscriber.java:205)
                       at rx.Subscriber.setProducer(Subscriber.java:205)
                       at rx.internal.operators.OperatorSubscribeOn$1$1.setProducer(OperatorSubscribeOn.java:76)
                       at rx.Subscriber.setProducer(Subscriber.java:205)
                       at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
                       at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
                       at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50)
                       at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
                       at rx.Observable.unsafeSubscribe(Observable.java:8666)
                       at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
                       at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:220)
                       at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
                       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
                       at java.util.concurrent.FutureTask.run(FutureTask.java:237)
                       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
                       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
                       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
                       at java.lang.Thread.run(Thread.java:761)
                    Caused by: org.xmlpull.v1.XmlPullParserException: Unexpected token (position:TEXT {"date":"20.11.2...@1:2378 in java.io.InputStreamReader@2ec8965) 
                       at org.kxml2.io.KXmlParser.next(KXmlParser.java:432)
                       at org.kxml2.io.KXmlParser.next(KXmlParser.java:313)
                       at org.simpleframework.xml.stream.PullReader.read(PullReader.java:105)
                       at org.simpleframework.xml.stream.PullReader.next(PullReader.java:89)
                       at org.simpleframework.xml.stream.NodeReader.readElement(NodeReader.java:111)
                       at org.simpleframework.xml.stream.NodeReader.readRoot(NodeReader.java:85)
                       at org.simpleframework.xml.stream.NodeBuilder.read(NodeBuilder.java:84)
                       at org.simpleframework.xml.stream.NodeBuilder.read(NodeBuilder.java:71)
                       at org.simpleframework.xml.core.Persister.read(Persister.java:562)
                       at retrofit2.converter.simplexml.SimpleXmlResponseBodyConverter.convert(SimpleXmlResponseBodyConverter.java:36)
                       at retrofit2.converter.simplexml.SimpleXmlResponseBodyConverter.convert(SimpleXmlResponseBodyConverter.java:23) 
                       at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117) 
                       at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211) 
                       at retrofit2.OkHttpCall.execute(OkHttpCall.java:174) 
                       at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171) 
                       at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:80) 
                       at rx.Subscriber.setProducer(Subscriber.java:209) 
                       at rx.Subscriber.setProducer(Subscriber.java:205) 
                       at rx.Subscriber.setProducer(Subscriber.java:205) 
                       at rx.internal.operators.OperatorSubscribeOn$1$1.setProducer(OperatorSubscribeOn.java:76) 
                       at rx.Subscriber.setProducer(Subscriber.java:205) 
                       at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152) 
                       at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138) 
                       at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50) 
                       at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) 
                       at rx.Observable.unsafeSubscribe(Observable.java:8666) 
                       at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94) 
                       at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:220) 
                       at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) 
                       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428) 
                       at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
                       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272) 
                       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133) 
                       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607) 
                       at java.lang.Thread.run(Thread.java:761) 

如果我尝试切换添加转换器的顺序,我将在接收XML时得到类似的错误(显然,因为Gson转换器试图转换XML响应):

D/OkHttp: <exchangerate><exchangerate ccy="EUR" ccy_name_ru="Евро                               " ccy_name_ua="Євро                               " ccy_name_en="Euro                               " base_ccy="UA" buy="27247312" unit="100.00000" date="2016.11.29" /><exchangerate ccy="RUR" ccy_name_ru="Российский рубль                   " ccy_name_ua="Росiйський рубль                   " ccy_name_en="Russian Rouble                     " base_ccy="UA" buy="39810" unit="10.00000" date="2016.11.29" /><exchangerate ccy="USD" ccy_name_ru="Доллар США                         " ccy_name_ua="Долар США                          " ccy_name_en="US Dollar                          " base_ccy="UA" buy="25724426" unit="100.00000" date="2016.11.29" /></exchangerate>
D/OkHttp: <-- END HTTP (799-byte body)
E/SyncService: Error while loading data occurred!
           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:1559)
               at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1401)
               at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:593)
               at com.google.gson.stream.JsonReader.peek(JsonReader.java:425)
               at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:205)
               at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
               at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
               at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
               at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
               at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
               at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:171)
               at rx.internal.operators.OperatorSubscribeOn$1$1$1.request(OperatorSubscribeOn.java:80)
               at rx.Subscriber.setProducer(Subscriber.java:209)
               at rx.Subscriber.setProducer(Subscriber.java:205)
               at rx.Subscriber.setProducer(Subscriber.java:205)
               at rx.internal.operators.OperatorSubscribeOn$1$1.setProducer(OperatorSubscribeOn.java:76)
               at rx.Subscriber.setProducer(Subscriber.java:205)
               at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
               at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
               at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50)
               at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
               at rx.Observable.unsafeSubscribe(Observable.java:8666)
               at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
               at rx.internal.schedulers.CachedThreadScheduler$EventLoopWorker$1.call(CachedThreadScheduler.java:220)
               at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
               at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
               at java.util.concurrent.FutureTask.run(FutureTask.java:237)
               at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
               at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
               at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
               at java.lang.Thread.run(Thread.java:761)

我可以做什么来使每个转换器转换适当的响应?

UPD:根据Marcin Jedynak的建议,我做了以下几点:

public class RetrofitUniversalConverter extends Converter.Factory {

    private final Converter.Factory xml;
    private final Converter.Factory json;

    @Inject
    public RetrofitUniversalConverter(@NonNull Gson gson) {
        xml = SimpleXmlConverterFactory.create();
        json = GsonConverterFactory.create(gson);
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {

        for (Annotation annotation : annotations) {

            if (annotation.getClass() == Xml.class) {
                return xml.responseBodyConverter(type, annotations, retrofit);
            }

            if (annotation.getClass() == Json.class) {
                return json.responseBodyConverter(type, annotations, retrofit);
            }

        }

        return null;
    }
}
@Provides
@Singleton
public Gson providesGson() {
    return new GsonBuilder().create();
}

@Provides
@Singleton
public Retrofit providesRetrofit(@NonNull OkHttpClient okHttpClient,
                                 @NonNull RetrofitUniversalConverter converter) {
    return new Retrofit.Builder()
            .baseUrl(ConstantsManager.BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(converter)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();
}
public interface PrivatbankApi {

    @GET @Xml
    Observable<CurrentRates> loadCurrentRates(@NonNull @Url String url);

    @GET("exchange_rates") @Json
    Observable<DateRates> loadDateRates(@NonNull @Query("json") Boolean json, @NonNull @Query("date") String date);

}
E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.vedmedenko.exchangerates, PID: 5432
              java.lang.RuntimeException: Unable to start service com.vedmedenko.exchangerates.core.services.SyncService@68cf714 with Intent { flg=0x4 cmp=com.vedmedenko.exchangerates/.core.services.SyncService (has extras) }: java.lang.IllegalArgumentException: Unable to create converter for class com.vedmedenko.exchangerates.core.rest.models.current.CurrentRates
                  for method PrivatbankApi.loadCurrentRates
                  at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3343)
                  at android.app.ActivityThread.-wrap21(ActivityThread.java)
                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1582)
                  at android.os.Handler.dispatchMessage(Handler.java:102)
                  at android.os.Looper.loop(Looper.java:154)
                  at android.app.ActivityThread.main(ActivityThread.java:6119)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
               Caused by: java.lang.IllegalArgumentException: Unable to create converter for class com.vedmedenko.exchangerates.core.rest.models.current.CurrentRates
                  for method PrivatbankApi.loadCurrentRates
                  at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:720)
                  at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:706)
                  at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:167)
                  at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:166)
                  at retrofit2.Retrofit$1.invoke(Retrofit.java:145)
                  at java.lang.reflect.Proxy.invoke(Proxy.java:813)
                  at $Proxy0.loadCurrentRates(Unknown Source)
                  at com.vedmedenko.exchangerates.core.DataManager.loadCurrentRates(DataManager.java:29)
                  at com.vedmedenko.exchangerates.core.services.SyncService.onStartCommand(SyncService.java:81)
                  at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3326)
                    ... 8 more
               Caused by: java.lang.IllegalArgumentException: Could not locate ResponseBody converter for class com.vedmedenko.exchangerates.core.rest.models.current.CurrentRates.
                Tried:
                 * retrofit2.BuiltInConverters
                 * com.vedmedenko.exchangerates.core.rest.converters.RetrofitUniversalConverter
                  at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:346)
                  at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:308)
                  at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:704)
                    ... 16 more

共有1个答案

魏君博
2023-03-14

看看Jake Wharton的这篇演讲,他精确地解决了你描述的问题(并提出了许多其他有用的技巧)。

简而言之,他建议创建表示预期数据格式的注释(例如,在您的例子中是JSONXML),并相应地注释API调用。然后定义自定义的ConverterFactory,根据遇到的批注,将其委托给GSONConverterFactorySimplexMLConverterFactory

但是,您需要在Jake的解决方案中添加两件事。

首先,不要忘记用@retention(retentionpolicy.runtime)注释您的注释,否则它们将不会在运行时保留:

@Retention(RetentionPolicy.RUNTIME)
public @interface Json {}

其次,您在ResponseBodyConverter方法中收到的批注实际上不是您的批注。它们是系统为您的注释创建的代理。因此,您需要替换以下条件:

annotation.getClass() == Json.class
annotation.getClass() == Xml.class

与:

annotation.annotationType() == Json.class
annotation.annotationType() == Xml.class
 类似资料:
  • 问题内容: 我碰到SimpleXML对象转换为阵列的这种功能在这里: 因此,我对XML字符串的采用类似于: 它工作得很好,但似乎有点黑吗?有没有更有效/更强大的方法来做到这一点? 我知道SimpleXML对象与数组足够接近,因为它利用了PHP中的ArrayAccess接口,但与多维数组(即循环)一起使用时,仍然不能很好地工作。 谢谢大家的帮助 问题答案: 我在PHP手册注释中找到了这个: 它可以帮

  • 问题内容: 如何在PHP中将数组转换为SimpleXML对象? 问题答案: 一个简短的: 结果是 键和值被交换- 您可以在array_walk之前用来解决。需要PHP5。您可以改用PHP 5 ,但那时您将不会进入xml。

  • 在我的spark代码中有gson,它存储在字符串RDD中,我将其转换为键和值。 (1). (2). 但我收到了以下错误消息。谷歌。格森。JsonSyntaxException:java。伊奥。EOFEException:com第1行第2列的输入结束。谷歌。格森。格森。com上的fromJson(Gson.java:813)。谷歌。格森。格森。com上的fromJson(Gson.java:768)

  • 我正试图将我的列表写到抽象模型中的jtable,然后它返回给我这个错误。在我看来,这可能是由列表格式引起的?名称和金额在错误的地方。这是我的完整错误消息:线程“awt-eventqueue-0”java.lang.ClassCastException:类com.google.gson.internal.LinkedTreemap不能强制转换为类model.medicine(com.google.g

  • 假设我已将以下对象序列化为json字符串: 现在我想反序列化它,但是我想把名称分成两个字段,和。我该怎么做呢? 我希望最终的结果是类似于: 这对Gson有可能吗?