Use of Parceler with Kotlin data class with constructor for serialization - parcelable

Is there a way to use Parceler with Kotlin data classes and constructor for serialization without using #ParcelProperty annotation for each field?
If I try and use library like this:
#Parcel
data class Valve #ParcelConstructor constructor(val size: Int)
I get Error:Parceler: No corresponding property found for constructor parameter arg0. But if I add #ParcelProperty("size") it works just fine.
Why is that?
Update:
There are other another way to use this library.
I could just remove #ParcelConstructor annotation, but then I will get error
Error:Parceler: No #ParcelConstructor annotated constructor and no default empty bean constructor found.
I think (haven't tested it) I also could make all constructor parameters optional and add #JvmOverloads but that has a side effect that I have to check all properties of the class if they are null or not.
Update 2:
This is what worked for me:
#Parcel
data class Valve(val size: Int? = null)
In short generated Java class must have default empty constructor. One way to achieve that is to do as above - all variables should have default values.

According to the docs, Parceler by default works with public fields. But a usual Kotlin data class (as in your example) is rather a "traditional getter/setter bean", since every Kotlin property is represented by a private field and a getter/[setter].
TL; DR: I think this will work:
#Parcel(Serialization.BEAN)
data class Valve(val size: Int = 10)
Note the default value, it allows Kotlin to automatically generate an additional empty constructor, which is required by the Java Been specification.
Another way would be to mark the constructor that we already have:
#Parcel(Serialization.BEAN)
data class Driver #ParcelConstructor constructor(val name: String)
The specific document: https://github.com/johncarl81/parceler#gettersetter-serialization

I know this question already has an answer, but for future viewers who are also struggling to get Parceler to work with kotlin data objects, I wrote a new annotation processor to generate the Parcelable boilerplate for Kotlin data classes. It's designed to massively reduce the boilerplate code in making your data classes Parcelable:
https://github.com/grandstaish/paperparcel
Usage:
Annotate your data class with #PaperParcel, implement PaperParcelable, and add a JVM static instance of the generated CREATOR e.g.:
#PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable {
companion object {
#JvmField val CREATOR = PaperParcelExample.CREATOR
}
}
Now your data class is Parcelable and can be passed directly to a Bundle or Intent
Edit: Update with latest API

Just add the default constructor:
#Parcel
data class Valve(val size: Int) {
constructor() : this(0)
}

if you use Kotlin 1.1.4 or above it's easier to use #Parcelize annotation
For doing this first add this to build.gradle
android {
//other codes
//for using latest experimental build of Android Extensions
androidExtensions {
experimental = true
}
}
Then change your class like this
#Parcelize
data class Valve(val size: Int? = null) : Parcelable

Related

#SerialInfo - How to manage user-defined serial annotations with Kotlinx serialization?

