23

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.

Martynas Jurkus
  • 9,231
  • 13
  • 59
  • 101

4 Answers4

29

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

voddan
  • 31,956
  • 8
  • 77
  • 87
  • That wouldn't work since it requires default empty bean constructor. I'll update my question. – Martynas Jurkus Nov 24 '15 at 11:51
  • to have an empty constructor you can provide a default value to `size`. Updated – voddan Nov 24 '15 at 11:54
  • or annotate the primary constructor with `@ParcelConstructor` just as in your example – voddan Nov 24 '15 at 11:55
  • 1
    btw, for having an empty constructor you should not need `@JvmOverloads`, what I said above should be sufficient. Let me know when you try it out – voddan Nov 24 '15 at 12:15
  • @voddan how did you compiled parceler to kotlin? im not being able to make Parcel to work in Kotlin. – Fábio Carballo Jan 12 '16 at 14:31
  • @FábioCarballo I never did. For my project (a server) I use Jackson (kotlin module). I answered because the issue with Parcel was of the same nature that the one I solver with Jackson, and the syntax I looked up in the docs. – voddan Jan 12 '16 at 16:24
  • 1
    You may ask @MartynasJurkus, he made it work (obviously). For that you may want to post the compilation error you are getting. Or create a separate question, since chances are you problem is build-related, not kotlin-related. – voddan Jan 12 '16 at 16:27
  • 1
    @FábioCarballo see my updated question on how I got it working based on this answer – Martynas Jurkus Jan 12 '16 at 20:29
  • Using this approach I can get everything to compile, but it doesn't work, because the generated `Value$$Parcelable` class doesn't contain any code to write/read `size`. Am I missing something, has this been proved to work? – Jonathan Caryl Sep 29 '16 at 15:32
  • also worth noting in some scenarios `val` should be `var` if the value is expected to change – Ryhan Nov 09 '16 at 21:55
10

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

Bradley Campbell
  • 9,298
  • 6
  • 37
  • 47
5

Just add the default constructor:

@Parcel
data class Valve(val size: Int) {
    constructor() : this(0)
}
dmitriyzaitsev
  • 716
  • 9
  • 17
4

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
Radesh
  • 13,084
  • 4
  • 51
  • 64