diff --git a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt index 3c8bfd9..4a8736f 100644 --- a/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt +++ b/kotlin/src/main/java/com/squareup/moshi/KotlinJsonAdapter.kt @@ -162,17 +162,32 @@ class KotlinJsonAdapterFactory : JsonAdapter.Factory { if (!annotations.isEmpty()) return null val rawType = Types.getRawType(type) + if (rawType.isInterface) return null if (rawType.isEnum) return null if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null if (ClassJsonAdapter.isPlatformType(rawType)) return null - val constructor = rawType.kotlin.primaryConstructor ?: return null + if (rawType.isLocalClass) { + throw IllegalArgumentException("Cannot serialize local class or object expression ${rawType.name}") + } + val rawTypeKotlin = rawType.kotlin + if (rawTypeKotlin.isAbstract) { + throw IllegalArgumentException("Cannot serialize abstract class ${rawType.name}") + } + if (rawTypeKotlin.isInner) { + throw IllegalArgumentException("Cannot serialize inner class ${rawType.name}") + } + if (rawTypeKotlin.objectInstance != null) { + throw IllegalArgumentException("Cannot serialize object declaration ${rawType.name}") + } + + val constructor = rawTypeKotlin.primaryConstructor ?: return null val parametersByName = constructor.parameters.associateBy { it.name } constructor.isAccessible = true val bindingsByName = LinkedHashMap>() - for (property in rawType.kotlin.memberProperties) { + for (property in rawTypeKotlin.memberProperties) { val parameter = parametersByName[property.name] if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) { diff --git a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt index a298d89..1af9183 100644 --- a/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt +++ b/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt @@ -134,9 +134,8 @@ class KotlinJsonAdapterTest { class RequiredValueAbsent(var a: Int = 3, var b: Int) @Test fun nonNullConstructorParameterCalledWithNullFailsWithJsonDataException() { - class Data(val a: String) val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() - val jsonAdapter = moshi.adapter(Data::class.java) + val jsonAdapter = moshi.adapter(HasNonNullConstructorParameter::class.java) try { jsonAdapter.fromJson("{\"a\":null}") @@ -146,10 +145,11 @@ class KotlinJsonAdapterTest { } } + class HasNonNullConstructorParameter(val a: String) + @Test fun nonNullPropertySetToNullFailsWithJsonDataException() { - class Data { var a: String = "" } val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() - val jsonAdapter = moshi.adapter(Data::class.java) + val jsonAdapter = moshi.adapter(HasNonNullProperty::class.java) try { jsonAdapter.fromJson("{\"a\":null}") @@ -159,6 +159,10 @@ class KotlinJsonAdapterTest { } } + class HasNonNullProperty { + var a: String = "" + } + @Test fun duplicatedValue() { val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() val jsonAdapter = moshi.adapter(DuplicateValue::class.java) @@ -576,6 +580,86 @@ class KotlinJsonAdapterTest { A, B } + @Test fun interfacesNotSupported() { + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + try { + moshi.adapter(Interface::class.java) + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessage("No JsonAdapter for interface " + + "com.squareup.moshi.KotlinJsonAdapterTest\$Interface annotated []") + } + } + + interface Interface + + @Test fun abstractClassesNotSupported() { + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + try { + moshi.adapter(AbstractClass::class.java) + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessage( + "Cannot serialize abstract class com.squareup.moshi.KotlinJsonAdapterTest\$AbstractClass") + } + } + + abstract class AbstractClass(val a: Int) + + @Test fun innerClassesNotSupported() { + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + try { + moshi.adapter(InnerClass::class.java) + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessage( + "Cannot serialize inner class com.squareup.moshi.KotlinJsonAdapterTest\$InnerClass") + } + } + + inner class InnerClass(val a: Int) + + @Test fun localClassesNotSupported() { + class LocalClass(val a: Int) + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + try { + moshi.adapter(LocalClass::class.java) + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessage("Cannot serialize local class or object expression " + + "com.squareup.moshi.KotlinJsonAdapterTest\$localClassesNotSupported\$LocalClass") + } + } + + @Test fun objectDeclarationsNotSupported() { + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + try { + moshi.adapter(ObjectDeclaration.javaClass) + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessage("Cannot serialize object declaration " + + "com.squareup.moshi.KotlinJsonAdapterTest\$ObjectDeclaration") + } + } + + object ObjectDeclaration { + var a = 5 + } + + @Test fun objectExpressionsNotSupported() { + val expression = object : Any() { + var a = 5 + } + val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + try { + moshi.adapter(expression.javaClass) + fail() + } catch (e: IllegalArgumentException) { + assertThat(e).hasMessage("Cannot serialize local class or object expression " + + "com.squareup.moshi.KotlinJsonAdapterTest\$objectExpressionsNotSupported\$expression$1") + } + } + @Test fun manyProperties32() { val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() val jsonAdapter = moshi.adapter(ManyProperties32::class.java)