Jackson produces invalid field names from Kotlin value classes - kotlin

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.

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 to deserialize json to generic type in Kotlin using Jackson/kotlinx.serialization

I have a generic class class MyClass<T> : MyInterface<T> and I want to deserialize a json to generic type T. I tried using Jackson and kotlinx.serialization libraries to deserialize json but I get following error
cannot use T as reified type parameter. Use class instead.
My understanding of why this is happening is because both Jackson and kotlinx deserialize function expect reified T but in my class there is no way to know the type of T at compile time. Is my understanding of this error correct? Is there any way to resolve this error?
My code snippet
class MyClass<T> : MyInterface<T>{
.... <some code> ...
fun readFromJson(json: String){
val obj = jacksonObjectMapper().readValue<T>(json)
// same error if I use kotlinx Json.decodeFromString<T>(json)
...
}
.... <some code> ...
}
My understanding of why this is happening is because both Jackson and kotlinx deserialize function expect reified T but in my class there is no way to know the type of T at compile time. Is my understanding of this error correct?
Correct.
Is there any way to resolve this error?
It depends on what you're trying to do with the T in question. The best would be to lift readFromJson() out of this class, to a place where T can actually be reified.
If you really do need this function to be present in your class (e.g. you need to access some internal state or something), then you'll have to pass a KClass<T>/Class<T> (for Jackson) or a DeserializationStrategy<T> (for Kotlinx serialization) to the constructor of your class, so that you can use the non-reified overloads of readValue() or decodeFromString() which take this extra info as parameter.

Confused about the following line #field:[Expose SerializedName("id")]

I have the following data class that will retrieve data from an API:
data class Users(
#field:[Expose SerializedName("id")]
val id: Int)
I am just wondering what the #field: means.
Normally, I have always done like this:
data class Users(
#Expose
#SerializedName("id")
val id: Int)
I understand the meaning of expose and serializedName.
Just a few questions:
My best guess would be for the #field:[] would be to take an array of annotations, instead of putting them on each line as in the second example?
But is the field a Kotlin keyword or an annotation as it's preceded by the #?
Where else could you use the #field?
The val id in your example is declaring several different things in one go:
A constructor parameter.
A property of the class, implemented as a getter method.
A backing field for the property.
So which of those does an annotation get applied to?  It defaults to the parameter, and that's what your second example does.
If you want it to apply to the field instead, as in your first example, you use the field: target.
(It usually applies to single annotations, but it can apply to an array of them, as in this case.)
For more details, see the link jonrsharpe provided: https://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets
The field:, property:, file:, &c targets are only for use with annotations.  (field is also a keyword within getter/setter definitions.)

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

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.

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