Inserting data into database returns MismatchedInputException error - serialization

I am trying to insert some data into the database, and am getting the following error:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `org.joda.time.DateTime` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('2019-04-19')
My content negotiation
install(ContentNegotiation) {
jackson {
enable(SerializationFeature.INDENT_OUTPUT)
}
}
And my model:
data class User(
//some codes
val registrationDate = DateTime // org.joda.time.DateTime
)
And when will I send by json:
{
//some other data
"registrationDate" : "2019-07-15"
}
Can someone help me, please?

You have to install the Joda module for Jackson https://github.com/FasterXML/jackson-datatype-joda and add it to your jackson configuration in ktor :
install(ContentNegotiation) {
jackson {
registerModule(JodaModule())
enable(SerializationFeature.INDENT_OUTPUT)
}
}
You can also control serialization/deserialization behavior with annotations on your data class properties :
data class Account(
val uid: String? = null,
val firstName: String,
val lastName: String,
val email: String,
#get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
val createdTime: DateTime? = null
)

Related

Deserialize JSON from Riot API with kotlinx.serialization

I have some difficulties to deserialise this JSON object from RIOT API:
{
"type":"champion",
"version":"6.1.1",
"data":{
"Thresh":{
"id":412,
"key":"Thresh",
"name":"Thresh",
"title":"the Chain Warden"
},
"Aatrox":{
"id":266,
"key":"Aatrox",
"name":"Aatrox",
"title":"the Darkin Blade"
},...
}
}
Inside the data object we have an other object with fields of all champions.
To not create all champions objects, I want de deserialise this to an list of Champion object, I expect something like that:
{
"type":"champion",
"version":"6.1.1",
"data":[
{
"id":412,
"key":"Thresh",
"name":"Thresh",
"title":"the Chain Warden"
},
{
"id":266,
"key":"Aatrox",
"name":"Aatrox",
"title":"the Darkin Blade"
},...
]
}
I think I have to create a custom Serializer that extends KSerialize but I didn't really know how to do it, can someone help me ?
On C# stackoverflow response is : Deserialize JSON from Riot API C#
There is my solution:
(If someone know witch descriptor to put there I'm interested)
object ChampionsSerializer : KSerializer<List<NetworkChampion>> {
// TODO : Not the good descriptor, fix me
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("data", kind = PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): List<NetworkChampion> {
val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
val fieldsAsJson = jsonInput.decodeJsonElement().jsonObject
val jsonParser = jsonInput.json
return fieldsAsJson.map {
jsonParser.decodeFromJsonElement(it.value)
}
}
override fun serialize(encoder: Encoder, value: List<NetworkChampion>) {
}
}
#Serializable
data class NetworkChampionsResponse(
val type: String,
val format: String,
val version: String,
#Serializable(ChampionsSerializer::class)
val data: List<NetworkChampion>
)
Json link:
https://ddragon.leagueoflegends.com/cdn/13.1.1/data/fr_FR/champion.json

Parse to different type based on property

I am new to Kotlin, and I'm trying to figure out how parsing unknown JSON works.
I have the following set of classes:
#Serializable
abstract class Node {
val name: String = ""
val type: String = ""
abstract val spec: Any
}
#Serializable
class TestNode : Node() {
override val spec: TestNodeSpec = TestNodeSpec()
}
#Serializable
class TestNodeSpec {
val test: String = "testSpec"
}
I can successfully parse an object directly to a TestNode, but for my purpose I want to be able to read the type to determine the correct class to parse it as. How is this done in Kotlin?

Generic Base Respose models in Kotlin

Everyone following is my json response:
{
"requestResponse": {
"status": 1,
"result": true,
"msg": "Success"
},
"userId": 5504
}
And following is my Base Response class:
class BaseResponses<T>{
lateinit var requestResponse: RequestResponse
}
and following are my User data class parameters.
data class User(val userId:Int)
And below as implementation:
#POST(ApiUrls.CREATE_USER)
fun createUser(#Body body: CreateUser): Single<BaseResponses<User>>
my question is that how can I access T type which is User in the Base class would highly appreciate the help.
Thanks
You don't need a genetic type - you need to inherit the properties.
data class BaseResponses { // Remove T, it's doing nothing
lateinit var requestResponse: RequestResponse
}
// Extend base class to inherit common `requestResponse` field
data class User(val userId:Int) : BaseResponses()
// User now will contain requestResponse object
#POST(ApiUrls.CREATE_USER)
fun createUser(#Body body: CreateUser): Single<User>
I might be understanding you wrong, you just want to re-use the RequestResponse class since it is generic and will be common in all your APIs. So just have it as a parameter in User data class.
So it will be like this
data class User(
val requestResponse: RequestResponse,
val userId: Int
)
Now you can simply access it directly from User object. You can even go a step further and assign it default values like this
data class User(
val requestResponse: RequestResponse = RequestResponse(),
val userId: Int = 0
)
data class RequestResponse(
val msg: String = "",
val result: Boolean = false,
val status: Int = 0
)

Serialize enum field into JSON in Kotlin

I've got a stupid question that stunned me a bit.
I have an enum and a data class like this:
enum class MyEventType(val typeName: String) {
FIRST("firstEventReceived")
}
data class MyEvent(
val id: String,
val event: MyEventType
)
I need to send this as a json string, but common desearilizer makes such a json
{
"id": "identifier",
"event": "FIRST"
}
but i need
{
"id": "identifier",
"event": "firstEventReceived"
}
As far as i understand, kotlin allows to override getter in data class, but i didn't succeed in it... Trying to make
data class MyEvent(
val id: String
) {
val event: MyEventType get() event.typeName
}
but i've missed something, i guess...
The simplest way is probably to annotate the property with #JsonValue:
enum class MyEventType(#JsonValue val typeName: String) {
FIRST("firstEventReceived")
}
data class MyEvent(
val id: String,
val event: MyEventType
)
fun main() {
MyEvent(id = "foo", event = MyEventType.FIRST)
.let { jacksonObjectMapper().writeValueAsString(it) }
.let { println(it) }
}
Prints:
{"id":"foo","event":"firstEventReceived"}
The easiest way is to annotate the typeName with #JsonValue. This will serialise and deserialise the enum field as you want.
enum class MyEventType(#JsonValue val typeName: String) {
FIRST("firstEventReceived");
}
An alternative is to use #JsonFormat (if you are using jackson version < 2.9);
enum class MyEventType(#JsonFormat(shape = JsonFormat.Shape.OBJECT) val typeName: String) {
FIRST("firstEventReceived");
}
Herer's an example;
#JvmStatic
fun main(args: Array<String>) {
val mapper = jacksonObjectMapper()
val json = mapper.writeValueAsString(MyEvent("1", MyEventType.FIRST))
println(json)
val event = mapper.readValue<MyEvent>(json)
println(event)
}
You get the output;
{"id":"1","event":"firstEventReceived"}
MyEvent(id=1, event=FIRST)
I used Jackson version 2.12.0. Here's a good read on enum manipulation with Jackson - https://www.baeldung.com/jackson-serialize-enums
Also you can have enum with 2+ fields which you want to be serialized
enum class MyEventType(
val firstField: String,
val secondField: String,
val thirdField: String
) {
MY_ENUM("firstFieldValue", "secondFieldValue", "thirdFieldValue")
}
You can chose one of the following two options:
Put #JsonValue over a method(lets call it getter) that will return the required value(if you need only part of the fields):
#JsonValue
fun getSerializedObject(): String {
return "{firstField: $firstField, thirdField: $thirdField}"
}
Result will be "{firstField: firstFieldValue, thirdField: thirdFieldValue}"
Put #JsonFormat(shape = JsonFormat.Shape.OBJECT) over your enum class(for serialization class as common class):
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
enum class MyEventType(
val firstField: String,
val secondField: String,
val thirdField: String
) {
MY_ENUM("firstField", "secondField", "thirdField")
}
Result will be "{"firstField": "firstFieldValue", "secondField": "secondFieldValue", "thirdField": "thirdFieldValue"}"
For GSON users, you can use the #SerializedName annotation:
enum class ConnectionStatus {
#SerializedName("open")
OPEN,
#SerializedName("connecting")
CONNECTING,
#SerializedName("closed")
CLOSED
}

How to deserialize an array of values into a collection using kotlinx serialization

Hi I am a newbie to kotlinx serialization and I am using KMP, my requirement is a little different
my data class
#Serializable data class Student(val name : String , val age : Int)
and my simple JSON would be "['Avinash', 22]",
which should be deserialized to Student("Avinash", 22)
I'm not able to deserialize it can anyone help me
While input such as [Avinash, 22] is not well formed Json, you can still
work with it by parsing it into a JsonElement:
import kotlinx.serialization.json.*
data class Student(val name: String, val age: Int)
fun decode(stringData: String, parser: Json): List<Student> {
val element: JsonArray = parser.parseToJsonElement(stringData).jsonArray
return element.windowed(2, 2).map {
Student(
it[0].toString(),
it[1].toString().toInt()
)
}
}
fun main() {
val parser = Json { isLenient = true }
val students = decode("[A, 22, B, 33, C, 44]", parser)
println(students)
// [Student(name=A, age=22), Student(name=B, age=33), Student(name=C, age=44)]
}
Try this:
val student: Student = Json.decodeFromString("{\"name\": \"Avinash\", \"age\": \"22\"}")
Pay attention how to format your JSON string.
[] square brackets are for arrays
{} curly brackets are for objects
And you must provide your fields name, and use double quotes for fields and values, or use a less strict Json deserialization:
val json = Json {
isLenient = true
}
val student: Student = json.decodeFromString("{name: Avinash, age: 22}")
If you want a deep view on json schema, you can read here.