square / moshi

A modern JSON library for Kotlin and Java.
https://square.github.io/moshi/1.x/
Apache License 2.0
9.74k stars 758 forks source link

`@JsonClass(generateAdapter = true)` fails when class has field with type `Array<Array<T>>` with default #1862

Open SimoneBari-BS opened 3 months ago

SimoneBari-BS commented 3 months ago

Hello!

When testing Moshi CodeGen, I found a weird issue: when a class has a field with type Array<Array<Something>> with a default value, the generated adapter fails to find the correct constructor to create the class instance and deserialization fails with NoSuchMethodException

Here you can find a code snippet replicating the problem:

@Keep
@JsonClass(generateAdapter = true)
data class SerializableClass(
    val nestedArray: Array<Array<String>> = emptyArray()
)

fun main() {
    val json = "{}"
    val moshi = Moshi.Builder().build()
    val adapter = moshi.adapter(SerializableClass::class.java)
    val serializableClass = try {
        adapter.fromJson(json)
    } catch (e: Exception) {
        println("Failed to deserialize class: ${e.message}")
        throw e
    }
}

Since the JSON doesn't provide the nestedArray value, the generated adapter tries to retrieve the synthetic constructor, but probably uses the wrong type when doing so.

val localConstructor: Constructor<SerializableClass> = this.constructorRef ?:
          SerializableClass::class.java.getDeclaredConstructor(ReflectArray.newInstance(KotlinArray::class.java,
          0).javaClass, Int::class.javaPrimitiveType, Util.DEFAULT_CONSTRUCTOR_MARKER)

By replacing ReflectArray.newInstance(KotlinArray::class.java,0).javaClass with a simple Array<Array<String>>::class.java, everything works properly.

As a temporary workaround, using Array<List<T>> instead of Array<Array<T>> works correctly.

Additional notes

Platform: Android 14 Moshi library version: 1.15.1