I am trying to serialize derived object using Gson in Kotlin and on serialization I am getting only parent class attributes Gson is ignoring all derived class attributes.
Here is my code:
fun main(args: Array<String>) {
val somename = Sample(1, "somename")
val car = Car(somename, 4)
val car2 = Car(somename, 4)
val vehicles = object : ArrayList<Vehicle>() {
}
vehicles.add(car)
vehicles.add(car2)
val sampleClassResponse = SampleClassResponse(1, vehicles)
val gson = Gson()
val x = gson.toJson(sampleClassResponse)
println(x)
// System.out.println(car.tyre);
}
// Base class:
public class Vehicle {
int number;
String name;
}
Sub class:
import Sample.Sample;
public class Car extends Vehicle {
int tyre;
public Car(Vehicle sample, int tyre) {
super(sample.number, sample.name);
this.tyre = tyre;
}
}
SampleResponse class
public class SampleClassResponse {
private int status;
private List<Vehicle> vehicles;
public SampleClassResponse(int status, List<Vehicle> vehicles){
this.status = status;
this.vehicles = vehicles;
}
}
So here in the main class when I am serializing SampleClass response I am getting output as(with ignored Tyre values)
{"vehicles":[{"number":1,"name":"somename"},{"number":1,"name":"somename"}],"status":1}
and when same code is written in java I am getting output as:
{"status":1,"vehicles":[{"tyre":4,"number":1,"name":"somename"},{"tyre":4,"number":1,"name":"somename"}]}
Could anyone help me in figuring out what am i doing wrong in this kotlin code.
Note: I have used intellij converter to convert code to Kotlin
Here is the java code
public class MainObject {
public static void main(String[] args) {
Sample somename = new Sample(1, "somename");
Car car = new Car(somename, 4);
Car car2 = new Car(somename, 4);
ArrayList vehicles = new ArrayList<Vehicle>() {
};
vehicles.add(car);
vehicles.add(car2);
SampleClassResponse sampleClassResponse = new
SampleClassResponse(1, vehicles);
Gson gson = new Gson();
String x = gson.toJson(sampleClassResponse);
System.out.println(x);
}
}
Base Class:
public class Vehicle {
int number;
String name;
}
Derived class:
public class Car extends Vehicle {
int tyre;
public Car(Sample sample, int tyre) {
this.number = sample.number;
this.name = sample.name;
this.tyre = tyre;
}
}
SampleResponse class:
public class SampleClassResponse {
int status;
List<Vehicle> vehicles = new ArrayList<>();
public SampleClassResponse(int status, List<Vehicle> vehicles){
this.status = status;
this.vehicles = vehicles;
}
}
your code does not compile and that's why I do not know where the problem is.
my proposition of the code in the kotlin is as follows
fun main(args: Array<String>) {
val vehicle = Vehicle("somename", 1)
val car = Car(vehicle, 4)
val car2 = Car(vehicle, 4)
val vehicles = listOf(car, car2)
val response = SampleClassResponse(1, vehicles)
val json = Gson().toJson(response)
println(json)
}
class SampleClassResponse(val status: Int, val vehicles: List<Vehicle>)
open class Vehicle(var name: String?, var number: Int = 0)
class Car(sample: Vehicle, var tyre: Int) : Vehicle(sample.name, sample.number)
now is what you would like to get
{"status":1,"vehicles":[{"tyre":4,"name":"somename","number":1},{"tyre":4,"name":"somename","number":1}]}
Related
I have a annotation AggregateId that could be set on method params and properties and that I will use to retrieve some id :
#Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
#Retention(AnnotationRetention.RUNTIME)
annotation class AggregateId
I wrote that test case :
data class Example(
#AggregateId
val id: UUID
)
class AggregateIdTests {
private val exUuid = UUID.fromString("bae30706-f949-4eb5-b091-d51a13ddc832")
#Test
fun test() {
val ex = Example(id = exUuid)
val id = resolve(ex)
Assertions.assertThat(id).isEqualTo(exUuid)
}
private fun resolve(target: Any): UUID? {
val prop = target::class.declaredMemberProperties.find {
it.findAnnotation<AggregateId>() != null
}
return prop?.getter?.call(target) as UUID?
}
}
That actually works.
But if I add this class in the code :
class TestClass {
fun aMethod(#AggregateId param: UUID) {
}
}
Suddently the AggregateId changes of target for the other class. Even though I didn't change the rest of the code. What is the explaination of this ?
(using kotlin 1.5)
I'm working on data class extension with polymorphic property. Here's the dataclass:
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
data class CarModelResponse(
val models: List<CarType> = listOf(),
)
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
#JsonSubTypes(
JsonSubTypes.Type(MercedesType::class, name = "mercedes"),
JsonSubTypes.Type(OpelType::class, name = "opel"),
)
abstract class CarType(open val type: String) {
abstract fun getCarFeature(): Any
}
data class MercedesType(
val comfortClass: Int
) : CarType("mercedes") {
override fun getCarFeature(): Int = comfortClass
}
data class OpelType(
val coupon: String
) : CarType("opel") {
override fun getCarFeature(): String = coupon
}
and also i have a test, where i'm trying to get a typed car variable:
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.junit.jupiter.api.Test
class CarResponseTest {
#Test
fun getCarFeatures() {
val json: String = """
{
"models": [
{
"type": "mercedes",
"comfortClass": 1
},
{
"type": "opel",
"coupon": "Buy Opel and win a Mercedes! Coupon #1."
}
]
}""".trimIndent()
val response = jacksonObjectMapper().readValue<CarModelResponse>(json)
val comfortClass: Int = (response.models.first() as MercedesType).getCarFeature()
val couppon: String = (response.models.last() as OpelType).getCarFeature()
}
}
Deserialization works fine, but I need to retrieve car's feature without casting with as at last two lines of code, but I'm a little stuck how to do that. Could anyone advice how can I get rid of it?
You can't do it with no casting, because compiler needs to know what type must be assigned, however in this case you can avoid casting to MercedesType or OpelType:
val comfortClass: Int = response.models.first().getCarFeature() as Int
val coupon: String = response.models.last().getCarFeature() as String
Using Kotlin with Junit 4 I get the following exception for Parameter field injection:
java.lang.IllegalAccessException: Class org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters can not access a member of class MyTestClass with modifiers "private"
Here's the code:
#RunWith(Parameterized::class)
class MyTestClass {
#Rule
#JvmField
val mockitoRule: MockitoRule = MockitoJUnit.rule()
companion object {
#Parameters(name = "{0}")
#JvmStatic
fun testData() = listOf(
arrayOf(1, 1),
arrayOf(2, 2),
arrayOf(3, 3)
)
}
#Parameter
var input: Int = 0 // Public
#Parameter(1)
var expected: Int = 0 // Public
#Test
fun foo() {
assertEquals(expected, input)
}
}
Any ideas?
Tl;dr: Adding #JvmField to both fields solved the problem.
Like so:
#JvmField
#Parameter
var input: Int = 0
#JvmField
#Parameter(1)
var expected: Int = 0
Explanation: By default, Kotlin will make the fields private and generate getters/setters as can be seen from the decompiled java code below, as a result JUnit won't be able to read the private fields hence the message: can not access a member of class MyTestClass with modifiers "private"
#Parameter
private int input;
#Parameter(1)
private int expected;
public final int getInput() {
return this.input;
}
public final void setInput(int var1) {
this.input = var1;
}
public final int getExpected() {
return this.expected;
}
public final void setExpected(int var1) {
this.expected = var1;
}
Im trying to make universal function in Kotlin, which can instantiate every time different model classes.
Class type is a parameter, to make instance from that class and fill it with data from Json object.
fun<T> foo() {
var myModel = T()
myModel.id = 2
myModel.name = ""
}
You can use an inline reified function in combination with an interface.
With reified you can access the real class of the generic type parameter.
You still not allowed to call the constructor directly, but reflection will work.
To assign the id and name, you need to define an interface, that all of your model classes are required to implement:
interface Model {
var id: Int?
var name: String?
}
inline fun <reified T : Model> createModel() : T {
val myModel = T::class.createInstance()
myModel.id = 2
myModel.name = ""
return myModel
}
A simple example:
class TestModel() : Model {
override var id: Int? = null
override var name: String? = null
}
fun main() {
val model: TestModel = createModel()
}
You cannot use T definition itself, pass class to the function instead.
import kotlin.reflect.KClass
open class Model {
var id: Int? = null
var name: String? = null
fun say() {
println("hello.")
}
}
class MyModel: Model()
fun<T: Model> foo(type: KClass<T>): T {
val myModel = type.java.newInstance()
myModel.id = 2
myModel.name = ""
return myModel
}
val mymodel = foo(MyModel::class)
mymodel.say() // hello.
I'm converting a Java class that extends an abstract class as per the code below
public class BadRequestAlertException extends AbstractThrowableProblem {
private static final long serialVersionUID = 1L;
private final String entityName;
private final String errorKey;
public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) {
this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey);
}
public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) {
super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey));
this.entityName = entityName;
this.errorKey = errorKey;
}
public String getEntityName() {
return entityName;
}
public String getErrorKey() {
return errorKey;
}
private static Map<String, Object> getAlertParameters(String entityName, String errorKey) {
Map<String, Object> parameters = new HashMap<>();
parameters.put("message", "error." + errorKey);
parameters.put("params", entityName);
return parameters;
}
}
Into Kotlin as per the implementation below
class BadRequestAlertException(type: URI = ErrorConstants.DEFAULT_TYPE, defaultMessage: String, val entityName: String, val errorKey: String) : AbstractThrowableProblem(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey)) {
companion object {
private const val serialVersionUID = 1L
private fun getAlertParameters(entityName: String, errorKey: String): Map<String, Any> {
val parameters = HashMap<String, Any>()
parameters["message"] = "error.$errorKey"
parameters["params"] = entityName
return parameters
}
}
}
The Kotlin implementation raises the following error Class 'BadRequestAlertException' is not abstract and does not implement abstract base class member public abstract fun getCause(): Exceptional! defined in org.zalando.problem.AbstractThrowableProblem.
Why doesn't the Java version implement the getClause() method, but Kotlin requires the method to be overridden? Is there a problem with the conversion, or just the way Kotlin extends abstract classes?
FWIW it appears as though the legitimate bug that originally caused this issue is fixed in Kotlin 1.5.0. See KT-45853 ("JVM / IR: "Accidental override" caused by inheriting Throwable.getCause from Java interface") for details... the following Kotlin class compiles & runs as far as I can tell:
import com.fasterxml.jackson.annotation.JsonIgnore
import java.net.URI
import org.zalando.problem.AbstractThrowableProblem
import org.zalando.problem.Exceptional
import org.zalando.problem.Status
class BadRequestAlertExceptionKt(
type: URI = URI.create("about:blank"),
defaultMessage: String,
val entityName: String,
val errorKey: String
) : AbstractThrowableProblem(
type,
defaultMessage,
Status.BAD_REQUEST,
null,
null,
null,
getAlertParameters(entityName, errorKey)
) {
companion object {
private const val serialVersionUID = 1L
private fun getAlertParameters(entityName: String, errorKey: String): Map<String, Any> {
val parameters = HashMap<String, Any>()
parameters["message"] = "error.$errorKey"
parameters["params"] = entityName
return parameters
}
}
#JsonIgnore
override fun getCause(): Exceptional? = super.cause
}