具有重复字段的 Moshi
Moshi with duplicate fields
我收到的 json 响应在某些情况下看起来像这样:
{
"id" : 12345,
"events": [
{
"desc": "Bla bla"
...
},
{
"desc": "Yada yada",
...
},
]
}
而对于其他一些场景,它看起来像这样:
{
"id" : 12345,
"events": {
"desc": "Bla bla"
...
},
"events" : {
"desc": "Yada yada"
...
},
}
也就是说,有时events
会是一个数组,有时events
会重复多个值。这会使用 moshi + retrofit 抛出以下异常:
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: Multiple values for 'events' at $[0].events
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: com.squareup.moshi.JsonDataException: Multiple values for 'events' at $[0].events
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.kotlin.reflect.KotlinJsonAdapter.fromJson(KotlinJsonAdapter.kt:80)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.internal.NullSafeJsonAdapter.fromJson(NullSafeJsonAdapter.java:40)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.CollectionJsonAdapter.fromJson(CollectionJsonAdapter.java:76)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.CollectionJsonAdapter.fromJson(CollectionJsonAdapter.java:53)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.internal.NullSafeJsonAdapter.fromJson(NullSafeJsonAdapter.java:40)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.converter.moshi.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:45)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.converter.moshi.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:27)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:45)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Observable.subscribe(Observable.java:11194)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Observable.subscribe(Observable.java:11194)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onSuccess(SingleFlatMap.java:84)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleDoOnSuccess$DoOnSuccess.onSuccess(SingleDoOnSuccess.java:59)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:56)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFlatMap.subscribeActual(SingleFlatMap.java:36)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleMap.subscribeActual(SingleMap.java:34)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFlatMap.subscribeActual(SingleFlatMap.java:36)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleToFlowable.subscribeActual(SingleToFlowable.java:37)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13180)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableZip$ZipCoordinator.subscribe(FlowableZip.java:127)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableZip.subscribeActual(FlowableZip.java:79)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:38)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableOnErrorReturn.subscribeActual(FlowableOnErrorReturn.java:33)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13180)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2019-12-30 13:58:20.461 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.lang.Thread.run(Thread.java:764)
我想对这两种情况的输出进行标准化,也就是说,我想把它变成
data class Parcel(val events: List<Event>)
我知道第二种响应格式不正确,但我无法控制后端(它是我正在使用的外部服务),有没有办法解决这个问题?
我尝试摆弄自定义适配器,但我无法弄清楚如何去做:(
编辑:我对自定义适配器的最佳尝试:
class CorreosApiParcelAdapter(private val eventAdapter: JsonAdapter<CorreosApiEvent>,
private val errorAdapter: JsonAdapter<Error>) : JsonAdapter<CorreosApiParcel>() {
override fun fromJson(reader: JsonReader): CorreosApiParcel? = with(reader) {
val events = mutableListOf<CorreosApiEvent>()
val parcel = CorreosApiParcel.allNull()
beginObject()
while (hasNext()) {
val nextName = nextName()
if (nextName != "eventos" && nextName != "error") {
val value = reader.nextString()
when (nextName) {
"codEnvio" -> parcel.codEnvio = value
"refCliente" -> parcel.refCliente = value
"codProducto" -> parcel.codProducto = value
"fecha_calculada" -> parcel.fechaCalculada = value
"largo" -> parcel.largo = value
"ancho" -> parcel.ancho = value
"alto" -> parcel.alto = value
"peso" -> parcel.peso = value
}
continue
}
if (nextName == "error") {
val error = errorAdapter.fromJson(reader)
if (error != null) {
parcel.error = error
}
continue
}
if (peek() == JsonReader.Token.BEGIN_OBJECT) {
val fromJson = eventAdapter.fromJson(reader)
if (fromJson != null) {
events += fromJson
}
continue
}
beginArray()
while (hasNext()) {
val fromJson = eventAdapter.fromJson(reader)
if (fromJson != null) {
events += fromJson
}
}
endArray()
}
endObject()
return parcel
}
override fun toJson(writer: JsonWriter, value: CorreosApiParcel?) {
}
companion object {
val FACTORY: JsonAdapter.Factory = Factory { type, _, moshi ->
if (Types.getRawType(type) != CorreosApiParcel::class.java)
return@Factory null
val eventAdapter = moshi.adapter<CorreosApiEvent>(CorreosApiEvent::class.java)
val errorAdapter = moshi.adapter<Error>(Error::class.java)
return@Factory CorreosApiParcelAdapter(eventAdapter, errorAdapter)
}
}
}
据我所知,Moshi 和 Retrofit 本身不支持它,所以答案是否定的。
这是后台团队无法协调的事情吗?如果您尝试并设法在 Android 端处理它,它将变得非常复杂。我现在能看到的唯一解决方法是让后续方法调用进行验证,并使用您自己的实现将 JSON 字符串解析为有意义的数据类型。例如var events: List<Event>
.
有人讨论过是否允许这种类型的响应作为 JSON 文本格式标准化的一部分。
您可以在此处阅读更多相关信息:
Does JSON syntax allow duplicate keys in an object?
Moshi 不支持多个字段,因此您必须实施自定义 JsonAdapter
for it. With fromJson()
you have access to the JsonReader
,您可以使用它来确定 events
是一个对象或数组。
override fun fromJson(reader: JsonReader) = with(reader) {
val events = mutableList<Event>()
beginObject()
while (hasNext()) {
if (nextName() != "events") {
skipValue()
continue
}
if (peek() == BEGIN_OBJECT) {
events += eventAdapter.fromJson(reader)
continue
}
beginArray()
while(hasNext()) {
events += eventAdapter.fromJson(reader)
}
endArray()
}
endObject()
Parcel(events)
}
该实现将名为 events
的所有属性读取为对象或数组,具体取决于所提供的属性。所有其他属性都被跳过,因为它们与 Parcel
无关。 eventAdapter
是您必须提供的依赖项。它负责从 json.
中读取 Event
个对象
我收到的 json 响应在某些情况下看起来像这样:
{
"id" : 12345,
"events": [
{
"desc": "Bla bla"
...
},
{
"desc": "Yada yada",
...
},
]
}
而对于其他一些场景,它看起来像这样:
{
"id" : 12345,
"events": {
"desc": "Bla bla"
...
},
"events" : {
"desc": "Yada yada"
...
},
}
也就是说,有时events
会是一个数组,有时events
会重复多个值。这会使用 moshi + retrofit 抛出以下异常:
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: Multiple values for 'events' at $[0].events
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: com.squareup.moshi.JsonDataException: Multiple values for 'events' at $[0].events
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.kotlin.reflect.KotlinJsonAdapter.fromJson(KotlinJsonAdapter.kt:80)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.internal.NullSafeJsonAdapter.fromJson(NullSafeJsonAdapter.java:40)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.CollectionJsonAdapter.fromJson(CollectionJsonAdapter.java:76)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.CollectionJsonAdapter.fromJson(CollectionJsonAdapter.java:53)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at com.squareup.moshi.internal.NullSafeJsonAdapter.fromJson(NullSafeJsonAdapter.java:40)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.converter.moshi.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:45)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.converter.moshi.MoshiResponseBodyConverter.convert(MoshiResponseBodyConverter.java:27)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:225)
2019-12-30 13:58:20.458 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.OkHttpCall.execute(OkHttpCall.java:188)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:45)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Observable.subscribe(Observable.java:11194)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Observable.subscribe(Observable.java:11194)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFlatMap$SingleFlatMapCallback.onSuccess(SingleFlatMap.java:84)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleDoOnSuccess$DoOnSuccess.onSuccess(SingleDoOnSuccess.java:59)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:56)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFlatMap.subscribeActual(SingleFlatMap.java:36)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.459 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleMap.subscribeActual(SingleMap.java:34)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleFlatMap.subscribeActual(SingleFlatMap.java:36)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Single.subscribe(Single.java:3096)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.single.SingleToFlowable.subscribeActual(SingleToFlowable.java:37)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13180)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableZip$ZipCoordinator.subscribe(FlowableZip.java:127)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableZip.subscribeActual(FlowableZip.java:79)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableMap.subscribeActual(FlowableMap.java:38)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableOnErrorReturn.subscribeActual(FlowableOnErrorReturn.java:33)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13234)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.Flowable.subscribe(Flowable.java:13180)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
2019-12-30 13:58:20.460 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
2019-12-30 13:58:20.461 21419-21419/com.myapp.android.debug E/DetailFragment$loadUp: at java.lang.Thread.run(Thread.java:764)
我想对这两种情况的输出进行标准化,也就是说,我想把它变成
data class Parcel(val events: List<Event>)
我知道第二种响应格式不正确,但我无法控制后端(它是我正在使用的外部服务),有没有办法解决这个问题?
我尝试摆弄自定义适配器,但我无法弄清楚如何去做:(
编辑:我对自定义适配器的最佳尝试:
class CorreosApiParcelAdapter(private val eventAdapter: JsonAdapter<CorreosApiEvent>,
private val errorAdapter: JsonAdapter<Error>) : JsonAdapter<CorreosApiParcel>() {
override fun fromJson(reader: JsonReader): CorreosApiParcel? = with(reader) {
val events = mutableListOf<CorreosApiEvent>()
val parcel = CorreosApiParcel.allNull()
beginObject()
while (hasNext()) {
val nextName = nextName()
if (nextName != "eventos" && nextName != "error") {
val value = reader.nextString()
when (nextName) {
"codEnvio" -> parcel.codEnvio = value
"refCliente" -> parcel.refCliente = value
"codProducto" -> parcel.codProducto = value
"fecha_calculada" -> parcel.fechaCalculada = value
"largo" -> parcel.largo = value
"ancho" -> parcel.ancho = value
"alto" -> parcel.alto = value
"peso" -> parcel.peso = value
}
continue
}
if (nextName == "error") {
val error = errorAdapter.fromJson(reader)
if (error != null) {
parcel.error = error
}
continue
}
if (peek() == JsonReader.Token.BEGIN_OBJECT) {
val fromJson = eventAdapter.fromJson(reader)
if (fromJson != null) {
events += fromJson
}
continue
}
beginArray()
while (hasNext()) {
val fromJson = eventAdapter.fromJson(reader)
if (fromJson != null) {
events += fromJson
}
}
endArray()
}
endObject()
return parcel
}
override fun toJson(writer: JsonWriter, value: CorreosApiParcel?) {
}
companion object {
val FACTORY: JsonAdapter.Factory = Factory { type, _, moshi ->
if (Types.getRawType(type) != CorreosApiParcel::class.java)
return@Factory null
val eventAdapter = moshi.adapter<CorreosApiEvent>(CorreosApiEvent::class.java)
val errorAdapter = moshi.adapter<Error>(Error::class.java)
return@Factory CorreosApiParcelAdapter(eventAdapter, errorAdapter)
}
}
}
据我所知,Moshi 和 Retrofit 本身不支持它,所以答案是否定的。
这是后台团队无法协调的事情吗?如果您尝试并设法在 Android 端处理它,它将变得非常复杂。我现在能看到的唯一解决方法是让后续方法调用进行验证,并使用您自己的实现将 JSON 字符串解析为有意义的数据类型。例如var events: List<Event>
.
有人讨论过是否允许这种类型的响应作为 JSON 文本格式标准化的一部分。
您可以在此处阅读更多相关信息:
Does JSON syntax allow duplicate keys in an object?
Moshi 不支持多个字段,因此您必须实施自定义 JsonAdapter
for it. With fromJson()
you have access to the JsonReader
,您可以使用它来确定 events
是一个对象或数组。
override fun fromJson(reader: JsonReader) = with(reader) {
val events = mutableList<Event>()
beginObject()
while (hasNext()) {
if (nextName() != "events") {
skipValue()
continue
}
if (peek() == BEGIN_OBJECT) {
events += eventAdapter.fromJson(reader)
continue
}
beginArray()
while(hasNext()) {
events += eventAdapter.fromJson(reader)
}
endArray()
}
endObject()
Parcel(events)
}
该实现将名为 events
的所有属性读取为对象或数组,具体取决于所提供的属性。所有其他属性都被跳过,因为它们与 Parcel
无关。 eventAdapter
是您必须提供的依赖项。它负责从 json.
Event
个对象