Praveen P.
Praveen P.

Reputation: 1106

How can I map a DataModel class to a DomainModel class?

I am creating DataModel and DomainModel classes to avoid null checks in my code. How can I map a nested class? What default values can I give?

MyDataModel.kt (don't have much info about the API so I need to assume that all values can be null)

data class MyDataModel(
    @SerializedName("id")
    val id: Int?,
    @SerializedName("time_to_show")
    val timeToShow: Int?,
    @SerializedName("author")
    val author: Author?,
    @SerializedName("question")
    val question: Question?,
    @SerializedName("answers")
    val answers: List<Answer>?
) {
    data class Author(
        @SerializedName("first_name")
        val firstName: String?,
        @SerializedName("last_name")
        val lastName: String?,
        @SerializedName("image")
        val image: String?
    )

    data class Question(
        @SerializedName("id")
        val id: Long?,
        @SerializedName("title")
        val title: String?
    )

    data class Answer(
        @SerializedName("id")
        val id: Long?,
        @SerializedName("title")
        val title: String?,
        @SerializedName("image")
        val answerImage: AnswerImage?
    ) {
        data class AnswerImage(
            @SerializedName("0")
            val x0: AnswerImageData?
        ) {
            data class AnswerImageData(
                @SerializedName("id")
                val id: String?,
                @SerializedName("url")
                val url: String?
            )
        }
    }
}

MyDomainModel.kt (I will later on need to create a function to check if all properties on this class are valid)

data class MyDomainModel(
    val id: Int,
    val timeToShow: Int,
    val author: Author,
    val question: Question,
    val answers: List<Answer>
) {
    data class Author(
        val firstName: String,
        val lastName: String,
        val image: String
    )

    data class Question(
        val id: Long,
        val title: String
    )

    data class Answer(
        val id: Long,
        val title: String,
        val imageUrl: String?
    )
}

Mapper class

interface Mapper<F,S> {
    fun dataToDomainModel(dataModel: F): S
    fun domainToDataModel(domainModel: S): F
}


class DataToDomainMapper: Mapper<MyDataModel, MyDomainModel> {
    override fun dataToDomainModel(first: MyDataModel): MyDomainModel {
        return MyDomainModel(
            id = first.id ?: -1,
            title = first.title ?: "",
            question = ??????
        )
    }

    override fun domainToDataModel(second: MyDomainModel): MyDataModel {
        return MyDataModel(
            id = second.id,
            title = second.title,
            ...
        )
    }
}

Upvotes: 0

Views: 69

Answers (1)

Mohsen
Mohsen

Reputation: 1389

you don't need nested classes. bring classes out:

data class MyDataModel(
    @SerializedName("id")
    val id: Int?,
    @SerializedName("time_to_show")
    val timeToShow: Int?,
    @SerializedName("author")
    val author: Author?,
    @SerializedName("question")
    val question: Question?,
    @SerializedName("answers")
    val answers: List<Answer>?
) 

data class Author(
        @SerializedName("first_name")
        val firstName: String?,
        @SerializedName("last_name")
        val lastName: String?,
        @SerializedName("image")
        val image: String?
    )

data class Question(
        @SerializedName("id")
        val id: Long?,
        @SerializedName("title")
        val title: String?
    )

data class Answer(
        @SerializedName("id")
        val id: Long?,
        @SerializedName("title")
        val title: String?,
        @SerializedName("image")
        val answerImage: AnswerImage?
    )

data class AnswerImage(
            @SerializedName("0")
            val x0: AnswerImageData?
        )

data class AnswerImageData(
                @SerializedName("id")
                val id: String?,
                @SerializedName("url")
                val url: String?
            )

then you need to have a domain model for each of these classes and implement a mapper for each. then use the required mapper to map the properties of bigger classes( by bigger class I mean a class that have another one of the classes as a property)

your final code should look like this(with more mappers in constructor):

class DataToDomainMapper(val questionMapper: QuestionMapper): Mapper<MyDataModel, MyDomainModel> {
    override fun dataToDomainModel(first: MyDataModel): MyDomainModel {
        return MyDomainModel(
            id = first.id ?: -1,
            title = first.title ?: "",
            question = questionMapper.firstToSecond(first.question)
        )
    }

    override fun domainToDataModel(second: MyDomainModel): MyDataModel {
        return MyDataModel(
            id = second.id,
            title = second.title,
            ...
        )
    }
}

you need to handle nullable Question in the QuestionMapper

Upvotes: 1

Related Questions