Receiving NoClassDefFoundError when invoking generated serializer() method - kotlin

I’m getting a NoClassDefFoundError when trying to invoke the Foo.serializer() method on a #Serializable class.
Here's my test case:
#Serializable
data class Foo(val data: String)
val jsonString = json.stringify(
Foo.serializer(), // <= Error happens here
Foo(data = "foo")
)
Attempting to run the code results in the following stack trace:
java.lang.NoSuchMethodError: 'void kotlinx.serialization.internal.SerialClassDescImpl.<init>(java.lang.String, kotlinx.serialization.internal.GeneratedSerializer, int)'
at com.example.Foo$$serializer.<clinit>(Foo.kt:7)
at com.example.Foo$Companion.serializer(Foo.kt)

This is the result of version mismatches between Kotlin and Kotlinx.serialization, as they are relatively tightly coupled. In my case I was using Kotlin 1.3.71 and kotlinx.serialization 0.14.0, so the solution was to upgrade kotlinx.serialization to 0.20.0.

Related

How to properly user Parcelize with generic type in Kotlin?

Can't make the parcelization work as expceted.
That is the class:
#Parcelize
class ResultsWrapper<T>(
#SerializedName("results") var results: T
): Parcelable
In that case it says: Type is not directly supported by 'Parcelize'. Annotate the parameter type with '#RawValue' if you want it to be serialized using 'writeValue()'
Parcelization can't know the T implements Parcelable? No problem. Writing like this:
#Parcelize
class ResultsWrapper<T: Parcelable>(
#SerializedName("results") var results: T
): Parcelable
Now there is no compilation error, but there is build time error:
error: non-static type variable T cannot be referenced from a static context (in newArray and createFromParcel generated functions in static Creator)
Using #RawValue lead to same error.
How to make it right?

Kotlin Multiplatform Expected Annotations Returns `This class does not have a constructor` with neo4j actual typealias