Kotlinx serialization documentation
According to Kotlinx.serialization user-defined annotations doc:
"Inside a process of serialization/deserialization, your own annotation class are available in SerialDescriptor object" :
override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean {
val annotations = desc.getElementAnnotations(index)
...
}
What I want to do
I need a #Transient equivalent, but conditional:
classic way where : Json.stringify(serializer, myClass) works as usual.
custom way where : Json.stringify(customSerializer, myClass) would return usual json but exculding all #MyAnnotation-tagged values.
Here is my code
#SerialInfo
#Target(AnnotationTarget.PROPERTY)
annotation class CustomAnnotation
#Serializable
data class MyClass(val a: String, #CustomAnnotation val b: Int = -1)
And I would like to build a custom Serializer and achieve something like
override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean {
val isTaggedAsCustomAnnotation = desc.getElementAnnotations(index).any{ it is CustomAnnotation }
val myCondition = mySerializer.getMyConditionBlablabla
if(myCondition && isTaggedAsCustomAnnotation) {
encode()
}
...
}
What I found
abstract class ElementValueEncoder : Encoder, CompositeEncoder {
...
open fun encodeElement(desc: SerialDescriptor, index: Int): Boolean = true
}
But I don't know how I can build a custom Serializer so that I can override that function Encoder.encodeElement. Where can I access to ElementValueEncoder in a custom Serializer ?
I also found this sample demo in kotlinx.serialization github repo. It's using TaggedEncoder & TaggedDecoder where I'm able to override encodeTaggedValue. But here again I don't know how I can use those encoder/decoder in a process of serialization/deserialization.
Finally
Where can I override fun encodeElement(desc: SerialDescriptor, index: Int): Boolean, and how I can handle my own-defined serialization annotation ?
Thanks !!
First of all, you need to grasp the difference between Serializer and Encoder. Serializer (represented by KSerializer) defines how your class looks like, and Encoder (represented by e.g. JsonOutput) defines how data will be recorded. You can find more info on that topic here: https://github.com/Kotlin/KEEP/blob/master/proposals/extensions/serialization.md#core-api-overview-and-mental-model .
So, custom annotations feature is mainly used for providing format-specific information to Encoder. Typical usage of such an annotation is ProtoId – property id, specific to protobuf format, that should be recognized by ProtobufEncoder. Such annotations are usually defined by format authors alongside their encoders.
What you want to do here, as I can see, is to use already existing encoder (JSON format), so overriding encodeElement is impossible since Json encoders can not be subclassed. I'd advise you to use custom json transofrming serializer to achieve your goal. Unfortunately, currently kotlinx.serialization does not have mechanism to generalize such a transformation, so you need to write such serializer for each class.

Can you define alternative shorthands for Kotlin Annotations?

I am using annotations and reflection to create a parser for some custom made files used in the project I work with
I have this annotation that will be used to annotate most data class constructor parameters
annotation class Element(val name: String = "",val type: ElementType = ElementType.Value)
the enum ElementType has these values
enum class XElementType {
Value,
Attribute,
Ignore
}
is there a way to create a shorthand or alternate so that instead of using
#Element(type=ElementType.Ignore)
val ignoredVariable:String
I can use something like
#IgnoreElement
val ignoredVariable:String
which will resolve to Element("",ElementType.Ignore) ?

Generic constraint for "data" class objects

I would like to semantically constrain a map to only accept "data" class object types as the value in kotlin like so:
class Test(
val test : Int
)
data class Test2 (
val test : Int
)
fun test(map : Map<String, /* compile error on Test, but accept Test2 or any other data class */>) {
}
I'm mainly trying to do this so that I can keep everything in the map cloneable, but when I do this:
fun <T: Cloneable> test(map : Map<String, T>) {
// test clone
map.map { it.key.uuid to it.value.clone() } .toMap() // error on .clone() Cannot access 'clone': it is protected in 'Cloneable'
}
but I thought implementing the Cloneable interface made your clone method public? Essentially I'm looking for a compile time guarantee that all data is copyable in that method invocation, (is a primitive type, a data class that I can call .copy() on, or any object that has implemented Cloneable). Is my only option reflection and runtime assertions?
I thought implementing the Cloneable interface made your clone method public?
No, it's simply a marker interface, which tells the protected Object.clone() method not to throw a CloneNotSupportedException.  In practice, classes that implement Cloneable will usually override clone() and make it public, but that's not necessary.  And of course that's no help when you don't know the exact type!
The cloning mechanism was an early part of Java, and not very well-designed.  (Effective Java calls it “a highly atypical use of interfaces and not one to be emulated”.)  But it's still used, so we're stuck with it…
(See also these related answers.)
I don't know whether this is the best way or not, but how about you to use property like below.
SomeClass::class.isData
Kdoc says
true if this class is a data class.

What is legitimate way to get annotations of a pure Kotlin property via reflection, are they always missing?

I'm trying to get annotations from Kotlin data class
package some.meaningless.package.name
import kotlin.reflect.full.memberProperties
annotation class MyAnnotation()
#MyAnnotation
data class TestDto(#MyAnnotation val answer: Int = 42)
fun main(args: Array<String>) {
TestDto::class.memberProperties.forEach { p -> println(p.annotations) }
println(TestDto::class.annotations)
}
I need to process class annotation to make a custom name serialization of GSON however no matter how I declare annotation class it never gets detected
The program always outputs
[]
[#some.meaningless.package.name.MyAnnotation()]
which means only class level annotations are present
Ok,
it seems that the culprit was, that Kotlin annotations have default #Target(AnnotationTarget.CLASS) which is not stressed enough in documentation.
After I added #Target to the annotation class it now works properly
#Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
annotation class MyAnnotation()
Now it prints out
[#some.meaningless.package.name.MyAnnotation()]
[#some.meaningless.package.name.MyAnnotation()]
As a side affect it will force the compiler to check that the annotation is applied as required, in current version of Kotlin, if explicit #Targetis not present only class level annotations are kept but no validity checks performed.
As Kotlin reference said as below:
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following: param > property > field.
To make the annotation annotated on a property, you should use site target, for example:
#MyAnnotation
data class TestDto(#property:MyAnnotation val answer: Int = 42)
However, annotations with property target in Kotlin are not visible to Java, so you should double the annotation, for example:
#MyAnnotation // v--- used for property v--- used for params in Java
data class TestDto(#property:MyAnnotation #MyAnnotation val answer: Int = 42)

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)