mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 07:59:21 +08:00
Switch to spotless and format code (#1196)
* Add spotless configuration * Reformat! * Add copyright config for build.gradle.kts files * Add toeholds for headers
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
@@ -28,8 +29,8 @@ tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
freeCompilerArgs = listOf(
|
||||
"-progressive",
|
||||
"-Xopt-in=com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview"
|
||||
"-progressive",
|
||||
"-Xopt-in=com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -80,8 +81,10 @@ val shadowJar = tasks.shadowJar.apply {
|
||||
archiveClassifier.set("")
|
||||
configurations = listOf(shade)
|
||||
relocate("com.squareup.kotlinpoet.metadata", "com.squareup.moshi.kotlinpoet.metadata")
|
||||
relocate("com.squareup.kotlinpoet.classinspector",
|
||||
"com.squareup.moshi.kotlinpoet.classinspector")
|
||||
relocate(
|
||||
"com.squareup.kotlinpoet.classinspector",
|
||||
"com.squareup.moshi.kotlinpoet.classinspector"
|
||||
)
|
||||
relocate("kotlinx.metadata", "com.squareup.moshi.kotlinx.metadata")
|
||||
transformers.add(ServiceFileTransformer())
|
||||
}
|
||||
|
@@ -60,8 +60,8 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
|
||||
*/
|
||||
const val OPTION_GENERATED = "moshi.generated"
|
||||
private val POSSIBLE_GENERATED_NAMES = setOf(
|
||||
"javax.annotation.processing.Generated",
|
||||
"javax.annotation.Generated"
|
||||
"javax.annotation.processing.Generated",
|
||||
"javax.annotation.Generated"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
|
||||
generatedType = processingEnv.options[OPTION_GENERATED]?.let {
|
||||
require(it in POSSIBLE_GENERATED_NAMES) {
|
||||
"Invalid option value for $OPTION_GENERATED. Found $it, " +
|
||||
"allowable values are $POSSIBLE_GENERATED_NAMES."
|
||||
"allowable values are $POSSIBLE_GENERATED_NAMES."
|
||||
}
|
||||
processingEnv.elementUtils.getTypeElement(it)
|
||||
}
|
||||
@@ -104,30 +104,34 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
|
||||
for (type in roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||
if (type !is TypeElement) {
|
||||
messager.printMessage(
|
||||
Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $type: must be a Kotlin class",
|
||||
type)
|
||||
Diagnostic.Kind.ERROR,
|
||||
"@JsonClass can't be applied to $type: must be a Kotlin class",
|
||||
type
|
||||
)
|
||||
continue
|
||||
}
|
||||
val jsonClass = type.getAnnotation(annotation)
|
||||
if (jsonClass.generateAdapter && jsonClass.generator.isEmpty()) {
|
||||
val generator = adapterGenerator(type, cachedClassInspector) ?: continue
|
||||
val preparedAdapter = generator
|
||||
.prepare { spec ->
|
||||
spec.toBuilder()
|
||||
.apply {
|
||||
generatedType?.asClassName()?.let { generatedClassName ->
|
||||
addAnnotation(
|
||||
AnnotationSpec.builder(generatedClassName)
|
||||
.addMember("value = [%S]",
|
||||
JsonClassCodegenProcessor::class.java.canonicalName)
|
||||
.addMember("comments = %S", "https://github.com/square/moshi")
|
||||
.build()
|
||||
.prepare { spec ->
|
||||
spec.toBuilder()
|
||||
.apply {
|
||||
generatedType?.asClassName()?.let { generatedClassName ->
|
||||
addAnnotation(
|
||||
AnnotationSpec.builder(generatedClassName)
|
||||
.addMember(
|
||||
"value = [%S]",
|
||||
JsonClassCodegenProcessor::class.java.canonicalName
|
||||
)
|
||||
}
|
||||
}
|
||||
.addOriginatingElement(type)
|
||||
.build()
|
||||
}
|
||||
.addMember("comments = %S", "https://github.com/square/moshi")
|
||||
.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
.addOriginatingElement(type)
|
||||
.build()
|
||||
}
|
||||
|
||||
preparedAdapter.spec.writeTo(filer)
|
||||
preparedAdapter.proguardConfig?.writeTo(filer, type)
|
||||
@@ -138,8 +142,8 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
|
||||
}
|
||||
|
||||
private fun adapterGenerator(
|
||||
element: TypeElement,
|
||||
cachedClassInspector: MoshiCachedClassInspector
|
||||
element: TypeElement,
|
||||
cachedClassInspector: MoshiCachedClassInspector
|
||||
): AdapterGenerator? {
|
||||
val type = targetType(messager, elements, types, element, cachedClassInspector) ?: return null
|
||||
|
||||
@@ -154,9 +158,10 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
|
||||
for ((name, parameter) in type.constructor.parameters) {
|
||||
if (type.properties[parameter.name] == null && !parameter.hasDefault) {
|
||||
messager.printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"No property for required constructor parameter $name",
|
||||
element)
|
||||
Diagnostic.Kind.ERROR,
|
||||
"No property for required constructor parameter $name",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
@@ -34,4 +34,4 @@ internal class MoshiCachedClassInspector(private val classInspector: ClassInspec
|
||||
toTypeSpec(toImmutableKmClass(element.metadata))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -52,40 +52,42 @@ private const val TO_STRING_SIZE_BASE = TO_STRING_PREFIX.length + 1 // 1 is the
|
||||
|
||||
/** Generates a JSON adapter for a target type. */
|
||||
internal class AdapterGenerator(
|
||||
private val target: TargetType,
|
||||
private val propertyList: List<PropertyGenerator>
|
||||
private val target: TargetType,
|
||||
private val propertyList: List<PropertyGenerator>
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val INT_TYPE_BLOCK = CodeBlock.of("%T::class.javaPrimitiveType", INT)
|
||||
private val DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK = CodeBlock.of(
|
||||
"%T.DEFAULT_CONSTRUCTOR_MARKER", Util::class)
|
||||
"%T.DEFAULT_CONSTRUCTOR_MARKER",
|
||||
Util::class
|
||||
)
|
||||
private val CN_MOSHI = Moshi::class.asClassName()
|
||||
private val CN_TYPE = Type::class.asClassName()
|
||||
|
||||
private val COMMON_SUPPRESS = arrayOf(
|
||||
// https://github.com/square/moshi/issues/1023
|
||||
"DEPRECATION",
|
||||
// Because we look it up reflectively
|
||||
"unused",
|
||||
// Because we include underscores
|
||||
"ClassName",
|
||||
// Because we generate redundant `out` variance for some generics and there's no way
|
||||
// for us to know when it's redundant.
|
||||
"REDUNDANT_PROJECTION",
|
||||
// Because we may generate redundant explicit types for local vars with default values.
|
||||
// Example: 'var fooSet: Boolean = false'
|
||||
"RedundantExplicitType",
|
||||
// NameAllocator will just add underscores to differentiate names, which Kotlin doesn't
|
||||
// like for stylistic reasons.
|
||||
"LocalVariableName"
|
||||
// https://github.com/square/moshi/issues/1023
|
||||
"DEPRECATION",
|
||||
// Because we look it up reflectively
|
||||
"unused",
|
||||
// Because we include underscores
|
||||
"ClassName",
|
||||
// Because we generate redundant `out` variance for some generics and there's no way
|
||||
// for us to know when it's redundant.
|
||||
"REDUNDANT_PROJECTION",
|
||||
// Because we may generate redundant explicit types for local vars with default values.
|
||||
// Example: 'var fooSet: Boolean = false'
|
||||
"RedundantExplicitType",
|
||||
// NameAllocator will just add underscores to differentiate names, which Kotlin doesn't
|
||||
// like for stylistic reasons.
|
||||
"LocalVariableName"
|
||||
).let { suppressions ->
|
||||
AnnotationSpec.builder(Suppress::class)
|
||||
.addMember(
|
||||
suppressions.indices.joinToString { "%S" },
|
||||
*suppressions
|
||||
)
|
||||
.build()
|
||||
.addMember(
|
||||
suppressions.indices.joinToString { "%S" },
|
||||
*suppressions
|
||||
)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ internal class AdapterGenerator(
|
||||
private val visibility = target.visibility
|
||||
private val typeVariables = target.typeVariables
|
||||
private val targetConstructorParams = target.constructor.parameters
|
||||
.mapKeys { (_, param) -> param.index }
|
||||
.mapKeys { (_, param) -> param.index }
|
||||
|
||||
private val nameAllocator = NameAllocator()
|
||||
private val adapterName = "${className.simpleNames.joinToString(separator = "_")}JsonAdapter"
|
||||
@@ -102,48 +104,57 @@ internal class AdapterGenerator(
|
||||
private val originalRawTypeName = originalTypeName.rawType()
|
||||
|
||||
private val moshiParam = ParameterSpec.builder(
|
||||
nameAllocator.newName("moshi"),
|
||||
CN_MOSHI).build()
|
||||
nameAllocator.newName("moshi"),
|
||||
CN_MOSHI
|
||||
).build()
|
||||
private val typesParam = ParameterSpec.builder(
|
||||
nameAllocator.newName("types"),
|
||||
ARRAY.parameterizedBy(CN_TYPE))
|
||||
.build()
|
||||
nameAllocator.newName("types"),
|
||||
ARRAY.parameterizedBy(CN_TYPE)
|
||||
)
|
||||
.build()
|
||||
private val readerParam = ParameterSpec.builder(
|
||||
nameAllocator.newName("reader"),
|
||||
JsonReader::class)
|
||||
.build()
|
||||
nameAllocator.newName("reader"),
|
||||
JsonReader::class
|
||||
)
|
||||
.build()
|
||||
private val writerParam = ParameterSpec.builder(
|
||||
nameAllocator.newName("writer"),
|
||||
JsonWriter::class)
|
||||
.build()
|
||||
nameAllocator.newName("writer"),
|
||||
JsonWriter::class
|
||||
)
|
||||
.build()
|
||||
private val valueParam = ParameterSpec.builder(
|
||||
nameAllocator.newName("value"),
|
||||
originalTypeName.copy(nullable = true))
|
||||
.build()
|
||||
nameAllocator.newName("value"),
|
||||
originalTypeName.copy(nullable = true)
|
||||
)
|
||||
.build()
|
||||
private val jsonAdapterTypeName = JsonAdapter::class.asClassName().parameterizedBy(
|
||||
originalTypeName)
|
||||
originalTypeName
|
||||
)
|
||||
|
||||
// selectName() API setup
|
||||
private val optionsProperty = PropertySpec.builder(
|
||||
nameAllocator.newName("options"), JsonReader.Options::class.asTypeName(),
|
||||
KModifier.PRIVATE)
|
||||
.initializer(
|
||||
"%T.of(%L)",
|
||||
JsonReader.Options::class.asTypeName(),
|
||||
nonTransientProperties
|
||||
.map { CodeBlock.of("%S", it.jsonName) }
|
||||
.joinToCode(", ")
|
||||
)
|
||||
.build()
|
||||
nameAllocator.newName("options"),
|
||||
JsonReader.Options::class.asTypeName(),
|
||||
KModifier.PRIVATE
|
||||
)
|
||||
.initializer(
|
||||
"%T.of(%L)",
|
||||
JsonReader.Options::class.asTypeName(),
|
||||
nonTransientProperties
|
||||
.map { CodeBlock.of("%S", it.jsonName) }
|
||||
.joinToCode(", ")
|
||||
)
|
||||
.build()
|
||||
|
||||
private val constructorProperty = PropertySpec.builder(
|
||||
nameAllocator.newName("constructorRef"),
|
||||
Constructor::class.asClassName().parameterizedBy(originalTypeName).copy(nullable = true),
|
||||
KModifier.PRIVATE)
|
||||
.addAnnotation(Volatile::class)
|
||||
.mutable(true)
|
||||
.initializer("null")
|
||||
.build()
|
||||
nameAllocator.newName("constructorRef"),
|
||||
Constructor::class.asClassName().parameterizedBy(originalTypeName).copy(nullable = true),
|
||||
KModifier.PRIVATE
|
||||
)
|
||||
.addAnnotation(Volatile::class)
|
||||
.mutable(true)
|
||||
.initializer("null")
|
||||
.build()
|
||||
|
||||
fun prepare(typeHook: (TypeSpec) -> TypeSpec = { it }): PreparedAdapter {
|
||||
for (property in nonTransientProperties) {
|
||||
@@ -159,17 +170,17 @@ internal class AdapterGenerator(
|
||||
|
||||
private fun TypeSpec.createProguardRule(): ProguardConfig {
|
||||
val adapterProperties = propertySpecs
|
||||
.asSequence()
|
||||
.filter { prop ->
|
||||
prop.type.rawType() == JsonAdapter::class.asClassName()
|
||||
}
|
||||
.filter { prop -> prop.annotations.isNotEmpty() }
|
||||
.mapTo(mutableSetOf()) { prop ->
|
||||
QualifierAdapterProperty(
|
||||
name = prop.name,
|
||||
qualifiers = prop.annotations.mapTo(mutableSetOf()) { it.className }
|
||||
)
|
||||
}
|
||||
.asSequence()
|
||||
.filter { prop ->
|
||||
prop.type.rawType() == JsonAdapter::class.asClassName()
|
||||
}
|
||||
.filter { prop -> prop.annotations.isNotEmpty() }
|
||||
.mapTo(mutableSetOf()) { prop ->
|
||||
QualifierAdapterProperty(
|
||||
name = prop.name,
|
||||
qualifiers = prop.annotations.mapTo(mutableSetOf()) { it.className }
|
||||
)
|
||||
}
|
||||
|
||||
val adapterConstructorParams = when (requireNotNull(primaryConstructor).parameters.size) {
|
||||
1 -> listOf(CN_MOSHI.reflectionName())
|
||||
@@ -188,21 +199,21 @@ internal class AdapterGenerator(
|
||||
}
|
||||
hasDefaultProperties = propertyList.any { it.hasDefault }
|
||||
parameterTypes = AsmType.getArgumentTypes(constructorSignature.removePrefix("<init>"))
|
||||
.map { it.toReflectionString() }
|
||||
.map { it.toReflectionString() }
|
||||
}
|
||||
return ProguardConfig(
|
||||
targetClass = className,
|
||||
adapterName = adapterName,
|
||||
adapterConstructorParams = adapterConstructorParams,
|
||||
targetConstructorHasDefaults = hasDefaultProperties,
|
||||
targetConstructorParams = parameterTypes,
|
||||
qualifierProperties = adapterProperties
|
||||
targetClass = className,
|
||||
adapterName = adapterName,
|
||||
adapterConstructorParams = adapterConstructorParams,
|
||||
targetConstructorHasDefaults = hasDefaultProperties,
|
||||
targetConstructorParams = parameterTypes,
|
||||
qualifierProperties = adapterProperties
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateType(): TypeSpec {
|
||||
val result = TypeSpec.classBuilder(adapterName)
|
||||
.addAnnotation(COMMON_SUPPRESS)
|
||||
.addAnnotation(COMMON_SUPPRESS)
|
||||
|
||||
result.superclass(jsonAdapterTypeName)
|
||||
|
||||
@@ -211,19 +222,21 @@ internal class AdapterGenerator(
|
||||
// require(types.size == 1) {
|
||||
// "TypeVariable mismatch: Expecting 1 type(s) for generic type variables [T], but received ${types.size} with values $types"
|
||||
// }
|
||||
result.addInitializerBlock(CodeBlock.builder()
|
||||
result.addInitializerBlock(
|
||||
CodeBlock.builder()
|
||||
.beginControlFlow("require(types.size == %L)", typeVariables.size)
|
||||
.addStatement(
|
||||
"buildString·{·append(%S).append(%L).append(%S).append(%S).append(%S).append(%L)·}",
|
||||
"TypeVariable mismatch: Expecting ",
|
||||
typeVariables.size,
|
||||
" ${if (typeVariables.size == 1) "type" else "types"} for generic type variables [",
|
||||
typeVariables.joinToString(", ") { it.name },
|
||||
"], but received ",
|
||||
"${typesParam.name}.size"
|
||||
"buildString·{·append(%S).append(%L).append(%S).append(%S).append(%S).append(%L)·}",
|
||||
"TypeVariable mismatch: Expecting ",
|
||||
typeVariables.size,
|
||||
" ${if (typeVariables.size == 1) "type" else "types"} for generic type variables [",
|
||||
typeVariables.joinToString(", ") { it.name },
|
||||
"], but received ",
|
||||
"${typesParam.name}.size"
|
||||
)
|
||||
.endControlFlow()
|
||||
.build())
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
// TODO make this configurable. Right now it just matches the source model
|
||||
@@ -243,8 +256,14 @@ internal class AdapterGenerator(
|
||||
|
||||
result.addProperty(optionsProperty)
|
||||
for (uniqueAdapter in nonTransientProperties.distinctBy { it.delegateKey }) {
|
||||
result.addProperty(uniqueAdapter.delegateKey.generateProperty(
|
||||
nameAllocator, typeRenderer, moshiParam, uniqueAdapter.name))
|
||||
result.addProperty(
|
||||
uniqueAdapter.delegateKey.generateProperty(
|
||||
nameAllocator,
|
||||
typeRenderer,
|
||||
moshiParam,
|
||||
uniqueAdapter.name
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
result.addFunction(generateToStringFun())
|
||||
@@ -269,24 +288,24 @@ internal class AdapterGenerator(
|
||||
val name = originalRawTypeName.simpleNames.joinToString(".")
|
||||
val size = TO_STRING_SIZE_BASE + name.length
|
||||
return FunSpec.builder("toString")
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.returns(String::class)
|
||||
.addStatement(
|
||||
"return %M(%L)·{ append(%S).append(%S).append('%L') }",
|
||||
MemberName("kotlin.text", "buildString"),
|
||||
size,
|
||||
TO_STRING_PREFIX,
|
||||
name,
|
||||
")"
|
||||
)
|
||||
.build()
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.returns(String::class)
|
||||
.addStatement(
|
||||
"return %M(%L)·{ append(%S).append(%S).append('%L') }",
|
||||
MemberName("kotlin.text", "buildString"),
|
||||
size,
|
||||
TO_STRING_PREFIX,
|
||||
name,
|
||||
")"
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun generateFromJsonFun(classBuilder: TypeSpec.Builder): FunSpec {
|
||||
val result = FunSpec.builder("fromJson")
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.addParameter(readerParam)
|
||||
.returns(originalTypeName)
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.addParameter(readerParam)
|
||||
.returns(originalTypeName)
|
||||
|
||||
for (property in nonTransientProperties) {
|
||||
result.addCode("%L", property.generateLocalProperty())
|
||||
@@ -296,8 +315,8 @@ internal class AdapterGenerator(
|
||||
}
|
||||
|
||||
val propertiesByIndex = propertyList.asSequence()
|
||||
.filter { it.hasConstructorParameter }
|
||||
.associateBy { it.target.parameterIndex }
|
||||
.filter { it.hasConstructorParameter }
|
||||
.associateBy { it.target.parameterIndex }
|
||||
val components = mutableListOf<FromJsonComponent>()
|
||||
|
||||
// Add parameters (± properties) first, their index matters
|
||||
@@ -333,7 +352,7 @@ internal class AdapterGenerator(
|
||||
nameAllocator.newName("mask$index")
|
||||
}
|
||||
val useDefaultsConstructor = components.filterIsInstance<ParameterComponent>()
|
||||
.any { it.parameter.hasDefault }
|
||||
.any { it.parameter.hasDefault }
|
||||
if (useDefaultsConstructor) {
|
||||
// Initialize all our masks, defaulting to fully unset (-1)
|
||||
for (maskName in maskNames) {
|
||||
@@ -372,7 +391,8 @@ internal class AdapterGenerator(
|
||||
|
||||
for (input in components) {
|
||||
if (input is ParameterOnly ||
|
||||
(input is ParameterProperty && input.property.isTransient)) {
|
||||
(input is ParameterProperty && input.property.isTransient)
|
||||
) {
|
||||
updateMaskIndexes()
|
||||
constructorPropertyTypes += input.type.asTypeBlock()
|
||||
continue
|
||||
@@ -387,18 +407,30 @@ internal class AdapterGenerator(
|
||||
if (property.hasLocalIsPresentName || property.hasConstructorDefault) {
|
||||
result.beginControlFlow("%L ->", propertyIndex)
|
||||
if (property.delegateKey.nullable) {
|
||||
result.addStatement("%N = %N.fromJson(%N)",
|
||||
property.localName, nameAllocator[property.delegateKey], readerParam)
|
||||
result.addStatement(
|
||||
"%N = %N.fromJson(%N)",
|
||||
property.localName,
|
||||
nameAllocator[property.delegateKey],
|
||||
readerParam
|
||||
)
|
||||
} else {
|
||||
val exception = unexpectedNull(property, readerParam)
|
||||
result.addStatement("%N = %N.fromJson(%N) ?: throw·%L",
|
||||
property.localName, nameAllocator[property.delegateKey], readerParam, exception)
|
||||
result.addStatement(
|
||||
"%N = %N.fromJson(%N) ?: throw·%L",
|
||||
property.localName,
|
||||
nameAllocator[property.delegateKey],
|
||||
readerParam,
|
||||
exception
|
||||
)
|
||||
}
|
||||
if (property.hasConstructorDefault) {
|
||||
val inverted = (1 shl maskIndex).inv()
|
||||
result.addComment("\$mask = \$mask and (1 shl %L).inv()", maskIndex)
|
||||
result.addStatement("%1L = %1L and 0x%2L.toInt()", maskNames[maskNameIndex],
|
||||
Integer.toHexString(inverted))
|
||||
result.addStatement(
|
||||
"%1L = %1L and 0x%2L.toInt()",
|
||||
maskNames[maskNameIndex],
|
||||
Integer.toHexString(inverted)
|
||||
)
|
||||
} else {
|
||||
// Presence tracker for a mutable property
|
||||
result.addStatement("%N = true", property.localIsPresentName)
|
||||
@@ -406,13 +438,23 @@ internal class AdapterGenerator(
|
||||
result.endControlFlow()
|
||||
} else {
|
||||
if (property.delegateKey.nullable) {
|
||||
result.addStatement("%L -> %N = %N.fromJson(%N)",
|
||||
propertyIndex, property.localName, nameAllocator[property.delegateKey], readerParam)
|
||||
result.addStatement(
|
||||
"%L -> %N = %N.fromJson(%N)",
|
||||
propertyIndex,
|
||||
property.localName,
|
||||
nameAllocator[property.delegateKey],
|
||||
readerParam
|
||||
)
|
||||
} else {
|
||||
val exception = unexpectedNull(property, readerParam)
|
||||
result.addStatement("%L -> %N = %N.fromJson(%N) ?: throw·%L",
|
||||
propertyIndex, property.localName, nameAllocator[property.delegateKey], readerParam,
|
||||
exception)
|
||||
result.addStatement(
|
||||
"%L -> %N = %N.fromJson(%N) ?: throw·%L",
|
||||
propertyIndex,
|
||||
property.localName,
|
||||
nameAllocator[property.delegateKey],
|
||||
readerParam,
|
||||
exception
|
||||
)
|
||||
}
|
||||
}
|
||||
if (property.hasConstructorParameter) {
|
||||
@@ -447,13 +489,13 @@ internal class AdapterGenerator(
|
||||
// Dynamic default constructor call
|
||||
val nonNullConstructorType = constructorProperty.type.copy(nullable = false)
|
||||
val args = constructorPropertyTypes
|
||||
.plus(0.until(maskCount).map { INT_TYPE_BLOCK }) // Masks, one every 32 params
|
||||
.plus(DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK) // Default constructor marker is always last
|
||||
.joinToCode(", ")
|
||||
.plus(0.until(maskCount).map { INT_TYPE_BLOCK }) // Masks, one every 32 params
|
||||
.plus(DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK) // Default constructor marker is always last
|
||||
.joinToCode(", ")
|
||||
val coreLookupBlock = CodeBlock.of(
|
||||
"%T::class.java.getDeclaredConstructor(%L)",
|
||||
originalRawTypeName,
|
||||
args
|
||||
"%T::class.java.getDeclaredConstructor(%L)",
|
||||
originalRawTypeName,
|
||||
args
|
||||
)
|
||||
val lookupBlock = if (originalTypeName is ParameterizedTypeName) {
|
||||
CodeBlock.of("(%L·as·%T)", coreLookupBlock, nonNullConstructorType)
|
||||
@@ -461,23 +503,26 @@ internal class AdapterGenerator(
|
||||
coreLookupBlock
|
||||
}
|
||||
val initializerBlock = CodeBlock.of(
|
||||
"this.%1N·?: %2L.also·{ this.%1N·= it }",
|
||||
constructorProperty,
|
||||
lookupBlock
|
||||
"this.%1N·?: %2L.also·{ this.%1N·= it }",
|
||||
constructorProperty,
|
||||
lookupBlock
|
||||
)
|
||||
val localConstructorProperty = PropertySpec.builder(
|
||||
nameAllocator.newName("localConstructor"),
|
||||
nonNullConstructorType)
|
||||
.addAnnotation(AnnotationSpec.builder(Suppress::class)
|
||||
.addMember("%S", "UNCHECKED_CAST")
|
||||
.build())
|
||||
.initializer(initializerBlock)
|
||||
.build()
|
||||
nameAllocator.newName("localConstructor"),
|
||||
nonNullConstructorType
|
||||
)
|
||||
.addAnnotation(
|
||||
AnnotationSpec.builder(Suppress::class)
|
||||
.addMember("%S", "UNCHECKED_CAST")
|
||||
.build()
|
||||
)
|
||||
.initializer(initializerBlock)
|
||||
.build()
|
||||
result.addCode("%L", localConstructorProperty)
|
||||
result.addCode(
|
||||
"«%L%N.newInstance(",
|
||||
returnOrResultAssignment,
|
||||
localConstructorProperty
|
||||
"«%L%N.newInstance(",
|
||||
returnOrResultAssignment,
|
||||
localConstructorProperty
|
||||
)
|
||||
} else {
|
||||
// Standard constructor call. Don't omit generics for parameterized types even if they can be
|
||||
@@ -505,8 +550,13 @@ internal class AdapterGenerator(
|
||||
val property = input.property
|
||||
if (!property.isTransient && property.isRequired) {
|
||||
val missingPropertyBlock =
|
||||
CodeBlock.of("%T.missingProperty(%S, %S, %N)",
|
||||
MOSHI_UTIL, property.localName, property.jsonName, readerParam)
|
||||
CodeBlock.of(
|
||||
"%T.missingProperty(%S, %S, %N)",
|
||||
MOSHI_UTIL,
|
||||
property.localName,
|
||||
property.jsonName,
|
||||
readerParam
|
||||
)
|
||||
result.addCode(" ?: throw·%L", missingPropertyBlock)
|
||||
}
|
||||
}
|
||||
@@ -526,11 +576,20 @@ internal class AdapterGenerator(
|
||||
continue // Property already handled.
|
||||
}
|
||||
if (property.hasLocalIsPresentName) {
|
||||
result.addStatement("%1N.%2N = if (%3N) %4N else %1N.%2N",
|
||||
resultName, property.name, property.localIsPresentName, property.localName)
|
||||
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(
|
||||
"%1N.%2N = %3N ?: %1N.%2N",
|
||||
resultName,
|
||||
property.name,
|
||||
property.localName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,27 +600,40 @@ internal class AdapterGenerator(
|
||||
}
|
||||
|
||||
private fun unexpectedNull(property: PropertyGenerator, reader: ParameterSpec): CodeBlock {
|
||||
return CodeBlock.of("%T.unexpectedNull(%S, %S, %N)",
|
||||
MOSHI_UTIL, property.localName, property.jsonName, reader)
|
||||
return CodeBlock.of(
|
||||
"%T.unexpectedNull(%S, %S, %N)",
|
||||
MOSHI_UTIL,
|
||||
property.localName,
|
||||
property.jsonName,
|
||||
reader
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateToJsonFun(): FunSpec {
|
||||
val result = FunSpec.builder("toJson")
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.addParameter(writerParam)
|
||||
.addParameter(valueParam)
|
||||
.addModifiers(KModifier.OVERRIDE)
|
||||
.addParameter(writerParam)
|
||||
.addParameter(valueParam)
|
||||
|
||||
result.beginControlFlow("if (%N == null)", valueParam)
|
||||
result.addStatement("throw·%T(%S)", NullPointerException::class,
|
||||
"${valueParam.name} was null! Wrap in .nullSafe() to write nullable values.")
|
||||
result.addStatement(
|
||||
"throw·%T(%S)",
|
||||
NullPointerException::class,
|
||||
"${valueParam.name} was null! Wrap in .nullSafe() to write nullable values."
|
||||
)
|
||||
result.endControlFlow()
|
||||
|
||||
result.addStatement("%N.beginObject()", writerParam)
|
||||
nonTransientProperties.forEach { property ->
|
||||
// We manually put in quotes because we know the jsonName is already escaped
|
||||
result.addStatement("%N.name(%S)", writerParam, property.jsonName)
|
||||
result.addStatement("%N.toJson(%N, %N.%N)",
|
||||
nameAllocator[property.delegateKey], writerParam, valueParam, property.name)
|
||||
result.addStatement(
|
||||
"%N.toJson(%N, %N.%N)",
|
||||
nameAllocator[property.delegateKey],
|
||||
writerParam,
|
||||
valueParam,
|
||||
property.name
|
||||
)
|
||||
}
|
||||
result.addStatement("%N.endObject()", writerParam)
|
||||
|
||||
@@ -610,20 +682,20 @@ private sealed class FromJsonComponent {
|
||||
abstract val type: TypeName
|
||||
|
||||
data class ParameterOnly(
|
||||
override val parameter: TargetParameter
|
||||
override val parameter: TargetParameter
|
||||
) : FromJsonComponent(), ParameterComponent {
|
||||
override val type: TypeName = parameter.type
|
||||
}
|
||||
|
||||
data class PropertyOnly(
|
||||
override val property: PropertyGenerator
|
||||
override val property: PropertyGenerator
|
||||
) : FromJsonComponent(), PropertyComponent {
|
||||
override val type: TypeName = property.target.type
|
||||
}
|
||||
|
||||
data class ParameterProperty(
|
||||
override val parameter: TargetParameter,
|
||||
override val property: PropertyGenerator
|
||||
override val parameter: TargetParameter,
|
||||
override val property: PropertyGenerator
|
||||
) : FromJsonComponent(), ParameterComponent, PropertyComponent {
|
||||
override val type: TypeName = parameter.type
|
||||
}
|
||||
|
@@ -50,24 +50,28 @@ internal data class DelegateKey(
|
||||
"At${it.className.simpleName}"
|
||||
}
|
||||
val adapterName = nameAllocator.newName(
|
||||
"${type.toVariableName().decapitalize()}${qualifierNames}Adapter", this)
|
||||
"${type.toVariableName().decapitalize()}${qualifierNames}Adapter",
|
||||
this
|
||||
)
|
||||
|
||||
val adapterTypeName = JsonAdapter::class.asClassName().parameterizedBy(type)
|
||||
val standardArgs = arrayOf(moshiParameter,
|
||||
typeRenderer.render(type))
|
||||
val standardArgs = arrayOf(
|
||||
moshiParameter,
|
||||
typeRenderer.render(type)
|
||||
)
|
||||
val (initializerString, args) = when {
|
||||
jsonQualifiers.isEmpty() -> ", %M()" to arrayOf(MemberName("kotlin.collections", "emptySet"))
|
||||
else -> {
|
||||
", %T.getFieldJsonQualifierAnnotations(javaClass, " +
|
||||
"%S)" to arrayOf(Types::class.asTypeName(), adapterName)
|
||||
"%S)" to arrayOf(Types::class.asTypeName(), adapterName)
|
||||
}
|
||||
}
|
||||
val finalArgs = arrayOf(*standardArgs, *args, propertyName)
|
||||
|
||||
return PropertySpec.builder(adapterName, adapterTypeName, KModifier.PRIVATE)
|
||||
.addAnnotations(jsonQualifiers)
|
||||
.initializer("%N.adapter(%L$initializerString, %S)", *finalArgs)
|
||||
.build()
|
||||
.addAnnotations(jsonQualifiers)
|
||||
.initializer("%N.adapter(%L$initializerString, %S)", *finalArgs)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,20 +20,20 @@ import javax.tools.StandardLocation
|
||||
* class with a deterministic name (see [outputFile]) with an appropriate originating element.
|
||||
*/
|
||||
internal data class ProguardConfig(
|
||||
val targetClass: ClassName,
|
||||
val adapterName: String,
|
||||
val adapterConstructorParams: List<String>,
|
||||
val targetConstructorHasDefaults: Boolean,
|
||||
val targetConstructorParams: List<String>,
|
||||
val qualifierProperties: Set<QualifierAdapterProperty>
|
||||
val targetClass: ClassName,
|
||||
val adapterName: String,
|
||||
val adapterConstructorParams: List<String>,
|
||||
val targetConstructorHasDefaults: Boolean,
|
||||
val targetConstructorParams: List<String>,
|
||||
val qualifierProperties: Set<QualifierAdapterProperty>
|
||||
) {
|
||||
private val outputFile = "META-INF/proguard/moshi-${targetClass.canonicalName}.pro"
|
||||
|
||||
/** Writes this to `filer`. */
|
||||
fun writeTo(filer: Filer, vararg originatingElements: Element) {
|
||||
filer.createResource(StandardLocation.CLASS_OUTPUT, "", outputFile, *originatingElements)
|
||||
.openWriter()
|
||||
.use(::writeTo)
|
||||
.openWriter()
|
||||
.use(::writeTo)
|
||||
}
|
||||
|
||||
private fun writeTo(out: Appendable): Unit = out.run {
|
||||
@@ -64,13 +64,13 @@ internal data class ProguardConfig(
|
||||
appendln("}")
|
||||
|
||||
qualifierProperties.asSequence()
|
||||
.flatMap { it.qualifiers.asSequence() }
|
||||
.map(ClassName::reflectionName)
|
||||
.sorted()
|
||||
.forEach { qualifier ->
|
||||
appendln("-if class $targetName")
|
||||
appendln("-keep @interface $qualifier")
|
||||
}
|
||||
.flatMap { it.qualifiers.asSequence() }
|
||||
.map(ClassName::reflectionName)
|
||||
.sorted()
|
||||
.forEach { qualifier ->
|
||||
appendln("-if class $targetName")
|
||||
appendln("-keep @interface $qualifier")
|
||||
}
|
||||
|
||||
if (targetConstructorHasDefaults) {
|
||||
// If the target class has default parameter values, keep its synthetic constructor
|
||||
|
@@ -56,24 +56,24 @@ internal class PropertyGenerator(
|
||||
|
||||
fun generateLocalProperty(): PropertySpec {
|
||||
return PropertySpec.builder(localName, target.type.copy(nullable = true))
|
||||
.mutable(true)
|
||||
.apply {
|
||||
if (hasConstructorDefault) {
|
||||
// We default to the primitive default type, as reflectively invoking the constructor
|
||||
// without this (even though it's a throwaway) will fail argument type resolution in
|
||||
// the reflective invocation.
|
||||
initializer(target.type.defaultPrimitiveValue())
|
||||
} else {
|
||||
initializer("null")
|
||||
}
|
||||
.mutable(true)
|
||||
.apply {
|
||||
if (hasConstructorDefault) {
|
||||
// We default to the primitive default type, as reflectively invoking the constructor
|
||||
// without this (even though it's a throwaway) will fail argument type resolution in
|
||||
// the reflective invocation.
|
||||
initializer(target.type.defaultPrimitiveValue())
|
||||
} else {
|
||||
initializer("null")
|
||||
}
|
||||
.build()
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
fun generateLocalIsPresentProperty(): PropertySpec {
|
||||
return PropertySpec.builder(localIsPresentName, BOOLEAN)
|
||||
.mutable(true)
|
||||
.initializer("false")
|
||||
.build()
|
||||
.mutable(true)
|
||||
.initializer("false")
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
@@ -60,9 +60,11 @@ abstract class TypeRenderer {
|
||||
is ParameterizedTypeName -> {
|
||||
// If it's an Array type, we shortcut this to return Types.arrayOf()
|
||||
if (typeName.rawType == ARRAY) {
|
||||
CodeBlock.of("%T.arrayOf(%L)",
|
||||
Types::class,
|
||||
renderObjectType(typeName.typeArguments[0]))
|
||||
CodeBlock.of(
|
||||
"%T.arrayOf(%L)",
|
||||
Types::class,
|
||||
renderObjectType(typeName.typeArguments[0])
|
||||
)
|
||||
} else {
|
||||
val builder = CodeBlock.builder().apply {
|
||||
add("%T.", Types::class)
|
||||
@@ -95,7 +97,8 @@ abstract class TypeRenderer {
|
||||
method = "subtypeOf"
|
||||
}
|
||||
else -> throw IllegalArgumentException(
|
||||
"Unrepresentable wildcard type. Cannot have more than one bound: $typeName")
|
||||
"Unrepresentable wildcard type. Cannot have more than one bound: $typeName"
|
||||
)
|
||||
}
|
||||
CodeBlock.of("%T.%L(%L)", Types::class, method, render(target, forceBox = true))
|
||||
}
|
||||
|
@@ -48,18 +48,18 @@ internal fun TypeName.rawType(): ClassName {
|
||||
}
|
||||
|
||||
internal fun TypeName.defaultPrimitiveValue(): CodeBlock =
|
||||
when (this) {
|
||||
BOOLEAN -> CodeBlock.of("false")
|
||||
CHAR -> CodeBlock.of("0.toChar()")
|
||||
BYTE -> CodeBlock.of("0.toByte()")
|
||||
SHORT -> CodeBlock.of("0.toShort()")
|
||||
INT -> CodeBlock.of("0")
|
||||
FLOAT -> CodeBlock.of("0f")
|
||||
LONG -> CodeBlock.of("0L")
|
||||
DOUBLE -> CodeBlock.of("0.0")
|
||||
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
|
||||
else -> CodeBlock.of("null")
|
||||
}
|
||||
when (this) {
|
||||
BOOLEAN -> CodeBlock.of("false")
|
||||
CHAR -> CodeBlock.of("0.toChar()")
|
||||
BYTE -> CodeBlock.of("0.toByte()")
|
||||
SHORT -> CodeBlock.of("0.toShort()")
|
||||
INT -> CodeBlock.of("0")
|
||||
FLOAT -> CodeBlock.of("0f")
|
||||
LONG -> CodeBlock.of("0L")
|
||||
DOUBLE -> CodeBlock.of("0.0")
|
||||
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
|
||||
else -> CodeBlock.of("null")
|
||||
}
|
||||
|
||||
internal fun TypeName.asTypeBlock(): CodeBlock {
|
||||
if (annotations.isNotEmpty()) {
|
||||
@@ -103,12 +103,12 @@ internal fun KModifier.checkIsVisibility() {
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified T: TypeName> TypeName.mapTypes(noinline transform: T.() -> TypeName?): TypeName {
|
||||
internal inline fun <reified T : TypeName> TypeName.mapTypes(noinline transform: T.() -> TypeName?): TypeName {
|
||||
return mapTypes(T::class, transform)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal fun <T: TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.() -> TypeName?): TypeName {
|
||||
internal fun <T : TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.() -> TypeName?): TypeName {
|
||||
if (target.java == javaClass) {
|
||||
return (this as T).transform() ?: return this
|
||||
}
|
||||
@@ -116,7 +116,7 @@ internal fun <T: TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.()
|
||||
is ClassName -> this
|
||||
is ParameterizedTypeName -> {
|
||||
(rawType.mapTypes(target, transform) as ClassName).parameterizedBy(typeArguments.map { it.mapTypes(target, transform) })
|
||||
.copy(nullable = isNullable, annotations = annotations)
|
||||
.copy(nullable = isNullable, annotations = annotations)
|
||||
}
|
||||
is TypeVariableName -> {
|
||||
copy(bounds = bounds.map { it.mapTypes(target, transform) })
|
||||
@@ -129,11 +129,11 @@ internal fun <T: TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.()
|
||||
this == STAR -> this
|
||||
outTypes.isNotEmpty() && inTypes.isEmpty() -> {
|
||||
WildcardTypeName.producerOf(outTypes[0].mapTypes(target, transform))
|
||||
.copy(nullable = isNullable, annotations = annotations)
|
||||
.copy(nullable = isNullable, annotations = annotations)
|
||||
}
|
||||
inTypes.isNotEmpty() -> {
|
||||
WildcardTypeName.consumerOf(inTypes[0].mapTypes(target, transform))
|
||||
.copy(nullable = isNullable, annotations = annotations)
|
||||
.copy(nullable = isNullable, annotations = annotations)
|
||||
}
|
||||
else -> throw UnsupportedOperationException("Not possible.")
|
||||
}
|
||||
|
@@ -65,10 +65,10 @@ private val JSON_QUALIFIER = JsonQualifier::class.java
|
||||
private val JSON = Json::class.asClassName()
|
||||
private val OBJECT_CLASS = ClassName("java.lang", "Object")
|
||||
private val VISIBILITY_MODIFIERS = setOf(
|
||||
KModifier.INTERNAL,
|
||||
KModifier.PRIVATE,
|
||||
KModifier.PROTECTED,
|
||||
KModifier.PUBLIC
|
||||
KModifier.INTERNAL,
|
||||
KModifier.PRIVATE,
|
||||
KModifier.PROTECTED,
|
||||
KModifier.PUBLIC
|
||||
)
|
||||
|
||||
private fun Collection<KModifier>.visibility(): KModifier {
|
||||
@@ -77,10 +77,10 @@ private fun Collection<KModifier>.visibility(): KModifier {
|
||||
|
||||
@KotlinPoetMetadataPreview
|
||||
internal fun primaryConstructor(
|
||||
targetElement: TypeElement,
|
||||
kotlinApi: TypeSpec,
|
||||
elements: Elements,
|
||||
messager: Messager
|
||||
targetElement: TypeElement,
|
||||
kotlinApi: TypeSpec,
|
||||
elements: Elements,
|
||||
messager: Messager
|
||||
): TargetConstructor? {
|
||||
val primaryConstructor = kotlinApi.primaryConstructor ?: return null
|
||||
|
||||
@@ -88,38 +88,47 @@ internal fun primaryConstructor(
|
||||
for ((index, parameter) in primaryConstructor.parameters.withIndex()) {
|
||||
val name = parameter.name
|
||||
parameters[name] = TargetParameter(
|
||||
name = name,
|
||||
index = index,
|
||||
type = parameter.type,
|
||||
hasDefault = parameter.defaultValue != null,
|
||||
qualifiers = parameter.annotations.qualifiers(elements),
|
||||
jsonName = parameter.annotations.jsonName()
|
||||
name = name,
|
||||
index = index,
|
||||
type = parameter.type,
|
||||
hasDefault = parameter.defaultValue != null,
|
||||
qualifiers = parameter.annotations.qualifiers(elements),
|
||||
jsonName = parameter.annotations.jsonName()
|
||||
)
|
||||
}
|
||||
|
||||
val kmConstructorSignature = primaryConstructor.tag<ImmutableKmConstructor>()?.signature?.toString()
|
||||
?: run {
|
||||
messager.printMessage(ERROR, "No KmConstructor found for primary constructor.",
|
||||
targetElement)
|
||||
null
|
||||
}
|
||||
return TargetConstructor(parameters, primaryConstructor.modifiers.visibility(),
|
||||
kmConstructorSignature)
|
||||
?: run {
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"No KmConstructor found for primary constructor.",
|
||||
targetElement
|
||||
)
|
||||
null
|
||||
}
|
||||
return TargetConstructor(
|
||||
parameters,
|
||||
primaryConstructor.modifiers.visibility(),
|
||||
kmConstructorSignature
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns a target type for `element`, or null if it cannot be used with code gen. */
|
||||
@KotlinPoetMetadataPreview
|
||||
internal fun targetType(messager: Messager,
|
||||
elements: Elements,
|
||||
types: Types,
|
||||
element: TypeElement,
|
||||
cachedClassInspector: MoshiCachedClassInspector
|
||||
internal fun targetType(
|
||||
messager: Messager,
|
||||
elements: Elements,
|
||||
types: Types,
|
||||
element: TypeElement,
|
||||
cachedClassInspector: MoshiCachedClassInspector
|
||||
): TargetType? {
|
||||
val typeMetadata = element.getAnnotation(Metadata::class.java)
|
||||
if (typeMetadata == null) {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must be a Kotlin class",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -127,54 +136,68 @@ internal fun targetType(messager: Messager,
|
||||
cachedClassInspector.toImmutableKmClass(typeMetadata)
|
||||
} catch (e: UnsupportedOperationException) {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass can't be applied to $element: must be a Class type",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must be a Class type",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
when {
|
||||
kmClass.isEnum -> {
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"@JsonClass with 'generateAdapter = \"true\"' can't be applied to $element: code gen for enums is not supported or necessary",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass with 'generateAdapter = \"true\"' can't be applied to $element: code gen for enums is not supported or necessary",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
!kmClass.isClass -> {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must be a Kotlin class",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
kmClass.isInner -> {
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must not be an inner class", element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must not be an inner class",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
kmClass.isSealed -> {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass can't be applied to $element: must not be sealed",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must not be sealed",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
kmClass.isAbstract -> {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass can't be applied to $element: must not be abstract",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must not be abstract",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
kmClass.isLocal -> {
|
||||
messager.printMessage(
|
||||
ERROR, "@JsonClass can't be applied to $element: must not be local",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must not be local",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
!kmClass.isPublic && !kmClass.isInternal -> {
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must be internal or public",
|
||||
element)
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: must be internal or public",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -185,13 +208,20 @@ internal fun targetType(messager: Messager,
|
||||
|
||||
val constructor = primaryConstructor(element, kotlinApi, elements, messager)
|
||||
if (constructor == null) {
|
||||
messager.printMessage(ERROR, "No primary constructor found on $element",
|
||||
element)
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"No primary constructor found on $element",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
if (constructor.visibility != KModifier.INTERNAL && constructor.visibility != KModifier.PUBLIC) {
|
||||
messager.printMessage(ERROR, "@JsonClass can't be applied to $element: " +
|
||||
"primary constructor is not internal or public", element)
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: " +
|
||||
"primary constructor is not internal or public",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -199,66 +229,68 @@ internal fun targetType(messager: Messager,
|
||||
|
||||
val resolvedTypes = mutableListOf<ResolvedTypeMapping>()
|
||||
val superTypes = appliedType.supertypes(types)
|
||||
.filterNot { supertype ->
|
||||
supertype.element.asClassName() == OBJECT_CLASS || // Don't load properties for java.lang.Object.
|
||||
supertype.element.kind != ElementKind.CLASS // Don't load properties for interface types.
|
||||
.filterNot { supertype ->
|
||||
supertype.element.asClassName() == OBJECT_CLASS || // Don't load properties for java.lang.Object.
|
||||
supertype.element.kind != ElementKind.CLASS // Don't load properties for interface types.
|
||||
}
|
||||
.onEach { supertype ->
|
||||
if (supertype.element.getAnnotation(Metadata::class.java) == null) {
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
|
||||
element
|
||||
)
|
||||
return null
|
||||
}
|
||||
.onEach { supertype ->
|
||||
if (supertype.element.getAnnotation(Metadata::class.java) == null) {
|
||||
messager.printMessage(ERROR,
|
||||
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
|
||||
element)
|
||||
return null
|
||||
}
|
||||
}
|
||||
.associateWithTo(LinkedHashMap()) { supertype ->
|
||||
// Load the kotlin API cache into memory eagerly so we can reuse the parsed APIs
|
||||
val api = if (supertype.element == element) {
|
||||
// We've already parsed this api above, reuse it
|
||||
kotlinApi
|
||||
} else {
|
||||
cachedClassInspector.toTypeSpec(supertype.element)
|
||||
}
|
||||
.associateWithTo(LinkedHashMap()) { supertype ->
|
||||
// Load the kotlin API cache into memory eagerly so we can reuse the parsed APIs
|
||||
val api = if (supertype.element == element) {
|
||||
// We've already parsed this api above, reuse it
|
||||
kotlinApi
|
||||
} else {
|
||||
cachedClassInspector.toTypeSpec(supertype.element)
|
||||
}
|
||||
|
||||
val apiSuperClass = api.superclass
|
||||
if (apiSuperClass is ParameterizedTypeName) {
|
||||
//
|
||||
// This extends a typed generic superclass. We want to construct a mapping of the
|
||||
// superclass typevar names to their materialized types here.
|
||||
//
|
||||
// class Foo extends Bar<String>
|
||||
// class Bar<T>
|
||||
//
|
||||
// We will store {Foo : {T : [String]}}.
|
||||
//
|
||||
// Then when we look at Bar<T> later, we'll look up to the descendent Foo and extract its
|
||||
// materialized type from there.
|
||||
//
|
||||
val superSuperClass = supertype.element.superclass as DeclaredType
|
||||
val apiSuperClass = api.superclass
|
||||
if (apiSuperClass is ParameterizedTypeName) {
|
||||
//
|
||||
// This extends a typed generic superclass. We want to construct a mapping of the
|
||||
// superclass typevar names to their materialized types here.
|
||||
//
|
||||
// class Foo extends Bar<String>
|
||||
// class Bar<T>
|
||||
//
|
||||
// We will store {Foo : {T : [String]}}.
|
||||
//
|
||||
// Then when we look at Bar<T> later, we'll look up to the descendent Foo and extract its
|
||||
// materialized type from there.
|
||||
//
|
||||
val superSuperClass = supertype.element.superclass as DeclaredType
|
||||
|
||||
// Convert to an element and back to wipe the typed generics off of this
|
||||
val untyped = superSuperClass.asElement().asType().asTypeName() as ParameterizedTypeName
|
||||
resolvedTypes += ResolvedTypeMapping(
|
||||
target = untyped.rawType,
|
||||
args = untyped.typeArguments.asSequence()
|
||||
.cast<TypeVariableName>()
|
||||
.map(TypeVariableName::name)
|
||||
.zip(apiSuperClass.typeArguments.asSequence())
|
||||
.associate { it }
|
||||
)
|
||||
}
|
||||
|
||||
return@associateWithTo api
|
||||
// Convert to an element and back to wipe the typed generics off of this
|
||||
val untyped = superSuperClass.asElement().asType().asTypeName() as ParameterizedTypeName
|
||||
resolvedTypes += ResolvedTypeMapping(
|
||||
target = untyped.rawType,
|
||||
args = untyped.typeArguments.asSequence()
|
||||
.cast<TypeVariableName>()
|
||||
.map(TypeVariableName::name)
|
||||
.zip(apiSuperClass.typeArguments.asSequence())
|
||||
.associate { it }
|
||||
)
|
||||
}
|
||||
|
||||
return@associateWithTo api
|
||||
}
|
||||
|
||||
for ((localAppliedType, supertypeApi) in superTypes.entries) {
|
||||
val appliedClassName = localAppliedType.element.asClassName()
|
||||
val supertypeProperties = declaredProperties(
|
||||
constructor = constructor,
|
||||
kotlinApi = supertypeApi,
|
||||
allowedTypeVars = typeVariables.toSet(),
|
||||
currentClass = appliedClassName,
|
||||
resolvedTypes = resolvedTypes
|
||||
constructor = constructor,
|
||||
kotlinApi = supertypeApi,
|
||||
allowedTypeVars = typeVariables.toSet(),
|
||||
currentClass = appliedClassName,
|
||||
resolvedTypes = resolvedTypes
|
||||
)
|
||||
for ((name, property) in supertypeProperties) {
|
||||
properties.putIfAbsent(name, property)
|
||||
@@ -273,18 +305,19 @@ internal fun targetType(messager: Messager,
|
||||
} else {
|
||||
// Implicitly public, so now look up the hierarchy
|
||||
val forceInternal = generateSequence<Element>(element) { it.enclosingElement }
|
||||
.filterIsInstance<TypeElement>()
|
||||
.map { cachedClassInspector.toImmutableKmClass(it.metadata) }
|
||||
.any { it.isInternal }
|
||||
.filterIsInstance<TypeElement>()
|
||||
.map { cachedClassInspector.toImmutableKmClass(it.metadata) }
|
||||
.any { it.isInternal }
|
||||
if (forceInternal) KModifier.INTERNAL else visibility
|
||||
}
|
||||
return TargetType(
|
||||
typeName = element.asType().asTypeName(),
|
||||
constructor = constructor,
|
||||
properties = properties,
|
||||
typeVariables = typeVariables,
|
||||
isDataClass = KModifier.DATA in kotlinApi.modifiers,
|
||||
visibility = resolvedVisibility)
|
||||
typeName = element.asType().asTypeName(),
|
||||
constructor = constructor,
|
||||
properties = properties,
|
||||
typeVariables = typeVariables,
|
||||
isDataClass = KModifier.DATA in kotlinApi.modifiers,
|
||||
visibility = resolvedVisibility
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,11 +327,11 @@ internal fun targetType(messager: Messager,
|
||||
private data class ResolvedTypeMapping(val target: ClassName, val args: Map<String, TypeName>)
|
||||
|
||||
private fun resolveTypeArgs(
|
||||
targetClass: ClassName,
|
||||
propertyType: TypeName,
|
||||
resolvedTypes: List<ResolvedTypeMapping>,
|
||||
allowedTypeVars: Set<TypeVariableName>,
|
||||
entryStartIndex: Int = resolvedTypes.indexOfLast { it.target == targetClass }
|
||||
targetClass: ClassName,
|
||||
propertyType: TypeName,
|
||||
resolvedTypes: List<ResolvedTypeMapping>,
|
||||
allowedTypeVars: Set<TypeVariableName>,
|
||||
entryStartIndex: Int = resolvedTypes.indexOfLast { it.target == targetClass }
|
||||
): TypeName {
|
||||
val unwrappedType = propertyType.unwrapTypeAlias()
|
||||
|
||||
@@ -315,8 +348,8 @@ private fun resolveTypeArgs(
|
||||
// We need to us a non-nullable version for mapping since we're just mapping based on raw java
|
||||
// type vars, but then can re-copy nullability back if it is found.
|
||||
val resolvedType = targetMappings[unwrappedType.name]
|
||||
?.copy(nullable = unwrappedType.isNullable)
|
||||
?: unwrappedType
|
||||
?.copy(nullable = unwrappedType.isNullable)
|
||||
?: unwrappedType
|
||||
|
||||
return when {
|
||||
resolvedType !is TypeVariableName -> resolvedType
|
||||
@@ -336,29 +369,29 @@ private fun resolveTypeArgs(
|
||||
/** Returns the properties declared by `typeElement`. */
|
||||
@KotlinPoetMetadataPreview
|
||||
private fun declaredProperties(
|
||||
constructor: TargetConstructor,
|
||||
kotlinApi: TypeSpec,
|
||||
allowedTypeVars: Set<TypeVariableName>,
|
||||
currentClass: ClassName,
|
||||
resolvedTypes: List<ResolvedTypeMapping>
|
||||
constructor: TargetConstructor,
|
||||
kotlinApi: TypeSpec,
|
||||
allowedTypeVars: Set<TypeVariableName>,
|
||||
currentClass: ClassName,
|
||||
resolvedTypes: List<ResolvedTypeMapping>
|
||||
): Map<String, TargetProperty> {
|
||||
|
||||
val result = mutableMapOf<String, TargetProperty>()
|
||||
for (initialProperty in kotlinApi.propertySpecs) {
|
||||
val resolvedType = resolveTypeArgs(
|
||||
targetClass = currentClass,
|
||||
propertyType = initialProperty.type,
|
||||
resolvedTypes = resolvedTypes,
|
||||
allowedTypeVars = allowedTypeVars
|
||||
targetClass = currentClass,
|
||||
propertyType = initialProperty.type,
|
||||
resolvedTypes = resolvedTypes,
|
||||
allowedTypeVars = allowedTypeVars
|
||||
)
|
||||
val property = initialProperty.toBuilder(type = resolvedType).build()
|
||||
val name = property.name
|
||||
val parameter = constructor.parameters[name]
|
||||
result[name] = TargetProperty(
|
||||
propertySpec = property,
|
||||
parameter = parameter,
|
||||
visibility = property.modifiers.visibility(),
|
||||
jsonName = parameter?.jsonName ?: property.annotations.jsonName()
|
||||
propertySpec = property,
|
||||
parameter = parameter,
|
||||
visibility = property.modifiers.visibility(),
|
||||
jsonName = parameter?.jsonName ?: property.annotations.jsonName()
|
||||
?: name.escapeDollarSigns()
|
||||
)
|
||||
}
|
||||
@@ -370,9 +403,9 @@ private val TargetProperty.isTransient get() = propertySpec.annotations.any { it
|
||||
private val TargetProperty.isSettable get() = propertySpec.mutable || parameter != null
|
||||
private val TargetProperty.isVisible: Boolean
|
||||
get() {
|
||||
return visibility == KModifier.INTERNAL
|
||||
|| visibility == KModifier.PROTECTED
|
||||
|| visibility == KModifier.PUBLIC
|
||||
return visibility == KModifier.INTERNAL ||
|
||||
visibility == KModifier.PROTECTED ||
|
||||
visibility == KModifier.PUBLIC
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,23 +413,28 @@ private val TargetProperty.isVisible: Boolean
|
||||
* cannot be used with code gen, or if no codegen is necessary for this property.
|
||||
*/
|
||||
internal fun TargetProperty.generator(
|
||||
messager: Messager,
|
||||
sourceElement: TypeElement,
|
||||
elements: Elements
|
||||
messager: Messager,
|
||||
sourceElement: TypeElement,
|
||||
elements: Elements
|
||||
): PropertyGenerator? {
|
||||
if (isTransient) {
|
||||
if (!hasDefault) {
|
||||
messager.printMessage(
|
||||
ERROR, "No default value for transient property $name",
|
||||
sourceElement)
|
||||
ERROR,
|
||||
"No default value for transient property $name",
|
||||
sourceElement
|
||||
)
|
||||
return null
|
||||
}
|
||||
return PropertyGenerator(this, DelegateKey(type, emptyList()), true)
|
||||
}
|
||||
|
||||
if (!isVisible) {
|
||||
messager.printMessage(ERROR, "property $name is not visible",
|
||||
sourceElement)
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"property $name is not visible",
|
||||
sourceElement
|
||||
)
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -410,29 +448,35 @@ internal fun TargetProperty.generator(
|
||||
val qualifierRawType = jsonQualifier.typeName.rawType()
|
||||
// Check Java types since that covers both Java and Kotlin annotations.
|
||||
val annotationElement = elements.getTypeElement(qualifierRawType.canonicalName)
|
||||
?: continue
|
||||
?: continue
|
||||
annotationElement.getAnnotation(Retention::class.java)?.let {
|
||||
if (it.value != RetentionPolicy.RUNTIME) {
|
||||
messager.printMessage(ERROR,
|
||||
"JsonQualifier @${qualifierRawType.simpleName} must have RUNTIME retention")
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"JsonQualifier @${qualifierRawType.simpleName} must have RUNTIME retention"
|
||||
)
|
||||
}
|
||||
}
|
||||
annotationElement.getAnnotation(Target::class.java)?.let {
|
||||
if (ElementType.FIELD !in it.value) {
|
||||
messager.printMessage(ERROR,
|
||||
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target")
|
||||
messager.printMessage(
|
||||
ERROR,
|
||||
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jsonQualifierSpecs = qualifiers.map {
|
||||
it.toBuilder()
|
||||
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
|
||||
.build()
|
||||
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
|
||||
.build()
|
||||
}
|
||||
|
||||
return PropertyGenerator(this,
|
||||
DelegateKey(type, jsonQualifierSpecs))
|
||||
return PropertyGenerator(
|
||||
this,
|
||||
DelegateKey(type, jsonQualifierSpecs)
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<AnnotationSpec>?.qualifiers(elements: Elements): Set<AnnotationSpec> {
|
||||
@@ -478,7 +522,7 @@ internal fun TypeName.unwrapTypeAlias(): TypeName {
|
||||
internal val TypeElement.metadata: Metadata
|
||||
get() {
|
||||
return getAnnotation(Metadata::class.java)
|
||||
?: throw IllegalStateException("Not a kotlin type! $this")
|
||||
?: throw IllegalStateException("Not a kotlin type! $this")
|
||||
}
|
||||
|
||||
private fun <E> Sequence<*>.cast(): Sequence<E> {
|
||||
|
@@ -41,10 +41,12 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun privateConstructor() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class PrivateConstructor private constructor(var a: Int, var b: Int) {
|
||||
fun a() = a
|
||||
@@ -54,7 +56,8 @@ class JsonClassCodegenProcessorTest {
|
||||
}
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains("constructor is not internal or public")
|
||||
}
|
||||
@@ -62,14 +65,17 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun privateConstructorParameter() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class PrivateConstructorParameter(private var a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains("property a is not visible")
|
||||
}
|
||||
@@ -77,7 +83,9 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun privateProperties() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@@ -87,7 +95,8 @@ class JsonClassCodegenProcessorTest {
|
||||
private var b: Int = -1
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains("property a is not visible")
|
||||
}
|
||||
@@ -95,226 +104,279 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun interfacesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
interface Interface
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to Interface: must be a Kotlin class")
|
||||
"error: @JsonClass can't be applied to Interface: must be a Kotlin class"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun interfacesDoNotErrorWhenGeneratorNotSet() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true, generator="customGenerator")
|
||||
interface Interface
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun abstractClassesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
abstract class AbstractClass(val a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to AbstractClass: must not be abstract")
|
||||
"error: @JsonClass can't be applied to AbstractClass: must not be abstract"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun sealedClassesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
sealed class SealedClass(val a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to SealedClass: must not be sealed")
|
||||
"error: @JsonClass can't be applied to SealedClass: must not be sealed"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun innerClassesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
class Outer {
|
||||
@JsonClass(generateAdapter = true)
|
||||
inner class InnerClass(val a: Int)
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to Outer.InnerClass: must not be an inner class")
|
||||
"error: @JsonClass can't be applied to Outer.InnerClass: must not be an inner class"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun enumClassesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
enum class KotlinEnum {
|
||||
A, B
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass with 'generateAdapter = \"true\"' can't be applied to KotlinEnum: code gen for enums is not supported or necessary")
|
||||
"error: @JsonClass with 'generateAdapter = \"true\"' can't be applied to KotlinEnum: code gen for enums is not supported or necessary"
|
||||
)
|
||||
}
|
||||
|
||||
// Annotation processors don't get called for local classes, so we don't have the opportunity to
|
||||
@Ignore
|
||||
@Test
|
||||
fun localClassesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
fun outer() {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class LocalClass(val a: Int)
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to LocalClass: must not be local")
|
||||
"error: @JsonClass can't be applied to LocalClass: must not be local"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun privateClassesNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
private class PrivateClass(val a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to PrivateClass: must be internal or public")
|
||||
"error: @JsonClass can't be applied to PrivateClass: must be internal or public"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun objectDeclarationsNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
object ObjectDeclaration {
|
||||
var a = 5
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to ObjectDeclaration: must be a Kotlin class")
|
||||
"error: @JsonClass can't be applied to ObjectDeclaration: must be a Kotlin class"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun objectExpressionsNotSupported() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
val expression = object : Any() {
|
||||
var a = 5
|
||||
}
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: @JsonClass can't be applied to getExpression\$annotations(): must be a Kotlin class")
|
||||
"error: @JsonClass can't be applied to getExpression\$annotations(): must be a Kotlin class"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun requiredTransientConstructorParameterFails() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class RequiredTransientConstructorParameter(@Transient var a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: No default value for transient property a")
|
||||
"error: No default value for transient property a"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun nonPropertyConstructorParameter() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
@JsonClass(generateAdapter = true)
|
||||
class NonPropertyConstructorParameter(a: Int, val b: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains(
|
||||
"error: No property for required constructor parameter a")
|
||||
"error: No property for required constructor parameter a"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun badGeneratedAnnotation() {
|
||||
val result = prepareCompilation(kotlin("source.kt",
|
||||
val result = prepareCompilation(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Foo(val a: Int)
|
||||
"""
|
||||
)).apply {
|
||||
)
|
||||
).apply {
|
||||
kaptArgs[JsonClassCodegenProcessor.OPTION_GENERATED] = "javax.annotation.GeneratedBlerg"
|
||||
}.compile()
|
||||
assertThat(result.messages).contains(
|
||||
"Invalid option value for ${JsonClassCodegenProcessor.OPTION_GENERATED}")
|
||||
"Invalid option value for ${JsonClassCodegenProcessor.OPTION_GENERATED}"
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun multipleErrors() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@@ -324,7 +386,8 @@ class JsonClassCodegenProcessorTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class Class2(private var c: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains("property a is not visible")
|
||||
assertThat(result.messages).contains("property c is not visible")
|
||||
@@ -333,7 +396,9 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun extendPlatformType() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.util.Date
|
||||
@@ -341,31 +406,37 @@ class JsonClassCodegenProcessorTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ExtendsPlatformClass(var a: Int) : Date()
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.messages).contains("supertype java.util.Date is not a Kotlin type")
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun extendJavaType() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.kotlin.codegen.JavaSuperclass
|
||||
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ExtendsJavaType(var b: Int) : JavaSuperclass()
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages)
|
||||
.contains("supertype com.squareup.moshi.kotlin.codegen.JavaSuperclass is not a Kotlin type")
|
||||
.contains("supertype com.squareup.moshi.kotlin.codegen.JavaSuperclass is not a Kotlin type")
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun nonFieldApplicableQualifier() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
@@ -382,7 +453,8 @@ class JsonClassCodegenProcessorTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ClassWithQualifier(@UpperCase val a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target")
|
||||
}
|
||||
@@ -390,7 +462,9 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun nonRuntimeQualifier() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
@@ -408,7 +482,8 @@ class JsonClassCodegenProcessorTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ClassWithQualifier(@UpperCase val a: Int)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
|
||||
assertThat(result.messages).contains("JsonQualifier @UpperCase must have RUNTIME retention")
|
||||
}
|
||||
@@ -416,37 +491,42 @@ class JsonClassCodegenProcessorTest {
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun `TypeAliases with the same backing type should share the same adapter`() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
|
||||
typealias FirstName = String
|
||||
typealias LastName = String
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Person(val firstName: FirstName, val lastName: LastName, val hairColor: String)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
|
||||
|
||||
// We're checking here that we only generate one `stringAdapter` that's used for both the
|
||||
// regular string properties as well as the the aliased ones.
|
||||
val adapterClass = result.classLoader.loadClass("PersonJsonAdapter").kotlin
|
||||
assertThat(adapterClass.declaredMemberProperties.map { it.returnType }).containsExactly(
|
||||
JsonReader.Options::class.createType(),
|
||||
JsonAdapter::class.parameterizedBy(String::class)
|
||||
JsonReader.Options::class.createType(),
|
||||
JsonAdapter::class.parameterizedBy(String::class)
|
||||
)
|
||||
}
|
||||
|
||||
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
|
||||
@Test
|
||||
fun `Processor should generate comprehensive proguard rules`() {
|
||||
val result = compile(kotlin("source.kt",
|
||||
val result = compile(
|
||||
kotlin(
|
||||
"source.kt",
|
||||
"""
|
||||
package testPackage
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonQualifier
|
||||
|
||||
|
||||
typealias FirstName = String
|
||||
typealias LastName = String
|
||||
|
||||
@@ -473,7 +553,7 @@ class JsonClassCodegenProcessorTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Complex<T>(val firstName: FirstName = "", @MyQualifier val names: MutableList<String>, val genericProp: T)
|
||||
|
||||
|
||||
object NestedType {
|
||||
@JsonQualifier
|
||||
annotation class NestedQualifier
|
||||
@@ -552,36 +632,44 @@ class JsonClassCodegenProcessorTest {
|
||||
val arg65: Long = 65
|
||||
)
|
||||
"""
|
||||
))
|
||||
)
|
||||
)
|
||||
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
|
||||
|
||||
result.generatedFiles.filter { it.extension == "pro" }.forEach { generatedFile ->
|
||||
when (generatedFile.nameWithoutExtension) {
|
||||
"moshi-testPackage.Aliases" -> assertThat(generatedFile).hasContent("""
|
||||
"moshi-testPackage.Aliases" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.Aliases
|
||||
-keepnames class testPackage.Aliases
|
||||
-if class testPackage.Aliases
|
||||
-keep class testPackage.AliasesJsonAdapter {
|
||||
public <init>(com.squareup.moshi.Moshi);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.Simple" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.Simple" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.Simple
|
||||
-keepnames class testPackage.Simple
|
||||
-if class testPackage.Simple
|
||||
-keep class testPackage.SimpleJsonAdapter {
|
||||
public <init>(com.squareup.moshi.Moshi);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.Generic" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.Generic" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.Generic
|
||||
-keepnames class testPackage.Generic
|
||||
-if class testPackage.Generic
|
||||
-keep class testPackage.GenericJsonAdapter {
|
||||
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.UsingQualifiers" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.UsingQualifiers" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.UsingQualifiers
|
||||
-keepnames class testPackage.UsingQualifiers
|
||||
-if class testPackage.UsingQualifiers
|
||||
@@ -591,16 +679,20 @@ class JsonClassCodegenProcessorTest {
|
||||
}
|
||||
-if class testPackage.UsingQualifiers
|
||||
-keep @interface testPackage.MyQualifier
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.MixedTypes
|
||||
-keepnames class testPackage.MixedTypes
|
||||
-if class testPackage.MixedTypes
|
||||
-keep class testPackage.MixedTypesJsonAdapter {
|
||||
public <init>(com.squareup.moshi.Moshi);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.DefaultParams" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.DefaultParams" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.DefaultParams
|
||||
-keepnames class testPackage.DefaultParams
|
||||
-if class testPackage.DefaultParams
|
||||
@@ -613,8 +705,10 @@ class JsonClassCodegenProcessorTest {
|
||||
-keepclassmembers class testPackage.DefaultParams {
|
||||
public synthetic <init>(java.lang.String,int,kotlin.jvm.internal.DefaultConstructorMarker);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.Complex" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.Complex" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.Complex
|
||||
-keepnames class testPackage.Complex
|
||||
-if class testPackage.Complex
|
||||
@@ -630,8 +724,10 @@ class JsonClassCodegenProcessorTest {
|
||||
-keepclassmembers class testPackage.Complex {
|
||||
public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.MultipleMasks" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.MultipleMasks" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.MultipleMasks
|
||||
-keepnames class testPackage.MultipleMasks
|
||||
-if class testPackage.MultipleMasks
|
||||
@@ -644,8 +740,10 @@ class JsonClassCodegenProcessorTest {
|
||||
-keepclassmembers class testPackage.MultipleMasks {
|
||||
public synthetic <init>(long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,int,int,int,kotlin.jvm.internal.DefaultConstructorMarker);
|
||||
}
|
||||
""".trimIndent())
|
||||
"moshi-testPackage.NestedType.NestedSimple" -> assertThat(generatedFile).hasContent("""
|
||||
""".trimIndent()
|
||||
)
|
||||
"moshi-testPackage.NestedType.NestedSimple" -> assertThat(generatedFile).hasContent(
|
||||
"""
|
||||
-if class testPackage.NestedType${'$'}NestedSimple
|
||||
-keepnames class testPackage.NestedType${'$'}NestedSimple
|
||||
-if class testPackage.NestedType${'$'}NestedSimple
|
||||
@@ -655,7 +753,8 @@ class JsonClassCodegenProcessorTest {
|
||||
}
|
||||
-if class testPackage.NestedType${'$'}NestedSimple
|
||||
-keep @interface testPackage.NestedType${'$'}NestedQualifier
|
||||
""".trimIndent())
|
||||
""".trimIndent()
|
||||
)
|
||||
else -> error("Unexpected proguard file! ${generatedFile.name}")
|
||||
}
|
||||
}
|
||||
@@ -663,13 +762,13 @@ class JsonClassCodegenProcessorTest {
|
||||
|
||||
private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation {
|
||||
return KotlinCompilation()
|
||||
.apply {
|
||||
workingDir = temporaryFolder.root
|
||||
annotationProcessors = listOf(JsonClassCodegenProcessor())
|
||||
inheritClassPath = true
|
||||
sources = sourceFiles.asList()
|
||||
verbose = false
|
||||
}
|
||||
.apply {
|
||||
workingDir = temporaryFolder.root
|
||||
annotationProcessors = listOf(JsonClassCodegenProcessor())
|
||||
inheritClassPath = true
|
||||
sources = sourceFiles.asList()
|
||||
verbose = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result {
|
||||
@@ -682,12 +781,11 @@ class JsonClassCodegenProcessorTest {
|
||||
|
||||
private fun KClassifier.parameterizedBy(vararg types: KType): KType {
|
||||
return createType(
|
||||
types.map { it.asProjection() }
|
||||
types.map { it.asProjection() }
|
||||
)
|
||||
}
|
||||
|
||||
private fun KType.asProjection(variance: KVariance? = INVARIANT): KTypeProjection {
|
||||
return KTypeProjection(variance, this)
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -18,8 +18,8 @@ package com.squareup.moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
|
||||
@Deprecated(
|
||||
message = "this moved to avoid a package name conflict in the Java Platform Module System.",
|
||||
replaceWith = ReplaceWith("com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory")
|
||||
message = "this moved to avoid a package name conflict in the Java Platform Module System.",
|
||||
replaceWith = ReplaceWith("com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory")
|
||||
)
|
||||
class KotlinJsonAdapterFactory
|
||||
: JsonAdapter.Factory by KotlinJsonAdapterFactory()
|
||||
class KotlinJsonAdapterFactory :
|
||||
JsonAdapter.Factory by KotlinJsonAdapterFactory()
|
||||
|
@@ -28,7 +28,6 @@ import com.squareup.moshi.internal.Util.resolve
|
||||
import java.lang.reflect.Modifier
|
||||
import java.lang.reflect.Type
|
||||
import java.util.AbstractMap.SimpleEntry
|
||||
import kotlin.collections.Map.Entry
|
||||
import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
import kotlin.reflect.KParameter
|
||||
@@ -78,16 +77,17 @@ internal class KotlinJsonAdapter<T>(
|
||||
val propertyIndex = binding.propertyIndex
|
||||
if (values[propertyIndex] !== ABSENT_VALUE) {
|
||||
throw JsonDataException(
|
||||
"Multiple values for '${binding.property.name}' at ${reader.path}")
|
||||
"Multiple values for '${binding.property.name}' at ${reader.path}"
|
||||
)
|
||||
}
|
||||
|
||||
values[propertyIndex] = binding.adapter.fromJson(reader)
|
||||
|
||||
if (values[propertyIndex] == null && !binding.property.returnType.isMarkedNullable) {
|
||||
throw Util.unexpectedNull(
|
||||
binding.property.name,
|
||||
binding.jsonName,
|
||||
reader
|
||||
binding.property.name,
|
||||
binding.jsonName,
|
||||
reader
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -98,9 +98,9 @@ internal class KotlinJsonAdapter<T>(
|
||||
if (values[i] === ABSENT_VALUE && !constructor.parameters[i].isOptional) {
|
||||
if (!constructor.parameters[i].type.isMarkedNullable) {
|
||||
throw Util.missingProperty(
|
||||
constructor.parameters[i].name,
|
||||
allBindings[i]?.jsonName,
|
||||
reader
|
||||
constructor.parameters[i].name,
|
||||
allBindings[i]?.jsonName,
|
||||
reader
|
||||
)
|
||||
}
|
||||
values[i] = null // Replace absent with null.
|
||||
@@ -154,8 +154,8 @@ internal class KotlinJsonAdapter<T>(
|
||||
|
||||
/** A simple [Map] that uses parameter indexes instead of sorting or hashing. */
|
||||
class IndexedParameterMap(
|
||||
private val parameterKeys: List<KParameter>,
|
||||
private val parameterValues: Array<Any?>
|
||||
private val parameterKeys: List<KParameter>,
|
||||
private val parameterValues: Array<Any?>
|
||||
) : AbstractMutableMap<KParameter, Any?>() {
|
||||
|
||||
override fun put(key: KParameter, value: Any?): Any? = null
|
||||
@@ -180,110 +180,113 @@ internal class KotlinJsonAdapter<T>(
|
||||
}
|
||||
|
||||
class KotlinJsonAdapterFactory : JsonAdapter.Factory {
|
||||
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi)
|
||||
: JsonAdapter<*>? {
|
||||
if (annotations.isNotEmpty()) return null
|
||||
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi):
|
||||
JsonAdapter<*>? {
|
||||
if (annotations.isNotEmpty()) 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 (Util.isPlatformType(rawType)) return null
|
||||
try {
|
||||
val generatedAdapter = generatedAdapter(moshi, type, rawType)
|
||||
if (generatedAdapter != null) {
|
||||
return generatedAdapter
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
if (e.cause !is ClassNotFoundException) {
|
||||
throw e
|
||||
}
|
||||
// Fall back to a reflective adapter when the generated adapter is not found.
|
||||
}
|
||||
|
||||
require(!rawType.isLocalClass) {
|
||||
"Cannot serialize local class or object expression ${rawType.name}"
|
||||
}
|
||||
val rawTypeKotlin = rawType.kotlin
|
||||
require(!rawTypeKotlin.isAbstract) {
|
||||
"Cannot serialize abstract class ${rawType.name}"
|
||||
}
|
||||
require(!rawTypeKotlin.isInner) {
|
||||
"Cannot serialize inner class ${rawType.name}"
|
||||
}
|
||||
require(rawTypeKotlin.objectInstance == null) {
|
||||
"Cannot serialize object declaration ${rawType.name}"
|
||||
}
|
||||
require(!rawTypeKotlin.isSealed) {
|
||||
"Cannot reflectively serialize sealed class ${rawType.name}. Please register an adapter."
|
||||
}
|
||||
|
||||
val constructor = rawTypeKotlin.primaryConstructor ?: return null
|
||||
val parametersByName = constructor.parameters.associateBy { it.name }
|
||||
constructor.isAccessible = true
|
||||
|
||||
val bindingsByName = LinkedHashMap<String, KotlinJsonAdapter.Binding<Any, Any?>>()
|
||||
|
||||
for (property in rawTypeKotlin.memberProperties) {
|
||||
val parameter = parametersByName[property.name]
|
||||
|
||||
if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) {
|
||||
require(parameter == null || parameter.isOptional) {
|
||||
"No default value for transient constructor $parameter"
|
||||
val rawType = Types.getRawType(type)
|
||||
if (rawType.isInterface) return null
|
||||
if (rawType.isEnum) return null
|
||||
if (!rawType.isAnnotationPresent(KOTLIN_METADATA)) return null
|
||||
if (Util.isPlatformType(rawType)) return null
|
||||
try {
|
||||
val generatedAdapter = generatedAdapter(moshi, type, rawType)
|
||||
if (generatedAdapter != null) {
|
||||
return generatedAdapter
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
require(parameter == null || parameter.type == property.returnType) {
|
||||
"'${property.name}' has a constructor parameter of type ${parameter!!.type} but a property of type ${property.returnType}."
|
||||
}
|
||||
|
||||
if (property !is KMutableProperty1 && parameter == null) continue
|
||||
|
||||
property.isAccessible = true
|
||||
val allAnnotations = property.annotations.toMutableList()
|
||||
var jsonAnnotation = property.findAnnotation<Json>()
|
||||
|
||||
if (parameter != null) {
|
||||
allAnnotations += parameter.annotations
|
||||
if (jsonAnnotation == null) {
|
||||
jsonAnnotation = parameter.findAnnotation()
|
||||
} catch (e: RuntimeException) {
|
||||
if (e.cause !is ClassNotFoundException) {
|
||||
throw e
|
||||
}
|
||||
// Fall back to a reflective adapter when the generated adapter is not found.
|
||||
}
|
||||
|
||||
val name = jsonAnnotation?.name ?: property.name
|
||||
val resolvedPropertyType = resolve(type, rawType, property.returnType.javaType)
|
||||
val adapter = moshi.adapter<Any>(
|
||||
resolvedPropertyType, Util.jsonAnnotations(allAnnotations.toTypedArray()), property.name)
|
||||
require(!rawType.isLocalClass) {
|
||||
"Cannot serialize local class or object expression ${rawType.name}"
|
||||
}
|
||||
val rawTypeKotlin = rawType.kotlin
|
||||
require(!rawTypeKotlin.isAbstract) {
|
||||
"Cannot serialize abstract class ${rawType.name}"
|
||||
}
|
||||
require(!rawTypeKotlin.isInner) {
|
||||
"Cannot serialize inner class ${rawType.name}"
|
||||
}
|
||||
require(rawTypeKotlin.objectInstance == null) {
|
||||
"Cannot serialize object declaration ${rawType.name}"
|
||||
}
|
||||
require(!rawTypeKotlin.isSealed) {
|
||||
"Cannot reflectively serialize sealed class ${rawType.name}. Please register an adapter."
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
bindingsByName[property.name] = KotlinJsonAdapter.Binding(
|
||||
val constructor = rawTypeKotlin.primaryConstructor ?: return null
|
||||
val parametersByName = constructor.parameters.associateBy { it.name }
|
||||
constructor.isAccessible = true
|
||||
|
||||
val bindingsByName = LinkedHashMap<String, KotlinJsonAdapter.Binding<Any, Any?>>()
|
||||
|
||||
for (property in rawTypeKotlin.memberProperties) {
|
||||
val parameter = parametersByName[property.name]
|
||||
|
||||
if (Modifier.isTransient(property.javaField?.modifiers ?: 0)) {
|
||||
require(parameter == null || parameter.isOptional) {
|
||||
"No default value for transient constructor $parameter"
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
require(parameter == null || parameter.type == property.returnType) {
|
||||
"'${property.name}' has a constructor parameter of type ${parameter!!.type} but a property of type ${property.returnType}."
|
||||
}
|
||||
|
||||
if (property !is KMutableProperty1 && parameter == null) continue
|
||||
|
||||
property.isAccessible = true
|
||||
val allAnnotations = property.annotations.toMutableList()
|
||||
var jsonAnnotation = property.findAnnotation<Json>()
|
||||
|
||||
if (parameter != null) {
|
||||
allAnnotations += parameter.annotations
|
||||
if (jsonAnnotation == null) {
|
||||
jsonAnnotation = parameter.findAnnotation()
|
||||
}
|
||||
}
|
||||
|
||||
val name = jsonAnnotation?.name ?: property.name
|
||||
val resolvedPropertyType = resolve(type, rawType, property.returnType.javaType)
|
||||
val adapter = moshi.adapter<Any>(
|
||||
resolvedPropertyType,
|
||||
Util.jsonAnnotations(allAnnotations.toTypedArray()),
|
||||
property.name
|
||||
)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
bindingsByName[property.name] = KotlinJsonAdapter.Binding(
|
||||
name,
|
||||
jsonAnnotation?.name ?: name,
|
||||
adapter,
|
||||
property as KProperty1<Any, Any?>,
|
||||
parameter,
|
||||
parameter?.index ?: -1
|
||||
)
|
||||
}
|
||||
|
||||
val bindings = ArrayList<KotlinJsonAdapter.Binding<Any, Any?>?>()
|
||||
|
||||
for (parameter in constructor.parameters) {
|
||||
val binding = bindingsByName.remove(parameter.name)
|
||||
require(binding != null || parameter.isOptional) {
|
||||
"No property for required constructor $parameter"
|
||||
)
|
||||
}
|
||||
bindings += binding
|
||||
}
|
||||
|
||||
var index = bindings.size
|
||||
for (bindingByName in bindingsByName) {
|
||||
bindings += bindingByName.value.copy(propertyIndex = index++)
|
||||
}
|
||||
val bindings = ArrayList<KotlinJsonAdapter.Binding<Any, Any?>?>()
|
||||
|
||||
val nonTransientBindings = bindings.filterNotNull()
|
||||
val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray())
|
||||
return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe()
|
||||
}
|
||||
for (parameter in constructor.parameters) {
|
||||
val binding = bindingsByName.remove(parameter.name)
|
||||
require(binding != null || parameter.isOptional) {
|
||||
"No property for required constructor $parameter"
|
||||
}
|
||||
bindings += binding
|
||||
}
|
||||
|
||||
var index = bindings.size
|
||||
for (bindingByName in bindingsByName) {
|
||||
bindings += bindingByName.value.copy(propertyIndex = index++)
|
||||
}
|
||||
|
||||
val nonTransientBindings = bindings.filterNotNull()
|
||||
val options = JsonReader.Options.of(*nonTransientBindings.map { it.name }.toTypedArray())
|
||||
return KotlinJsonAdapter(constructor, bindings, nonTransientBindings, options).nullSafe()
|
||||
}
|
||||
}
|
||||
|
@@ -12,11 +12,11 @@ class KotlinJsonAdapterTest {
|
||||
@Test
|
||||
fun fallsBackToReflectiveAdapterWithoutCodegen() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val adapter = moshi.adapter(Data::class.java)
|
||||
assertThat(adapter.toString()).isEqualTo(
|
||||
"KotlinJsonAdapter(com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest.Data).nullSafe()"
|
||||
"KotlinJsonAdapter(com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest.Data).nullSafe()"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
@@ -23,9 +24,9 @@ plugins {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
freeCompilerArgs = listOf(
|
||||
"-Werror",
|
||||
"-Xopt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-Xinline-classes"
|
||||
"-Werror",
|
||||
"-Xopt-in=kotlin.ExperimentalStdlibApi",
|
||||
"-Xinline-classes"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -8,9 +8,10 @@ class DefaultConstructorTest {
|
||||
|
||||
@Test fun minimal() {
|
||||
val expected = TestClass("requiredClass")
|
||||
val json = """{"required":"requiredClass"}"""
|
||||
val json =
|
||||
"""{"required":"requiredClass"}"""
|
||||
val instance = Moshi.Builder().build().adapter<TestClass>(TestClass::class.java)
|
||||
.fromJson(json)!!
|
||||
.fromJson(json)!!
|
||||
check(instance == expected) {
|
||||
"No match:\nActual : $instance\nExpected: $expected"
|
||||
}
|
||||
@@ -18,9 +19,10 @@ class DefaultConstructorTest {
|
||||
|
||||
@Test fun allSet() {
|
||||
val expected = TestClass("requiredClass", "customOptional", 4, "setDynamic", 5, 6)
|
||||
val json = """{"required":"requiredClass","optional":"customOptional","optional2":4,"dynamicSelfReferenceOptional":"setDynamic","dynamicOptional":5,"dynamicInlineOptional":6}"""
|
||||
val json =
|
||||
"""{"required":"requiredClass","optional":"customOptional","optional2":4,"dynamicSelfReferenceOptional":"setDynamic","dynamicOptional":5,"dynamicInlineOptional":6}"""
|
||||
val instance = Moshi.Builder().build().adapter<TestClass>(TestClass::class.java)
|
||||
.fromJson(json)!!
|
||||
.fromJson(json)!!
|
||||
check(instance == expected) {
|
||||
"No match:\nActual : $instance\nExpected: $expected"
|
||||
}
|
||||
@@ -28,9 +30,10 @@ class DefaultConstructorTest {
|
||||
|
||||
@Test fun customDynamic() {
|
||||
val expected = TestClass("requiredClass", "customOptional")
|
||||
val json = """{"required":"requiredClass","optional":"customOptional"}"""
|
||||
val json =
|
||||
"""{"required":"requiredClass","optional":"customOptional"}"""
|
||||
val instance = Moshi.Builder().build().adapter<TestClass>(TestClass::class.java)
|
||||
.fromJson(json)!!
|
||||
.fromJson(json)!!
|
||||
check(instance == expected) {
|
||||
"No match:\nActual : $instance\nExpected: $expected"
|
||||
}
|
||||
@@ -39,20 +42,20 @@ class DefaultConstructorTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TestClass(
|
||||
val required: String,
|
||||
val optional: String = "optional",
|
||||
val optional2: Int = 2,
|
||||
val dynamicSelfReferenceOptional: String = required,
|
||||
val dynamicOptional: Int = createInt(),
|
||||
val dynamicInlineOptional: Int = createInlineInt()
|
||||
val required: String,
|
||||
val optional: String = "optional",
|
||||
val optional2: Int = 2,
|
||||
val dynamicSelfReferenceOptional: String = required,
|
||||
val dynamicOptional: Int = createInt(),
|
||||
val dynamicInlineOptional: Int = createInlineInt()
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/905
|
||||
// Just needs to compile
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GenericTestClassWithDefaults<T>(
|
||||
val input: String = "",
|
||||
val genericInput: T
|
||||
val input: String = "",
|
||||
val genericInput: T
|
||||
)
|
||||
|
||||
private fun createInt(): Int {
|
||||
|
@@ -32,22 +32,23 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
@JvmStatic
|
||||
fun parameters(): List<Array<*>> {
|
||||
return listOf(
|
||||
arrayOf(true),
|
||||
arrayOf(false)
|
||||
arrayOf(true),
|
||||
arrayOf(false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val moshi = Moshi.Builder()
|
||||
.apply {
|
||||
if (useReflection) {
|
||||
add(KotlinJsonAdapterFactory())
|
||||
add(object : Factory {
|
||||
.apply {
|
||||
if (useReflection) {
|
||||
add(KotlinJsonAdapterFactory())
|
||||
add(
|
||||
object : Factory {
|
||||
override fun create(
|
||||
type: Type,
|
||||
annotations: MutableSet<out Annotation>,
|
||||
moshi: Moshi
|
||||
type: Type,
|
||||
annotations: MutableSet<out Annotation>,
|
||||
moshi: Moshi
|
||||
): JsonAdapter<*>? {
|
||||
// Prevent falling back to generated adapter lookup
|
||||
val rawType = Types.getRawType(type)
|
||||
@@ -57,11 +58,11 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
}
|
||||
return moshi.nextAdapter<Any>(this, type, annotations)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.build()
|
||||
|
||||
}
|
||||
.build()
|
||||
|
||||
@Test fun requiredValueAbsent() {
|
||||
val jsonAdapter = moshi.adapter<RequiredValueAbsent>()
|
||||
@@ -107,13 +108,15 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@Test fun nonNullPropertySetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(object {
|
||||
.add(
|
||||
object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
})
|
||||
.build()
|
||||
.adapter<HasNonNullProperty>()
|
||||
}
|
||||
)
|
||||
.build()
|
||||
.adapter<HasNonNullProperty>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
@@ -143,13 +146,15 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@Test fun nonNullPropertyWithJsonNameSetToNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(object {
|
||||
.add(
|
||||
object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
})
|
||||
.build()
|
||||
.adapter<HasNonNullPropertyDifferentJsonName>()
|
||||
}
|
||||
)
|
||||
.build()
|
||||
.adapter<HasNonNullPropertyDifferentJsonName>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
@@ -179,13 +184,15 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@Test fun nonNullConstructorParameterCalledWithNullFromAdapterFailsWithJsonDataException() {
|
||||
val jsonAdapter = moshi.newBuilder()
|
||||
.add(object {
|
||||
.add(
|
||||
object {
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
@FromJson
|
||||
fun fromJson(string: String): String? = null
|
||||
})
|
||||
.build()
|
||||
.adapter<HasNonNullConstructorParameter>()
|
||||
}
|
||||
)
|
||||
.build()
|
||||
.adapter<HasNonNullConstructorParameter>()
|
||||
|
||||
try {
|
||||
//language=JSON
|
||||
@@ -207,7 +214,8 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@Test fun delegatesToInstalledAdaptersBeforeNullChecking() {
|
||||
val localMoshi = moshi.newBuilder()
|
||||
.add(object {
|
||||
.add(
|
||||
object {
|
||||
@FromJson
|
||||
fun fromJson(@Nullable string: String?): String {
|
||||
return string ?: "fallback"
|
||||
@@ -217,23 +225,30 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
fun toJson(@Nullable value: String?): String {
|
||||
return value ?: "fallback"
|
||||
}
|
||||
})
|
||||
.build()
|
||||
}
|
||||
)
|
||||
.build()
|
||||
|
||||
val hasNonNullConstructorParameterAdapter =
|
||||
localMoshi.adapter<HasNonNullConstructorParameter>()
|
||||
assertThat(hasNonNullConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.fromJson("{\"a\":null}")).isEqualTo(HasNonNullConstructorParameter("fallback"))
|
||||
localMoshi.adapter<HasNonNullConstructorParameter>()
|
||||
assertThat(
|
||||
hasNonNullConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.fromJson("{\"a\":null}")
|
||||
).isEqualTo(HasNonNullConstructorParameter("fallback"))
|
||||
|
||||
val hasNullableConstructorParameterAdapter =
|
||||
localMoshi.adapter<HasNullableConstructorParameter>()
|
||||
assertThat(hasNullableConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.fromJson("{\"a\":null}")).isEqualTo(HasNullableConstructorParameter("fallback"))
|
||||
assertThat(hasNullableConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.toJson(HasNullableConstructorParameter(null))).isEqualTo("{\"a\":\"fallback\"}")
|
||||
localMoshi.adapter<HasNullableConstructorParameter>()
|
||||
assertThat(
|
||||
hasNullableConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.fromJson("{\"a\":null}")
|
||||
).isEqualTo(HasNullableConstructorParameter("fallback"))
|
||||
assertThat(
|
||||
hasNullableConstructorParameterAdapter
|
||||
//language=JSON
|
||||
.toJson(HasNullableConstructorParameter(null))
|
||||
).isEqualTo("{\"a\":\"fallback\"}")
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -258,10 +273,12 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
val inline = InlineClass(5)
|
||||
|
||||
val expectedJson = """{"i":5}"""
|
||||
val expectedJson =
|
||||
"""{"i":5}"""
|
||||
assertThat(adapter.toJson(inline)).isEqualTo(expectedJson)
|
||||
|
||||
val testJson = """{"i":6}"""
|
||||
val testJson =
|
||||
"""{"i":6}"""
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.i).isEqualTo(6)
|
||||
}
|
||||
@@ -275,11 +292,13 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val consumer = InlineConsumer(InlineClass(23))
|
||||
|
||||
@Language("JSON")
|
||||
val expectedJson = """{"inline":{"i":23}}"""
|
||||
val expectedJson =
|
||||
"""{"inline":{"i":23}}"""
|
||||
assertThat(adapter.toJson(consumer)).isEqualTo(expectedJson)
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"inline":{"i":42}}"""
|
||||
val testJson =
|
||||
"""{"inline":{"i":42}}"""
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.inline.i).isEqualTo(42)
|
||||
}
|
||||
@@ -289,7 +308,8 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<TextAssetMetaData>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"text":"text"}"""
|
||||
val testJson =
|
||||
"""{"text":"text"}"""
|
||||
|
||||
assertThat(adapter.toJson(TextAssetMetaData("text"))).isEqualTo(testJson)
|
||||
|
||||
@@ -308,7 +328,8 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<InternalAbstractProperty>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"test":"text"}"""
|
||||
val testJson =
|
||||
"""{"test":"text"}"""
|
||||
|
||||
assertThat(adapter.toJson(InternalAbstractProperty("text"))).isEqualTo(testJson)
|
||||
|
||||
@@ -338,7 +359,8 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
assertThat(adapter.toJson(MultipleConstructorsB(6))).isEqualTo("""{"f":{"f":6},"b":6}""")
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"b":6}"""
|
||||
val testJson =
|
||||
"""{"b":6}"""
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.b).isEqualTo(6)
|
||||
}
|
||||
@@ -348,14 +370,15 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleConstructorsB(val f: MultipleConstructorsA = MultipleConstructorsA(5), val b: Int) {
|
||||
constructor(f: Int, b: Int = 6): this(MultipleConstructorsA(f), b)
|
||||
constructor(f: Int, b: Int = 6) : this(MultipleConstructorsA(f), b)
|
||||
}
|
||||
|
||||
@Test fun `multiple non-property parameters`() {
|
||||
val adapter = moshi.adapter<MultipleNonPropertyParameters>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"prop":7}"""
|
||||
val testJson =
|
||||
"""{"prop":7}"""
|
||||
|
||||
assertThat(adapter.toJson(MultipleNonPropertyParameters(7))).isEqualTo(testJson)
|
||||
|
||||
@@ -365,9 +388,9 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleNonPropertyParameters(
|
||||
val prop: Int,
|
||||
param1: Int = 1,
|
||||
param2: Int = 2
|
||||
val prop: Int,
|
||||
param1: Int = 1,
|
||||
param2: Int = 2
|
||||
) {
|
||||
init {
|
||||
// Ensure the params always uses their default value
|
||||
@@ -381,10 +404,11 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<OnlyMultipleNonPropertyParameters>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"prop":7}"""
|
||||
val testJson =
|
||||
"""{"prop":7}"""
|
||||
|
||||
assertThat(adapter.toJson(OnlyMultipleNonPropertyParameters().apply { prop = 7 }))
|
||||
.isEqualTo(testJson)
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.prop).isEqualTo(7)
|
||||
@@ -392,8 +416,8 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class OnlyMultipleNonPropertyParameters(
|
||||
param1: Int = 1,
|
||||
param2: Int = 2
|
||||
param1: Int = 1,
|
||||
param2: Int = 2
|
||||
) {
|
||||
init {
|
||||
// Ensure the params always uses their default value
|
||||
@@ -406,20 +430,21 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@Test fun typeAliasUnwrapping() {
|
||||
val adapter = moshi
|
||||
.newBuilder()
|
||||
.add(Types.supertypeOf(Int::class.javaObjectType), moshi.adapter<Int>())
|
||||
.build()
|
||||
.adapter<TypeAliasUnwrapping>()
|
||||
.newBuilder()
|
||||
.add(Types.supertypeOf(Int::class.javaObjectType), moshi.adapter<Int>())
|
||||
.build()
|
||||
.adapter<TypeAliasUnwrapping>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"simpleClass":6,"parameterized":{"value":6},"wildcardIn":{"value":6},"wildcardOut":{"value":6},"complex":{"value":[{"value":6}]}}"""
|
||||
val testJson =
|
||||
"""{"simpleClass":6,"parameterized":{"value":6},"wildcardIn":{"value":6},"wildcardOut":{"value":6},"complex":{"value":[{"value":6}]}}"""
|
||||
|
||||
val testValue = TypeAliasUnwrapping(
|
||||
simpleClass = 6,
|
||||
parameterized = GenericClass(6),
|
||||
wildcardIn = GenericClass(6),
|
||||
wildcardOut = GenericClass(6),
|
||||
complex = GenericClass(listOf(GenericClass(6)))
|
||||
simpleClass = 6,
|
||||
parameterized = GenericClass(6),
|
||||
wildcardIn = GenericClass(6),
|
||||
wildcardOut = GenericClass(6),
|
||||
complex = GenericClass(listOf(GenericClass(6)))
|
||||
)
|
||||
assertThat(adapter.toJson(testValue)).isEqualTo(testJson)
|
||||
|
||||
@@ -429,11 +454,11 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TypeAliasUnwrapping(
|
||||
val simpleClass: TypeAlias,
|
||||
val parameterized: GenericClass<TypeAlias>,
|
||||
val wildcardIn: GenericClass<in TypeAlias>,
|
||||
val wildcardOut: GenericClass<out TypeAlias>,
|
||||
val complex: GenericClass<GenericTypeAlias>?
|
||||
val simpleClass: TypeAlias,
|
||||
val parameterized: GenericClass<TypeAlias>,
|
||||
val wildcardIn: GenericClass<in TypeAlias>,
|
||||
val wildcardOut: GenericClass<out TypeAlias>,
|
||||
val complex: GenericClass<GenericTypeAlias>?
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/991
|
||||
@@ -441,21 +466,22 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<NullablePrimitives>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"objectType":"value","boolean":true,"byte":3,"char":"a","short":3,"int":3,"long":3,"float":3.2,"double":3.2}"""
|
||||
val testJson =
|
||||
"""{"objectType":"value","boolean":true,"byte":3,"char":"a","short":3,"int":3,"long":3,"float":3.2,"double":3.2}"""
|
||||
|
||||
val instance = NullablePrimitives(
|
||||
objectType = "value",
|
||||
boolean = true,
|
||||
byte = 3,
|
||||
char = 'a',
|
||||
short = 3,
|
||||
int = 3,
|
||||
long = 3,
|
||||
float = 3.2f,
|
||||
double = 3.2
|
||||
objectType = "value",
|
||||
boolean = true,
|
||||
byte = 3,
|
||||
char = 'a',
|
||||
short = 3,
|
||||
int = 3,
|
||||
long = 3,
|
||||
float = 3.2f,
|
||||
double = 3.2
|
||||
)
|
||||
assertThat(adapter.toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
@@ -463,23 +489,23 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NullablePrimitives(
|
||||
val objectType: String = "",
|
||||
val boolean: Boolean,
|
||||
val nullableBoolean: Boolean? = null,
|
||||
val byte: Byte,
|
||||
val nullableByte: Byte? = null,
|
||||
val char: Char,
|
||||
val nullableChar: Char? = null,
|
||||
val short: Short,
|
||||
val nullableShort: Short? = null,
|
||||
val int: Int,
|
||||
val nullableInt: Int? = null,
|
||||
val long: Long,
|
||||
val nullableLong: Long? = null,
|
||||
val float: Float,
|
||||
val nullableFloat: Float? = null,
|
||||
val double: Double,
|
||||
val nullableDouble: Double? = null
|
||||
val objectType: String = "",
|
||||
val boolean: Boolean,
|
||||
val nullableBoolean: Boolean? = null,
|
||||
val byte: Byte,
|
||||
val nullableByte: Byte? = null,
|
||||
val char: Char,
|
||||
val nullableChar: Char? = null,
|
||||
val short: Short,
|
||||
val nullableShort: Short? = null,
|
||||
val int: Int,
|
||||
val nullableInt: Int? = null,
|
||||
val long: Long,
|
||||
val nullableLong: Long? = null,
|
||||
val float: Float,
|
||||
val nullableFloat: Float? = null,
|
||||
val double: Double,
|
||||
val nullableDouble: Double? = null
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/990
|
||||
@@ -487,10 +513,11 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<NullableList>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"nullableList":null}"""
|
||||
val testJson =
|
||||
"""{"nullableList":null}"""
|
||||
|
||||
assertThat(adapter.serializeNulls().toJson(NullableList(null)))
|
||||
.isEqualTo(testJson)
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result.nullableList).isNull()
|
||||
@@ -503,11 +530,12 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<TypeAliasNullability>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"aShouldBeNonNull":3,"nullableAShouldBeNullable":null,"redundantNullableAShouldBeNullable":null,"manuallyNullableAShouldBeNullable":null,"convolutedMultiNullableShouldBeNullable":null,"deepNestedNullableShouldBeNullable":null}"""
|
||||
val testJson =
|
||||
"""{"aShouldBeNonNull":3,"nullableAShouldBeNullable":null,"redundantNullableAShouldBeNullable":null,"manuallyNullableAShouldBeNullable":null,"convolutedMultiNullableShouldBeNullable":null,"deepNestedNullableShouldBeNullable":null}"""
|
||||
|
||||
val instance = TypeAliasNullability(3, null, null, null, null, null)
|
||||
assertThat(adapter.serializeNulls().toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
@@ -516,12 +544,12 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
@Suppress("REDUNDANT_NULLABLE")
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TypeAliasNullability(
|
||||
val aShouldBeNonNull: A,
|
||||
val nullableAShouldBeNullable: NullableA,
|
||||
val redundantNullableAShouldBeNullable: NullableA?,
|
||||
val manuallyNullableAShouldBeNullable: A?,
|
||||
val convolutedMultiNullableShouldBeNullable: NullableB?,
|
||||
val deepNestedNullableShouldBeNullable: E
|
||||
val aShouldBeNonNull: A,
|
||||
val nullableAShouldBeNullable: NullableA,
|
||||
val redundantNullableAShouldBeNullable: NullableA?,
|
||||
val manuallyNullableAShouldBeNullable: A?,
|
||||
val convolutedMultiNullableShouldBeNullable: NullableB?,
|
||||
val deepNestedNullableShouldBeNullable: E
|
||||
)
|
||||
|
||||
// Regression test for https://github.com/square/moshi/issues/1009
|
||||
@@ -529,11 +557,12 @@ class DualKotlinTest(useReflection: Boolean) {
|
||||
val adapter = moshi.adapter<OutDeclaration<Int>>()
|
||||
|
||||
@Language("JSON")
|
||||
val testJson = """{"input":3}"""
|
||||
val testJson =
|
||||
"""{"input":3}"""
|
||||
|
||||
val instance = OutDeclaration(3)
|
||||
assertThat(adapter.serializeNulls().toJson(instance))
|
||||
.isEqualTo(testJson)
|
||||
.isEqualTo(testJson)
|
||||
|
||||
val result = adapter.fromJson(testJson)!!
|
||||
assertThat(result).isEqualTo(instance)
|
||||
|
@@ -18,7 +18,8 @@ class ComplexGenericsInheritanceTest {
|
||||
val adapter = moshi.adapter<PersonResponse>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
val json =
|
||||
"""{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = PersonResponse().apply {
|
||||
@@ -33,7 +34,8 @@ class ComplexGenericsInheritanceTest {
|
||||
val adapter = moshi.adapter<NestedPersonResponse>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
val json =
|
||||
"""{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = NestedPersonResponse().apply {
|
||||
@@ -48,7 +50,8 @@ class ComplexGenericsInheritanceTest {
|
||||
val adapter = moshi.adapter<UntypedNestedPersonResponse<Person>>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
val json =
|
||||
"""{"data":{"name":"foo"},"data2":"bar","data3":"baz"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = UntypedNestedPersonResponse<Person>().apply {
|
||||
@@ -63,16 +66,17 @@ class ComplexGenericsInheritanceTest {
|
||||
val adapter = moshi.adapter<Layer4<Person, UntypedNestedPersonResponse<Person>>>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"layer4E":{"name":"layer4E"},"layer4F":{"data":{"name":"layer4F"},"data2":"layer4F","data3":"layer4F"},"layer3C":[1,2,3],"layer3D":"layer3D","layer2":"layer2","layer1":"layer1"}"""
|
||||
val json =
|
||||
"""{"layer4E":{"name":"layer4E"},"layer4F":{"data":{"name":"layer4F"},"data2":"layer4F","data3":"layer4F"},"layer3C":[1,2,3],"layer3D":"layer3D","layer2":"layer2","layer1":"layer1"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
val testInstance = Layer4(
|
||||
layer4E = Person("layer4E"),
|
||||
layer4F = UntypedNestedPersonResponse<Person>().apply {
|
||||
data = Person("layer4F")
|
||||
data2 = "layer4F"
|
||||
data3 = "layer4F"
|
||||
}
|
||||
layer4E = Person("layer4E"),
|
||||
layer4F = UntypedNestedPersonResponse<Person>().apply {
|
||||
data = Person("layer4F")
|
||||
data2 = "layer4F"
|
||||
data3 = "layer4F"
|
||||
}
|
||||
).apply {
|
||||
layer3C = listOf(1, 2, 3)
|
||||
layer3D = "layer3D"
|
||||
@@ -97,7 +101,8 @@ data class Person(val name: String) : Personable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PersonResponse(
|
||||
val extra: String? = null) : ResponseWithSettableProperty<Person, String>()
|
||||
val extra: String? = null
|
||||
) : ResponseWithSettableProperty<Person, String>()
|
||||
|
||||
abstract class NestedResponse<T : Personable> : ResponseWithSettableProperty<T, String>()
|
||||
|
||||
@@ -106,7 +111,7 @@ data class NestedPersonResponse(val extra: String? = null) : NestedResponse<Pers
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class UntypedNestedPersonResponse<T : Personable>(
|
||||
val extra: String? = null
|
||||
val extra: String? = null
|
||||
) : NestedResponse<T>()
|
||||
|
||||
interface LayerInterface<I>
|
||||
@@ -126,6 +131,6 @@ abstract class Layer3<C, D> : Layer2<D>() {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Layer4<E : Personable, F>(
|
||||
val layer4E: E,
|
||||
val layer4F: F? = null
|
||||
val layer4E: E,
|
||||
val layer4F: F? = null
|
||||
) : Layer3<List<Int>, String>(), LayerInterface<String>
|
||||
|
@@ -50,17 +50,22 @@ class GeneratedAdaptersTest {
|
||||
|
||||
// Read
|
||||
@Language("JSON")
|
||||
val json = """{"foo": "bar"}"""
|
||||
val json =
|
||||
"""{"foo": "bar"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance.bar).isEqualTo("bar")
|
||||
|
||||
// Write
|
||||
@Language("JSON")
|
||||
val expectedJson = """{"foo":"baz"}"""
|
||||
val expectedJson =
|
||||
"""{"foo":"baz"}"""
|
||||
|
||||
assertThat(adapter.toJson(
|
||||
JsonAnnotation("baz"))).isEqualTo(expectedJson)
|
||||
assertThat(
|
||||
adapter.toJson(
|
||||
JsonAnnotation("baz")
|
||||
)
|
||||
).isEqualTo(expectedJson)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -79,8 +84,11 @@ class GeneratedAdaptersTest {
|
||||
// Write
|
||||
val expectedJson = "{\"\$foo\":\"baz\"}"
|
||||
|
||||
assertThat(adapter.toJson(
|
||||
JsonAnnotationWithDollarSign("baz"))).isEqualTo(expectedJson)
|
||||
assertThat(
|
||||
adapter.toJson(
|
||||
JsonAnnotationWithDollarSign("baz")
|
||||
)
|
||||
).isEqualTo(expectedJson)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -91,16 +99,21 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<JsonAnnotationWithQuotationMark>()
|
||||
|
||||
// Read
|
||||
val json = """{"\"foo\"": "bar"}"""
|
||||
val json =
|
||||
"""{"\"foo\"": "bar"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance.bar).isEqualTo("bar")
|
||||
|
||||
// Write
|
||||
val expectedJson = """{"\"foo\"":"baz"}"""
|
||||
val expectedJson =
|
||||
"""{"\"foo\"":"baz"}"""
|
||||
|
||||
assertThat(adapter.toJson(
|
||||
JsonAnnotationWithQuotationMark("baz"))).isEqualTo(expectedJson)
|
||||
assertThat(
|
||||
adapter.toJson(
|
||||
JsonAnnotationWithQuotationMark("baz")
|
||||
)
|
||||
).isEqualTo(expectedJson)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -112,7 +125,8 @@ class GeneratedAdaptersTest {
|
||||
|
||||
// Read/write with default values
|
||||
@Language("JSON")
|
||||
val json = """{"foo":"fooString"}"""
|
||||
val json =
|
||||
"""{"foo":"fooString"}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance.foo).isEqualTo("fooString")
|
||||
@@ -123,13 +137,18 @@ class GeneratedAdaptersTest {
|
||||
isEmpty()
|
||||
}
|
||||
|
||||
@Language("JSON") val expected = """{"foo":"fooString","bar":"","bazList":[]}"""
|
||||
assertThat(adapter.toJson(
|
||||
DefaultValues("fooString"))).isEqualTo(expected)
|
||||
@Language("JSON") val expected =
|
||||
"""{"foo":"fooString","bar":"","bazList":[]}"""
|
||||
assertThat(
|
||||
adapter.toJson(
|
||||
DefaultValues("fooString")
|
||||
)
|
||||
).isEqualTo(expected)
|
||||
|
||||
// Read/write with real values
|
||||
@Language("JSON")
|
||||
val json2 = """
|
||||
val json2 =
|
||||
"""
|
||||
{"foo":"fooString","bar":"barString","nullableBar":"bar","bazList":["baz"]}
|
||||
""".trimIndent()
|
||||
|
||||
@@ -146,14 +165,16 @@ class GeneratedAdaptersTest {
|
||||
val foo: String,
|
||||
val bar: String = "",
|
||||
val nullableBar: String? = null,
|
||||
val bazList: List<String> = emptyList())
|
||||
val bazList: List<String> = emptyList()
|
||||
)
|
||||
|
||||
@Test
|
||||
fun nullableArray() {
|
||||
val adapter = moshi.adapter<NullableArray>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"data":[null,"why"]}"""
|
||||
val json =
|
||||
"""{"data":[null,"why"]}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance.data).containsExactly(null, "why")
|
||||
@@ -168,7 +189,8 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<PrimitiveArray>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"ints":[0,1]}"""
|
||||
val json =
|
||||
"""{"ints":[0,1]}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance.ints).containsExactly(0, 1)
|
||||
@@ -183,9 +205,11 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<NullabeTypes>()
|
||||
|
||||
@Language("JSON")
|
||||
val json = """{"foo":"foo","nullableString":null}"""
|
||||
val json =
|
||||
"""{"foo":"foo","nullableString":null}"""
|
||||
@Language("JSON")
|
||||
val invalidJson = """{"foo":null,"nullableString":null}"""
|
||||
val invalidJson =
|
||||
"""{"foo":null,"nullableString":null}"""
|
||||
|
||||
val instance = adapter.fromJson(json)!!
|
||||
assertThat(instance.foo).isEqualTo("foo")
|
||||
@@ -201,8 +225,8 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NullabeTypes(
|
||||
val foo: String,
|
||||
val nullableString: String?
|
||||
val foo: String,
|
||||
val nullableString: String?
|
||||
)
|
||||
|
||||
@Test
|
||||
@@ -210,12 +234,12 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<SpecialCollections>()
|
||||
|
||||
val specialCollections = SpecialCollections(
|
||||
mutableListOf(),
|
||||
mutableSetOf(),
|
||||
mutableMapOf(),
|
||||
emptyList(),
|
||||
emptySet(),
|
||||
emptyMap()
|
||||
mutableListOf(),
|
||||
mutableSetOf(),
|
||||
mutableMapOf(),
|
||||
emptyList(),
|
||||
emptySet(),
|
||||
emptyMap()
|
||||
)
|
||||
|
||||
val json = adapter.toJson(specialCollections)
|
||||
@@ -225,12 +249,12 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class SpecialCollections(
|
||||
val mutableList: MutableList<String>,
|
||||
val mutableSet: MutableSet<String>,
|
||||
val mutableMap: MutableMap<String, String>,
|
||||
val immutableList: List<String>,
|
||||
val immutableSet: Set<String>,
|
||||
val immutableMap: Map<String, String>
|
||||
val mutableList: MutableList<String>,
|
||||
val mutableSet: MutableSet<String>,
|
||||
val mutableMap: MutableMap<String, String>,
|
||||
val immutableList: List<String>,
|
||||
val immutableSet: Set<String>,
|
||||
val immutableMap: Map<String, String>
|
||||
)
|
||||
|
||||
@Test
|
||||
@@ -238,18 +262,18 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<MutableProperties>()
|
||||
|
||||
val mutableProperties = MutableProperties(
|
||||
"immutableProperty",
|
||||
"mutableProperty",
|
||||
mutableListOf("immutableMutableList"),
|
||||
mutableListOf("immutableImmutableList"),
|
||||
mutableListOf("mutableMutableList"),
|
||||
mutableListOf("mutableImmutableList"),
|
||||
"immutableProperty",
|
||||
"mutableProperty",
|
||||
mutableListOf("immutableMutableList"),
|
||||
mutableListOf("immutableImmutableList"),
|
||||
mutableListOf("mutableMutableList"),
|
||||
mutableListOf("mutableImmutableList")
|
||||
"immutableProperty",
|
||||
"mutableProperty",
|
||||
mutableListOf("immutableMutableList"),
|
||||
mutableListOf("immutableImmutableList"),
|
||||
mutableListOf("mutableMutableList"),
|
||||
mutableListOf("mutableImmutableList"),
|
||||
"immutableProperty",
|
||||
"mutableProperty",
|
||||
mutableListOf("immutableMutableList"),
|
||||
mutableListOf("immutableImmutableList"),
|
||||
mutableListOf("mutableMutableList"),
|
||||
mutableListOf("mutableImmutableList")
|
||||
)
|
||||
|
||||
val json = adapter.toJson(mutableProperties)
|
||||
@@ -259,42 +283,45 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MutableProperties(
|
||||
val immutableProperty: String,
|
||||
var mutableProperty: String,
|
||||
val immutableMutableList: MutableList<String>,
|
||||
val immutableImmutableList: List<String>,
|
||||
var mutableMutableList: MutableList<String>,
|
||||
var mutableImmutableList: List<String>,
|
||||
val nullableImmutableProperty: String?,
|
||||
var nullableMutableProperty: String?,
|
||||
val nullableImmutableMutableList: MutableList<String>?,
|
||||
val nullableImmutableImmutableList: List<String>?,
|
||||
var nullableMutableMutableList: MutableList<String>?,
|
||||
var nullableMutableImmutableList: List<String>
|
||||
val immutableProperty: String,
|
||||
var mutableProperty: String,
|
||||
val immutableMutableList: MutableList<String>,
|
||||
val immutableImmutableList: List<String>,
|
||||
var mutableMutableList: MutableList<String>,
|
||||
var mutableImmutableList: List<String>,
|
||||
val nullableImmutableProperty: String?,
|
||||
var nullableMutableProperty: String?,
|
||||
val nullableImmutableMutableList: MutableList<String>?,
|
||||
val nullableImmutableImmutableList: List<String>?,
|
||||
var nullableMutableMutableList: MutableList<String>?,
|
||||
var nullableMutableImmutableList: List<String>
|
||||
)
|
||||
|
||||
@Test
|
||||
fun nullableTypeParams() {
|
||||
val adapter = moshi.adapter<NullableTypeParams<Int>>(
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
GeneratedAdaptersTest::class.java,
|
||||
NullableTypeParams::class.java, Int::class.javaObjectType))
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
GeneratedAdaptersTest::class.java,
|
||||
NullableTypeParams::class.java,
|
||||
Int::class.javaObjectType
|
||||
)
|
||||
)
|
||||
val nullSerializing = adapter.serializeNulls()
|
||||
|
||||
val nullableTypeParams = NullableTypeParams(
|
||||
listOf("foo", null, "bar"),
|
||||
setOf("foo", null, "bar"),
|
||||
mapOf("foo" to "bar", "baz" to null),
|
||||
null,
|
||||
1
|
||||
listOf("foo", null, "bar"),
|
||||
setOf("foo", null, "bar"),
|
||||
mapOf("foo" to "bar", "baz" to null),
|
||||
null,
|
||||
1
|
||||
)
|
||||
|
||||
val noNullsTypeParams = NullableTypeParams(
|
||||
nullableTypeParams.nullableList,
|
||||
nullableTypeParams.nullableSet,
|
||||
nullableTypeParams.nullableMap.filterValues { it != null },
|
||||
null,
|
||||
1
|
||||
nullableTypeParams.nullableList,
|
||||
nullableTypeParams.nullableSet,
|
||||
nullableTypeParams.nullableMap.filterValues { it != null },
|
||||
null,
|
||||
1
|
||||
)
|
||||
|
||||
val json = adapter.toJson(nullableTypeParams)
|
||||
@@ -330,8 +357,10 @@ class GeneratedAdaptersTest {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<ConstructorParameters>()
|
||||
|
||||
val encoded = ConstructorParameters(3,
|
||||
5)
|
||||
val encoded = ConstructorParameters(
|
||||
3,
|
||||
5
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
@@ -367,7 +396,8 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<ConstructorParametersAndProperties>()
|
||||
|
||||
val encoded = ConstructorParametersAndProperties(
|
||||
3)
|
||||
3
|
||||
)
|
||||
encoded.b = 5
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||
|
||||
@@ -386,7 +416,9 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<ImmutableConstructorParameters>()
|
||||
|
||||
val encoded = ImmutableConstructorParameters(
|
||||
3, 5)
|
||||
3,
|
||||
5
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
@@ -420,7 +452,9 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<ConstructorDefaultValues>()
|
||||
|
||||
val encoded = ConstructorDefaultValues(
|
||||
3, 5)
|
||||
3,
|
||||
5
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"b":6}""")!!
|
||||
@@ -465,12 +499,14 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@Test fun constructorParameterWithQualifier() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<ConstructorParameterWithQualifier>()
|
||||
|
||||
val encoded = ConstructorParameterWithQualifier(
|
||||
"Android", "Banana")
|
||||
"Android",
|
||||
"Banana"
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":"ANDROID","b":"Banana"}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":"Android","b":"Banana"}""")!!
|
||||
@@ -483,8 +519,8 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@Test fun propertyWithQualifier() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<PropertyWithQualifier>()
|
||||
|
||||
val encoded = PropertyWithQualifier()
|
||||
@@ -508,7 +544,9 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<ConstructorParameterWithJsonName>()
|
||||
|
||||
val encoded = ConstructorParameterWithJsonName(
|
||||
3, 5)
|
||||
3,
|
||||
5
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"key a":3,"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"key a":4,"b":6}""")!!
|
||||
@@ -544,7 +582,9 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<TransientConstructorParameter>()
|
||||
|
||||
val encoded = TransientConstructorParameter(
|
||||
3, 5)
|
||||
3,
|
||||
5
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
@@ -618,7 +658,7 @@ class GeneratedAdaptersTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
class TransientDelegateProperty {
|
||||
|
||||
private fun <T>delegate(initial: T) = Delegates.observable(initial) { _, _, _-> }
|
||||
private fun <T> delegate(initial: T) = Delegates.observable(initial) { _, _, _ -> }
|
||||
|
||||
@delegate:Transient var a: Int by delegate(-1)
|
||||
@delegate:Transient private var b: Int by delegate(-1)
|
||||
@@ -637,14 +677,16 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<ManyProperties32>()
|
||||
|
||||
val encoded = ManyProperties32(
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132)
|
||||
val json = ("""
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132
|
||||
)
|
||||
val json = (
|
||||
"""
|
||||
|{
|
||||
|"v01":101,"v02":102,"v03":103,"v04":104,"v05":105,
|
||||
|"v06":106,"v07":107,"v08":108,"v09":109,"v10":110,
|
||||
@@ -654,7 +696,8 @@ class GeneratedAdaptersTest {
|
||||
|"v26":126,"v27":127,"v28":128,"v29":129,"v30":130,
|
||||
|"v31":131,"v32":132
|
||||
|}
|
||||
|""").trimMargin().replace("\n", "")
|
||||
|"""
|
||||
).trimMargin().replace("\n", "")
|
||||
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo(json)
|
||||
|
||||
@@ -665,27 +708,55 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ManyProperties32(
|
||||
var v01: Int, var v02: Int, var v03: Int, var v04: Int, var v05: Int,
|
||||
var v06: Int, var v07: Int, var v08: Int, var v09: Int, var v10: Int,
|
||||
var v11: Int, var v12: Int, var v13: Int, var v14: Int, var v15: Int,
|
||||
var v16: Int, var v17: Int, var v18: Int, var v19: Int, var v20: Int,
|
||||
var v21: Int, var v22: Int, var v23: Int, var v24: Int, var v25: Int,
|
||||
var v26: Int, var v27: Int, var v28: Int, var v29: Int, var v30: Int,
|
||||
var v31: Int, var v32: Int)
|
||||
var v01: Int,
|
||||
var v02: Int,
|
||||
var v03: Int,
|
||||
var v04: Int,
|
||||
var v05: Int,
|
||||
var v06: Int,
|
||||
var v07: Int,
|
||||
var v08: Int,
|
||||
var v09: Int,
|
||||
var v10: Int,
|
||||
var v11: Int,
|
||||
var v12: Int,
|
||||
var v13: Int,
|
||||
var v14: Int,
|
||||
var v15: Int,
|
||||
var v16: Int,
|
||||
var v17: Int,
|
||||
var v18: Int,
|
||||
var v19: Int,
|
||||
var v20: Int,
|
||||
var v21: Int,
|
||||
var v22: Int,
|
||||
var v23: Int,
|
||||
var v24: Int,
|
||||
var v25: Int,
|
||||
var v26: Int,
|
||||
var v27: Int,
|
||||
var v28: Int,
|
||||
var v29: Int,
|
||||
var v30: Int,
|
||||
var v31: Int,
|
||||
var v32: Int
|
||||
)
|
||||
|
||||
@Test fun manyProperties33() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val jsonAdapter = moshi.adapter<ManyProperties33>()
|
||||
|
||||
val encoded = ManyProperties33(
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132, 133)
|
||||
val json = ("""
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132, 133
|
||||
)
|
||||
val json = (
|
||||
"""
|
||||
|{
|
||||
|"v01":101,"v02":102,"v03":103,"v04":104,"v05":105,
|
||||
|"v06":106,"v07":107,"v08":108,"v09":109,"v10":110,
|
||||
@@ -695,7 +766,8 @@ class GeneratedAdaptersTest {
|
||||
|"v26":126,"v27":127,"v28":128,"v29":129,"v30":130,
|
||||
|"v31":131,"v32":132,"v33":133
|
||||
|}
|
||||
|""").trimMargin().replace("\n", "")
|
||||
|"""
|
||||
).trimMargin().replace("\n", "")
|
||||
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo(json)
|
||||
|
||||
@@ -707,13 +779,40 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class ManyProperties33(
|
||||
var v01: Int, var v02: Int, var v03: Int, var v04: Int, var v05: Int,
|
||||
var v06: Int, var v07: Int, var v08: Int, var v09: Int, var v10: Int,
|
||||
var v11: Int, var v12: Int, var v13: Int, var v14: Int, var v15: Int,
|
||||
var v16: Int, var v17: Int, var v18: Int, var v19: Int, var v20: Int,
|
||||
var v21: Int, var v22: Int, var v23: Int, var v24: Int, var v25: Int,
|
||||
var v26: Int, var v27: Int, var v28: Int, var v29: Int, var v30: Int,
|
||||
var v31: Int, var v32: Int, var v33: Int)
|
||||
var v01: Int,
|
||||
var v02: Int,
|
||||
var v03: Int,
|
||||
var v04: Int,
|
||||
var v05: Int,
|
||||
var v06: Int,
|
||||
var v07: Int,
|
||||
var v08: Int,
|
||||
var v09: Int,
|
||||
var v10: Int,
|
||||
var v11: Int,
|
||||
var v12: Int,
|
||||
var v13: Int,
|
||||
var v14: Int,
|
||||
var v15: Int,
|
||||
var v16: Int,
|
||||
var v17: Int,
|
||||
var v18: Int,
|
||||
var v19: Int,
|
||||
var v20: Int,
|
||||
var v21: Int,
|
||||
var v22: Int,
|
||||
var v23: Int,
|
||||
var v24: Int,
|
||||
var v25: Int,
|
||||
var v26: Int,
|
||||
var v27: Int,
|
||||
var v28: Int,
|
||||
var v29: Int,
|
||||
var v30: Int,
|
||||
var v31: Int,
|
||||
var v32: Int,
|
||||
var v33: Int
|
||||
)
|
||||
|
||||
@Test fun unsettablePropertyIgnored() {
|
||||
val moshi = Moshi.Builder().build()
|
||||
@@ -749,7 +848,7 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class GetterOnly(var a: Int, var b: Int) {
|
||||
val total : Int
|
||||
val total: Int
|
||||
get() = a + b
|
||||
}
|
||||
|
||||
@@ -775,7 +874,7 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class GetterAndSetter(var a: Int, var b: Int) {
|
||||
var total : Int
|
||||
var total: Int
|
||||
get() = a + b
|
||||
set(value) {
|
||||
b = value - a
|
||||
@@ -787,7 +886,9 @@ class GeneratedAdaptersTest {
|
||||
val jsonAdapter = moshi.adapter<SubtypeConstructorParameters>()
|
||||
|
||||
val encoded = SubtypeConstructorParameters(
|
||||
3, 5)
|
||||
3,
|
||||
5
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":3,"b":5}""")
|
||||
|
||||
val decoded = jsonAdapter.fromJson("""{"a":4,"b":6}""")!!
|
||||
@@ -831,7 +932,7 @@ class GeneratedAdaptersTest {
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4,"a":4}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Multiple values for 'a' at $.a")
|
||||
}
|
||||
}
|
||||
@@ -846,7 +947,7 @@ class GeneratedAdaptersTest {
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4,"a":4}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Multiple values for 'a' at $.a")
|
||||
}
|
||||
}
|
||||
@@ -881,18 +982,20 @@ class GeneratedAdaptersTest {
|
||||
/** https://github.com/square/moshi/issues/563 */
|
||||
@Test fun qualifiedAdaptersAreShared() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<MultiplePropertiesShareAdapter>()
|
||||
|
||||
val encoded = MultiplePropertiesShareAdapter(
|
||||
"Android", "Banana")
|
||||
"Android",
|
||||
"Banana"
|
||||
)
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("""{"a":"ANDROID","b":"BANANA"}""")
|
||||
|
||||
val delegateAdapters = GeneratedAdaptersTest_MultiplePropertiesShareAdapterJsonAdapter::class
|
||||
.memberProperties.filter {
|
||||
it.returnType.classifier == JsonAdapter::class
|
||||
}
|
||||
.memberProperties.filter {
|
||||
it.returnType.classifier == JsonAdapter::class
|
||||
}
|
||||
assertThat(delegateAdapters).hasSize(1)
|
||||
}
|
||||
|
||||
@@ -904,12 +1007,15 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@Test fun toJsonOnly() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(CustomToJsonOnlyAdapter())
|
||||
.build()
|
||||
.add(CustomToJsonOnlyAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<CustomToJsonOnly>()
|
||||
|
||||
assertThat(jsonAdapter.toJson(
|
||||
CustomToJsonOnly(1, 2))).isEqualTo("""[1,2]""")
|
||||
assertThat(
|
||||
jsonAdapter.toJson(
|
||||
CustomToJsonOnly(1, 2)
|
||||
)
|
||||
).isEqualTo("""[1,2]""")
|
||||
|
||||
val fromJson = jsonAdapter.fromJson("""{"a":3,"b":4}""")!!
|
||||
assertThat(fromJson.a).isEqualTo(3)
|
||||
@@ -920,19 +1026,22 @@ class GeneratedAdaptersTest {
|
||||
class CustomToJsonOnly(var a: Int, var b: Int)
|
||||
|
||||
class CustomToJsonOnlyAdapter {
|
||||
@ToJson fun toJson(v: CustomToJsonOnly) : List<Int> {
|
||||
@ToJson fun toJson(v: CustomToJsonOnly): List<Int> {
|
||||
return listOf(v.a, v.b)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun fromJsonOnly() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(CustomFromJsonOnlyAdapter())
|
||||
.build()
|
||||
.add(CustomFromJsonOnlyAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<CustomFromJsonOnly>()
|
||||
|
||||
assertThat(jsonAdapter.toJson(
|
||||
CustomFromJsonOnly(1, 2))).isEqualTo("""{"a":1,"b":2}""")
|
||||
assertThat(
|
||||
jsonAdapter.toJson(
|
||||
CustomFromJsonOnly(1, 2)
|
||||
)
|
||||
).isEqualTo("""{"a":1,"b":2}""")
|
||||
|
||||
val fromJson = jsonAdapter.fromJson("""[3,4]""")!!
|
||||
assertThat(fromJson.a).isEqualTo(3)
|
||||
@@ -943,7 +1052,7 @@ class GeneratedAdaptersTest {
|
||||
class CustomFromJsonOnly(var a: Int, var b: Int)
|
||||
|
||||
class CustomFromJsonOnlyAdapter {
|
||||
@FromJson fun fromJson(v: List<Int>) : CustomFromJsonOnly {
|
||||
@FromJson fun fromJson(v: List<Int>): CustomFromJsonOnly {
|
||||
return CustomFromJsonOnly(v[0], v[1])
|
||||
}
|
||||
}
|
||||
@@ -977,8 +1086,8 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@Test fun propertyIsNothing() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(NothingAdapter())
|
||||
.build()
|
||||
.add(NothingAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<HasNothingProperty>().serializeNulls()
|
||||
|
||||
val toJson = HasNothingProperty()
|
||||
@@ -995,7 +1104,7 @@ class GeneratedAdaptersTest {
|
||||
jsonWriter.nullValue()
|
||||
}
|
||||
|
||||
@FromJson fun fromJson(jsonReader: JsonReader) : Nothing? {
|
||||
@FromJson fun fromJson(jsonReader: JsonReader): Nothing? {
|
||||
jsonReader.skipValue()
|
||||
return null
|
||||
}
|
||||
@@ -1010,10 +1119,14 @@ class GeneratedAdaptersTest {
|
||||
@Test fun enclosedParameterizedType() {
|
||||
val jsonAdapter = moshi.adapter<HasParameterizedProperty>()
|
||||
|
||||
assertThat(jsonAdapter.toJson(
|
||||
assertThat(
|
||||
jsonAdapter.toJson(
|
||||
HasParameterizedProperty(
|
||||
Twins("1", "2"))))
|
||||
.isEqualTo("""{"twins":{"a":"1","b":"2"}}""")
|
||||
Twins("1", "2")
|
||||
)
|
||||
)
|
||||
)
|
||||
.isEqualTo("""{"twins":{"a":"1","b":"2"}}""")
|
||||
|
||||
val hasParameterizedProperty = jsonAdapter.fromJson("""{"twins":{"a":"3","b":"4"}}""")!!
|
||||
assertThat(hasParameterizedProperty.twins.a).isEqualTo("3")
|
||||
@@ -1033,8 +1146,11 @@ class GeneratedAdaptersTest {
|
||||
assertThat(instance.AAA).isEqualTo(1)
|
||||
assertThat(instance.BBB).isEqualTo(2)
|
||||
|
||||
assertThat(adapter.toJson(
|
||||
UppercasePropertyName(3, 4))).isEqualTo("""{"AAA":3,"BBB":4}""")
|
||||
assertThat(
|
||||
adapter.toJson(
|
||||
UppercasePropertyName(3, 4)
|
||||
)
|
||||
).isEqualTo("""{"AAA":3,"BBB":4}""")
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -1064,10 +1180,10 @@ class GeneratedAdaptersTest {
|
||||
annotation class Uppercase(val inFrench: Boolean, val onSundays: Boolean = false)
|
||||
|
||||
class UppercaseJsonAdapter {
|
||||
@ToJson fun toJson(@Uppercase(inFrench = true) s: String) : String {
|
||||
@ToJson fun toJson(@Uppercase(inFrench = true) s: String): String {
|
||||
return s.toUpperCase(Locale.US)
|
||||
}
|
||||
@FromJson @Uppercase(inFrench = true) fun fromJson(s: String) : String {
|
||||
@FromJson @Uppercase(inFrench = true) fun fromJson(s: String): String {
|
||||
return s.toLowerCase(Locale.US)
|
||||
}
|
||||
}
|
||||
@@ -1077,9 +1193,10 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@Test fun nullablePrimitivesUseBoxedPrimitiveAdapters() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(JsonAdapter.Factory { type, _, _ ->
|
||||
.add(
|
||||
JsonAdapter.Factory { type, _, _ ->
|
||||
if (Boolean::class.javaObjectType == type) {
|
||||
return@Factory object:JsonAdapter<Boolean?>() {
|
||||
return@Factory object : JsonAdapter<Boolean?>() {
|
||||
override fun fromJson(reader: JsonReader): Boolean? {
|
||||
if (reader.peek() != JsonReader.Token.BOOLEAN) {
|
||||
reader.skipValue()
|
||||
@@ -1094,13 +1211,17 @@ class GeneratedAdaptersTest {
|
||||
}
|
||||
}
|
||||
null
|
||||
})
|
||||
.build()
|
||||
}
|
||||
)
|
||||
.build()
|
||||
val adapter = moshi.adapter<HasNullableBoolean>().serializeNulls()
|
||||
assertThat(adapter.fromJson("""{"boolean":"not a boolean"}"""))
|
||||
.isEqualTo(HasNullableBoolean(null))
|
||||
assertThat(adapter.toJson(
|
||||
HasNullableBoolean(null))).isEqualTo("""{"boolean":null}""")
|
||||
.isEqualTo(HasNullableBoolean(null))
|
||||
assertThat(
|
||||
adapter.toJson(
|
||||
HasNullableBoolean(null)
|
||||
)
|
||||
).isEqualTo("""{"boolean":null}""")
|
||||
}
|
||||
|
||||
@Test fun adaptersAreNullSafe() {
|
||||
@@ -1118,13 +1239,16 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<HasCollectionOfPrimitives>()
|
||||
|
||||
val encoded = HasCollectionOfPrimitives(
|
||||
listOf(1, 2, -3))
|
||||
listOf(1, 2, -3)
|
||||
)
|
||||
assertThat(adapter.toJson(encoded)).isEqualTo("""{"listOfInts":[1,2,-3]}""")
|
||||
|
||||
val decoded = adapter.fromJson("""{"listOfInts":[4,-5,6]}""")!!
|
||||
assertThat(decoded).isEqualTo(
|
||||
HasCollectionOfPrimitives(
|
||||
listOf(4, -5, 6)))
|
||||
HasCollectionOfPrimitives(
|
||||
listOf(4, -5, 6)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true, generator = "custom")
|
||||
@@ -1135,7 +1259,8 @@ class GeneratedAdaptersTest {
|
||||
val adapter = moshi.adapter<CustomGeneratedClass>()
|
||||
val unwrapped = (adapter as NullSafeJsonAdapter<CustomGeneratedClass>).delegate()
|
||||
assertThat(unwrapped).isInstanceOf(
|
||||
GeneratedAdaptersTest_CustomGeneratedClassJsonAdapter::class.java)
|
||||
GeneratedAdaptersTest_CustomGeneratedClassJsonAdapter::class.java
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true, generator = "custom")
|
||||
@@ -1176,7 +1301,7 @@ class GeneratedAdaptersTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ClassWithFieldJson(
|
||||
@field:Json(name = "_links") val links: String
|
||||
@field:Json(name = "_links") val links: String
|
||||
) {
|
||||
@field:Json(name = "_ids") var ids: String? = null
|
||||
}
|
||||
@@ -1206,7 +1331,6 @@ class GeneratedAdaptersTest {
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class DeprecatedProperty(@Deprecated("Deprecated for reasons") val foo: String)
|
||||
|
||||
|
||||
@Target(TYPE)
|
||||
annotation class TypeAnnotation
|
||||
|
||||
@@ -1216,8 +1340,8 @@ class GeneratedAdaptersTest {
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TypeAnnotationClass(
|
||||
val propertyWithAnnotatedType: @TypeAnnotation String = "",
|
||||
val generic: List<@TypeAnnotation String>
|
||||
val propertyWithAnnotatedType: @TypeAnnotation String = "",
|
||||
val generic: List<@TypeAnnotation String>
|
||||
)
|
||||
|
||||
@Test fun typesSizeCheckMessages_noArgs() {
|
||||
@@ -1232,8 +1356,8 @@ class GeneratedAdaptersTest {
|
||||
@Test fun typesSizeCheckMessages_wrongNumberOfArgs() {
|
||||
try {
|
||||
GeneratedAdaptersTest_MultipleGenericsJsonAdapter<String, Any, Any, Any>(
|
||||
moshi,
|
||||
arrayOf(String::class.java)
|
||||
moshi,
|
||||
arrayOf(String::class.java)
|
||||
)
|
||||
fail("Should have failed to construct the adapter due to wrong number of generics")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
@@ -1249,13 +1373,13 @@ class GeneratedAdaptersTest {
|
||||
// Compile-only test
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class MismatchParentAndNestedClassVisibility(
|
||||
val type: Int,
|
||||
val name: String? = null
|
||||
val type: Int,
|
||||
val name: String? = null
|
||||
) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NestedClass(
|
||||
val nestedProperty: String
|
||||
val nestedProperty: String
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1263,22 +1387,22 @@ internal data class MismatchParentAndNestedClassVisibility(
|
||||
// Compile-only test
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class KeysWithSpaces(
|
||||
@Json(name = "1. Information") val information: String,
|
||||
@Json(name = "2. Symbol") val symbol: String,
|
||||
@Json(name = "3. Last Refreshed") val lastRefreshed: String,
|
||||
@Json(name = "4. Interval") val interval: String,
|
||||
@Json(name = "5. Output Size") val size: String,
|
||||
@Json(name = "6. Time Zone") val timeZone: String
|
||||
@Json(name = "1. Information") val information: String,
|
||||
@Json(name = "2. Symbol") val symbol: String,
|
||||
@Json(name = "3. Last Refreshed") val lastRefreshed: String,
|
||||
@Json(name = "4. Interval") val interval: String,
|
||||
@Json(name = "5. Output Size") val size: String,
|
||||
@Json(name = "6. Time Zone") val timeZone: String
|
||||
)
|
||||
|
||||
// Has to be outside to avoid Types seeing an owning class
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NullableTypeParams<T>(
|
||||
val nullableList: List<String?>,
|
||||
val nullableSet: Set<String?>,
|
||||
val nullableMap: Map<String, String?>,
|
||||
val nullableT: T?,
|
||||
val nonNullT: T
|
||||
val nullableList: List<String?>,
|
||||
val nullableSet: Set<String?>,
|
||||
val nullableMap: Map<String, String?>,
|
||||
val nullableT: T?,
|
||||
val nonNullT: T
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -1315,7 +1439,7 @@ data class SmokeTestType(
|
||||
// Compile only, regression test for https://github.com/square/moshi/issues/848
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Hotwords(
|
||||
val `class`: List<String>?
|
||||
val `class`: List<String>?
|
||||
)
|
||||
|
||||
typealias TypeAliasName = String
|
||||
|
@@ -38,10 +38,11 @@ class MultipleMasksTest {
|
||||
|
||||
// Set some arbitrary values to make sure offsets are aligning correctly
|
||||
@Language("JSON")
|
||||
val json = """{"arg50":500,"arg3":34,"arg11":11,"arg65":67}"""
|
||||
val json =
|
||||
"""{"arg50":500,"arg3":34,"arg11":11,"arg65":67}"""
|
||||
|
||||
val instance = Moshi.Builder().build().adapter(MultipleMasks::class.java)
|
||||
.fromJson(json)!!
|
||||
.fromJson(json)!!
|
||||
|
||||
assertEquals(instance.arg2, 2)
|
||||
assertEquals(instance.arg3, 34)
|
||||
@@ -55,70 +56,70 @@ class MultipleMasksTest {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class MultipleMasks(
|
||||
val arg0: Long = 0,
|
||||
val arg1: Long = 1,
|
||||
val arg2: Long = 2,
|
||||
val arg3: Long = 3,
|
||||
val arg4: Long = 4,
|
||||
val arg5: Long = 5,
|
||||
val arg6: Long = 6,
|
||||
val arg7: Long = 7,
|
||||
val arg8: Long = 8,
|
||||
val arg9: Long = 9,
|
||||
val arg10: Long = 10,
|
||||
val arg11: Long,
|
||||
val arg12: Long = 12,
|
||||
val arg13: Long = 13,
|
||||
val arg14: Long = 14,
|
||||
val arg15: Long = 15,
|
||||
val arg16: Long = 16,
|
||||
val arg17: Long = 17,
|
||||
val arg18: Long = 18,
|
||||
val arg19: Long = 19,
|
||||
@Suppress("UNUSED_PARAMETER") arg20: Long = 20,
|
||||
val arg21: Long = 21,
|
||||
val arg22: Long = 22,
|
||||
val arg23: Long = 23,
|
||||
val arg24: Long = 24,
|
||||
val arg25: Long = 25,
|
||||
val arg26: Long = 26,
|
||||
val arg27: Long = 27,
|
||||
val arg28: Long = 28,
|
||||
val arg29: Long = 29,
|
||||
val arg30: Long = 30,
|
||||
val arg31: Long = 31,
|
||||
val arg32: Long = 32,
|
||||
val arg33: Long = 33,
|
||||
val arg34: Long = 34,
|
||||
val arg35: Long = 35,
|
||||
val arg36: Long = 36,
|
||||
val arg37: Long = 37,
|
||||
val arg38: Long = 38,
|
||||
@Transient val arg39: Long = 39,
|
||||
val arg40: Long = 40,
|
||||
val arg41: Long = 41,
|
||||
val arg42: Long = 42,
|
||||
val arg43: Long = 43,
|
||||
val arg44: Long = 44,
|
||||
val arg45: Long = 45,
|
||||
val arg46: Long = 46,
|
||||
val arg47: Long = 47,
|
||||
val arg48: Long = 48,
|
||||
val arg49: Long = 49,
|
||||
val arg50: Long = 50,
|
||||
val arg51: Long = 51,
|
||||
val arg52: Long = 52,
|
||||
@Transient val arg53: Long = 53,
|
||||
val arg54: Long = 54,
|
||||
val arg55: Long = 55,
|
||||
val arg56: Long = 56,
|
||||
val arg57: Long = 57,
|
||||
val arg58: Long = 58,
|
||||
val arg59: Long = 59,
|
||||
val arg60: Long = 60,
|
||||
val arg61: Long = 61,
|
||||
val arg62: Long = 62,
|
||||
val arg63: Long = 63,
|
||||
val arg64: Long = 64,
|
||||
val arg65: Long = 65
|
||||
val arg0: Long = 0,
|
||||
val arg1: Long = 1,
|
||||
val arg2: Long = 2,
|
||||
val arg3: Long = 3,
|
||||
val arg4: Long = 4,
|
||||
val arg5: Long = 5,
|
||||
val arg6: Long = 6,
|
||||
val arg7: Long = 7,
|
||||
val arg8: Long = 8,
|
||||
val arg9: Long = 9,
|
||||
val arg10: Long = 10,
|
||||
val arg11: Long,
|
||||
val arg12: Long = 12,
|
||||
val arg13: Long = 13,
|
||||
val arg14: Long = 14,
|
||||
val arg15: Long = 15,
|
||||
val arg16: Long = 16,
|
||||
val arg17: Long = 17,
|
||||
val arg18: Long = 18,
|
||||
val arg19: Long = 19,
|
||||
@Suppress("UNUSED_PARAMETER") arg20: Long = 20,
|
||||
val arg21: Long = 21,
|
||||
val arg22: Long = 22,
|
||||
val arg23: Long = 23,
|
||||
val arg24: Long = 24,
|
||||
val arg25: Long = 25,
|
||||
val arg26: Long = 26,
|
||||
val arg27: Long = 27,
|
||||
val arg28: Long = 28,
|
||||
val arg29: Long = 29,
|
||||
val arg30: Long = 30,
|
||||
val arg31: Long = 31,
|
||||
val arg32: Long = 32,
|
||||
val arg33: Long = 33,
|
||||
val arg34: Long = 34,
|
||||
val arg35: Long = 35,
|
||||
val arg36: Long = 36,
|
||||
val arg37: Long = 37,
|
||||
val arg38: Long = 38,
|
||||
@Transient val arg39: Long = 39,
|
||||
val arg40: Long = 40,
|
||||
val arg41: Long = 41,
|
||||
val arg42: Long = 42,
|
||||
val arg43: Long = 43,
|
||||
val arg44: Long = 44,
|
||||
val arg45: Long = 45,
|
||||
val arg46: Long = 46,
|
||||
val arg47: Long = 47,
|
||||
val arg48: Long = 48,
|
||||
val arg49: Long = 49,
|
||||
val arg50: Long = 50,
|
||||
val arg51: Long = 51,
|
||||
val arg52: Long = 52,
|
||||
@Transient val arg53: Long = 53,
|
||||
val arg54: Long = 54,
|
||||
val arg55: Long = 55,
|
||||
val arg56: Long = 56,
|
||||
val arg57: Long = 57,
|
||||
val arg58: Long = 58,
|
||||
val arg59: Long = 59,
|
||||
val arg60: Long = 60,
|
||||
val arg61: Long = 61,
|
||||
val arg62: Long = 62,
|
||||
val arg63: Long = 63,
|
||||
val arg64: Long = 64,
|
||||
val arg65: Long = 65
|
||||
)
|
||||
|
@@ -140,7 +140,7 @@ class KotlinJsonAdapterTest {
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4,"a":4}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Multiple values for 'a' at $.a")
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ class KotlinJsonAdapterTest {
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4,"a":4}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Multiple values for 'a' at $.a")
|
||||
}
|
||||
}
|
||||
@@ -201,7 +201,7 @@ class KotlinJsonAdapterTest {
|
||||
try {
|
||||
jsonAdapter.fromJson("""{"a":4,"b":null,"b":6}""")
|
||||
fail()
|
||||
} catch(expected: JsonDataException) {
|
||||
} catch (expected: JsonDataException) {
|
||||
assertThat(expected).hasMessage("Multiple values for 'b' at $.b")
|
||||
}
|
||||
}
|
||||
@@ -210,9 +210,9 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun constructorParameterWithQualifier() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<ConstructorParameterWithQualifier>()
|
||||
|
||||
val encoded = ConstructorParameterWithQualifier("Android", "Banana")
|
||||
@@ -227,9 +227,9 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun propertyWithQualifier() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.add(UppercaseJsonAdapter())
|
||||
.build()
|
||||
val jsonAdapter = moshi.adapter<PropertyWithQualifier>()
|
||||
|
||||
val encoded = PropertyWithQualifier()
|
||||
@@ -315,9 +315,11 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<RequiredTransientConstructorParameter>()
|
||||
fail()
|
||||
} catch (expected: IllegalArgumentException) {
|
||||
assertThat(expected).hasMessage("No default value for transient constructor parameter #0 " +
|
||||
assertThat(expected).hasMessage(
|
||||
"No default value for transient constructor parameter #0 " +
|
||||
"a of fun <init>(kotlin.Int): " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest.RequiredTransientConstructorParameter")
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest.RequiredTransientConstructorParameter"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,8 +359,10 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<ConstructorParameterWithSameNameAsPropertyButDifferentType>()
|
||||
fail()
|
||||
} catch (expected: IllegalArgumentException) {
|
||||
assertThat(expected).hasMessage("'a' has a constructor parameter of type " +
|
||||
"kotlin.Int but a property of type kotlin.String.")
|
||||
assertThat(expected).hasMessage(
|
||||
"'a' has a constructor parameter of type " +
|
||||
"kotlin.Int but a property of type kotlin.String."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,7 +447,8 @@ class KotlinJsonAdapterTest {
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage(
|
||||
"Platform class kotlin.Triple in kotlin.Triple<java.lang.Object, java.lang.Object, java.lang.Object> requires explicit JsonAdapter to be registered")
|
||||
"Platform class kotlin.Triple in kotlin.Triple<java.lang.Object, java.lang.Object, java.lang.Object> requires explicit JsonAdapter to be registered"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +552,7 @@ class KotlinJsonAdapterTest {
|
||||
}
|
||||
|
||||
class GetterOnly(var a: Int, var b: Int) {
|
||||
val total : Int
|
||||
val total: Int
|
||||
get() = a + b
|
||||
}
|
||||
|
||||
@@ -572,7 +577,7 @@ class KotlinJsonAdapterTest {
|
||||
}
|
||||
|
||||
class GetterAndSetter(var a: Int, var b: Int) {
|
||||
var total : Int
|
||||
var total: Int
|
||||
get() = a + b
|
||||
set(value) {
|
||||
b = value - a
|
||||
@@ -584,10 +589,11 @@ class KotlinJsonAdapterTest {
|
||||
try {
|
||||
moshi.adapter<NonPropertyConstructorParameter>()
|
||||
fail()
|
||||
} catch(expected: IllegalArgumentException) {
|
||||
} catch (expected: IllegalArgumentException) {
|
||||
assertThat(expected).hasMessage(
|
||||
"No property for required constructor parameter #0 a of fun <init>(" +
|
||||
"kotlin.Int, kotlin.Int): ${NonPropertyConstructorParameter::class.qualifiedName}")
|
||||
"No property for required constructor parameter #0 a of fun <init>(" +
|
||||
"kotlin.Int, kotlin.Int): ${NonPropertyConstructorParameter::class.qualifiedName}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,8 +618,10 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<Interface>()
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage("No JsonAdapter for interface " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$Interface (with no annotations)")
|
||||
assertThat(e).hasMessage(
|
||||
"No JsonAdapter for interface " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$Interface (with no annotations)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,8 +633,10 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<AbstractClass>()
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage("Cannot serialize abstract class " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$AbstractClass")
|
||||
assertThat(e).hasMessage(
|
||||
"Cannot serialize abstract class " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$AbstractClass"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,8 +648,10 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<InnerClass>()
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage("Cannot serialize inner class " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$InnerClass")
|
||||
assertThat(e).hasMessage(
|
||||
"Cannot serialize inner class " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$InnerClass"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,8 +664,10 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<LocalClass>()
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage("Cannot serialize local class or object expression " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$localClassesNotSupported\$LocalClass")
|
||||
assertThat(e).hasMessage(
|
||||
"Cannot serialize local class or object expression " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$localClassesNotSupported\$LocalClass"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -663,8 +677,10 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter<ObjectDeclaration>()
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage("Cannot serialize object declaration " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$ObjectDeclaration")
|
||||
assertThat(e).hasMessage(
|
||||
"Cannot serialize object declaration " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$ObjectDeclaration"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,9 +697,11 @@ class KotlinJsonAdapterTest {
|
||||
moshi.adapter(expression.javaClass)
|
||||
fail()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
assertThat(e).hasMessage("Cannot serialize local class or object expression " +
|
||||
assertThat(e).hasMessage(
|
||||
"Cannot serialize local class or object expression " +
|
||||
"com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest\$objectExpressionsNotSupported" +
|
||||
"\$expression$1")
|
||||
"\$expression$1"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,14 +710,16 @@ class KotlinJsonAdapterTest {
|
||||
val jsonAdapter = moshi.adapter<ManyProperties32>()
|
||||
|
||||
val encoded = ManyProperties32(
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132)
|
||||
val json = ("""
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132
|
||||
)
|
||||
val json = (
|
||||
"""
|
||||
|{
|
||||
|"v01":101,"v02":102,"v03":103,"v04":104,"v05":105,
|
||||
|"v06":106,"v07":107,"v08":108,"v09":109,"v10":110,
|
||||
@@ -709,7 +729,8 @@ class KotlinJsonAdapterTest {
|
||||
|"v26":126,"v27":127,"v28":128,"v29":129,"v30":130,
|
||||
|"v31":131,"v32":132
|
||||
|}
|
||||
|""").trimMargin().replace("\n", "")
|
||||
|"""
|
||||
).trimMargin().replace("\n", "")
|
||||
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo(json)
|
||||
|
||||
@@ -719,27 +740,55 @@ class KotlinJsonAdapterTest {
|
||||
}
|
||||
|
||||
class ManyProperties32(
|
||||
var v01: Int, var v02: Int, var v03: Int, var v04: Int, var v05: Int,
|
||||
var v06: Int, var v07: Int, var v08: Int, var v09: Int, var v10: Int,
|
||||
var v11: Int, var v12: Int, var v13: Int, var v14: Int, var v15: Int,
|
||||
var v16: Int, var v17: Int, var v18: Int, var v19: Int, var v20: Int,
|
||||
var v21: Int, var v22: Int, var v23: Int, var v24: Int, var v25: Int,
|
||||
var v26: Int, var v27: Int, var v28: Int, var v29: Int, var v30: Int,
|
||||
var v31: Int, var v32: Int)
|
||||
var v01: Int,
|
||||
var v02: Int,
|
||||
var v03: Int,
|
||||
var v04: Int,
|
||||
var v05: Int,
|
||||
var v06: Int,
|
||||
var v07: Int,
|
||||
var v08: Int,
|
||||
var v09: Int,
|
||||
var v10: Int,
|
||||
var v11: Int,
|
||||
var v12: Int,
|
||||
var v13: Int,
|
||||
var v14: Int,
|
||||
var v15: Int,
|
||||
var v16: Int,
|
||||
var v17: Int,
|
||||
var v18: Int,
|
||||
var v19: Int,
|
||||
var v20: Int,
|
||||
var v21: Int,
|
||||
var v22: Int,
|
||||
var v23: Int,
|
||||
var v24: Int,
|
||||
var v25: Int,
|
||||
var v26: Int,
|
||||
var v27: Int,
|
||||
var v28: Int,
|
||||
var v29: Int,
|
||||
var v30: Int,
|
||||
var v31: Int,
|
||||
var v32: Int
|
||||
)
|
||||
|
||||
@Test fun manyProperties33() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val jsonAdapter = moshi.adapter<ManyProperties33>()
|
||||
|
||||
val encoded = ManyProperties33(
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132, 133)
|
||||
val json = ("""
|
||||
101, 102, 103, 104, 105,
|
||||
106, 107, 108, 109, 110,
|
||||
111, 112, 113, 114, 115,
|
||||
116, 117, 118, 119, 120,
|
||||
121, 122, 123, 124, 125,
|
||||
126, 127, 128, 129, 130,
|
||||
131, 132, 133
|
||||
)
|
||||
val json = (
|
||||
"""
|
||||
|{
|
||||
|"v01":101,"v02":102,"v03":103,"v04":104,"v05":105,
|
||||
|"v06":106,"v07":107,"v08":108,"v09":109,"v10":110,
|
||||
@@ -749,7 +798,8 @@ class KotlinJsonAdapterTest {
|
||||
|"v26":126,"v27":127,"v28":128,"v29":129,"v30":130,
|
||||
|"v31":131,"v32":132,"v33":133
|
||||
|}
|
||||
|""").trimMargin().replace("\n", "")
|
||||
|"""
|
||||
).trimMargin().replace("\n", "")
|
||||
|
||||
assertThat(jsonAdapter.toJson(encoded)).isEqualTo(json)
|
||||
|
||||
@@ -760,21 +810,52 @@ class KotlinJsonAdapterTest {
|
||||
}
|
||||
|
||||
class ManyProperties33(
|
||||
var v01: Int, var v02: Int, var v03: Int, var v04: Int, var v05: Int,
|
||||
var v06: Int, var v07: Int, var v08: Int, var v09: Int, var v10: Int,
|
||||
var v11: Int, var v12: Int, var v13: Int, var v14: Int, var v15: Int,
|
||||
var v16: Int, var v17: Int, var v18: Int, var v19: Int, var v20: Int,
|
||||
var v21: Int, var v22: Int, var v23: Int, var v24: Int, var v25: Int,
|
||||
var v26: Int, var v27: Int, var v28: Int, var v29: Int, var v30: Int,
|
||||
var v31: Int, var v32: Int, var v33: Int)
|
||||
var v01: Int,
|
||||
var v02: Int,
|
||||
var v03: Int,
|
||||
var v04: Int,
|
||||
var v05: Int,
|
||||
var v06: Int,
|
||||
var v07: Int,
|
||||
var v08: Int,
|
||||
var v09: Int,
|
||||
var v10: Int,
|
||||
var v11: Int,
|
||||
var v12: Int,
|
||||
var v13: Int,
|
||||
var v14: Int,
|
||||
var v15: Int,
|
||||
var v16: Int,
|
||||
var v17: Int,
|
||||
var v18: Int,
|
||||
var v19: Int,
|
||||
var v20: Int,
|
||||
var v21: Int,
|
||||
var v22: Int,
|
||||
var v23: Int,
|
||||
var v24: Int,
|
||||
var v25: Int,
|
||||
var v26: Int,
|
||||
var v27: Int,
|
||||
var v28: Int,
|
||||
var v29: Int,
|
||||
var v30: Int,
|
||||
var v31: Int,
|
||||
var v32: Int,
|
||||
var v33: Int
|
||||
)
|
||||
|
||||
data class Box<out T>(val data: T)
|
||||
|
||||
@Test fun genericTypes() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val stringBoxAdapter = moshi.adapter<Box<String>>(
|
||||
Types.newParameterizedTypeWithOwner(KotlinJsonAdapterTest::class.java, Box::class.java,
|
||||
String::class.java))
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
KotlinJsonAdapterTest::class.java,
|
||||
Box::class.java,
|
||||
String::class.java
|
||||
)
|
||||
)
|
||||
assertThat(stringBoxAdapter.fromJson("""{"data":"hello"}""")).isEqualTo(Box("hello"))
|
||||
assertThat(stringBoxAdapter.toJson(Box("hello"))).isEqualTo("""{"data":"hello"}""")
|
||||
}
|
||||
@@ -784,18 +865,19 @@ class KotlinJsonAdapterTest {
|
||||
@Test fun nestedGenericTypes() {
|
||||
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
|
||||
val type = Types.newParameterizedTypeWithOwner(
|
||||
KotlinJsonAdapterTest::class.java,
|
||||
NestedGenerics::class.java,
|
||||
String::class.java,
|
||||
Int::class.javaObjectType,
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
KotlinJsonAdapterTest::class.java,
|
||||
NestedGenerics::class.java,
|
||||
String::class.java,
|
||||
Int::class.javaObjectType,
|
||||
Types.newParameterizedTypeWithOwner(
|
||||
KotlinJsonAdapterTest::class.java,
|
||||
Box::class.java,
|
||||
String::class.java
|
||||
)
|
||||
Box::class.java,
|
||||
String::class.java
|
||||
)
|
||||
)
|
||||
val adapter = moshi.adapter<NestedGenerics<String, Int, Box<String>>>(type).indent(" ")
|
||||
val json = """
|
||||
val json =
|
||||
"""
|
||||
|{
|
||||
| "value": {
|
||||
| "hello": {
|
||||
@@ -818,16 +900,18 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mixingReflectionAndCodegen() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val generatedAdapter = moshi.adapter<UsesGeneratedAdapter>()
|
||||
val reflectionAdapter = moshi.adapter<UsesReflectionAdapter>()
|
||||
|
||||
assertThat(generatedAdapter.toString())
|
||||
.isEqualTo("GeneratedJsonAdapter(KotlinJsonAdapterTest.UsesGeneratedAdapter).nullSafe()")
|
||||
.isEqualTo("GeneratedJsonAdapter(KotlinJsonAdapterTest.UsesGeneratedAdapter).nullSafe()")
|
||||
assertThat(reflectionAdapter.toString())
|
||||
.isEqualTo("KotlinJsonAdapter(com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest" +
|
||||
".UsesReflectionAdapter).nullSafe()")
|
||||
.isEqualTo(
|
||||
"KotlinJsonAdapter(com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterTest" +
|
||||
".UsesReflectionAdapter).nullSafe()"
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -841,10 +925,10 @@ class KotlinJsonAdapterTest {
|
||||
annotation class Uppercase
|
||||
|
||||
class UppercaseJsonAdapter {
|
||||
@ToJson fun toJson(@Uppercase s: String) : String {
|
||||
@ToJson fun toJson(@Uppercase s: String): String {
|
||||
return s.toUpperCase(Locale.US)
|
||||
}
|
||||
@FromJson @Uppercase fun fromJson(s: String) : String {
|
||||
@FromJson @Uppercase fun fromJson(s: String): String {
|
||||
return s.toLowerCase(Locale.US)
|
||||
}
|
||||
}
|
||||
@@ -853,9 +937,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun nullablePrimitivesUseBoxedPrimitiveAdapters() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(JsonAdapter.Factory { type, _, _ ->
|
||||
.add(
|
||||
JsonAdapter.Factory { type, _, _ ->
|
||||
if (Boolean::class.javaObjectType == type) {
|
||||
return@Factory object: JsonAdapter<Boolean?>() {
|
||||
return@Factory object : JsonAdapter<Boolean?>() {
|
||||
override fun fromJson(reader: JsonReader): Boolean? {
|
||||
if (reader.peek() != JsonReader.Token.BOOLEAN) {
|
||||
reader.skipValue()
|
||||
@@ -870,19 +955,20 @@ class KotlinJsonAdapterTest {
|
||||
}
|
||||
}
|
||||
null
|
||||
})
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
)
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
val adapter = moshi.adapter<HasNullableBoolean>().serializeNulls()
|
||||
assertThat(adapter.fromJson("""{"boolean":"not a boolean"}"""))
|
||||
.isEqualTo(HasNullableBoolean(null))
|
||||
.isEqualTo(HasNullableBoolean(null))
|
||||
assertThat(adapter.toJson(HasNullableBoolean(null))).isEqualTo("""{"boolean":null}""")
|
||||
}
|
||||
|
||||
@Test fun adaptersAreNullSafe() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
// TODO in CR: We had to mark this as nullable, vs before the jsonadapter factory would always run
|
||||
val adapter = moshi.adapter<HasNullableBoolean?>()
|
||||
@@ -904,9 +990,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToStandardReflectionWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToStandardReflection::class.java,
|
||||
"""{"map":{"key":"value"}}""",
|
||||
MapOfStringToStandardReflection(mapOf("key" to "value")))
|
||||
MapOfStringToStandardReflection::class.java,
|
||||
"""{"map":{"key":"value"}}""",
|
||||
MapOfStringToStandardReflection(mapOf("key" to "value"))
|
||||
)
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
@@ -914,9 +1001,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToStandardCodegenWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToStandardCodegen::class.java,
|
||||
"""{"map":{"key":"value"}}""",
|
||||
MapOfStringToStandardCodegen(mapOf("key" to "value")))
|
||||
MapOfStringToStandardCodegen::class.java,
|
||||
"""{"map":{"key":"value"}}""",
|
||||
MapOfStringToStandardCodegen(mapOf("key" to "value"))
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -925,9 +1013,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToEnumReflectionWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToEnumReflection::class.java,
|
||||
"""{"map":{"key":"A"}}""",
|
||||
MapOfStringToEnumReflection(mapOf("key" to KotlinEnum.A)))
|
||||
MapOfStringToEnumReflection::class.java,
|
||||
"""{"map":{"key":"A"}}""",
|
||||
MapOfStringToEnumReflection(mapOf("key" to KotlinEnum.A))
|
||||
)
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
@@ -935,9 +1024,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToEnumCodegenWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToEnumCodegen::class.java,
|
||||
"""{"map":{"key":"A"}}""",
|
||||
MapOfStringToEnumCodegen(mapOf("key" to KotlinEnum.A)))
|
||||
MapOfStringToEnumCodegen::class.java,
|
||||
"""{"map":{"key":"A"}}""",
|
||||
MapOfStringToEnumCodegen(mapOf("key" to KotlinEnum.A))
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -946,9 +1036,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToCollectionReflectionWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToCollectionReflection::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToCollectionReflection(mapOf("key" to listOf())))
|
||||
MapOfStringToCollectionReflection::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToCollectionReflection(mapOf("key" to listOf()))
|
||||
)
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
@@ -956,9 +1047,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToCollectionCodegenWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToCollectionCodegen::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToCollectionCodegen(mapOf("key" to listOf())))
|
||||
MapOfStringToCollectionCodegen::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToCollectionCodegen(mapOf("key" to listOf()))
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -967,9 +1059,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToMapReflectionWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToMapReflection::class.java,
|
||||
"""{"map":{"key":{}}}""",
|
||||
MapOfStringToMapReflection(mapOf("key" to mapOf())))
|
||||
MapOfStringToMapReflection::class.java,
|
||||
"""{"map":{"key":{}}}""",
|
||||
MapOfStringToMapReflection(mapOf("key" to mapOf()))
|
||||
)
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
@@ -977,9 +1070,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToMapCodegenWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToMapCodegen::class.java,
|
||||
"""{"map":{"key":{}}}""",
|
||||
MapOfStringToMapCodegen(mapOf("key" to mapOf())))
|
||||
MapOfStringToMapCodegen::class.java,
|
||||
"""{"map":{"key":{}}}""",
|
||||
MapOfStringToMapCodegen(mapOf("key" to mapOf()))
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -988,9 +1082,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToArrayReflectionWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToArrayReflection::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToArrayReflection(mapOf("key" to arrayOf())))
|
||||
MapOfStringToArrayReflection::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToArrayReflection(mapOf("key" to arrayOf()))
|
||||
)
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
@@ -998,9 +1093,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToArrayCodegenWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToArrayCodegen::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToArrayCodegen(mapOf("key" to arrayOf())))
|
||||
MapOfStringToArrayCodegen::class.java,
|
||||
"""{"map":{"key":[]}}""",
|
||||
MapOfStringToArrayCodegen(mapOf("key" to arrayOf()))
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -1009,9 +1105,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToClassReflectionWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToClassReflection::class.java,
|
||||
"""{"map":{"key":{"a":19,"b":42}}}""",
|
||||
MapOfStringToClassReflection(mapOf("key" to ConstructorParameters(19, 42))))
|
||||
MapOfStringToClassReflection::class.java,
|
||||
"""{"map":{"key":{"a":19,"b":42}}}""",
|
||||
MapOfStringToClassReflection(mapOf("key" to ConstructorParameters(19, 42)))
|
||||
)
|
||||
}
|
||||
|
||||
@JvmSuppressWildcards(suppress = false)
|
||||
@@ -1019,9 +1116,10 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun mapOfStringToClassCodegenWildcards() {
|
||||
mapWildcardsParameterizedTest(
|
||||
MapOfStringToClassCodegen::class.java,
|
||||
"""{"map":{"key":{"a":19,"b":42}}}""",
|
||||
MapOfStringToClassCodegen(mapOf("key" to ConstructorParameters(19, 42))))
|
||||
MapOfStringToClassCodegen::class.java,
|
||||
"""{"map":{"key":{"a":19,"b":42}}}""",
|
||||
MapOfStringToClassCodegen(mapOf("key" to ConstructorParameters(19, 42)))
|
||||
)
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@@ -1030,8 +1128,8 @@ class KotlinJsonAdapterTest {
|
||||
|
||||
@Test fun sealedClassesAreRejected() {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
try {
|
||||
moshi.adapter<SealedClass>()
|
||||
|
Reference in New Issue
Block a user