Support non-data classes for generated JsonAdapters
This is towards making the reflection and codegen adapters work the same. The process is relatively straightforward: try to promote all of the tests in KotlinCodeGenTest to be passing tests in GeneratedAdaptersTest or compile failures in CompilerTest
This commit is contained in:
parent
7750d179be
commit
b3d7dfd603
6 changed files with 263 additions and 167 deletions
|
@ -35,20 +35,17 @@ import javax.lang.model.util.Elements
|
||||||
|
|
||||||
/** Generates a JSON adapter for a target type. */
|
/** Generates a JSON adapter for a target type. */
|
||||||
internal class AdapterGenerator(
|
internal class AdapterGenerator(
|
||||||
val fqClassName: String,
|
val className: ClassName,
|
||||||
val packageName: String,
|
|
||||||
val propertyList: List<PropertyGenerator>,
|
val propertyList: List<PropertyGenerator>,
|
||||||
val originalElement: Element,
|
val originalElement: Element,
|
||||||
name: String = fqClassName.substringAfter(packageName)
|
val isDataClass: Boolean,
|
||||||
.replace('.', '_')
|
|
||||||
.removePrefix("_"),
|
|
||||||
val hasCompanionObject: Boolean,
|
val hasCompanionObject: Boolean,
|
||||||
val visibility: ProtoBuf.Visibility,
|
val visibility: ProtoBuf.Visibility,
|
||||||
val elements: Elements,
|
val elements: Elements,
|
||||||
val genericTypeNames: List<TypeVariableName>?
|
val genericTypeNames: List<TypeVariableName>?
|
||||||
) {
|
) {
|
||||||
val nameAllocator = NameAllocator()
|
val nameAllocator = NameAllocator()
|
||||||
val adapterName = "${name}JsonAdapter"
|
val adapterName = "${className.simpleNames().joinToString(separator = "_")}JsonAdapter"
|
||||||
val originalTypeName = originalElement.asType().asTypeName()
|
val originalTypeName = originalElement.asType().asTypeName()
|
||||||
|
|
||||||
val moshiParam = ParameterSpec.builder(
|
val moshiParam = ParameterSpec.builder(
|
||||||
|
@ -92,7 +89,7 @@ internal class AdapterGenerator(
|
||||||
property.allocateNames(nameAllocator)
|
property.allocateNames(nameAllocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
val result = FileSpec.builder(packageName, adapterName)
|
val result = FileSpec.builder(className.packageName(), adapterName)
|
||||||
if (hasCompanionObject) {
|
if (hasCompanionObject) {
|
||||||
result.addFunction(generateJsonAdapterFun())
|
result.addFunction(generateJsonAdapterFun())
|
||||||
}
|
}
|
||||||
|
@ -148,6 +145,8 @@ internal class AdapterGenerator(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateFromJsonFun(): FunSpec {
|
private fun generateFromJsonFun(): FunSpec {
|
||||||
|
val resultName = nameAllocator.newName("result")
|
||||||
|
|
||||||
val result = FunSpec.builder("fromJson")
|
val result = FunSpec.builder("fromJson")
|
||||||
.addModifiers(KModifier.OVERRIDE)
|
.addModifiers(KModifier.OVERRIDE)
|
||||||
.addParameter(readerParam)
|
.addParameter(readerParam)
|
||||||
|
@ -187,36 +186,72 @@ internal class AdapterGenerator(
|
||||||
result.endControlFlow() // while
|
result.endControlFlow() // while
|
||||||
result.addStatement("%N.endObject()", readerParam)
|
result.addStatement("%N.endObject()", readerParam)
|
||||||
|
|
||||||
val propertiesWithoutDefaults = propertyList.filter { !it.hasDefault }
|
// Call the constructor providing only required parameters.
|
||||||
result.addCode("%[return %T(\n", originalTypeName)
|
var hasOptionalParameters = false
|
||||||
propertiesWithoutDefaults.forEachIndexed { index, property ->
|
result.addCode("%[var %N = %T(", resultName, originalTypeName)
|
||||||
|
var separator = "\n"
|
||||||
|
for (property in propertyList) {
|
||||||
|
if (!property.hasConstructorParameter) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (property.hasDefault) {
|
||||||
|
hasOptionalParameters = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result.addCode(separator)
|
||||||
result.addCode("%N = %N", property.name, property.localName)
|
result.addCode("%N = %N", property.name, property.localName)
|
||||||
if (property.isRequired) {
|
if (property.isRequired) {
|
||||||
result.addCode(" ?: throw %T(\"Required property '%L' missing at \${%N.path}\")",
|
result.addCode(" ?: throw %T(\"Required property '%L' missing at \${%N.path}\")",
|
||||||
JsonDataException::class, property.localName, readerParam)
|
JsonDataException::class, property.localName, readerParam)
|
||||||
}
|
}
|
||||||
result.addCode(if (index + 1 < propertiesWithoutDefaults.size) ",\n" else "\n")
|
separator = ",\n"
|
||||||
}
|
}
|
||||||
result.addCode("%])\n", originalTypeName)
|
result.addCode(")%]\n", originalTypeName)
|
||||||
|
|
||||||
val propertiesWithDefaults = propertyList.filter { it.hasDefault }
|
// Call either the constructor again, or the copy() method, this time providing any optional
|
||||||
if (!propertiesWithDefaults.isEmpty()) {
|
// parameters that we have.
|
||||||
result.addCode(".let {%>\n")
|
if (hasOptionalParameters) {
|
||||||
result.addCode("%[it.copy(\n")
|
if (isDataClass) {
|
||||||
propertiesWithDefaults.forEachIndexed { index, property ->
|
result.addCode("%[%1N = %1N.copy(", resultName)
|
||||||
if (property.differentiateAbsentFromNull) {
|
} else {
|
||||||
result.addCode("%1N = if (%2N) %3N else it.%1N",
|
result.addCode("%[%1N = %2T(", resultName, originalTypeName)
|
||||||
property.name, property.localIsPresentName, property.localName)
|
}
|
||||||
} else {
|
separator = "\n"
|
||||||
result.addCode("%1N = %2N ?: it.%1N",
|
for (property in propertyList) {
|
||||||
property.name, property.localName)
|
if (!property.hasConstructorParameter) {
|
||||||
|
continue // No constructor parameter for this property.
|
||||||
}
|
}
|
||||||
result.addCode(if (index + 1 < propertiesWithDefaults.size) ",\n" else "\n")
|
if (isDataClass && !property.hasDefault) {
|
||||||
|
continue // Property already assigned.
|
||||||
|
}
|
||||||
|
|
||||||
|
result.addCode(separator)
|
||||||
|
if (property.differentiateAbsentFromNull) {
|
||||||
|
result.addCode("%2N = if (%3N) %4N else %1N.%2N",
|
||||||
|
resultName, property.name, property.localIsPresentName, property.localName)
|
||||||
|
} else {
|
||||||
|
result.addCode("%2N = %3N ?: %1N.%2N", resultName, property.name, property.localName)
|
||||||
|
}
|
||||||
|
separator = ",\n"
|
||||||
}
|
}
|
||||||
result.addCode("%])\n")
|
result.addCode("%])\n")
|
||||||
result.addCode("%<}\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign properties not present in the constructor.
|
||||||
|
for (property in propertyList) {
|
||||||
|
if (property.hasConstructorParameter) {
|
||||||
|
continue // Property already handled.
|
||||||
|
}
|
||||||
|
if (property.differentiateAbsentFromNull) {
|
||||||
|
result.addStatement("%1N.%2N = if (%3N) %4N else %1N.%2N",
|
||||||
|
resultName, property.name, property.localIsPresentName, property.localName)
|
||||||
|
} else {
|
||||||
|
result.addStatement("%1N.%2N = %3N ?: %1N.%2N",
|
||||||
|
resultName, property.name, property.localName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.addStatement("return %1N", resultName)
|
||||||
return result.build()
|
return result.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +279,7 @@ internal class AdapterGenerator(
|
||||||
|
|
||||||
private fun generateJsonAdapterFun(): FunSpec {
|
private fun generateJsonAdapterFun(): FunSpec {
|
||||||
val rawType = when (originalTypeName) {
|
val rawType = when (originalTypeName) {
|
||||||
is TypeVariableName -> throw IllegalArgumentException(
|
is TypeVariableName -> throw IllegalArgumentException("Cannot get raw type of TypeVariable!")
|
||||||
"Cannot get raw type of TypeVariable!")
|
|
||||||
is ParameterizedTypeName -> originalTypeName.rawType
|
is ParameterizedTypeName -> originalTypeName.rawType
|
||||||
else -> originalTypeName as ClassName
|
else -> originalTypeName as ClassName
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,16 @@ package com.squareup.moshi
|
||||||
|
|
||||||
import com.google.auto.common.AnnotationMirrors
|
import com.google.auto.common.AnnotationMirrors
|
||||||
import com.google.auto.service.AutoService
|
import com.google.auto.service.AutoService
|
||||||
|
import com.squareup.kotlinpoet.ClassName
|
||||||
import com.squareup.kotlinpoet.KModifier.OUT
|
import com.squareup.kotlinpoet.KModifier.OUT
|
||||||
|
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||||
import com.squareup.kotlinpoet.TypeSpec
|
import com.squareup.kotlinpoet.TypeSpec
|
||||||
import com.squareup.kotlinpoet.TypeVariableName
|
import com.squareup.kotlinpoet.TypeVariableName
|
||||||
|
import com.squareup.kotlinpoet.asTypeName
|
||||||
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
|
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
|
||||||
import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
|
import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
|
||||||
|
import me.eugeniomarletti.kotlin.metadata.classKind
|
||||||
import me.eugeniomarletti.kotlin.metadata.declaresDefaultValue
|
import me.eugeniomarletti.kotlin.metadata.declaresDefaultValue
|
||||||
import me.eugeniomarletti.kotlin.metadata.extractFullName
|
|
||||||
import me.eugeniomarletti.kotlin.metadata.isDataClass
|
import me.eugeniomarletti.kotlin.metadata.isDataClass
|
||||||
import me.eugeniomarletti.kotlin.metadata.isPrimary
|
import me.eugeniomarletti.kotlin.metadata.isPrimary
|
||||||
import me.eugeniomarletti.kotlin.metadata.jvm.getJvmConstructorSignature
|
import me.eugeniomarletti.kotlin.metadata.jvm.getJvmConstructorSignature
|
||||||
|
@ -31,14 +34,17 @@ import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
|
||||||
import me.eugeniomarletti.kotlin.metadata.visibility
|
import me.eugeniomarletti.kotlin.metadata.visibility
|
||||||
import me.eugeniomarletti.kotlin.processing.KotlinAbstractProcessor
|
import me.eugeniomarletti.kotlin.processing.KotlinAbstractProcessor
|
||||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||||
|
import org.jetbrains.kotlin.serialization.ProtoBuf.ValueParameter
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import javax.annotation.processing.Processor
|
import javax.annotation.processing.Processor
|
||||||
import javax.annotation.processing.RoundEnvironment
|
import javax.annotation.processing.RoundEnvironment
|
||||||
import javax.lang.model.SourceVersion
|
import javax.lang.model.SourceVersion
|
||||||
|
import javax.lang.model.element.AnnotationMirror
|
||||||
import javax.lang.model.element.Element
|
import javax.lang.model.element.Element
|
||||||
import javax.lang.model.element.ElementKind
|
import javax.lang.model.element.ElementKind
|
||||||
import javax.lang.model.element.ExecutableElement
|
import javax.lang.model.element.ExecutableElement
|
||||||
import javax.lang.model.element.TypeElement
|
import javax.lang.model.element.TypeElement
|
||||||
|
import javax.lang.model.element.VariableElement
|
||||||
import javax.tools.Diagnostic.Kind.ERROR
|
import javax.tools.Diagnostic.Kind.ERROR
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,24 +83,24 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
||||||
val metadata = element.kotlinMetadata
|
val metadata = element.kotlinMetadata
|
||||||
|
|
||||||
if (metadata !is KotlinClassMetadata) {
|
if (metadata !is KotlinClassMetadata) {
|
||||||
errorMustBeDataClass(element)
|
errorMustBeKotlinClass(element)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val classData = metadata.data
|
val classData = metadata.data
|
||||||
val (nameResolver, classProto) = classData
|
val (nameResolver, classProto) = classData
|
||||||
|
|
||||||
fun ProtoBuf.Type.extractFullName() = extractFullName(classData)
|
if (classProto.classKind != ProtoBuf.Class.Kind.CLASS) {
|
||||||
|
errorMustBeKotlinClass(element)
|
||||||
if (!classProto.isDataClass) {
|
|
||||||
errorMustBeDataClass(element)
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
val fqClassName = nameResolver.getString(classProto.fqName).replace('/', '.')
|
val typeName = element.asType().asTypeName()
|
||||||
|
val className = when (typeName) {
|
||||||
val packageName = nameResolver.getString(classProto.fqName).substringBeforeLast('/').replace(
|
is ClassName -> typeName
|
||||||
'/', '.')
|
is ParameterizedTypeName -> typeName.rawType
|
||||||
|
else -> throw IllegalStateException("unexpected TypeName: ${typeName::class}")
|
||||||
|
}
|
||||||
|
|
||||||
val hasCompanionObject = classProto.hasCompanionObjectName()
|
val hasCompanionObject = classProto.hasCompanionObjectName()
|
||||||
// todo allow custom constructor
|
// todo allow custom constructor
|
||||||
|
@ -113,34 +119,49 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
||||||
.first()
|
.first()
|
||||||
// TODO Temporary until jvm method signature matching is better
|
// TODO Temporary until jvm method signature matching is better
|
||||||
// .single { it.jvmMethodSignature == constructorJvmSignature }
|
// .single { it.jvmMethodSignature == constructorJvmSignature }
|
||||||
val parameters = protoConstructor
|
val parameters: Map<String, ValueParameter> = protoConstructor.valueParameterList.associateBy {
|
||||||
.valueParameterList
|
nameResolver.getString(it.name)
|
||||||
.mapIndexed { index, valueParameter ->
|
}
|
||||||
val paramName = nameResolver.getString(valueParameter.name)
|
|
||||||
|
|
||||||
val nullable = valueParameter.type.nullable
|
val properties = classData.classProto.propertyList.associateBy {
|
||||||
val paramFqcn = valueParameter.type.extractFullName()
|
nameResolver.getString(it.name)
|
||||||
.replace("`", "")
|
}
|
||||||
.removeSuffix("?")
|
|
||||||
|
|
||||||
val actualElement = constructor.parameters[index]
|
val propertyGenerators = mutableListOf<PropertyGenerator>()
|
||||||
|
for (enclosedElement in element.enclosedElements) {
|
||||||
|
if (enclosedElement !is VariableElement) continue
|
||||||
|
|
||||||
val serializedName = actualElement.getAnnotation(Json::class.java)?.name
|
val name = enclosedElement.simpleName.toString()
|
||||||
?: paramName
|
val property = properties[name] ?: continue
|
||||||
|
val parameter = parameters[name]
|
||||||
|
|
||||||
val jsonQualifiers = AnnotationMirrors.getAnnotatedAnnotations(actualElement,
|
val parameterElement = if (parameter != null) {
|
||||||
JsonQualifier::class.java)
|
val parameterIndex = protoConstructor.valueParameterList.indexOf(parameter)
|
||||||
|
constructor.parameters[parameterIndex]
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
PropertyGenerator(
|
if (property.visibility != ProtoBuf.Visibility.INTERNAL
|
||||||
name = paramName,
|
&& property.visibility != ProtoBuf.Visibility.PROTECTED
|
||||||
serializedName = serializedName,
|
&& property.visibility != ProtoBuf.Visibility.PUBLIC) {
|
||||||
hasDefault = valueParameter.declaresDefaultValue,
|
messager.printMessage(ERROR, "property $name is not visible", enclosedElement)
|
||||||
nullable = nullable,
|
return null
|
||||||
typeName = valueParameter.type.asTypeName(nameResolver, classProto::getTypeParameter),
|
}
|
||||||
unaliasedName = valueParameter.type.asTypeName(nameResolver,
|
|
||||||
classProto::getTypeParameter, true),
|
propertyGenerators += PropertyGenerator(
|
||||||
jsonQualifiers = jsonQualifiers)
|
name,
|
||||||
}
|
serializedName(name, enclosedElement, parameterElement),
|
||||||
|
parameter != null,
|
||||||
|
parameter?.declaresDefaultValue ?: true,
|
||||||
|
property.returnType.nullable,
|
||||||
|
property.returnType.asTypeName(nameResolver, classProto::getTypeParameter),
|
||||||
|
property.returnType.asTypeName(nameResolver, classProto::getTypeParameter, true),
|
||||||
|
jsonQualifiers(enclosedElement, parameterElement))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort properties so that those with constructor parameters come first.
|
||||||
|
propertyGenerators.sortBy { if (it.hasConstructorParameter) -1 else 1 }
|
||||||
|
|
||||||
val genericTypeNames = classProto.typeParameterList
|
val genericTypeNames = classProto.typeParameterList
|
||||||
.map {
|
.map {
|
||||||
|
@ -168,19 +189,56 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
return AdapterGenerator(
|
return AdapterGenerator(
|
||||||
fqClassName = fqClassName,
|
className,
|
||||||
packageName = packageName,
|
propertyList = propertyGenerators,
|
||||||
propertyList = parameters,
|
|
||||||
originalElement = element,
|
originalElement = element,
|
||||||
hasCompanionObject = hasCompanionObject,
|
hasCompanionObject = hasCompanionObject,
|
||||||
visibility = classProto.visibility!!,
|
visibility = classProto.visibility!!,
|
||||||
genericTypeNames = genericTypeNames,
|
genericTypeNames = genericTypeNames,
|
||||||
elements = elementUtils)
|
elements = elementUtils,
|
||||||
|
isDataClass = classProto.isDataClass)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun errorMustBeDataClass(element: Element) {
|
/** Returns the JsonQualifiers on the field and parameter of a property. */
|
||||||
|
private fun jsonQualifiers(
|
||||||
|
field: VariableElement,
|
||||||
|
parameter: VariableElement?
|
||||||
|
): Set<AnnotationMirror> {
|
||||||
|
val fieldJsonQualifiers = AnnotationMirrors.getAnnotatedAnnotations(
|
||||||
|
field, JsonQualifier::class.java)
|
||||||
|
|
||||||
|
val parameterJsonQualifiers: Set<AnnotationMirror> = if (parameter != null) {
|
||||||
|
AnnotationMirrors.getAnnotatedAnnotations(parameter, JsonQualifier::class.java)
|
||||||
|
} else {
|
||||||
|
setOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(jwilson): union the qualifiers somehow?
|
||||||
|
if (fieldJsonQualifiers.isNotEmpty()) {
|
||||||
|
return fieldJsonQualifiers
|
||||||
|
} else {
|
||||||
|
return parameterJsonQualifiers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the @Json name of a property, or `propertyName` if none is provided. */
|
||||||
|
private fun serializedName(
|
||||||
|
propertyName: String,
|
||||||
|
field: VariableElement,
|
||||||
|
parameter: VariableElement?
|
||||||
|
): String {
|
||||||
|
val fieldAnnotation = field.getAnnotation(Json::class.java)
|
||||||
|
if (fieldAnnotation != null) return fieldAnnotation.name
|
||||||
|
|
||||||
|
val parameterAnnotation = parameter?.getAnnotation(Json::class.java)
|
||||||
|
if (parameterAnnotation != null) return parameterAnnotation.name
|
||||||
|
|
||||||
|
return propertyName
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun errorMustBeKotlinClass(element: Element) {
|
||||||
messager.printMessage(ERROR,
|
messager.printMessage(ERROR,
|
||||||
"@${JsonClass::class.java.simpleName} can't be applied to $element: must be a Kotlin data class",
|
"@${JsonClass::class.java.simpleName} can't be applied to $element: must be a Kotlin class",
|
||||||
element)
|
element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import javax.lang.model.element.AnnotationMirror
|
||||||
internal class PropertyGenerator(
|
internal class PropertyGenerator(
|
||||||
val name: String,
|
val name: String,
|
||||||
val serializedName: String,
|
val serializedName: String,
|
||||||
|
val hasConstructorParameter: Boolean,
|
||||||
val hasDefault: Boolean,
|
val hasDefault: Boolean,
|
||||||
val nullable: Boolean,
|
val nullable: Boolean,
|
||||||
val typeName: TypeName,
|
val typeName: TypeName,
|
||||||
|
|
|
@ -35,13 +35,11 @@ class CompilerTest {
|
||||||
|import com.squareup.moshi.JsonClass
|
|import com.squareup.moshi.JsonClass
|
||||||
|
|
|
|
||||||
|@JsonClass(generateAdapter = true)
|
|@JsonClass(generateAdapter = true)
|
||||||
|class ConstructorParameters(var a: Int, var b: Int)
|
|class PrivateConstructorParameter(private var a: Int)
|
||||||
|""".trimMargin())
|
|""".trimMargin())
|
||||||
|
|
||||||
val result = call.execute()
|
val result = call.execute()
|
||||||
assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR)
|
assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR)
|
||||||
assertThat(result.systemErr).contains(
|
assertThat(result.systemErr).contains("property a is not visible")
|
||||||
"@JsonClass can't be applied to ConstructorParameters: must be a Kotlin data class")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,7 +20,7 @@ import org.assertj.core.api.Assertions.fail
|
||||||
import org.intellij.lang.annotations.Language
|
import org.intellij.lang.annotations.Language
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class DataClassesTest {
|
class GeneratedAdaptersTest {
|
||||||
|
|
||||||
private val moshi = Moshi.Builder().build()
|
private val moshi = Moshi.Builder().build()
|
||||||
|
|
||||||
|
@ -252,6 +252,107 @@ class DataClassesTest {
|
||||||
|
|
||||||
@JsonClass(generateAdapter = false)
|
@JsonClass(generateAdapter = false)
|
||||||
data class DoNotGenerateAdapter(val foo: String)
|
data class DoNotGenerateAdapter(val foo: String)
|
||||||
|
|
||||||
|
@Test fun constructorParameters() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(ConstructorParameters::class.java)
|
||||||
|
|
||||||
|
val encoded = ConstructorParameters(3, 5)
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(4)
|
||||||
|
assertThat(decoded.b).isEqualTo(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class ConstructorParameters(var a: Int, var b: Int)
|
||||||
|
|
||||||
|
@Test fun properties() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(Properties::class.java)
|
||||||
|
|
||||||
|
val encoded = Properties()
|
||||||
|
encoded.a = 3
|
||||||
|
encoded.b = 5
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":3,"b":5}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(3)
|
||||||
|
assertThat(decoded.b).isEqualTo(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class Properties {
|
||||||
|
var a: Int = -1
|
||||||
|
var b: Int = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun constructorParametersAndProperties() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(ConstructorParametersAndProperties::class.java)
|
||||||
|
|
||||||
|
val encoded = ConstructorParametersAndProperties(3)
|
||||||
|
encoded.b = 5
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(4)
|
||||||
|
assertThat(decoded.b).isEqualTo(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class ConstructorParametersAndProperties(var a: Int) {
|
||||||
|
var b: Int = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun immutableConstructorParameters() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(ImmutableConstructorParameters::class.java)
|
||||||
|
|
||||||
|
val encoded = ImmutableConstructorParameters(3, 5)
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(4)
|
||||||
|
assertThat(decoded.b).isEqualTo(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class ImmutableConstructorParameters(val a: Int, val b: Int)
|
||||||
|
|
||||||
|
@Test fun immutableProperties() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(ImmutableProperties::class.java)
|
||||||
|
|
||||||
|
val encoded = ImmutableProperties(3, 5)
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"a":3,"b":5}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(3)
|
||||||
|
assertThat(decoded.b).isEqualTo(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class ImmutableProperties(a: Int, b: Int) {
|
||||||
|
val a = a
|
||||||
|
val b = b
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun constructorDefaults() {
|
||||||
|
val moshi = Moshi.Builder().build()
|
||||||
|
val jsonAdapter = moshi.adapter(ConstructorDefaultValues::class.java)
|
||||||
|
|
||||||
|
val encoded = ConstructorDefaultValues(3, 5)
|
||||||
|
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||||
|
|
||||||
|
val decoded = jsonAdapter.fromJson("""{"b":6}""")!!
|
||||||
|
assertThat(decoded.a).isEqualTo(-1)
|
||||||
|
assertThat(decoded.b).isEqualTo(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has to be outside to avoid Types seeing an owning class
|
// Has to be outside to avoid Types seeing an owning class
|
|
@ -25,102 +25,6 @@ import java.util.SimpleTimeZone
|
||||||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||||
|
|
||||||
class KotlinCodeGenTest {
|
class KotlinCodeGenTest {
|
||||||
@Ignore @Test fun constructorParameters() {
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(ConstructorParameters::class.java)
|
|
||||||
|
|
||||||
val encoded = ConstructorParameters(3, 5)
|
|
||||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
|
||||||
|
|
||||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
|
||||||
assertThat(decoded.a).isEqualTo(4)
|
|
||||||
assertThat(decoded.b).isEqualTo(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConstructorParameters(var a: Int, var b: Int)
|
|
||||||
|
|
||||||
@Ignore @Test fun properties() {
|
|
||||||
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(Properties::class.java)
|
|
||||||
|
|
||||||
val encoded = Properties()
|
|
||||||
encoded.a = 3
|
|
||||||
encoded.b = 5
|
|
||||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
|
||||||
|
|
||||||
val decoded = jsonAdapter.fromJson("""{"a":3,"b":5}""")!!
|
|
||||||
assertThat(decoded.a).isEqualTo(3)
|
|
||||||
assertThat(decoded.b).isEqualTo(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Properties {
|
|
||||||
var a: Int = -1
|
|
||||||
var b: Int = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore @Test fun constructorParametersAndProperties() {
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(ConstructorParametersAndProperties::class.java)
|
|
||||||
|
|
||||||
val encoded = ConstructorParametersAndProperties(3)
|
|
||||||
encoded.b = 5
|
|
||||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
|
||||||
|
|
||||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
|
||||||
assertThat(decoded.a).isEqualTo(4)
|
|
||||||
assertThat(decoded.b).isEqualTo(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConstructorParametersAndProperties(var a: Int) {
|
|
||||||
var b: Int = -1
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore @Test fun immutableConstructorParameters() {
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(ImmutableConstructorParameters::class.java)
|
|
||||||
|
|
||||||
val encoded = ImmutableConstructorParameters(3, 5)
|
|
||||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
|
||||||
|
|
||||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
|
||||||
assertThat(decoded.a).isEqualTo(4)
|
|
||||||
assertThat(decoded.b).isEqualTo(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ImmutableConstructorParameters(val a: Int, val b: Int)
|
|
||||||
|
|
||||||
@Ignore @Test fun immutableProperties() {
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(ImmutableProperties::class.java)
|
|
||||||
|
|
||||||
val encoded = ImmutableProperties(3, 5)
|
|
||||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
|
||||||
|
|
||||||
val decoded = jsonAdapter.fromJson("""{"a":3,"b":5}""")!!
|
|
||||||
assertThat(decoded.a).isEqualTo(3)
|
|
||||||
assertThat(decoded.b).isEqualTo(5)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ImmutableProperties(a: Int, b: Int) {
|
|
||||||
val a = a
|
|
||||||
val b = b
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore @Test fun constructorDefaults() {
|
|
||||||
val moshi = Moshi.Builder().build()
|
|
||||||
val jsonAdapter = moshi.adapter(ConstructorDefaultValues::class.java)
|
|
||||||
|
|
||||||
val encoded = ConstructorDefaultValues(3, 5)
|
|
||||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
|
||||||
|
|
||||||
val decoded = jsonAdapter.fromJson("""{"b":6}""")!!
|
|
||||||
assertThat(decoded.a).isEqualTo(-1)
|
|
||||||
assertThat(decoded.b).isEqualTo(6)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConstructorDefaultValues(var a: Int = -1, var b: Int = -2)
|
|
||||||
|
|
||||||
@Ignore @Test fun requiredValueAbsent() {
|
@Ignore @Test fun requiredValueAbsent() {
|
||||||
val moshi = Moshi.Builder().build()
|
val moshi = Moshi.Builder().build()
|
||||||
val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java)
|
val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java)
|
||||||
|
|
Loading…
Reference in a new issue