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

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.

Related

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.

Replacement for struct in Kotlin and how to store the data in the run time

I have to store and update the below variables in Kotlin
string name;
Array of Class Objects(5)
Array of Int(5)
C++ format:
struct subject
{
string name;
Array of Class Objects(5)
Array of Int(5)
};
vector<subject> sub;
In other programming languages C/C++ for ex, we use struct and put everything above in that.
Questions:
How to store and update above values with mixture of different types like Array, string, etc., in Kotlin?
Arrays will not get updated in one stretch. Ex: When someone calls AIDL interface with name, I create instance of class and stored the object in array of class obj(0) and integer array(0) as well updated with some value.
When the same AIDL interface is called with same name again, second instance of class will be created and store in **array of class obj(1)**and integer array(1) as well updated. As name is same, there is no need to update it again.
How to check the name and update the other arrays in the run time?
An additional use case, I need to make vector of that struct(according to C++). How I can achieve this in Kotlin?
Instead of a struct you would use a class in Kotlin: https://kotlinlang.org/docs/classes.html. There are several differences between the two that are relevant:
The declaration and class members and there implementation are done in the same place.
The constructor declaration is built into the class declaration.
Kotlin leans towards immutability. While you can reassign fields more often you will see val (like const) and immutable collections.
With that said, you would do something like this to implement your struct in Kotlin. The following isn't a literal 1 for 1 translation, but rather how you might solve your problem with idiomatic Kotlin:
class Subject(val name: String) {
val objects = mutableListOf<NameOfThatClass>()
val numbers = mutableListOf<Int>()
}
What's going on in that code snippet is that we are declaring a class Subject. It has a constructor that takes one argument called name of type String. The val keyword means that the argument will also be kept as a member variable, and that member variable cannot be reassigned. Next, in the class body, we declare and assign two more member variables. objects and numbers will also not be reassignable because of the val keyword, but instead of receiving a constructor argument as a value they receive the result of calling mutableListOf(), which creates more or less the equivalent of a vector. We could also use arrayOfNulls(5) and arrayOfInt(5), but unless you very specifically need fixed-sized arrays it's easier and more common to use lists in Kotlin.
You would then use it like so:
val myName = "foo"
val myFirstObject = ...
val myFirstNumber = 1
val mySubject = Subject(myName)
mySubject.objects += myFirstObject
mySubject.numbers += myFirstNumber
The += you see there isn't an actual reassignment, but an operator overload that acts as Kotlin's equivalent of std::vector's push_back(): https://kotlinlang.org/docs/collection-write.html#adding-elements.
Finally, as mentioned above, Kotlin's lists are what you would normally use in place of vector. However, it sounds like you want to be able to look up a specific entry by name, which is more efficient to do with a map https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/. You could do something like this:
val myMap = mutableMapOf<String, Subject>()
// add to the map like this
myMap[name] = Subject(name)
// get from the map like this (returns null if not in the map)
val mySubject = myMap[name]
// check if the subject is already in the map like this
myMap.containsKey(name)
Then, if you need to iterate over all the Subjects like you would with a vector, you can use myMap.values to get just the Subjects.

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.

Realm: How to tell if a field is nullable via DynamicRealm?

Does DynamicRealm offer a way to determine if a field is nullable on a kotlin RealmModel subclass?
For example, given the following realm models:
open class IntObj(
var i: Int = 1
): RealmObject()
open class NullIntObj(
var i: Int? = null
): RealmObject()
If I fetch a dynamic realm object representation of each type and try to determine the field type of parameter "i" via dynamicRealmObj.getFieldType("i"), they both return "INTEGER".
Is there a way I can determine the nullability of a field on a dynamic realm object? Do I have to use reflection to try and determine what the underlying Java field is annotated with?
Well I'm an idiot - turns out dynamicRealm.isNullable(fieldName) totally exists :)