33

I have a working code serializing/deserializing data using Moshi 1.8.0

Upgrading to 1.9.1 now leads to a crash when attempting to serialize:

java.lang.IllegalArgumentException: Cannot serialize Kotlin type com.xxx.Spot. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapter from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.

Here is the serializer code:

val moshi = Moshi.Builder().build()
val dataListType = newParameterizedType(List::class.java, T::class.java)
val adapter: JsonAdapter<List<T>> = moshi.adapter(dataListType)
val json = adapter.toJson(dataList)

and the corresponding T class is

@IgnoreExtraProperties
data class Spot(
    var id: String = "",
    var localizedName: String? = null,
    var type: String = "",
    var location: Location? = null
)

I'm totally clueless about what to do here.

Thanks for the help!

Wang Liang
  • 4,244
  • 6
  • 22
  • 45
lorenzo
  • 1,487
  • 1
  • 17
  • 25

4 Answers4

52

You need to add @JsonClass(generateAdapter = true) before your data class

@JsonClass(generateAdapter = true) 
data class Spot(
    var id: String = "",
    var localizedName: String? = null,
    var type: String = "",
    var location: Location? = null
)
Olle Ekberg
  • 804
  • 8
  • 11
  • 3
    Why is that needed? – Morten Holmgaard Nov 01 '19 at 20:41
  • 9
    ... and include the corresponding kapt config in your build. Details here: https://github.com/square/moshi/blob/master/README.md#kotlin – Jesse Wilson Nov 02 '19 at 05:32
  • 1
    Thanks, it worked! Adding @JsonClass(generateAdapter = true) + changing the gradle implementation from 'moshi' to 'moshi-kotlin' + kapt did the trick – lorenzo Nov 02 '19 at 12:54
  • 2
    Even after using the JsonClass annotation and trying both the reflection method (moshi-kotlin gradle implementation) and the codegen method Moshi still threw the "Cannot Serialize" runtime exception. Has anyone else had trouble even after making these changes? – Cody Aug 12 '20 at 19:07
  • @Cody - do you have enums in the class that you want to serialize? – rimes Jan 08 '21 at 09:43
  • @rimes yep, i do – Cody Jan 08 '21 at 16:30
  • Then you need to exclude them from generating an adapter. Add this to the enum class @JsonClass(generateAdapter = false) and add this to the proguard file -keepclassmembers @com.squareup.moshi.JsonClass class * extends java.lang.Enum { ; **[] values(); } Here you can read about serializing enums with moshi https://stackoverflow.com/questions/64068151/enum-parsing-fails-with-moshi-and-r8 – rimes Jan 09 '21 at 16:29
20

The other option if you don't want to add @JsonClass annotations all over the place is to add KotlinJsonAdapterFactory to the Moshi Builder.

val moshi = Moshi.Builder()
    .addLast(KotlinJsonAdapterFactory())
    .build()

This uses reflection and you need to add a dependency to com.squareup.moshi:moshi-kotlin as explained here https://github.com/square/moshi#kotlin

miguel
  • 16,205
  • 4
  • 53
  • 64
  • 1
    Important to note that you need to use `.addLast()` instead of `.add()` for Moshi v1.12+, as per their github docs https://github.com/square/moshi/blob/master/README.md#kotlin – Cody May 07 '21 at 22:39
0

You can suppress wild cards using @JvmSuppressWildcards.

like this

val adapter: JsonAdapter<List<@JvmSuppressWildcards T>> = moshi.adapter(dataListType)
Khaled Qasem
  • 879
  • 7
  • 20
0

You should add this annotation

@JsonClass(generateAdapter = true) 

to your data class to be like this

@JsonClass(generateAdapter = true) 
data class Spot(
        var id: String = "",
        var localizedName: String? = null,
        var type: String = "",
        var location: Location? = null
     )

then you can use Moshi kotlin extensions to deserialize your json array like this:

val Spots: List<Spot>? = yourJson.deserializeList()
Mazen Rashed
  • 139
  • 1
  • 3