Adrian Le Roy Devezin
Adrian Le Roy Devezin

Reputation: 843

How to reduce similar code that relies on class type to the parent?

I am using Jackson-Kotlin to serialize and deserialize JSON objects in my kotlin project. The problem is that I have to define the same method in every class with only one typeValue changed. This seems like very bad practice so I am coming to the community on what is the best way to handle this scenario.

Example:

class User: Model() {
    //...
    companion object {

        fun fromJson(json: String): User {
            return ObjectMapper().registerKotlinModule().readValue<User>(json)
        }

        fun toJson(user: User): String {
            return ObjectMapper().registerKotlinModule().writeValueAsString(user)
        }


    }
}

class Friend: Model() {
    //...
    companion object {

        fun fromJson(json: String): User {
            return ObjectMapper().registerKotlinModule().readValue<Friend>(json)
        }

        fun toJson(friend: Friend): String {
            return ObjectMapper().registerKotlinModule().writeValueAsString(user)
        }


    }
}

As you can see the code is very similar other than the type parameter on the readValue<>() method. Is there a way i can simply define one method in the Model() class instead of on each class that inherits from Model?

Upvotes: 0

Views: 222

Answers (2)

Alexey Romanov
Alexey Romanov

Reputation: 170835

You don't want to create an ObjectMapper and register a module each time.

class Model {
    companion object {
        // could be public if you want to do anything else with it
        private val objectMapper = ObjectMapper().registerKotlinModule()

        inline fun <reified T : Model> fromJson(json: String): User {
            return objectMapper.readValue<T>(json)
        }

        fun toJson(value: Model): String {
            return objectMapper.writeValueAsString(value)
        }
    }
}

Use:

val friend = Model.fromJson<Friend>(...)

or just Model.fromJson(...) when there is an expected type.

Also from the same page:

Input: byte[] is best if you have it; InputStream second best; followed by Reader -- and in every case, do NOT try reading input into a String!

Output: OutputStream is best; Writer second best; and calling writeValueAsString() is the least efficient (why construct an intermediate String?)

Upvotes: 0

urgentx
urgentx

Reputation: 3931

It looks like you can employ generics here.

Companion objects have no notion of type arguments of the class, as they are not instances of the class, so you can either make your functions instance methods to be inherited:

class Model<T> {
    fun fromJson(json: String): User {
        return ObjectMapper().registerKotlinModule().readValue<T>(json)
    }

    fun toJson(friend: T): String {
        return ObjectMapper().registerKotlinModule().writeValueAsString(friend)
    }
}

or let the functions themselves accept type parameters:

class Model {
    companion object {
        fun <T> fromJson(json: String): User {
            return ObjectMapper().registerKotlinModule().readValue<T>(json)
        }

        fun <T> toJson(friend: T): String {
            return ObjectMapper().registerKotlinModule().writeValueAsString(friend)
        }
    }
}

Upvotes: 2

Related Questions