首页 > 解决方案 > 改造 API 调用返回 null

问题描述

这是我尝试使用的 API/JSON 数据:https ://pogoapi.net/api/v1/pokemon_names.json

问题是,当我尝试解析 JSON 响应时,只会返回 null。

楷模:

data class ListOfReleasedPokemon(
    val releasedPokemon: List<PokemonResponse>
)

data class PokemonResponse(
    val pokemonMap: Map<String, ReleasedPokemonModel>
)

data class ReleasedPokemonModel(
    val id: Int,
    val name: String
)

接口调用:

interface PogoApi {
    @GET("released_pokemon.json")
    suspend fun getReleasedPokemon(): ListOfReleasedPokemon
}

应用模块:

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Singleton
    @Provides
    fun providePokemonRepository(
        pogoApi: PogoApi
    ) = PokemonRepository(pogoApi)

    @Singleton
    @Provides
    fun providePogoApi(): PogoApi{
//        val gson = GsonBuilder()
//            .registerTypeAdapter(ReleasedPokemonModel::class.java, JsonDeserializer())
//            .create()
        return Retrofit.Builder()
            .baseUrl(POGO_API_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(PogoApi::class.java)
    }
}

存储库(发现错误的地方):

@ActivityScoped
class PokemonRepository @Inject constructor(
    private val pogoApi: PogoApi
){
    suspend fun getReleasedPokemonList(): Resource<ListOfReleasedPokemon> {
        val response = try{
            pogoApi.getReleasedPokemon()
//          Returns null
        } catch(e: IOException){
            return Resource.Error("Please check your internet connection")
        } catch(e: HttpException){
            return Resource.Error("Unexpected response")
        }
        Log.d("MainActivity", "GetPokemonReleasedList Run with no errors")
        return Resource.Success(data = response)
    }
}

任何帮助将不胜感激!

标签: androidjsonkotlinretrofit

解决方案


哦,我现在才注意到您似乎有一个与此 Json 解析器相关的问题。

好吧,在此评论中,我将详细指导您的这个项目。因为上一个问题您只问了如何使用未定义的键解析 Json。

首先,创建一个自定义的 Json Deserializer。因为这里你的数据 api 有一个未定义的键。在这里,我将使用自定义 Json Deserializer 创建 1 个 gson。如下:

口袋妖怪反应

data class PokemonResponse(
    val pokemonMap: List<StringReleasedPokemonModel>
)

data class ReleasedPokemonModel(
    val id: Int,
    val name: String
)

GsonHelper.kt

object GsonHelper {
    fun create(): Gson = GsonBuilder().apply {
        registerTypeAdapter(PokemonResponse::class.java, PokemonType())
        setLenient()
    }.create()

    private class PokemonType : JsonDeserializer<PokemonResponse> {
        override fun deserialize(
            json: JsonElement?,
            typeOfT: Type?,
            context: JsonDeserializationContext?
        ): PokemonResponse {
            val list = mutableListOf<ReleasedPokemonModel>()
            // Get your all key
            val keys = json?.asJsonObject?.keySet()
            keys?.forEach { key ->
                // Get your item with key
                val item = Gson().fromJson<ReleasedPokemonModel>(
                    json.asJsonObject[key],
                    object : TypeToken<ReleasedPokemonModel>() {}.type
                )
                list.add(item)
            }
            return PokemonResponse(list)
        }
    }
}

接下来,将此 Gson 提供给您的 Dagger 或 Hilt。

应用模块.kt

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    fun provideGson(): Gson = GsonHelper.create()
    ...
}

然后你修改你addConverterFactory的如下:

应用模块.kt

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    fun provideGson(): Gson = GsonHelper.create()

    @Singleton
    @Provides
    fun providePogoApi(gson: Gson): PogoApi = Retrofit.Builder()
        .baseUrl(POGO_API_BASE_URL)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
        .create(PogoApi::class.java)
}

PogoApi中,将返回类型更改getReleasedPokemon()PokemonResponse

interface PogoApi {
    @GET("released_pokemon.json")
    suspend fun getReleasedPokemon(): PokemonResponse
}

最后,在您的存储库中,编辑以下内容:

PokemonRepository.kt

@ActivityScoped
class PokemonRepository @Inject constructor(
    private val pogoApi: PogoApi
){
    suspend fun getReleasedPokemonList(): Resource<PokemonResponse> = try {
        val response = pogoApi.getReleasedPokemon()
        Log.d("MainActivity", "list: ${response}")
        Resource.Success(data = response)
    } catch(e: IOException){
        Resource.Error("Please check your internet connection")
    } catch(e: HttpException){
        Resource.Error("Unexpected response")
    }
}

推荐阅读