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. */
|
||||
internal class AdapterGenerator(
|
||||
val fqClassName: String,
|
||||
val packageName: String,
|
||||
val className: ClassName,
|
||||
val propertyList: List<PropertyGenerator>,
|
||||
val originalElement: Element,
|
||||
name: String = fqClassName.substringAfter(packageName)
|
||||
.replace('.', '_')
|
||||
.removePrefix("_"),
|
||||
val isDataClass: Boolean,
|
||||
val hasCompanionObject: Boolean,
|
||||
val visibility: ProtoBuf.Visibility,
|
||||
val elements: Elements,
|
||||
val genericTypeNames: List<TypeVariableName>?
|
||||
) {
|
||||
val nameAllocator = NameAllocator()
|
||||
val adapterName = "${name}JsonAdapter"
|
||||
val adapterName = "${className.simpleNames().joinToString(separator = "_")}JsonAdapter"
|
||||
val originalTypeName = originalElement.asType().asTypeName()
|
||||
|
||||
val moshiParam = ParameterSpec.builder(
|
||||
|
@ -92,7 +89,7 @@ internal class AdapterGenerator(
|
|||
property.allocateNames(nameAllocator)
|
||||
}
|
||||
|
||||
val result = FileSpec.builder(packageName, adapterName)
|
||||
val result = FileSpec.builder(className.packageName(), adapterName)
|
||||
if (hasCompanionObject) {
|
||||
result.addFunction(generateJsonAdapterFun())
|
||||
}
|
||||
|
@ -148,6 +145,8 @@ internal class AdapterGenerator(
|
|||
}
|
||||
|
||||
private fun generateFromJsonFun(): FunSpec {
|
||||
val resultName = nameAllocator.newName("result")
|
||||
|
||||
val result = FunSpec.builder("fromJson")
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.addParameter(readerParam)
|
||||
|
@ -187,36 +186,72 @@ internal class AdapterGenerator(
|
|||
result.endControlFlow() // while
|
||||
result.addStatement("%N.endObject()", readerParam)
|
||||
|
||||
val propertiesWithoutDefaults = propertyList.filter { !it.hasDefault }
|
||||
result.addCode("%[return %T(\n", originalTypeName)
|
||||
propertiesWithoutDefaults.forEachIndexed { index, property ->
|
||||
// Call the constructor providing only required parameters.
|
||||
var hasOptionalParameters = false
|
||||
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)
|
||||
if (property.isRequired) {
|
||||
result.addCode(" ?: throw %T(\"Required property '%L' missing at \${%N.path}\")",
|
||||
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 }
|
||||
if (!propertiesWithDefaults.isEmpty()) {
|
||||
result.addCode(".let {%>\n")
|
||||
result.addCode("%[it.copy(\n")
|
||||
propertiesWithDefaults.forEachIndexed { index, property ->
|
||||
if (property.differentiateAbsentFromNull) {
|
||||
result.addCode("%1N = if (%2N) %3N else it.%1N",
|
||||
property.name, property.localIsPresentName, property.localName)
|
||||
// Call either the constructor again, or the copy() method, this time providing any optional
|
||||
// parameters that we have.
|
||||
if (hasOptionalParameters) {
|
||||
if (isDataClass) {
|
||||
result.addCode("%[%1N = %1N.copy(", resultName)
|
||||
} else {
|
||||
result.addCode("%1N = %2N ?: it.%1N",
|
||||
property.name, property.localName)
|
||||
result.addCode("%[%1N = %2T(", resultName, originalTypeName)
|
||||
}
|
||||
result.addCode(if (index + 1 < propertiesWithDefaults.size) ",\n" else "\n")
|
||||
separator = "\n"
|
||||
for (property in propertyList) {
|
||||
if (!property.hasConstructorParameter) {
|
||||
continue // No constructor parameter for this property.
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
|
@ -244,8 +279,7 @@ internal class AdapterGenerator(
|
|||
|
||||
private fun generateJsonAdapterFun(): FunSpec {
|
||||
val rawType = when (originalTypeName) {
|
||||
is TypeVariableName -> throw IllegalArgumentException(
|
||||
"Cannot get raw type of TypeVariable!")
|
||||
is TypeVariableName -> throw IllegalArgumentException("Cannot get raw type of TypeVariable!")
|
||||
is ParameterizedTypeName -> originalTypeName.rawType
|
||||
else -> originalTypeName as ClassName
|
||||
}
|
||||
|
|
|
@ -17,13 +17,16 @@ package com.squareup.moshi
|
|||
|
||||
import com.google.auto.common.AnnotationMirrors
|
||||
import com.google.auto.service.AutoService
|
||||
import com.squareup.kotlinpoet.ClassName
|
||||
import com.squareup.kotlinpoet.KModifier.OUT
|
||||
import com.squareup.kotlinpoet.ParameterizedTypeName
|
||||
import com.squareup.kotlinpoet.TypeSpec
|
||||
import com.squareup.kotlinpoet.TypeVariableName
|
||||
import com.squareup.kotlinpoet.asTypeName
|
||||
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
|
||||
import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
|
||||
import me.eugeniomarletti.kotlin.metadata.classKind
|
||||
import me.eugeniomarletti.kotlin.metadata.declaresDefaultValue
|
||||
import me.eugeniomarletti.kotlin.metadata.extractFullName
|
||||
import me.eugeniomarletti.kotlin.metadata.isDataClass
|
||||
import me.eugeniomarletti.kotlin.metadata.isPrimary
|
||||
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.processing.KotlinAbstractProcessor
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf.ValueParameter
|
||||
import java.io.File
|
||||
import javax.annotation.processing.Processor
|
||||
import javax.annotation.processing.RoundEnvironment
|
||||
import javax.lang.model.SourceVersion
|
||||
import javax.lang.model.element.AnnotationMirror
|
||||
import javax.lang.model.element.Element
|
||||
import javax.lang.model.element.ElementKind
|
||||
import javax.lang.model.element.ExecutableElement
|
||||
import javax.lang.model.element.TypeElement
|
||||
import javax.lang.model.element.VariableElement
|
||||
import javax.tools.Diagnostic.Kind.ERROR
|
||||
|
||||
/**
|
||||
|
@ -77,24 +83,24 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
|||
val metadata = element.kotlinMetadata
|
||||
|
||||
if (metadata !is KotlinClassMetadata) {
|
||||
errorMustBeDataClass(element)
|
||||
errorMustBeKotlinClass(element)
|
||||
return null
|
||||
}
|
||||
|
||||
val classData = metadata.data
|
||||
val (nameResolver, classProto) = classData
|
||||
|
||||
fun ProtoBuf.Type.extractFullName() = extractFullName(classData)
|
||||
|
||||
if (!classProto.isDataClass) {
|
||||
errorMustBeDataClass(element)
|
||||
if (classProto.classKind != ProtoBuf.Class.Kind.CLASS) {
|
||||
errorMustBeKotlinClass(element)
|
||||
return null
|
||||
}
|
||||
|
||||
val fqClassName = nameResolver.getString(classProto.fqName).replace('/', '.')
|
||||
|
||||
val packageName = nameResolver.getString(classProto.fqName).substringBeforeLast('/').replace(
|
||||
'/', '.')
|
||||
val typeName = element.asType().asTypeName()
|
||||
val className = when (typeName) {
|
||||
is ClassName -> typeName
|
||||
is ParameterizedTypeName -> typeName.rawType
|
||||
else -> throw IllegalStateException("unexpected TypeName: ${typeName::class}")
|
||||
}
|
||||
|
||||
val hasCompanionObject = classProto.hasCompanionObjectName()
|
||||
// todo allow custom constructor
|
||||
|
@ -113,35 +119,50 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
|||
.first()
|
||||
// TODO Temporary until jvm method signature matching is better
|
||||
// .single { it.jvmMethodSignature == constructorJvmSignature }
|
||||
val parameters = protoConstructor
|
||||
.valueParameterList
|
||||
.mapIndexed { index, valueParameter ->
|
||||
val paramName = nameResolver.getString(valueParameter.name)
|
||||
|
||||
val nullable = valueParameter.type.nullable
|
||||
val paramFqcn = valueParameter.type.extractFullName()
|
||||
.replace("`", "")
|
||||
.removeSuffix("?")
|
||||
|
||||
val actualElement = constructor.parameters[index]
|
||||
|
||||
val serializedName = actualElement.getAnnotation(Json::class.java)?.name
|
||||
?: paramName
|
||||
|
||||
val jsonQualifiers = AnnotationMirrors.getAnnotatedAnnotations(actualElement,
|
||||
JsonQualifier::class.java)
|
||||
|
||||
PropertyGenerator(
|
||||
name = paramName,
|
||||
serializedName = serializedName,
|
||||
hasDefault = valueParameter.declaresDefaultValue,
|
||||
nullable = nullable,
|
||||
typeName = valueParameter.type.asTypeName(nameResolver, classProto::getTypeParameter),
|
||||
unaliasedName = valueParameter.type.asTypeName(nameResolver,
|
||||
classProto::getTypeParameter, true),
|
||||
jsonQualifiers = jsonQualifiers)
|
||||
val parameters: Map<String, ValueParameter> = protoConstructor.valueParameterList.associateBy {
|
||||
nameResolver.getString(it.name)
|
||||
}
|
||||
|
||||
val properties = classData.classProto.propertyList.associateBy {
|
||||
nameResolver.getString(it.name)
|
||||
}
|
||||
|
||||
val propertyGenerators = mutableListOf<PropertyGenerator>()
|
||||
for (enclosedElement in element.enclosedElements) {
|
||||
if (enclosedElement !is VariableElement) continue
|
||||
|
||||
val name = enclosedElement.simpleName.toString()
|
||||
val property = properties[name] ?: continue
|
||||
val parameter = parameters[name]
|
||||
|
||||
val parameterElement = if (parameter != null) {
|
||||
val parameterIndex = protoConstructor.valueParameterList.indexOf(parameter)
|
||||
constructor.parameters[parameterIndex]
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (property.visibility != ProtoBuf.Visibility.INTERNAL
|
||||
&& property.visibility != ProtoBuf.Visibility.PROTECTED
|
||||
&& property.visibility != ProtoBuf.Visibility.PUBLIC) {
|
||||
messager.printMessage(ERROR, "property $name is not visible", enclosedElement)
|
||||
return null
|
||||
}
|
||||
|
||||
propertyGenerators += PropertyGenerator(
|
||||
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
|
||||
.map {
|
||||
val variance = it.variance.asKModifier().let {
|
||||
|
@ -168,19 +189,56 @@ class JsonClassCodeGenProcessor : KotlinAbstractProcessor(), KotlinMetadataUtils
|
|||
}
|
||||
|
||||
return AdapterGenerator(
|
||||
fqClassName = fqClassName,
|
||||
packageName = packageName,
|
||||
propertyList = parameters,
|
||||
className,
|
||||
propertyList = propertyGenerators,
|
||||
originalElement = element,
|
||||
hasCompanionObject = hasCompanionObject,
|
||||
visibility = classProto.visibility!!,
|
||||
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,
|
||||
"@${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)
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import javax.lang.model.element.AnnotationMirror
|
|||
internal class PropertyGenerator(
|
||||
val name: String,
|
||||
val serializedName: String,
|
||||
val hasConstructorParameter: Boolean,
|
||||
val hasDefault: Boolean,
|
||||
val nullable: Boolean,
|
||||
val typeName: TypeName,
|
||||
|
|
|
@ -35,13 +35,11 @@ class CompilerTest {
|
|||
|import com.squareup.moshi.JsonClass
|
||||
|
|
||||
|@JsonClass(generateAdapter = true)
|
||||
|class ConstructorParameters(var a: Int, var b: Int)
|
||||
|class PrivateConstructorParameter(private var a: Int)
|
||||
|""".trimMargin())
|
||||
|
||||
val result = call.execute()
|
||||
assertThat(result.exitCode).isEqualTo(ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.systemErr).contains(
|
||||
"@JsonClass can't be applied to ConstructorParameters: must be a Kotlin data class")
|
||||
assertThat(result.systemErr).contains("property a is not visible")
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,7 @@ import org.assertj.core.api.Assertions.fail
|
|||
import org.intellij.lang.annotations.Language
|
||||
import org.junit.Test
|
||||
|
||||
class DataClassesTest {
|
||||
class GeneratedAdaptersTest {
|
||||
|
||||
private val moshi = Moshi.Builder().build()
|
||||
|
||||
|
@ -252,6 +252,107 @@ class DataClassesTest {
|
|||
|
||||
@JsonClass(generateAdapter = false)
|
||||
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
|
|
@ -25,102 +25,6 @@ import java.util.SimpleTimeZone
|
|||
import kotlin.annotation.AnnotationRetention.RUNTIME
|
||||
|
||||
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() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter(RequiredValueAbsent::class.java)
|
||||
|
|
Loading…
Reference in a new issue