I have a multiplatform project with a modal class, User.
User.kt
class User {
val id = -1
val username = ""
val age = -1
val nickname = ""
}
I also have expected and actual annotations
Annotation.kt [Common Module]
expect annotation class NodeEntity
expect annotation class Id
expect annotation class GeneratedValue
More over, I have their actual implementation
Annotation.kt [JVM Module]
actual typealias ValueFor = org.neo4j.ogm.annotation.ValueFor
actual typealias NodeEntity = org.neo4j.ogm.annotation.NodeEntity
actual typealias Id = org.neo4j.ogm.annotation.Id
actual typealias GeneratedValue = org.neo4j.ogm.annotation.GeneratedValue
actual typealias Relationship = org.neo4j.ogm.annotation.Relationship
I then went back and annotated my User.kt
#NodeEntity
class User {
#Id
#GeneratedValue
val id = -1
val username = ""
val age = -1
val nickname = ""
}
But When I compile it, I get this error
Task :compileKotlinJvm FAILED
e: ...User.kt: (13, 2): This class does not have a constructor
e: ...User.kt: (21, 6): This class does not have a constructor
e: ...User.kt: (22, 6): This class does not have a constructor
What Am I doing wrong?
N:B. Attempts done
Made the expected annotations have a constructor [no success]
Made the expected annotations match with a constructor [ERROR: Parameter ''{0}'' has conflicting values in the expected and actual annotation]
FYI: My build.gradle already has the noArg in place, so that the User.kt class is compiled with a no argument public constructor
Your expect annotations probably need explicit parenthesis.
expect annotation class SharedImmutable()
actual typealias SharedImmutable = kotlin.native.SharedImmutable
https://github.com/touchlab/Stately/blob/4b17057ad5d55f51f4ccf971cf79e51585ad2324/src/commonMain/kotlin/co/touchlab/stately/annotation/Annotations.kt#L26
I experienced a similar issue, but seemingly like the OP, I already included the explicit parenthesis. My particular issue had to do with the Java Library, in the Java Source Set, not being available to another Gradle Sub-project that was depending on it.
TL;DR
Assert that you are properly exposing the platform-specific dependencies. For instance, properly using implementation and api in the build.gradle files.
Elaborating on my scenario
I had a Gradle multi-project build:
Project
AppSubProject
LibrarySubProject
Where AppSubProject depended on LibrarySubProject. Both Gradle Sub-projects were Kotlin Multi-platform Modules.
In LibrarySubProject, there was an exposed Annotation Class:
Common Source Set:
expect annotation class Inject()
JVM Source Set:
actual typealias Inject = javax.inject.Inject
The Kotlin Common Inject annotation was available to AppSubProject since it depended on the LibrarySubProject.
AppSubProject/build.gradle:
...
commonMain {
dependencies {
implementation project(":LibrarySubProject")
...
The Cause of the Issue
In the LibrarySubProject/build.gradle file I wasn't exposing the JVM dependency:
...
jvmMain {
dependencies {
implementation "javax.inject:javax.inject:1"
...
As you can see, I was using implementation instead of api. So when I used the annotation on a constructor of a class in AppSubProject:
class Example #Inject constructor()
and when I built the AppSubProject, it couldn't resolve the JVM dependency, and was cryptically giving me the following error:
e: Example.kt: This class does not have a constructor
The Solution
The solution was simply to expose the JVM dependency so that it could be resolved in the other module. So changing the implementation to api resolved the issue.
...
jvmMain {
dependencies {
api "javax.inject:javax.inject:1"
...
Summation
If you experience this issue, assert the following:
The Kotlin Common Annotation Class explicitly provides the constructor parenthesis as noted in this answer
All necessary platform-specific dependencies are exposed properly

Kotlin parcelize issue with gson

I am using #parcelize for gson
Here is my class
#Parcelize
data class CommunityModel(#SerializedName("post") val post: PostModel,
#SerializedName("is_liked") val isLiked: Boolean,
#SerializedName("post_like") val postLike: QuestionModel,
#SerializedName("polling_options") val pollingOptions: List<PollingModel>,
#SerializedName("post_polled") val postPolled: Boolean) : Parcelable
I got error Unable to invoke no-args constructor for class. Register an InstanceCreator with Gson for this type may fix this problem..
But this error only presents on older android versions (below 5.0)
I tried implementing default constructor :
constructor: this(PostModel(), true, QuestionModel(), emptyList(), true)
But it gave me java.lang.VerifyError instead
I am using retrofit2 with rxjava2 and gson converter Version 2.3
My kotlin version is 1.1.51
Is it known bug? Or did I do something wrong?
No-arg compiler plugin
The no-arg compiler plugin generates an additional zero-argument constructor for classes with a specific annotation.
The generated constructor is synthetic so it can’t be directly called from Java or Kotlin, but it can be called using reflection.
This allows the Java Persistence API (JPA) to instantiate the data class although it doesn't have the zero-parameter constructor from Kotlin or Java point of view (see the description of kotlin-jpa plugin below).
Using in Gradle
The usage is pretty similar to all-open.
Add the plugin and specify the list of annotations that must lead to generating a no-arg constructor for the annotated classes.
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
}
}
apply plugin: "kotlin-noarg"
Source https://kotlinlang.org/docs/reference/compiler-plugins.html

How do I get the class name from a type name?

I am trying to deserialize a Json string into an object of type OperationResult<String> using Jackson with Kotlin.
I need to construct a type object like so:
val mapper : ObjectMapper = ObjectMapper();
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(*/ class of OperationResult */,,
/* class of String */);
val result : OperationResult<String> = mapper.readValue(
responseString, type);
I've tried the following but they do not work.
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(
javaClass<OperationResult>,
javaClass<String>); // Unresolved javaClass<T>
val type : JavaType = mapper.getTypeFactory()
.constructParametricType(
OperationResult::class,
String::class);
How do I get a java class from the type names?
You need to obtain instance of Class not KClass. To get it you simply use ::class.java instead of ::class.
val type : JavaType = mapper.typeFactory.constructParametricType(OperationResult::class.java, String::class.java)
Kotlin has a few things that become a concern when using Jackson, GSON or other libraries that instantiate Kotlin objects. One, is how do you get the Class, TypeToken, TypeReference or other specialized class that some libraries want to know about. The other is how can they construct classes that do not always have default constructors, or are immutable.
For Jackson, a module was built specifically to cover these cases. It is mentioned in #miensol's answer. He shows an example similar to:
import com.fasterxml.jackson.module.kotlin.* // added for clarity
val operationalResult: OperationalResult<Long> = mapper.readValue(""{"result":"5"}""")
This is actually calling an inline extension function added to ObjectMapper by the Kotlin module, and it uses the inferred type of the result grabbing the reified generics (available to inline functions) to do whatever is needed to tell Jackson about the data type. It creates a Jackson TypeReference behind the scenes for you and passes it along to Jackson. This is the source of the function:
inline fun <reified T: Any> ObjectMapper.readValue(content: String): T = readValue(content, object: TypeReference<T>() {})
You can easily code the same, but the module has a larger number of these helpers to do this work for you. In addition it handles being able to call non-default constructors and static factory methods for you as well. And in Jackson 2.8.+ it also can deal more intelligently with nullability and default method parameters (allowing the values to be missing in the JSON and therefore using the default value). Without the module, you will soon find new errors.
As for your use of mapper.typeFactory.constructParametricType you should use TypeReference instead, it is much easier and follows the same pattern as above.
val myTypeRef = object: TypeReference<SomeOtherClass>() {}
This code creates an anonymous instance of a class (via an object expression) that has a super type of TypeRefrence with your generic class specified. Java reflection can then query this information.
Be careful using Class directly because it erases generic type information, so using SomeOtherClass::class or SomeOtherClass::class.java all lose the generics and should be avoided for things that require knowledge of them.
So even if you can get away with some things without using the Jackson-Kotlin module, you'll soon run into a lot of pain later. Instead of having to mangle your Kotlin this module removes these types of errors and lets you do things more in the "Kotlin way."
The following works as expected:
val type = mapper.typeFactory.constructParametricType(OperationalResult::class.java, String::class.java)
val operationalResult = mapper.readValue<OperationalResult<String>>("""{"result":"stack"}""", type)
println(operationalResult.result) // -> stack
A simpler alternative to deserialize generic types using com.fasterxml.jackson.core.type.TypeReference:
val operationalResult = mapper.readValue<OperationalResult<Double>>("""{"result":"5.5"}""",
object : TypeReference<OperationalResult<Double>>() {})
println(operationalResult.result) // -> 5.5
And with the aid of jackson-kotlin-module you can even write:
val operationalResult = mapper.readValue<OperationalResult<Long>>("""{"result":"5"}""")
println(operationalResult.result)

Kotlin Annotation: Type mismatch: inferred type is java.lang.Class<foo> but java.lang.Class<out jet.Annotation> was expected

Given the following Kotlin Annotation:
public Retention(RetentionPolicy.RUNTIME) annotation class foo(val text : String)
and the following code to check if a class is annotated by above annotation:
if (javaClass<Bar>().isAnnotationPresent(javaClass<foo>())) {
// do something here.
}
If the annotation class (foo) and the code which is using it were located in the same module (maven artifact), the code can compile and run without any issue.
But, if I separate them into different modules, the following compile error happens:
Kotlin: Type mismatch: inferred type is java.lang.Class but
java.lang.Class was expected
Any idea what could be the issue?
Problem seems to be marked as fixed. https://youtrack.jetbrains.com/issue/KT-3197 This was a bug.