Search code examples
androidkotlingsonretrofit2

Unable to create converter for class,retrofit


enter image description here I use an image because the formatting of the code here in stack overflow is giving me a lot of problems

This is the error I get trying to call a method of the following interface:

interface TvShowService {

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

In many answers to a problem EQUAL to mine it is advised to check if, in the class to be mapped there are equal attributes but, as you can see there are none:

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.kt

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

I checked the dependencies and they are up to date, also I have a method that performs the same operations but for movies and it works, so it succeeds in populating MovieDetailsDTO.

What could be a possible problem or at least a possible solution. Looking around I have mostly found solutions involving names between duplicate attributes.


Solution

  • Solutions

    1° problem

    There were two errors that actually involved identical parent and child class attributes, but at first it was not possible for me to intercept them.

    Let us examine the two classes TvShowDetailsDTO which inherits from 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, <-- ERROR
         genres: List<GenreDTO>,
       //...
       ):TvShowDTO(
         backdropPath,
         firstAirDate,
         / /...
       )
    

    And his class father:

    open class TvShowDTO(
        @SerializedName("backdrop_path")
        val backdropPath: String?,
        @SerializedName("first_air_date")
        private val firstAirDate: String,<-- THE REAL ONE
        //...
        )
    

    By making this attribute of the parent class, private, this was not visible in the child class (TvShowDetailsDTO) and thus was defined twice and obviously could not be mapped leading to an error in the mapper.

    1° Solution

    The solution was to make the parent class attribute visible only at the hierarchy level (protected) and to remove in the child class the val keyword in front of the duplicate attribute, like this:

    open class TvShowDTO(
    @SerializedName("backdrop_path")
    val backdropPath: String?,
    @SerializedName("first_air_date")
    protected val firstAirDate: String,
    
    class TvShowDetailsDTO(
    backdropPath: String?,
    @SerializedName("created_by")
    val createdBy: List<CreatedByDTO>,
    @SerializedName("episode_run_time")
    private val episodeRunTime: List<Int>,
    firstAirDate: String,
    

    2° problem

    Another very similar problem resided in the List<CreatedByDTO> attribute of TvShowDetailsDTO, here is the class:

        class CreatedByDTO(
        @SerializedName("credit_id")
        val creditId: String,
        val gender: Int?,
        id: Int,
        name: String,
        profilePath: String?
    ) : PersonDTO(
        id, name, profilePath, gender
    )
    
    
    open class PersonDTO(
    val adult: Boolean,
    @SerializedName("also_known_as")
    val alsoKnownAs: List<String>,
    val biography: String,
    private val birthday: String?,
    @SerializedName("deathday")
    private val deathDay: String?,
    private val gender: Int?,
    

    Always a class hierarchy where making an attribute in the parent class, private leads to the same error, in this case the attribute was gender defined twice, the solution was always the same, make it protected in the parent class so as to remove the keyword and @SerializedName from the child class.