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
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
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?
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
)
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
}
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.