Gson未使用用于List的自定义JsonExclusializer

piv4azn7  于 2023-10-18  发布在  其他
关注(0)|答案(1)|浏览(2721)

我有一个问题,与analisation。我有这个json从服务器

{
  "rooms": [
    {
      "id": 1,
      "name": "Стандартный номер с видом на бассейн",
      "price": 186600,
      "price_per": "За 7 ночей с перелетом",
      "peculiarities": [
        "Включен только завтрак",
        "Кондиционер"
      ],
      "image_urls": [
        "https://www.atorus.ru/sites/default/files/upload/image/News/56871/%D1%80%D0%B8%D0%BA%D1%81%D0%BE%D1%81%20%D1%81%D0%B8%D0%B3%D0%B5%D0%B9%D1%82.jpg",
        "https://q.bstatic.com/xdata/images/hotel/max1024x768/267647265.jpg?k=c8233ff42c39f9bac99e703900a866dfbad8bcdd6740ba4e594659564e67f191&o=",
        "https://worlds-trip.ru/wp-content/uploads/2022/10/white-hills-resort-5.jpeg"
      ]
    },
    {
      "id": 2,
      "name": "Люкс номер с видом на море",
      "price": 289400,
      "price_per": "За 7 ночей с перелетом",
      "peculiarities": [
        "Все включено",
        "Кондиционер",
        "Собственный бассейн"
      ],
      "image_urls": [
        "https://mmf5angy.twic.pics/ahstatic/www.ahstatic.com/photos/b1j0_roskdc_00_p_1024x768.jpg?ritok=65&twic=v1/cover=800x600",
        "https://www.google.com/search?q=%D0%BD%D0%BE%D0%BC%D0%B5%D1%80+%D0%BB%D1%8E%D0%BA%D1%81+%D0%B2+%D0%BE%D1%82%D0%B5%D0%BB%D0%B8+%D0%B5%D0%B3%D0%B8%D0%BF%D1%82%D0%B0+%D1%81+%D1%81%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%BC+%D0%B1%D0%B0%D1%81%D1%81%D0%B5%D0%B9%D0%BD%D0%BE%D0%BC&tbm=isch&ved=2ahUKEwilufKp-4KBAxUfJxAIHR4uAToQ2-cCegQIABAA&oq=%D0%BD%D0%BE%D0%BC%D0%B5%D1%80+%D0%BB%D1%8E%D0%BA%D1%81+%D0%B2+%D0%BE%D1%82%D0%B5%D0%BB%D0%B8+%D0%B5%D0%B3%D0%B8%D0%BF%D1%82%D0%B0+%D1%81+%D1%81%D0%BE%D0%B1%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%BC+%D0%B1%D0%B0%D1%81%D1%81%D0%B5%D0%B9%D0%BD%D0%BE%D0%BC&gs_lcp=CgNpbWcQAzoECCMQJ1CqAVi6HGDmHWgAcAB4AIABXIgB3wySAQIyNZgBAKABAaoBC2d3cy13aXotaW1nwAEB&sclient=img&ei=Y3fuZOX7KJ_OwPAPntyE0AM&bih=815&biw=1440#imgrc=Nr2wzh3vuY4jEM&imgdii=zTCXWbFgrQ5HBM",
        "https://tour-find.ru/thumb/2/bsb2EIEFA8nm22MvHqMLlw/r/d/screenshot_3_94.png"
      ]
    }
  ]
}

我想要一份名单
HotelApi.kt

@GET("f9a38183-6f95-43aa-853a-9c83cbb05ecd")
suspend fun getRooms(): Response<List<Room>>

为此,我写了一个Jsonalizer
RoomListDeserializer.kt

class RoomListDeserializer : JsonDeserializer<List<Room>> {
    override fun deserialize(
        json: JsonElement,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): List<Room> {
        val roomList = mutableListOf<Room>()
        val roomsJsonArray = json.asJsonObject.getAsJsonArray("rooms")
        Log.d("TAG", "deserialize: ${roomsJsonArray}")
        roomsJsonArray?.forEach { roomJson ->
            val room = context?.deserialize<Room>(roomJson, Room::class.java)
            room?.let { roomList.add(it) }
        }
        return roomList
    }
}

(log我把它添加到GsonBuilder中
AppModule.kt

@Provides
    @Singleton
    fun provideGsonConverter(): Gson = GsonBuilder()
            .registerTypeAdapter(object : TypeToken<List<Room>>(){}.type, RoomListDeserializer())
            .create()

    @Provides
    @Singleton
    fun provideHotelApi(
        client: OkHttpClient,
        gson: Gson
    ): HotelsApi = Retrofit.Builder()
            .baseUrl(HotelsApi.BASE_URL)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build()
            .create(HotelsApi::class.java)

但它不起作用
HotelRepositoryImpl.kt

override suspend fun getRooms(): Flow<Resource<List<Room>>> = flow {
        try {
            val response = api.getRooms()

            if (response.body() != null) {
                emit(Resource.Success(response.body()!!))
            } else {
                emit(Resource.Error(context.getString(R.string.response_is_null)))
            }
        } catch (e: Exception) {
            Log.d("TAG", "getRooms: $e")
            emit(handleException(e, context))
        }
    }.flowOn(Dispatchers.IO)

Expection是getRooms: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $,但如果我创建一个类,其中包含一个带有Room列表的字段,则一切正常

data class Test(
    val rooms: List<Room>
)

我的房间实体

data class Room(
    val id: Int,
    @SerializedName("image_urls")
    val imageUrls: List<String>,
    val name: String,
    val peculiarities: List<String>,
    val price: Int,
    @SerializedName("price_per")
    val pricePer: String
)

我能实现我的目标吗?如何实现?

50pmv0ei

50pmv0ei1#

您为GsonBuilder.registerTypeAdapter提供的类型将被严格检查。虽然文件可能没有足够清楚地说明这一点。当您为List<Room>注册适配器时,您只为该类型注册它。它既不用于ArrayList<Room>,也不用于List<? extends Room>或类似设备。
这似乎是您的代码无法工作的部分原因。看起来Kotlin表达式object : TypeToken<List<Room>>(){}.type实际上创建了一个List<? extends Room>(在调试时可以看到)[1]。因此不会调用您的自定义编译器。
也许您可以通过将object : TypeToken<List<Room>>(){}.type替换为TypeToken.getParameterized(List::class.java, Room::class.java).type来解决这个问题,但这似乎相当脆弱。
另一种替代方法是使用TypeAdapterFactory来实现自定义的格式化逻辑,但是如果类型是List<Room>或子类型,则必须手动检查。
因此,也许最简单和最可靠的解决方案是使用具有rooms属性的封闭类型,就像您对Test数据类所做的那样。
[1]我不知道为什么Kotlin显然没有把getRooms()的返回类型中的List<Room>也当作List<? extends Room>

相关问题