Home > other >  Model class implementations for a generic solution
Model class implementations for a generic solution

Time:10-19

I have following project in Github : https://github.com/alirezaeiii/TMDb-Paging

I have a generic solution in my classes using TmdbItem interface, such as :

abstract class BaseFragment<T : TmdbItem> : BaseNavTypeFragment() {
    
   protected abstract val viewModel: BaseViewModel<T>

   protected lateinit var tmdbAdapter: TmdbAdapter<T>
}

TmdbItem interface and the class implementations are as follow :

interface TmdbItem : Parcelable {
    val id : Int
    val overview: String
    val releaseDate: String?
    val posterPath: String?
    val backdropPath: String?
    val name: String
    val voteAverage: Double
}

@Parcelize
data class Movie(
        override val id: Int,
        override val overview: String,
        @SerializedName("release_date")
        override val releaseDate: String?,
        @SerializedName("poster_path")
        override val posterPath: String?,
        @SerializedName("backdrop_path")
        override val backdropPath: String?,
        @SerializedName("title")
        override val name: String,
        @SerializedName("vote_average")
        override val voteAverage: Double) : TmdbItem

@Parcelize
data class TVShow(
        override val id: Int,
        override val overview: String,
        @SerializedName("first_air_date")
        override val releaseDate: String?,
        @SerializedName("poster_path")
        override val posterPath: String?,
        @SerializedName("backdrop_path")
        override val backdropPath: String?,
        override val name: String,
        @SerializedName("vote_average")
        override val voteAverage: Double) : TmdbItem

As you see I have to use @SerializedName in both classes even if the value is the same such as poster_path and backdrop_path. Is there anyway that I could write them in one place such as a base class instead of both class implementations?

CodePudding user response:

That is probably not easily possible with Gson. The closest you could get to avoid duplicating the name is to define the property names somewhere as const val and then refer to them in @SerializedName, e.g. @SerializedName(POSTER_PATH_NAME).

Another solution which is however quite complicated and error-prone would be:
Place @SerializedName on the getter functions of the interface, e.g. @get:SerializedName("..."). This is possible because @SerializedName supports METHOD as target, but Gson only considers it on fields by default. Then write a custom FieldNamingStrategy which does the following:

  1. Check if the field has a @SerializedName annotation, in that case return its value
  2. Otherwise, obtain the Kotlin property (if any) for the Java field and obtain its getter method:
    field.kotlinProperty?.javaGetter
    
  3. Check if the getter has a @SerializedName, in that case return its value
  4. Otherwise, go through the superclasses and then superinterfaces to check if any of them declare the getter (which is overridden) and have a @SerializedName

This is how it might work in theory; I have not tried to fully implement this yet. And as you can see the logic would be quite complex, so I am not sure if that would really be worth it.

Otherwise you might have to look for external Gson extensions which support getter methods, or other JSON libraries.

  • Related