You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit无法创建类转换器问题排查求助

Retrofit调用TvShowService.getDetails时出现“Unable to create converter for class”错误排查

调用以下TvShowService接口的getDetails方法时,出现**"Unable to create converter for class"**错误:

interface TvShowService {

    @GET("tv/{tv_id}")
    suspend fun getDetails(
        @Path("tv_id") id: Int,
        @QueryMap queries: Map<String, String>
    ): TvShowDetailsDTO
}

许多同类问题的解决方案建议检查待映射类是否存在重复属性,但TvShowDetailsDTO及其父类TvShowDTO并无重复属性,代码如下:

class TvShowDetailsDTO(
    backdropPath: String,
    @SerializedName("created_by")
    val createdBy: List<CreatedByDTO>,
    @SerializedName("episode_run_time")
    private val episodeRunTime: List<Int>,
    @SerializedName("first_air_date")
    val firstAirDate: String,
    genres: List<GenreDTO>,
    val homepage: String,
    id: Int,
    @SerializedName("in_production")
    val inProduction: Boolean,
    val languages: List<String>,
    @SerializedName("last_air_date")
    val lastAirDate: String,
    @SerializedName("last_episode_to_air")
    val lastEpisodeToAir: EpisodeDTO,
    name: String,
    val networks: List<NetworkDTO>,
    @SerializedName("next_episode_to_air")
    val nextEpisodeToAir: EpisodeDTO?,
    @SerializedName("number_of_episodes")
    val numberOfEpisodes: Int,
    @SerializedName("number_of_seasons")
    val numberOfSeasons: Int,
    originCountry: List<String>,
    originalLanguage: String,
    originalName: String,
    overview: String,
    popularity: Double,
    posterPath: String,
    @SerializedName("production_companies")
    val productionCompanies: List<ProductionCompanyDTO>,
    @SerializedName("production_countries")
    val productionCountries: List<ProductionCountryDTO>,
    val seasons: List<SeasonDTO>,
    @SerializedName("spoken_languages")
    val spokenLanguages: List<SpokenLanguageDTO>,
    val status: String,
    val tagline: String,
    val type: String,
    voteAverage: Double,
    voteCount: Int
) : TvShowDTO(
    backdropPath,
    firstAirDate,
    genres,
    id,
    name,
    originCountry,
    originalLanguage,
    originalName,
    overview,
    popularity,
    posterPath,
    voteAverage,
    voteCount
) {
    private fun formatTimeMeasurement(timeMeasurement: Int): String {
        val hours = timeMeasurement / 60
        val minutes = timeMeasurement % 60
        return String.format("%02d h %02d min", hours, minutes)
    }

    val formattedEpisodesRuntime: List<String>
        get() {
            return episodeRunTime.map { formatTimeMeasurement(it) }
        }
}

open class TvShowDTO(
    @SerializedName("backdrop_path")
    val backdropPath: String?,
    @SerializedName("first_air_date")
    private val firstAirDate: String,
    @SerialName("genre_ids")
    val genres: List<GenreDTO>,
    val id: Int,
    val name: String,
    @SerializedName("origin_country")
    val originCountry: List<String>,
    @SerializedName("original_language")
    val originalLanguage: String,
    @SerializedName("original_name")
    val originalName: String,
    val overview: String,
    val popularity: Double,
    @SerializedName("poster_path")
    val posterPath: String?,
    @SerializedName("vote_average")
    val voteAverage: Double,
    @SerializedName("vote_count")
    val voteCount: Int
) {
    private fun formatDate(date: String): String {
        val inputFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
        val outputFormat = SimpleDateFormat.getDateInstance()
        val formattedDate = inputFormat.parse(date)
        return outputFormat.format(formattedDate)
    }

    val formattedFirstAirDate: String
        get() {
            return if (firstAirDate.isNotEmpty()) {
                formatDate(firstAirDate)
            } else {
                ""
            }
        }
}

NetworkModule中的Retrofit配置如下:

@Singleton
@Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit =
    Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

已确认依赖为最新版本,且功能逻辑相同的电影接口可正常填充MovieDetailsDTO。


可能原因及解决方案

  • 注解不兼容:TvShowDTO中genres字段使用了@SerialName,而Gson仅识别@SerializedName注解,将@SerialName("genre_ids")替换为@SerializedName("genre_ids")即可。
  • 非空性不匹配:TvShowDetailsDTO构造函数中的backdropPath、posterPath定义为非空String,但父类TvShowDTO中这两个字段是可空String?,接口返回的JSON中这两个字段可能为null,导致Gson无法构造对象。将TvShowDetailsDTO构造函数中的backdropPath: String和posterPath: String改为String?。
  • 嵌套DTO类问题:检查CreatedByDTO、EpisodeDTO等嵌套类是否存在注解错误、非空性不匹配,或缺少无参构造函数。如果使用Kotlin数据类,可添加kotlin-kapt插件并使用@NoArg注解生成无参构造,确保Gson能正常反射实例化。
  • Gson Kotlin特性支持不足:默认Gson对Kotlin特性支持有限,可添加com.google.gson:gson-kotlin依赖,并自定义Gson实例:
    val gson = GsonBuilder()
        .registerTypeAdapterFactory(KotlinAdapterFactory())
        .create()
    
    然后在Retrofit配置中使用该实例:GsonConverterFactory.create(gson)

内容的提问来源于stack exchange,提问作者Antonio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:55:12