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
Related
In my Micronaut Kotlin project I have two repositories. I am using MapStruct in both of them.
Source Class
#JsonInclude
data class Source(
var id: String,
var no: String,
var value: String,
)
Destination Class
#JsonInclude
data class Destination(
var id: String,
var no: String,
var value: String,
)
Mapper
#Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
interface SourceMapper {
fun convertToDestination(doc: Source): Destination
}
One of them first extracted the values of source class attributes and then passes these values to the parameterised constructor generated by kotlin data class
public Destination convertToDestination(Source doc){
if ( doc = = null ) {
return null;
}
String id = null;
String no = null;
String value = null;
id = doc.getId();
no = doc.getNo();
value= doc.getValue();
Destination d = new Destination(id, no,value);
}
Another one first creates the object then uses setter to assign value which fails at compile time as default constructor is not present for the data class.
public Destination convertToDestination(Source doc)
{
if ( doc == null ) {
return null;
}
Destination d = new Destination();
d.setId(doc.getId());
d.setNo(doc.getNo());
d.setValue(doc.getValue());
return d;
}
I have compared both the Gradle files and I am using same version of mapstruct.
```kapt("org.mapstruct:mapstruct-processor:1.5.0.RC1")``` and ```implementation("org.mapstruct:mapstruct:1.5.0.RC1")```
Please help me understand the behaviour and how we can control it.
I need to search for objects using org.springframework.data.mongodb.core.query.Query and org.springframework.data.domain.Pageable.
I implemented a search with getting the number of objects each time. This is not very optimal, as I understand it.
fun search(query: Query, pageable: Pageable): Page<BookDocument> {
val bookObjects = mongoTemplate.find(
query.with(pageable),
BookDocument::class.java
)
val count = mongoTemplate.count(query, BookDocument::class.java)
return PageImpl(bookObjects, pageable, count)
}
Is it possible to implement a search without counting?
Actually my function should return such PageDto. But I don't know how it can be most optimally implemented.
class PageDto<T>(
val pageable: Pageable,
val data: List<T>,
val hasNext: Boolean,
val hasPrevious: Boolean // optional field
)
So, I solved this issue like this:
fun search(query: Query, pageable: Pageable): PageDto<BookDocument> {
val bookObjects = mongoTemplate.find(query, BookDocument::class.java)
return PageDto(pageable, bookObjects)
}
data class PageDto<T>(
val pageable: Pageable,
val content: List<T>,
val hasNext: Boolean,
val hasPrevious: Boolean
) {
constructor(
pageable: Pageable,
data: List<T>
) : this(
pageable = pageable,
content = data.take(pageable.pageSize),
hasNext = data.count() > pageable.pageSize,
hasPrevious = pageable.offset > 0
)
}
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.
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.
I'm learning the sample "Kotlin for Android Developers (the book) " at https://github.com/antoniolg/Kotlin-for-Android-Developers
There are three classes in different kt file, I think the three classes are similar, and these mulit classes make the program complex.
How can I redesign the project framework and make project more clear?
DbClasses.kt
class DayForecast(var map: MutableMap<String, Any?>) {
var _id: Long by map
var date: Long by map
var description: String by map
var high: Int by map
var low: Int by map
var iconUrl: String by map
var cityId: Long by map
constructor(date: Long, description: String, high: Int, low: Int, iconUrl: String, cityId: Long)
: this(HashMap()) {
this.date = date
this.description = description
this.high = high
this.low = low
this.iconUrl = iconUrl
this.cityId = cityId
}
}
tables.kt
object DayForecastTable {
val NAME = "DayForecast"
val ID = "_id"
val DATE = "date"
val DESCRIPTION = "description"
val HIGH = "high"
val LOW = "low"
val ICON_URL = "iconUrl"
val CITY_ID = "cityId"
}
DomainClasses.kt
data class Forecast(
val id: Long,
val date: Long,
val description: String,
val high: Int,
val low: Int,
val iconUrl: String
)
In terms of database related classes, you may think about using ORM Library that will annotate fields and generate database table schema (remove need for DayForecastTable) e.g. Room (https://developer.android.com/training/data-storage/room/index.html)
You could technically use those classes across your whole app to reduce the need for DomainClasses, although I would suggest keeping Domain layer classes to make domain model independent from database.