I am creating a string of data class for our API optimization like this
data class ex:
#MyAnnotation
data class Cast(
#Json(name = "cast_id")
val castId: Int,
val name: String,
#Json(name = "profile_path")
val profilePath: String?
)
wanted result:
{"cast_id":1, "name":1, "profile_path":1}
but currently able to generate it
"{"castId":1, "name":1, "profilePath":1}"
I want access #JSON (or #SerializedName) annotation and its value, how do I get it?
then Using Kotlin poet to create class with this string init.
Related
Between two separate data classes, Person and PersonRecord, which share the same attribute names, I want an elegant way to copy the values from one class's attributes to the other's.
I have a data class, say for example Person, that defines the business logic data of a person in the application.
import kotlinx.serialization.Serializable
data class Person(
val id: String,
val name: String,
val age: Int,
val currentEmployment: Employment,
val workPermit: WorkPermit
)
#Serializable
data class Employment(
val employer: String,
val job: String,
val yearsWithEmployer: Double
)
#Serializable
data class WorkPermit(
val nationality: String,
val visa: String
)
I need to use these with an AWS DynamoDB client, but this question doesn't really concern DynamoDB specifically. I'll explain my usage below.
For several reasons, I've decided to implement a DAO class that is essentially a copy of the class Person, called PersonRecord except the fields containing complex types, i.e., Employment and WorkPermit, are stored as Strings instead. Also, all the fields are mutable and nullable. I had to make it this way because it's supposed to be a mapper class for DynamoDB Enhanced Client (doc).
Annotating this class as #DynamoDbBean defines how the client writes items into a specified table.
package util
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable
import software.amazon.awssdk.enhanced.dynamodb.Key
import software.amazon.awssdk.enhanced.dynamodb.TableSchema
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey
#DynamoDbBean
internal data class PersonRecord(
#get: DynamoDbPartitionKey
#get: DynamoDbSortKey
var id: String? = null,
var name: String? = null,
var age: Int? = null,
var currentEmployment: String? = null,
var workPermit: String? = null,
)
class PersonDao(
ddb: DynamoDbEnhancedClient,
personTableName: String
) {
private val personTable: DynamoDbTable<PersonRecord> = ddb.table(
personTableName,
TableSchema.fromBean(PersonRecord::class.java)
)
private fun toPersonRecord(person: Person): PersonRecord =
PersonRecord(
id = person.id,
name = person.name,
age = person.age,
currentEmployment = Json.encodeToString(person.currentEmployment),
workPermit = Json.encodeToString(person.workPermit)
)
private fun toPerson(personRecord: PersonRecord): Person =
Person(
id = personRecord.id!!,
name = personRecord.name!!,
age = personRecord.age!!,
currentEmployment = Json.decodeFromString(
personRecord.currentEmployment!!
),
workPermit = Json.decodeFromString(
personRecord.workPermit!!
)
)
fun writePerson(person: Person) =
personTable.putItem(toPersonRecord(person))
fun readPerson(id: String): Person? {
val personRecord = personTable.getItem(
Key.builder()
.partitionValue(id)
.build()
)
return if (personRecord != null) toPerson(personRecord)
else null
}
}
I am using the public functions readPerson and writePerson to read and write the pretty Person class, while these functions internally convert to and fro PersonRecord.
Is there a way to copy between the different classes Person and PersonRecord more elegantly? If, in the future, we change the shape of Person slightly, there's a lot to change in the PersonRecord and PersonDao classes too. In particular, I need a way to handle decoding String to Employment and WorkPermit, and vice-versa.
In the example above, it'd be trivial to add a field or two, but in my actual application I'm dealing with over a dozen fields, and a bunch of unit tests intricately involved with the fields themselves.
Someone suggested to use class reflections, but I don't understand how I'd use it based on what the Kotlin docs describe.
You can try to read Person properties into a map via reflections (there is no other way) and use delegated properties feature to construct PersonRecord from that map.
https://kotlinlang.org/docs/delegated-properties.html#storing-properties-in-a-map
Here is a sample of reading via reflection https://stackoverflow.com/a/38688203/8642957
Yes, MapStruct is great and it's available in kotlin via kapt.
I have the following Kotlin data class:
data class Filter #JvmOverloads constructor(
val key: String? = null,
val operation: String? = null,
val value: String? = null,
val group: String? = null,
val searchInTranslations: Boolean? = null)
I create an instance of this class in Java code. But I want to create an instance with all the fields except for the group field. How can I do it in Java?
I can't assign a default value to group because in some Java classes I do assign a value to this field, in other Java classes I need to omit this field from object creation.
Java doesn't support this, but you can make a simple trick.
Generate two constructors, one with the optional field group, one without it.
So from the classes you don't need to assign a value to that field, simply make a call to the second constructor:
public YourClass(String key, String operation, String value, bool search)
{
this (key, operation, value, null, search); //null for the groups
}
Let's say i have any class, like this one:
class SomeClass(val aThing: String, val otherThing: Double)
Then I use reflection to analyze the fields of this class:
for(field in SomeClass.declaredMemberProperties){
}
How can I check the type of each field?
Since Kotlin does not have fields but only properties with backing fields, you should check the return type of the property.
Try this:
class SomeClass(val aThing: String, val otherThing: Double)
for(property in SomeClass::class.declaredMemberProperties) {
println("${property.name} ${property.returnType}")
}
UPDATE:
If the class does not use custom getters and/or setters without backing fields, you can get the type of the backing field like this:
property.javaField?.type
As a complete example, here is your class with an additional val property called foo with a custom getter (so no backing field is created). You will see that getJavaField() of that property will return null.
class SomeClass(val aThing: String, val otherThing: Double) {
val foo : String
get() = "foo"
}
for(property in SomeClass::class.declaredMemberProperties) {
println("${property.name} ${property.returnType} ${property.javaField?.type}")
}
UPDATE2:
Using String::class.createType() will return the KType for every KClass, so you can use e.g. property.returnType == String::class.createType() to find out if it's a (kotlin) String.
I can't figure out how should i deal with generics in kotlin.
I'm writing a history class for changes made on generic objects, which should get any type of class as parameter: after that, I would compare the old object values with the new object values, and if I found a difference, I'll write that in my data class.
I've succedeed doing that with java with bean.getClass().getMethods();, but I want to trying move to Kotlin.
class ChangeHistoryUtils<T> (val originalBean : T, username : String , var modifiedBean: T? = null) {
data class ChangeHistory(val username: String, val fieldName : String,
val oldValue : String , val newValue : String , val date : LocalDate = LocalDate.now())
fun compareBeans(){
//how to get all originalBean getters and its values?
}
}
I'm actually stuck here: how should obtain all the getters in my T object?
Let's guess i'll receive a class which with 10 getters, I want to call all these 10 getters in originalBean, and comparing its value with the ones in modifiedBean. If different, I will write it in my ChangeHistory
Thanks
You need to ensure that T itself is not a nullable type, i.e. use something like where T : Any on the class declaration, e.g.:
class ChangeHistoryUtils<T> (originalBean : T, username : String , modifiedBean: T? = null) where T : Any
If you do that you can afterwards just access the methods as you did in Java, e.g. if you just want to reuse the code you already have:
fun compareBeans(){
originalBean::class.java.methods // this is actually your originalBean.getClass().getMethods() !
// just print the methods for now...
.forEach(::println)
}
But as you are using Kotlin you may rather want to use the Kotlin approach then, e.g. just showing the properties, or similar:
originalBean::class.memberProperties
// again just printing them:
.forEach(::println)
You then need to add kotlin-reflect as dependency. You may also want to check the Kotlin reference regarding reflection.
I have a kotlin data class and I'm trying to call it from Java method.
data class Item (
#PrimaryKey(autoGenerate = true) var var1: Long? ,
#ColumnInfo(name ="var1") var var2: Long){}
From Java , I'm trying to save a list of Item so I need to instance Data class. I don't understand how I can do it.
Instantiating a data class is not different from instatiating a "normal" Kotlin class.
From your Java code, you instantiate it as if it were a Java class:
Item item = new Item(1L, 2L);
Just for reference, a data class is a class that automatically gets the following members (see documentation here):
equals()/hashCode() pair;
toString() of the form "MyClass(field1=value1, field2=value2)";
componentN() functions corresponding to the properties in their order of declaration; this can be useful for destructuring declarations, such as:
data class Item(val val1: Long, val val2: Long)
fun main(args: Array<String>) {
val item = Item(1L, 2L)
val (first, second) = item
println("$first, $second")
}
This will print: 1, 2
copy() function.
Your data class will be like this :
data class Item (#PrimaryKey(autoGenerate = true) var var1: Long?,
#ColumnInfo(name ="var1") var var2: Long);
From Java you can create create object like this:
Item item=new Item(1L,2L);
long firstValue=item.getVar1();
long secondValue=item.getVar2();
If you want to create instance in kotlin it will be like:
val item=Item(1L,2L);
val firstValue:Long?=item.var1;
val secondValue:Long?=item.var2;