Jackson ignores data class fields that aren't boolean but are of names starting with "is" - kotlin

I'm using Jackson with Kotlin binding in my project. We have a data class that has a field of type Map<A, B> and is named "isRecommended". When Jackson serializes the data class, this field gets omitted in the resultant JSON string.
A simple test to reproduce the same behavior:
class FooKotlin {
#Test
fun testFoo() {
println(jacksonObjectMapper().writeValueAsString(Foo1(true)))
println(jacksonObjectMapper().writeValueAsString(Foo2(1)))
println(jacksonObjectMapper().writeValueAsString(Foo3("true")))
}
}
data class Foo1(val isFoo: Boolean)
data class Foo2(val isFoo: Int)
data class Foo3(val isFoo: String)
The console prints:
{"foo":true}
{}
{}
When I decompile the Kotlin bytecode, the three classes seem to have almost identical content except for the type of the field. So what is the cause of this behavior of Jackson?

As mentioned by #chrsblck it is related to the jackson-module-kotlin issue #80
On the version 2.10.1 it's not reproducible, although serialized properties names are different (the "is" prefix is not removed):
{"isFoo":true}
{"isFoo":1}
{"isFoo":"true"}
On the earlier versions, the issue can be fixed with a JsonProperty annotation:
data class Foo1(val isFoo: Boolean)
data class Foo2(#get:JsonProperty("foo") val isFoo: Int)
data class Foo3(#get:JsonProperty("foo") val isFoo: String)
{"foo":true}
{"foo":1}
{"foo":"true"}
Technically, naming a non-boolean property "isSomthing" is incorrect and violates JavaBeans specification. Jackson relies on the JavaBeans conventions, thus it gets confused.
If you can avoid such naming, I would advise doing so. Otherwise, you may face the same problems when calling the Foo* classes from Java code.

Related

Jooq: JsonConverter not converting jsonb into list of class when fetching data

This is a continuation of a first question I asked here: Jooq: How can I map a JSONB column to a Kotlin data class field?
Although I'm able to create new records just fine with the changes mentioned there, I'm not being able to fetch data like so:
fun findAllTrackedEvents(): List<TrackedEvent> {
return dslContext.select(*TRACKED_EVENT.fields())
.from(TRACKED_EVENT)
.fetchInto(TrackedEvent::class.java)
}
It seems that jackson is mapping the rows into LinkedHashMaps instead of mapping them into the fields of the Metadata data class. This is the error I'm getting:
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON:
object is not an instance of declaring class; nested exception is com.fasterxml.jackson.databind.JsonMappingException:
object is not an instance of declaring class (through reference chain: java.util.ArrayList[0]->com.my.project.tracked_event.TrackedEvent["metadata"]->java.util.ArrayList[0]->java.util.LinkedHashMap["tableRef"])]
data class TrackedEvent(
val id: UUID,
// other fields
val metadata: List<Metadata> // this metadata field in the database is of type jsonb
)
data class Metadata(
val tableRef: String,
val value: UUID
)
So it can convert the field properly when inserting but not when fetching?
In my previous answer, I suggested you use arrays instead of lists. This had a reason. Consider this:
fun main() {
val a: Array<Int?> = arrayOf(1)
println(a::class.java)
val b: List<Int?> = listOf(1)
println(b::class.java)
}
It prints:
class [Ljava.lang.Integer;
class java.util.Collections$SingletonList
As you can see, while arrays are reified on the JVM, other generic types are not, and the T type variable of List<T> is erased. It is possible that Jackson cannot figure out the correct type to unmarshal at runtime using reflection, despite all the type information being available at compile time.
I would just use Array<Metadata> instead. Alternatively, of course, you can attach a custom converter to the column, instead of using the out of the box <jsonConverter>. That way, you're in full control of the mapping.

How can I encode a typed class with Kotlinx Serialization?

I'd like to encode a given class of type T: EventData with Kotlinx Serialization encodeToString.
This is my code:
class EventDispatcher<T: EventData>(
val pubSubTemplate: PubSubTemplate
) {
/**
* Dispatch an event to the game engine event manager pipeline
*/
fun dispatchEvent(event: T, initiator: String) {
val eventData: String = Json.encodeToString(event)
}
The compiler tells me:
Cannot use `T` as reified type parameter. Use a class instead
Is there a way to make this still work?
For Json.encodeToString(event) to work, it needs the type information for T. But, this type information is lost at runtime due to the way how generics work in Kotlin/Java.
One way to retain the type information would be by making dispatchEvent an inline function with T as a reified type parameter.
However, this also raises the question how you want to serialize event. You could also use polymorphic serialization of EventData, rather than trying to serialize T. This will include an additional class discriminator in your serialized output (it necessarily has to for polymorphic serialization/deserialization to work).
If you serialize the concrete type T, this class discriminator wouldn't be included, which is questionable; how would whoever will deserialize this know what type it is?
In short, I think you need polymorphic serialization.

Reflection and Generics in Kotlin

I've written myself into a corner where I want an instance of Class<Foo<Bar>>. While there's no apparent reason that this shouldn't be valid, there seems to be no way to create one. Foo<Bar>::class.java is a syntax error, and Kotlin does not provide a public constructor for Class.
The code I'm writing is an abstraction layer over gson. Below is an overly-simplified example:
class Boxed<T : Any> (val value: T)
class BaseParser<U : Any> (
private val clazz: Class<U>
) {
//This works for 98% of cases
open fun parse(s: String): U {
return gson.fromJson(s, clazz)
}
//Presume that clazz is required for other omitted functions
}
//Typical subclass:
class FooParser : BaseParser<Foo>(Foo::class.java)
// Edge Case
class BarParser : BaseParser<Boxed<Bar>>(Boxed<Bar>::class.java) {
override fun parse(s: String): Boxed<Bar> {
return Boxed(gson.fromJson(s, Bar::class.java))
}
}
// not valid: "Only classes are allowed on the left hand side of a class literal"
In my production code, there are already dozens of subclasses that inherit from the base class, and many that override the "parse" function Ideally I'd like a solution that doesn't require refactoring the existing subclasses.
Actually, there is a reason this is impossible. Class (or Kotlin's KClass) can't hold parameterized types. They can hold e.g. List, but they can't List<String>. To store Foo<Bar> you need Type (or Kotlin's KType) and specifically ParameterizedType. These classes are somewhat more complicated to use and harder to acquire than simple Class.
The easiest way to acquire Type in Kotlin is by using its typeOf() utility:
typeOf<Foo<Bar>>().javaType
Gson supports both Class and Type, so you should be able to use it instead.
The closest you'll get is Boxed::class.java. This is not a language restriction but a JVM restriction. JVM has type erasure, so no generic types exist after compilation (thats also one of the reasons generics cant be primitives, as they need to be reference types to behave).
Does it work with the raw Boxed type class?
For this case, it looks like
BaseParser<Boxed<Bar>>(Boxed::class.java as Class<Boxed<Bar>>)
could work (that is, it will both type-check and succeed at runtime). But it depends on what exactly happens in the "Presume that clazz is required for other omitted functions" part. And obviously it doesn't allow actually distinguishing Boxed<Foo> and Boxed<Bar> classes.
I'd also consider broot's approach if possible, maybe by making BaseParser and new
class TypeBaseParser<U : Any>(private val tpe: Type)
extend a common abstract class/interface.

Jackson produces invalid field names from Kotlin value classes

When a Kotlin class has a property that is one of the new #JvmInline value classes, Jackson converts it to JSON with a weird suffix on the field name.
e.g.
#JvmInline
value class ModelName(val value: String)
data class MyDto(val modelName: ModelName)
Jackson will produce JSON that looks like this:
{
"modelName-11MJ8YI": "Some Model Name"
}
I've tried adding a #JsonProperty("modelName") annotation but it doesn't make a difference.
Probably, upgrading jackson-module-kotlin will solve this problem.
https://github.com/FasterXML/jackson-module-kotlin/issues/356
If you can't upgrade, naming the getter as #get:JvmName("getModelName") should also solve the problem.
The reason for this problem is that the name of the method (getter) for the value class in Kotlin has a random suffix.

What is the purpose of empty class in Kotlin?

I was going through Kotlin reference document and then I saw this.
The class declaration consists of the class name, the class header
(specifying its type parameters, the primary constructor etc.) and the
class body, surrounded by curly braces. Both the header and the body
are optional; if the class has no body, curly braces can be omitted.
class Empty
Now I'm wondering what is the use of such class declaration without header and body
Empty classes can be useful to represent state along with other classes, especially when part of a sealed class. Eg.
sealed class MyState {
class Empty : MyState()
class Loading : MyState()
data class Content(content: String) : MyState()
data class Error(error: Throwable) : MyState()
}
In this way you can think of them like java enum entries with more flexibility.
tldr: they want to demonstrate it's possible
even an empty class is of type Any and therefore has certain methods automatically. I think in most cases, this does not make sense, but in the documentation case it's used to show the simplest possible definition of a class.
The Java equivalent would be:
public final class Empty {
}
From practical programmer day to day perspective empty class makes no much sense indeed. There are however cases where this behavior is desirable.
There are scenarios where we want to make sure that we want to define a class and at the same time, we want to make sure that instance of this class will never be created (type created from such class is called empty type or uninhabited type).
Perfect example of this is Kotlin Nothing class with do not have class declaration header and body (notice that it also have private constructor)
https://github.com/JetBrains/kotlin/blob/master/core/builtins/native/kotlin/Nothing.kt
There are few usages for Nothing in Kotlin language. One of them would be a function that does not return a value (do not confuse this with Unit where the function returns actually returns a value of type Unit). A typical example is an assertFail method used for testing or method that exits current process. Both methods will never actually return any value yet we need to explicitly say tell it to a compiler using special type (Nothing).
fun assertFail():Nothing {
throw Exception()
}
Nothing can be also used with start projections where type Function<*, String> can be in-projected to Function<in Nothing, String>
Another usage for empty class is type token or placeholder:
class DatabaseColumnName
class DatabaseTableName
addItem(DatabaseColumnName.javaClass, "Age")
addItem(DatabaseTableName.javaClass, "Person")
...
getItemsByType(DatabaseTableName.javaClass)
Some languages are using empty classes for metaprogramming although I haven't explored this part personally:
Advantages of an empty class in C++
An example of empty class usage from Spring Boot framework:
#SpringBootApplication
class FooApplication
fun main(args: Array<String>) {
runApplication<FooApplication>(*args)
}
It doesn't make much sense as a final result. However it can be useful in active development and at a design time as a placeholder of some sort, which may be expanded in the future. Such terse syntax allows you to quickly define such new types as needed. Something like:
class Person (
val FirstName: String,
val LastName: String,
// TODO
val Address: Address
)
class Address
I think main reason this is specifically mentioned in documentation is to demonstrate, that language syntax in general can be terse, not that it is specifically created for common usage.
Sealed classes, in a sense, an extension of enum classes: the set of values for an enum type is also restricted, but each enum constant exists only as a single instance, whereas a subclass of a sealed class can have multiple instances which can contain state.
reference