Create generic transform serializer - kotlin

I have a beautiful JSON, where numbers (but not only) are "nulled" in the flowing way:
[{
"bar": ""
},
{
"bar": 123
}]
I would like to parse it to the:
data class(val bar: Long?)
I found out it can be quite easily done with following transformer.
object NullableStringSerializer : JsonTransformingSerializer<Long>(serializer()) {
override fun transformDeserialize(element: JsonElement): JsonElement =
if (element is JsonPrimitive && element.isString && element.content.isEmpty())
JsonNull
else element
}
#Serializable
data class(#Serializable(with = NullableStringSerializer::class) val bar: Long?)
This works nice, however I would like to make it more generic, so I wont need to write this transformer for every possible type.
Sadly due to the "generics" works in Kotlin, adding the type parameter to the object is not possible, and after changing it to be a class, serializer<T>() is crying about not having a refined type.
How can I make my NullableStringSerializer generic?

I just couldn't get NullableStringSerializer to work with empty strings at all (are you sure yours actually works?) In the end, I got it working like this:
#Serializable
data class X(val bar: JsonPrimitive) {
fun barValue() = bar.longOrNull
}

Related

Is it possible to easily build DSL in Kotlin?

TypeScript allows very nice and clean and 100% type-safe way to build data-like DSLs. I wonder if it's possible in Kotlin?
For example, in TypeScript code below (playground) we defining columns for data table. It checks that values are correct (string enums), checks all the optional / required fields, has autocomplete etc. And it just works out of the box, all you need to do is define types.
Is it possible to use something like that in Kotlin? It's possible to use Java Builder-pattern, but it's not ideal, and it requires to write lots of code for builder-methods. Also, Kotlin doesn't have a way to use "number" enum, it would be Type.number, doesn't look nice. Or maybe I'm missing something and there's a way to build nice and clean DSL in Kotlin without too much boilerplate code?
// Defining DSL ---------------------------------------------
type Type = "string" | "number" | "boolean" | "unknown"
interface StringFormatOptions {
type: "string"
}
interface LineFormatOptions {
type: "line"
ticks?: number[]
}
interface Column {
type: Type
format?: StringFormatOptions | LineFormatOptions
}
// Using DSL ------------------------------------------------
const columns: Column[] = [
{
type: "number",
format: { type: "line", ticks: [1000] }
},
{
type: "string"
}
]
Yes, you can create type-safe DSLs in Kotlin. It may be tricky to understand at first, but it really become very easy when you get used to it.
It works by creating functions that receive lambdas which have a specific receiver type... Well... let's try again. Assuming you are the user of already existing DSL, this is what happens:
There is a function that requires providing a lambda to it.
You provide a lambda.
The function provides a this parameter of a specific type to your lambda.
You can use properties/functions of provided this object in the lambda, effectively making possible to go deeper into DSL chain.
Let's see this example:
fun copy(init: CopyBuilder.() -> Unit) { TODO() }
interface CopyBuilder {
var from: String
var to: String
fun options(init: CopyOptionsBuilder.() -> Unit) { TODO() }
}
interface CopyOptionsBuilder {
var copyAttributes: Boolean
var followSymlinks: Boolean
}
We have a copy() function which receives a lambda. Provided lambda will have access to CopyBuilder object as this, so it will have access to e.g. from and to properties. By calling options() from the lambda we move deeper and now we have access to CopyOptionsBuilder object.
copy() is responsible for providing a proper implementation of CopyBuilder object to your lambda. Similarly, implementation of options() need to provide a proper implementation of CopyOptionsBuilder. This was omitted from the example above.
Then it can be used as:
copy {
from = "source"
to = "destination"
options {
copyAttributes = true
followSymlinks = false
}
}
If you use Gradle with Kotlin DSL then build.gradle.kts file is actually a regular Kotlin file. It just starts with some variables provided to you. Another good example of DSL in Kotlin is kotlinx.html library. It generates HTML code with syntax like this:
html {
body {
div {
a("https://kotlinlang.org") {
target = ATarget.blank
+"Main site"
}
}
}
}
You can read more about this here: https://kotlinlang.org/docs/type-safe-builders.html

How to chain functions returning Validated, Option, Either? (Monad Transformer)

I have simple three functions returning arrow-kt data types
fun validate(input): Validated<Error, Input> = ...
fun fetch(input): Option<Error, InputEntity> = ...
fun performAction(inputEntity): Either<Error, Output> = ...
And want to chain something like this (can use any available function instead of map)
validate(input)
.map{fetch(it)}
.map{performAction(it)}
Only solution I could come up with is to replace Validated and Option with Either and chain using flatMap. Is there any better functional way to make it work without updating the existing functions?
👋 What #pablisco described is correct, but you can keep it simpler by using some syntax extensions we provide to convert from one type to the other. Note that both options are correct, but Monad Transformers can be a bit convoluted and too powerful, and they're also prone to get removed from Arrow soon, once we finally figure out our delimited continuations style completely. But that is out of scope here. Here is how you could solve it by using the extensions I mentioned:
import arrow.core.*
import arrow.core.extensions.fx
sealed class Error {
object Error1 : Error()
object Error2 : Error()
}
data class InputEntity(val input: String)
data class Output(val input: InputEntity)
fun validate(input: String): Validated<Error, InputEntity> = InputEntity(input).valid()
fun fetch(input: String): Option<InputEntity> = InputEntity(input).some()
fun performAction(inputModel: InputEntity): Either<Error, Output> = Output(inputModel).right()
fun main() {
val input = "Some input"
Either.fx<Error, Output> {
val validatedInput = !validate(input).toEither()
val fetched = !fetch(validatedInput.input).toEither { Error.Error1 /* map errors here */ }
!performAction(fetched)
}
}
Hope it was useful 👍
What you are looking for is called a Monad Transformer. In Arrow, you may have seen them already, they end with a T at the end. Like OptionT or EitherT.
There are some good examples here for EitherT:
https://arrow-kt.io/docs/0.10/arrow/mtl/eithert/
And here for OptionT:
https://arrow-kt.io/docs/0.10/arrow/mtl/optiont/
The idea would be that to choose what your final value is going to be (let's say Either) and using an FX block you can then use EitherT to convert the other types to an Either.

Why is `foo in list` returning false even though the arrayListOf contains it?

I was trying out this piece of code in Kotlin which I wrote out(I am a beginner in Kotlin). I expected to receive "True" however I received "False" even though listo contains it. This is my code:
fun main(args: Array<String>) {
class product(var product: String, var productName: String)
val listo = arrayListOf(
product("shirt", "yoyo")
)
val testing = product("shirt", "yoyo")
if (testing in listo) {
println("True")
} else {
println("False")
}
}
How can I solve this problem? Any help is really appreciated
The product class doesn't override the equals method so it's doing an object instance comparison and the two lists contains different objects.
You can declare product with data class product(... which auto generates the equals method which will compare the two string properties meaning listo will contain testing. It also generates a bunch of other convenient methods.
https://kotlinlang.org/docs/reference/data-classes.html

is it possible to add a template to the getter/setter of a data class?

for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.

How do I cast a JavaScript object to a Kotlin object?

I have received a JavaScript object in response to a remote HTTP request. I have a kotlin model (trait) that defines the various fields I expect on the object (the nullable ones are optional).
First, I want to do an is check to make sure my object is in fact of the expected type. I initially tried payload is MyModel but that doesn't work due to the way the is operator is written in kotlin.js.
Second, I want to cast to MyModel so I can get auto-complete, etc. on the object while I work with it. Normally, the is alone would be enough but since that doesn't work I need something for this problem as well.
I would like to avoid manually populating my object from a dynamic. I wouldn't mind doing this so much if I could use by Delegates.mapVal(...) but that requires a Map<String, Any?> and I don't know how to get my dynamic/Any? payload into a Map<String, Any?>.
1) We don't have structure check for is in performance reasons.
I don't sure that we need generic solution for this, but anyway I created issue about it, feel free to vote or star it to get updates.
2) is enough if you use smart cast, like:
if (payload is MyModel) {
// call MyModel members on payload
}
But don't forget about (1) :)
3) You can write something like:
class MapDynamic<out V>(val d: dynamic) {
public fun get(thisRef: Any, desc: PropertyMetadata): V {
return d[desc.name]
}
}
class Foo(data: dynamic) {
val field: Int by MapDynamic(data)
}
fun main(args : Array<String>) {
val f = Foo(object { val field = 123 })
println(f.field)
}
But it looks too verbose, but You can add additional logic for e.g. when data don't have requested field. And if You don't need custom logic I think cast is enough.
For the second part, the cast, you can do:
fun responseHandler(payload: dynamic) {
val myModel = payload as MyModel
}
or
fun responseHandler(payload: dynamic) {
val myModel: MyModel = payload
}
This will throw an NPE if payload is null, but it won't actually validate that the payload matches MyModel. In particular, you may end up with null fields/properties that shouldn't be if the payload was missing those fields/properties.