open class Test {
fun getAsHashMap() : HashMap<String, Any> {
val hashMap = HashMap<String, Any>()
val className = this.javaClass.kotlin
for (prop in className::class.memberProperties) {
val field = className::class.java.getDeclaredField(prop.name)
val fieldSerializedName : SerializedName? = field.getAnnotation(SerializedName::class.java)
fieldSerializedName?.let {
hashMap[fieldSerializedName.value] = prop.get(this)!!
} ?: run {
hashMap[prop.name] = prop.get(this)!!
}
}
return hashMap
}
}
I have wrote above function to map the memberProperties of object instance of its child class to hashmap. It either uses serialized name of the member or prop name [Based on availability of serialized name for that property]
But unfortunately I get the following error.
This is my first time using reflection java/kotlin, please let me know if it can be fixed.
Edit 1:
It works perfectly if I use name of the this.javaClass.kotlin directly like this
data class ProductInformation (
#field:SerializedName("productid")
val productId: Int,
#field:SerializedName("productname")
val productName: String,
#field:SerializedName("brandname")
val brandName: String,
#field:SerializedName("originalprice")
val originalPrice: Int,
#field:SerializedName("sellingprice")
val sellingPrice: Int,
#field:SerializedName("productgender")
val productGender: String,
#field:SerializedName("productvariant")
val productVariant: String,
#field:SerializedName("discounted")
val discounted: String,
#field:SerializedName("productcategory")
val productCategory: String
) : StructuredEventAttribute {
override fun getAsHashMap(): HashMap<String, Any> {
val hashMap = HashMap<String, Any>()
for (prop in ProductInformation::class.memberProperties) {
val field = ProductInformation::class.java.getDeclaredField(prop.name)
val fieldSerializedName : SerializedName? = field.getAnnotation(SerializedName::class.java)
fieldSerializedName?.let {
hashMap[fieldSerializedName.value] = prop.get(this)!!
} ?: run {
hashMap[prop.name] = prop.get(this)!!
}
}
return hashMap
}
}
interface StructuredEventAttribute {
fun getAsHashMap() : HashMap<String, Any>
}
It works perfectly fine
ProductInformation::class.memberProperties returns a collection of ProductInformation class member properties.
className::class.memberProperties (where className = this.javaClass.kotlin) returns a collection of member properties of class of className, which is KClass<out Test>. In short you are getting members of KClass instead of Test.
Solution: change className::class.memberProperties to className.memberProperties.
Related
Can someone help me to fix this, I am trying to debug this method after upgrading from java conversion to java convertors. I am very new to scala.
def parse(table: String,json: String): Either[Map[String, _], Map[String, Object]] = {
try {
val parsedMap = jsonObjectMapper.readValue(json, classOf[MessageDetails])
//Convert from class type to Map type
val javaMap = jsonObjectMapper.convertValue(parsedMap,classOf[java.util.Map[String,Object]])
val msgBodyContent = javaMap.get("MessageDetails").asInstanceOf[java.util.LinkedHashMap[String,Object]]
// Move messageBody content to top level
var outMap = javaMap
if (msgBodyContent != null) {
outMap = javaMap - "MessageDetails" ++ msgBodyContent
}
Right(outMap.toMap)
} catch {
case e: Exception =>
logger.error("unable to parse JSON", e)
Left(createErrorRec(json,"JSON_PARSE_ERROR",e.getMessage,table))
}
}
getting error
value - is not a member of java.util.Map[String,Object] &
value toMap is not a member of java.util.Map[String,Object]
I am trying to use reflection to get all the optional fields in a class and their default values (if any). This is my attempt:
fun getOptionalFields(componentType: KClass<out Any>): Map<String, DefaultValueData> {
val cons = componentType.primaryConstructor
val constructorSetup = cons?.parameters
?.filterNot { it.isOptional }
?.associate { it to getValueForNonOptional(it) } ?: error("Something went wrong when choosing default val")
val constructorInst = (cons.callBy(constructorSetup)::class as KClass<Any>)
val conParams = (componentType.primaryConstructor?.parameters ?: emptyList())
.filter { p -> p.isOptional }
.associate { p ->
Pair(p.name ?: "",
DefaultValueData(
p.type,
// the below line crashes
constructorInst.memberProperties.first { m -> m.name == p.name }.get(constructorInst)
)
)
}
return conParams
}
The error: Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
I am a bit puzzled at what get() wants me to pass if not the instance to get the value from?
You are trying to get the value by providing a KClass instead of the actual instance.
This is a working solution based on your method signature and your code above:
data class Test(
val required: String,
val optional: Int = 7
)
val componentType = Test::class
val constructorInst = Test("required") // i skipped constructing the class via constructor
val conParams = (componentType.primaryConstructor?.parameters ?: emptyList())
.filter { p -> p.isOptional }
.associate { p ->
Pair(p.name ?: "",
Pair(
p.type,
componentType.memberProperties.first { m -> m.name == p.name }.get(constructorInst)
)
)
}
println(conParams) // <- OUTPUTS: {optional=(kotlin.Int, 7)}
Why have i removed this code?
val constructorSetup = cons?.parameters
?.filterNot { it.isOptional }
?.associate { it to getValueForNonOptional(it) } ?: error("Something went wrong when choosing default val")
val constructorInst = (cons.callBy(constructorSetup)::class as KClass<Any>)
The resulting object cons.callBy(constructorSetup) is unused because calling ::class on the expression rendered it useless. Additionally it is not required to perform the requested task in your question.
When updating your above code, result will look like
fun getOptionalFields(componentType: KClass<out Any>): Map<String, DefaultValueData> {
val cons = componentType.primaryConstructor
val constructorSetup = cons?.parameters
?.filterNot { it.isOptional }
?.associate { it to getValueForNonOptional(it) } ?: error("Something went wrong when choosing default val")
val constructorInst = cons.callBy(constructorSetup) // <- removed ::class and cast
val conParams = (componentType.primaryConstructor?.parameters ?: emptyList())
.filter { p -> p.isOptional }
.associate { p ->
val value = constructorInst::class.memberProperties.first { m -> m.name == p.name }.get(constructorInst) as KProperty1<out Any, out Any>
Pair(p.name ?: "",
DefaultValueData(
p.type,
value
)
)
}
return conParams
}
fun getListSize(clazz: KClass<*>) {
val session = hibernate.openSession()
val cb = session.criteriaBuilder
val criteria = cb.createQuery(clazz.java)
val root = criteria.from(clazz.java)
criteria.select(root)
val query = session.createQuery(criteria)
return query.resultList.size
}
I am getting an error on the line criteria.select(root) stating: Kotlin: Type mismatch: inferred type is Root<out Any!>! but Selection<out Nothing!>! was expected
It works perfectly fine with the class is specified such as Country::class.java but otherwise fails.
fun <T : Any> getListSize(clazz: KClass<T>) {
val session = hibernate.openSession()
val cb = session.criteriaBuilder
val criteria = cb.createQuery(clazz.java)
val root = criteria.from(clazz.java)
criteria.select(root)
val query = session.createQuery(criteria)
return query.resultList.size
}
The following changes to the function allowed the criteria.select(root) to work.
Adding <T : Any> to the function and passing the T as the type into KClass worked for me.
I have json as in below, I'm really new on kotlin and i tried all examples but cannot set my nested class values when i convert to json
Here my json
{"Init":{"MOP":[{"Id":"1","Type":"0","ProtocolVersion":"1.0","MopCode":"*NEXB","TerminalId":"'P400Plus-275008565'","IP":"'192.168.1.15'","Currency":"EUR"},{"Id":"2","Type":"0","ProtocolVersion":"1.0","MopCode":"*NEXF","TerminalId":"'P400Plus-275008565'","IP":"'10.0.0.0:901'","Currency":"EUR"}]}}
Here my POJO
class Root {
#JsonProperty("Init")
var init: Init? = null
}
class MOP {
#JsonProperty("Id")
var id: String? = null
#JsonProperty("Type")
var type: String? = null
#JsonProperty("ProtocolVersion")
var protocolVersion: String? = null
#JsonProperty("MopCode")
var mopCode: String? = null
#JsonProperty("TerminalId")
var terminalId: String? = null
#JsonProperty("IP")
var ip: String? = null
#JsonProperty("Currency")
var currency: String? = null
}
class Init {
#JsonProperty("MOP")
var mop: List<MOP>? = null
}
Here my trial
val root: TestClass.Root = gson.fromJson(receiveString,TestClass.Root::class.java)
val initList = HashMap<String?,String?>()
if (root.init != null){
val mopList = root.init!!.mop
if (mopList != null) {
for (item in mopList){
initList.put(item.mopCode,item.id)
}
}
}
Always root.init and root.init.mop are null
What you can suggest me?
Thanks
Your Json construction has different tree.
You should use following structure:
data class Root (
#SerializedName("Init") val init : Init
)
data class Init (
#SerializedName("MOP") val mOP : List<MOP>
)
data class MOP (
#SerializedName("Id") val id : Int,
#SerializedName("Type") val type : Int,
#SerializedName("ProtocolVersion") val protocolVersion : Double,
#SerializedName("MopCode") val mopCode : String,
#SerializedName("TerminalId") val terminalId : String,
#SerializedName("IP") val iP : String,
#SerializedName("Currency") val currency : String
)
And you can parse just with:
Gson().fromJson(data,Root::class.java)
Also if you are using Gson, you should use SerializedName instead JsonProperty.
I'm using Kotlin 1.3.10 (and bound to this version) and Kotlinx-Serialization 0.13 and I'm having trouble with serializing a map in Kotlinx-Serialization.
I have the following code:
#Serializer(forClass = LocalDate::class)
object LocalDateSerializer : KSerializer<LocalDate> {
private val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
override val descriptor: SerialDescriptor
get() = StringDescriptor.withName("LocalDate")
override fun serialize(encoder: Encoder, obj: LocalDate) {
encoder.encodeString(obj.format(formatter))
}
override fun deserialize(decoder: Decoder): LocalDate {
return LocalDate.parse(decoder.decodeString(), formatter)
}
}
#Serializable
data class MyClass (
val students: Map<String,LocalDate>
)
#UnstableDefault
#Test
fun decodeEncodeSerialization() {
val jsonParser = Json(
JsonConfiguration(
allowStructuredMapKeys = true
)
)
val mc = MyClass(
mapOf("Alex" to LocalDate.of(1997,2,23))
)
val mcJson = jsonParser.stringify(MyClass.serializer(), mc)
val mcObject = jsonParser.parse(MyClass.serializer(), mcJson)
assert(true)
}
There is a red line when inspecting the code which says "Serializer has not been found for 'LocalDate'. To use context serializer as fallback, explicitly annotate type or property with #ContextualSerialization."
With other types of fields, it would have been enough to add #Serialization to it.
#Serializable
data class Student (
val name: String,
#Serializable(with = LocalDateSerializer::class)
val dob: LocalDate
)
But with a map I can't seem to figure out how. I put it above, or beside the object...
#Serializable
data class MyClass (
val students: Map<String,#Serializable(with = LocalDateSerializer::class) LocalDate> //here
//or
//#Serializable(with = LocalDateSerializer::class)
//val students2: Map<String, LocalDate> //here
)
...but tests still fail with
kotlinx.serialization.SerializationException: Can't locate argument-less serializer for class java.time.LocalDate (Kotlin reflection is not available). For generic classes, such as lists, please provide serializer explicitly.
And the workaround I have for it is
#Serializable
data class MyClass (
val students: List<Student>
)
#Serializable
data class Student (
val name: String,
#Serializable(with = LocalDateSerializer::class)
val dob: LocalDate
)
Is there a way I would not resort to the workaround? Thank you!
#file:UseSerializers(LocalDateSerializer::class)
put this in the file where your object is declared it should use LocalDateSerializer every time it sees Local Date