From 6393d65d94b7c421c3d6996498550dac55604718 Mon Sep 17 00:00:00 2001 From: RicardoJiang <2868405029@qq.com> Date: Sun, 12 Nov 2023 23:34:13 +0800 Subject: [PATCH] ADD: JsonReader null validator testcase --- .github/scripts/tests.sh | 4 + .../json/reader/KudosAndroidJsonReader.kt | 7 ++ .../json/reader/adapter/KudosJsonAdapter.kt | 36 +++++++-- .../compiler/KudosFromJsonFunctionBuilder.kt | 43 ++++++++-- .../kudos/compiler/KudosIrClassTransformer.kt | 10 +-- .../com/kanyun/kudos/compiler/KudosNames.kt | 8 ++ .../symbol/FromJsonFunctionDescriptorImpl.kt | 2 +- .../com/kanyun/kudos/compiler/KudosTests.kt | 42 ++++++---- .../kanyun/kudos/compiler/base/TestBase.kt | 11 ++- kudos-compiler/testData/common/notNull.kt | 80 +++++++++++++++++++ .../testData/{gson => common}/validator.kt | 2 +- kudos-compiler/testData/gson/notNull.kt | 19 +++-- kudos-compiler/testData/jackson/notNull.kt | 78 ++++++++++++++++++ kudos-compiler/testData/jsonReader/notNull.kt | 80 +++++++++++++++++++ kudos-compiler/testData/jsonReader/simple.kt | 8 ++ .../kanyun/kudos/validator/KudosValidator.kt | 2 +- 16 files changed, 383 insertions(+), 49 deletions(-) create mode 100644 kudos-compiler/testData/common/notNull.kt rename kudos-compiler/testData/{gson => common}/validator.kt (94%) create mode 100644 kudos-compiler/testData/jackson/notNull.kt create mode 100644 kudos-compiler/testData/jsonReader/notNull.kt diff --git a/.github/scripts/tests.sh b/.github/scripts/tests.sh index 2e1fa96..3295a82 100644 --- a/.github/scripts/tests.sh +++ b/.github/scripts/tests.sh @@ -25,4 +25,8 @@ do ./gradlew :kudos-compiler:test -PJACKSON_VERSION=$jackson_version -PVARIANT=jackson -PKOTLIN_COMPILER=K2 done +# Android JsonReader +echo "[Kudos] Testing with Android JsonReader" +./gradlew :kudos-compiler:test -PVARIANT=jsonReader -PKOTLIN_COMPILER=K2 + cd - diff --git a/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/KudosAndroidJsonReader.kt b/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/KudosAndroidJsonReader.kt index 39c8ffe..a860f74 100644 --- a/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/KudosAndroidJsonReader.kt +++ b/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/KudosAndroidJsonReader.kt @@ -18,6 +18,8 @@ package com.kanyun.kudos.json.reader import android.util.JsonReader import com.kanyun.kudos.json.reader.adapter.KudosJsonAdapter +import com.kanyun.kudos.json.reader.adapter.parseKudosObject +import java.lang.reflect.Type object KudosAndroidJsonReader { inline fun fromJson(json: String): T { @@ -33,4 +35,9 @@ object KudosAndroidJsonReader { throw IllegalArgumentException("class ${clazz.name} must implement KudosJsonAdapter") } } + + fun fromJson(json: String, type: Type): T { + val jsonReader = JsonReader(json.reader()) + return parseKudosObject(jsonReader, type) as T + } } diff --git a/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/adapter/KudosJsonAdapter.kt b/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/adapter/KudosJsonAdapter.kt index ba038c7..2a0abba 100644 --- a/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/adapter/KudosJsonAdapter.kt +++ b/kudos-android-json-reader/src/main/java/com/kanyun/kudos/json/reader/adapter/KudosJsonAdapter.kt @@ -17,13 +17,17 @@ package com.kanyun.kudos.json.reader.adapter import android.util.JsonReader +import android.util.JsonToken +import com.kanyun.kudos.collections.KudosCollection +import com.kanyun.kudos.collections.KudosList +import com.kanyun.kudos.collections.KudosSet import java.lang.reflect.Type interface KudosJsonAdapter { fun fromJson(jsonReader: JsonReader): T } -fun parseKudosObject(jsonReader: JsonReader, type: Type): Any { +fun parseKudosObject(jsonReader: JsonReader, type: Type): Any? { return if (type is ParameterizedTypeImpl) { parseKudosObjectInternal(jsonReader, type.rawType, type.actualTypeArguments) } else { @@ -31,8 +35,8 @@ fun parseKudosObject(jsonReader: JsonReader, type: Type): Any { } } -private fun parseKudosList(jsonReader: JsonReader, typeArguments: Array): List { - val list = mutableListOf() +private fun parseKudosList(jsonReader: JsonReader, typeArguments: Array): List { + val list = mutableListOf() jsonReader.beginArray() while (jsonReader.hasNext()) { list.add(parseKudosObject(jsonReader, typeArguments[0])) @@ -41,6 +45,19 @@ private fun parseKudosList(jsonReader: JsonReader, typeArguments: Array): return list } +private fun parseKudosCollection(jsonReader: JsonReader, type: Type, typeArguments: Array): KudosCollection { + val list = KudosList() + jsonReader.beginArray() + while (jsonReader.hasNext()) { + if (jsonReader.peek() == JsonToken.NULL) { + throw NullPointerException("Element cannot be null for ${type.typeName}.") + } + list.add(parseKudosObject(jsonReader, typeArguments[0])!!) + } + jsonReader.endArray() + return list +} + private fun parseKudosArray(jsonReader: JsonReader, typeArguments: Array): Any { val list = parseKudosList(jsonReader, typeArguments) val array = java.lang.reflect.Array.newInstance(typeArguments[0] as Class<*>, list.size) @@ -50,8 +67,8 @@ private fun parseKudosArray(jsonReader: JsonReader, typeArguments: Array): return array } -private fun parseKudosMap(jsonReader: JsonReader, typeArguments: Array): Map { - val resultMap = mutableMapOf() +private fun parseKudosMap(jsonReader: JsonReader, typeArguments: Array): Map { + val resultMap = mutableMapOf() jsonReader.beginObject() while (jsonReader.hasNext()) { val key = jsonReader.nextName() @@ -66,7 +83,11 @@ private fun parseKudosObjectInternal( jsonReader: JsonReader, type: Type, typeArguments: Array, -): Any { +): Any? { + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.skipValue() + return null + } val value = when (type) { String::class.javaObjectType -> jsonReader.nextString() Int::class.javaObjectType -> jsonReader.nextInt() @@ -77,6 +98,9 @@ private fun parseKudosObjectInternal( List::class.javaObjectType -> parseKudosList(jsonReader, typeArguments) Set::class.javaObjectType -> parseKudosList(jsonReader, typeArguments).toSet() Map::class.javaObjectType -> parseKudosMap(jsonReader, typeArguments) + KudosList::class.javaObjectType -> parseKudosCollection(jsonReader, type, typeArguments) + KudosSet::class.javaObjectType -> parseKudosCollection(jsonReader, type, typeArguments).toCollection(KudosSet()) + KudosCollection::class.javaObjectType -> parseKudosCollection(jsonReader, type, typeArguments) else -> { parseKudosObjectSpecial(jsonReader, type, typeArguments) } diff --git a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosFromJsonFunctionBuilder.kt b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosFromJsonFunctionBuilder.kt index 8a95fde..840ae36 100644 --- a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosFromJsonFunctionBuilder.kt +++ b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosFromJsonFunctionBuilder.kt @@ -16,6 +16,10 @@ package com.kanyun.kudos.compiler +import com.kanyun.kudos.compiler.KudosNames.JSON_READER_PEEK_CALLABLE_ID +import com.kanyun.kudos.compiler.KudosNames.JSON_READER_SKIP_VALUE_CALLABLE_ID +import com.kanyun.kudos.compiler.KudosNames.JSON_TOKEN_CLASS_ID +import com.kanyun.kudos.compiler.KudosNames.JSON_TOKEN_NULL_IDENTIFIER import com.kanyun.kudos.compiler.KudosNames.KUDOS_JSON_ADAPTER_CLASS_ID import com.kanyun.kudos.compiler.utils.irThis import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext @@ -25,10 +29,12 @@ import org.jetbrains.kotlin.ir.builders.Scope import org.jetbrains.kotlin.ir.builders.irBlock import org.jetbrains.kotlin.ir.builders.irBranch import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.builders.irContinue import org.jetbrains.kotlin.ir.builders.irElseBranch import org.jetbrains.kotlin.ir.builders.irEquals import org.jetbrains.kotlin.ir.builders.irGet import org.jetbrains.kotlin.ir.builders.irGetField +import org.jetbrains.kotlin.ir.builders.irIfThen import org.jetbrains.kotlin.ir.builders.irNotEquals import org.jetbrains.kotlin.ir.builders.irNull import org.jetbrains.kotlin.ir.builders.irReturn @@ -39,11 +45,13 @@ import org.jetbrains.kotlin.ir.builders.irVararg import org.jetbrains.kotlin.ir.builders.irWhen import org.jetbrains.kotlin.ir.builders.irWhile import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrEnumEntry import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrBranch import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrGetEnumValueImpl import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType @@ -106,6 +114,31 @@ internal class KudosFromJsonFunctionBuilder( dispatchReceiver = irGet(jsonReader) }, ) + val jsonReaderPeekExpression = irCall(pluginContext.referenceFunctions(JSON_READER_PEEK_CALLABLE_ID).first()).apply { + dispatchReceiver = irGet(jsonReader) + } + val jsonTokenClass = pluginContext.referenceClass(JSON_TOKEN_CLASS_ID)!! + val jsonTokenNullEntry = jsonTokenClass.owner.declarations.filterIsInstance().first { + it.name == JSON_TOKEN_NULL_IDENTIFIER + } + val jsonTokenNullExpression = IrGetEnumValueImpl( + startOffset, + endOffset, + jsonTokenClass.defaultType, + jsonTokenNullEntry.symbol, + ) + +irIfThen( + context.irBuiltIns.unitType, + irEquals(jsonReaderPeekExpression, jsonTokenNullExpression), + irBlock { + +irCall( + pluginContext.referenceFunctions(JSON_READER_SKIP_VALUE_CALLABLE_ID).first(), + ).apply { + dispatchReceiver = irGet(jsonReader) + } + +irContinue(this@apply) + }, + ) val branches = ArrayList() fields.forEach { field -> branches.add( @@ -113,7 +146,7 @@ internal class KudosFromJsonFunctionBuilder( irEquals(irGet(name), irString(field.name.asString())), irBlock { +irSetField(irFunction.irThis(), field, getNextValue(field)) - if (kudosStatusField!=null) { + if (kudosStatusField != null) { +irCall( pluginContext.referenceFunctions( CallableId(FqName("java.util"), FqName("Map"), Name.identifier("put")), @@ -124,17 +157,13 @@ internal class KudosFromJsonFunctionBuilder( dispatchReceiver = irGetField(irFunction.irThis(), kudosStatusField) } } - } + }, ), ) } branches.add( irElseBranch( - irCall( - pluginContext.referenceFunctions( - CallableId(FqName("android.util"), FqName("JsonReader"), Name.identifier("skipValue")), - ).first(), - ).apply { + irCall(pluginContext.referenceFunctions(JSON_READER_SKIP_VALUE_CALLABLE_ID).first()).apply { dispatchReceiver = irGet(jsonReader) }, ), diff --git a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosIrClassTransformer.kt b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosIrClassTransformer.kt index fa96679..3d49107 100644 --- a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosIrClassTransformer.kt +++ b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosIrClassTransformer.kt @@ -357,8 +357,8 @@ class KudosIrClassTransformer( private fun needsNoargConstructor(declaration: IrClass): Boolean = declaration.kind == ClassKind.CLASS && - declaration.hasKudosAnnotation() && - declaration.constructors.none { it.isZeroParameterConstructor() } + declaration.hasKudosAnnotation() && + declaration.constructors.none { it.isZeroParameterConstructor() } // Returns true if this constructor is callable with no arguments by JVM rules, i.e. will have descriptor `()V`. private fun IrConstructor.isZeroParameterConstructor(): Boolean { @@ -371,7 +371,7 @@ class KudosIrClassTransformer( if (irClass.hasKudosAnnotation()) { val fieldType = context.irBuiltIns.mapClass.typeWith( context.irBuiltIns.stringClass.defaultType, - context.irBuiltIns.booleanClass.defaultType + context.irBuiltIns.booleanClass.defaultType, ) val initExpression = context.referenceFunctions( CallableId(FqName("kotlin.collections"), Name.identifier("hashMapOf")), @@ -379,13 +379,13 @@ class KudosIrClassTransformer( val kudosStatusField = if (validatorFunction != null) { irClass.addField( KUDOS_FIELD_STATUS_MAP_IDENTIFIER, - fieldType + fieldType, ).apply { initializer = DeclarationIrBuilder( context, symbol, symbol.owner.startOffset, - symbol.owner.endOffset + symbol.owner.endOffset, ).run { irExprBody(irCall(initExpression)) } diff --git a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosNames.kt b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosNames.kt index 454d0a7..f3dd1d4 100644 --- a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosNames.kt +++ b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/KudosNames.kt @@ -16,6 +16,7 @@ package com.kanyun.kudos.compiler +import org.jetbrains.kotlin.name.CallableId import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name @@ -41,11 +42,18 @@ object KudosNames { val KUDOS_VALIDATOR_CLASS_ID = ClassId(FqName("com.kanyun.kudos.validator"), Name.identifier("KudosValidator")) val KUDOS_JSON_ADAPTER_CLASS_ID = ClassId(FqName("com.kanyun.kudos.json.reader.adapter"), Name.identifier("KudosJsonAdapter")) val JSON_READER_CLASS_ID = ClassId.fromString("android/util/JsonReader") + val JSON_TOKEN_CLASS_ID = ClassId(FqName("android.util"), Name.identifier("JsonToken")) + + // CallableId + val JSON_READER_SKIP_VALUE_CALLABLE_ID = CallableId(FqName("android.util"), FqName("JsonReader"), Name.identifier("skipValue")) + val JSON_READER_PEEK_CALLABLE_ID = CallableId(FqName("android.util"), FqName("JsonReader"), Name.identifier("peek")) + val JSON_TOKEN_NULL_CALLABLE_ID = CallableId(FqName("android.util"), FqName("JsonToken"), Name.identifier("NULL")) // Name.identifier val KUDOS_FROM_JSON_IDENTIFIER = Name.identifier("fromJson") val JSON_READER_IDENTIFIER = Name.identifier("jsonReader") val KUDOS_FIELD_STATUS_MAP_IDENTIFIER = Name.identifier("kudosFieldStatusMap") + val JSON_TOKEN_NULL_IDENTIFIER = Name.identifier("NULL") val CONTAINER_FQ_NAMES = setOf( "kotlin.Array", diff --git a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/k1/symbol/FromJsonFunctionDescriptorImpl.kt b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/k1/symbol/FromJsonFunctionDescriptorImpl.kt index 8a1fae3..08ce71b 100644 --- a/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/k1/symbol/FromJsonFunctionDescriptorImpl.kt +++ b/kudos-compiler/src/main/java/com/kanyun/kudos/compiler/k1/symbol/FromJsonFunctionDescriptorImpl.kt @@ -44,7 +44,7 @@ class FromJsonFunctionDescriptorImpl( emptyList(), valueParameters, classDescriptor.defaultType, - Modality.FINAL, + Modality.OPEN, DescriptorVisibilities.PUBLIC, ) } diff --git a/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/KudosTests.kt b/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/KudosTests.kt index 8af1077..e4aee6f 100644 --- a/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/KudosTests.kt +++ b/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/KudosTests.kt @@ -23,46 +23,56 @@ import org.junit.Test * Created by Benny Huo */ class KudosTests : TestBase() { - + @Test fun `common_classDeclarationCheck`() = testBase() - + @Test fun `common_constructor`() = testBase() - + @Test fun `common_defaultValue`() = testBase() - + @Test fun `common_initBlock`() = testBase() - + + @Test + fun `common_notNull`() = testBase() + @Test fun `common_propertyTypeCheck`() = testBase() - + + @Test + fun `common_validator`() = testBase() + @Test fun `gson_jsonAdapterCheck`() = testBase() - + @Test fun `gson_notNull`() = testBase() - + @Test - fun `gson_validator`() = testBase() - + fun `jackson_notNull`() = testBase() + @Test fun `jsonReader_deserialize`() = testBase() - + @Test fun `jsonReader_deserializeArrayType`() = testBase() - + @Test fun `jsonReader_deserializeFloatType`() = testBase() - + @Test fun `jsonReader_deserializeMapType`() = testBase() - + @Test fun `jsonReader_deserializeSetType`() = testBase() - + + @Test + fun `jsonReader_notNull`() = testBase() + @Test fun `jsonReader_simple`() = testBase() -} + +} \ No newline at end of file diff --git a/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/base/TestBase.kt b/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/base/TestBase.kt index ee1138d..4ba48aa 100644 --- a/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/base/TestBase.kt +++ b/kudos-compiler/src/test/java/com/kanyun/kudos/compiler/base/TestBase.kt @@ -68,8 +68,9 @@ open class TestBase { import com.google.gson.annotations.JsonAdapter import com.kanyun.kudos.gson.kudosGson import com.kanyun.kudos.gson.adapter.KudosReflectiveTypeAdapterFactory + import java.lang.reflect.Type - inline fun deserialize(string: String): T? { + inline fun deserialize(string: String, type: Type = T::class.java): T? { val gson = kudosGson() return try { val t: T = gson.fromJson(string, object: TypeToken() {}.type) @@ -89,8 +90,9 @@ open class TestBase { import com.kanyun.kudos.jackson.kudosObjectMapper import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.DeserializationFeature + import java.lang.reflect.Type - inline fun deserialize(string: String): T? { + inline fun deserialize(string: String, type: Type = T::class.java): T? { val mapper = kudosObjectMapper() mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.disable(DeserializationFeature.WRAP_EXCEPTIONS); @@ -110,10 +112,11 @@ open class TestBase { return """ // FILE: JsonReader.kt import com.kanyun.kudos.json.reader.KudosAndroidJsonReader + import java.lang.reflect.Type - inline fun deserialize(string: String): T? { + inline fun deserialize(string: String, type: Type = T::class.java): T? { return try { - val t: T = KudosAndroidJsonReader.fromJson(string) + val t: T = KudosAndroidJsonReader.fromJson(string, type) println(t) t } catch (e: Exception) { diff --git a/kudos-compiler/testData/common/notNull.kt b/kudos-compiler/testData/common/notNull.kt new file mode 100644 index 0000000..d1b8be3 --- /dev/null +++ b/kudos-compiler/testData/common/notNull.kt @@ -0,0 +1,80 @@ +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl + +// SOURCE +{{deserialize}} +// FILE: Main.kt [MainKt#main] +import com.kanyun.kudos.annotations.Kudos +import com.kanyun.kudos.collections.KudosCollection +import com.kanyun.kudos.collections.KudosList +import com.kanyun.kudos.collections.KudosSet +import com.kanyun.kudos.json.reader.adapter.ParameterizedTypeImpl + +@Kudos +data class User(val id: Long, val name: String, val age: Int = 10) { + val city: String = "beijing" +} + +@Kudos +data class Collections( + val list: List, + val list2: List, + val list3: List?, +) +@Kudos +class Arrays( + val array: Array, + val array2: Array, + val array3: Array?, +) { + override fun toString(): String { + return "Arrays(array=${array.contentToString()}, array2=${array2.contentToString()}, array3=${array3?.contentToString()})" + } +} + +@Kudos +class Project(val id: Int,val projectDesc: Desc) + +@Kudos +class Desc(val des: String) + +fun main() { + deserialize("""{}""") + deserialize("""{"id": 10}""") + deserialize("""{"name": "Bob"}""") + deserialize>("""[{"id": 10}, {"id": 11}]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize>("""[{"id": 10, "name": "Bob"}]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosList::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosSet::class.java, arrayOf(User::class.java))) + deserialize("""{"id": 10, "projectDesc": null}""") + + // Maybe supported with Java 8 annotated type. But ... not the moment. + deserialize>("""[null]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize("""{"name": "Bob"}""") + deserialize("""{"id": 10, "name": "Bob"}""") + + deserialize("""{"list": [null], "list2": []}""") + deserialize("""{"list": ["kudos"], "list2": [null]}""") + + deserialize("""{"array": [null], "array2": []}""") + deserialize("""{"array": ["kudos"], "array2": [null]}""") +} + +// EXPECT +// FILE: MainKt.main.stdout +java.lang.NullPointerException: Missing non-null field 'id'. +java.lang.NullPointerException: Missing non-null field 'name'. +java.lang.NullPointerException: Missing non-null field 'id'. +java.lang.NullPointerException: Missing non-null field 'name'. +[User(id=10, name=Bob, age=10)] +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosCollection. +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosList. +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosSet. +java.lang.NullPointerException: Missing non-null field 'projectDesc'. +[null] +java.lang.NullPointerException: Missing non-null field 'id'. +User(id=10, name=Bob, age=10) +java.lang.NullPointerException: Element must not be null in List 'list'. +Collections(list=[kudos], list2=[null], list3=null) +java.lang.NullPointerException: Element must not be null in array 'array'. +Arrays(array=[kudos], array2=[null], array3=null) \ No newline at end of file diff --git a/kudos-compiler/testData/gson/validator.kt b/kudos-compiler/testData/common/validator.kt similarity index 94% rename from kudos-compiler/testData/gson/validator.kt rename to kudos-compiler/testData/common/validator.kt index 3ae6779..558b364 100644 --- a/kudos-compiler/testData/gson/validator.kt +++ b/kudos-compiler/testData/common/validator.kt @@ -62,7 +62,7 @@ import com.kanyun.kudos.test.User fun main() { (User() as KudosValidator).validate(emptyMap()) - (Developer("kanyun") as KudosValidator).validate(emptyMap()) + (Developer("kanyun") as KudosValidator).validate(hashMapOf("company" to true)) } // EXPECT diff --git a/kudos-compiler/testData/gson/notNull.kt b/kudos-compiler/testData/gson/notNull.kt index c8cb685..d1b8be3 100644 --- a/kudos-compiler/testData/gson/notNull.kt +++ b/kudos-compiler/testData/gson/notNull.kt @@ -1,3 +1,5 @@ +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl + // SOURCE {{deserialize}} // FILE: Main.kt [MainKt#main] @@ -5,6 +7,7 @@ import com.kanyun.kudos.annotations.Kudos import com.kanyun.kudos.collections.KudosCollection import com.kanyun.kudos.collections.KudosList import com.kanyun.kudos.collections.KudosSet +import com.kanyun.kudos.json.reader.adapter.ParameterizedTypeImpl @Kudos data class User(val id: Long, val name: String, val age: Int = 10) { @@ -37,16 +40,16 @@ class Desc(val des: String) fun main() { deserialize("""{}""") deserialize("""{"id": 10}""") - deserialize("""{"name": "Bod"}""") - deserialize>("""[{"id": 10}, {"id": 11}]""") - deserialize>("""[{"id": 10, "name": "Bob"}]""") - deserialize>("""[null]""") - deserialize>("""[null]""") - deserialize>("""[null]""") + deserialize("""{"name": "Bob"}""") + deserialize>("""[{"id": 10}, {"id": 11}]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize>("""[{"id": 10, "name": "Bob"}]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosList::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosSet::class.java, arrayOf(User::class.java))) deserialize("""{"id": 10, "projectDesc": null}""") // Maybe supported with Java 8 annotated type. But ... not the moment. - deserialize>("""[null]""") + deserialize>("""[null]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) deserialize("""{"name": "Bob"}""") deserialize("""{"id": 10, "name": "Bob"}""") @@ -74,4 +77,4 @@ User(id=10, name=Bob, age=10) java.lang.NullPointerException: Element must not be null in List 'list'. Collections(list=[kudos], list2=[null], list3=null) java.lang.NullPointerException: Element must not be null in array 'array'. -Arrays(array=[kudos], array2=[null], array3=null) +Arrays(array=[kudos], array2=[null], array3=null) \ No newline at end of file diff --git a/kudos-compiler/testData/jackson/notNull.kt b/kudos-compiler/testData/jackson/notNull.kt new file mode 100644 index 0000000..3d92aca --- /dev/null +++ b/kudos-compiler/testData/jackson/notNull.kt @@ -0,0 +1,78 @@ +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl + +// SOURCE +{{deserialize}} +// FILE: Main.kt [MainKt#main] +import com.kanyun.kudos.annotations.Kudos +import com.kanyun.kudos.collections.KudosCollection +import com.kanyun.kudos.collections.KudosList +import com.kanyun.kudos.collections.KudosSet +import com.kanyun.kudos.json.reader.adapter.ParameterizedTypeImpl + +@Kudos +data class User(val id: Long, val name: String, val age: Int = 10) { + val city: String = "beijing" +} + +@Kudos +data class Collections( + val list: List, + val list2: List, + val list3: List?, +) +@Kudos +class Arrays( + val array: Array, + val array2: Array, + val array3: Array?, +) { + override fun toString(): String { + return "Arrays(array=${array.contentToString()}, array2=${array2.contentToString()}, array3=${array3?.contentToString()})" + } +} + +@Kudos +class Project(val id: Int,val projectDesc: Desc) + +@Kudos +class Desc(val des: String) + +fun main() { + deserialize("""{}""") + deserialize("""{"id": 10}""") + deserialize("""{"name": "Bob"}""") + deserialize>("""[{"id": 10}, {"id": 11}]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize>("""[{"id": 10, "name": "Bob"}]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosList::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosSet::class.java, arrayOf(User::class.java))) + + // Maybe supported with Java 8 annotated type. But ... not the moment. + deserialize>("""[null]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize("""{"name": "Bob"}""") + deserialize("""{"id": 10, "name": "Bob"}""") + + deserialize("""{"list": [null], "list2": []}""") + deserialize("""{"list": ["kudos"], "list2": [null]}""") + + deserialize("""{"array": [null], "array2": []}""") + deserialize("""{"array": ["kudos"], "array2": [null]}""") +} + +// EXPECT +// FILE: MainKt.main.stdout +java.lang.NullPointerException: Missing non-null field 'id'. +java.lang.NullPointerException: Missing non-null field 'name'. +java.lang.NullPointerException: Missing non-null field 'id'. +java.lang.NullPointerException: Missing non-null field 'name'. +[User(id=10, name=Bob, age=10)] +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosCollection. +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosList. +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosSet. +[null] +java.lang.NullPointerException: Missing non-null field 'id'. +User(id=10, name=Bob, age=10) +java.lang.NullPointerException: Element must not be null in List 'list'. +Collections(list=[kudos], list2=[null], list3=null) +java.lang.NullPointerException: Element must not be null in array 'array'. +Arrays(array=[kudos], array2=[null], array3=null) \ No newline at end of file diff --git a/kudos-compiler/testData/jsonReader/notNull.kt b/kudos-compiler/testData/jsonReader/notNull.kt new file mode 100644 index 0000000..d1b8be3 --- /dev/null +++ b/kudos-compiler/testData/jsonReader/notNull.kt @@ -0,0 +1,80 @@ +import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl + +// SOURCE +{{deserialize}} +// FILE: Main.kt [MainKt#main] +import com.kanyun.kudos.annotations.Kudos +import com.kanyun.kudos.collections.KudosCollection +import com.kanyun.kudos.collections.KudosList +import com.kanyun.kudos.collections.KudosSet +import com.kanyun.kudos.json.reader.adapter.ParameterizedTypeImpl + +@Kudos +data class User(val id: Long, val name: String, val age: Int = 10) { + val city: String = "beijing" +} + +@Kudos +data class Collections( + val list: List, + val list2: List, + val list3: List?, +) +@Kudos +class Arrays( + val array: Array, + val array2: Array, + val array3: Array?, +) { + override fun toString(): String { + return "Arrays(array=${array.contentToString()}, array2=${array2.contentToString()}, array3=${array3?.contentToString()})" + } +} + +@Kudos +class Project(val id: Int,val projectDesc: Desc) + +@Kudos +class Desc(val des: String) + +fun main() { + deserialize("""{}""") + deserialize("""{"id": 10}""") + deserialize("""{"name": "Bob"}""") + deserialize>("""[{"id": 10}, {"id": 11}]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize>("""[{"id": 10, "name": "Bob"}]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosCollection::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosList::class.java, arrayOf(User::class.java))) + deserialize>("""[null]""", ParameterizedTypeImpl(KudosSet::class.java, arrayOf(User::class.java))) + deserialize("""{"id": 10, "projectDesc": null}""") + + // Maybe supported with Java 8 annotated type. But ... not the moment. + deserialize>("""[null]""", ParameterizedTypeImpl(List::class.java, arrayOf(User::class.java))) + deserialize("""{"name": "Bob"}""") + deserialize("""{"id": 10, "name": "Bob"}""") + + deserialize("""{"list": [null], "list2": []}""") + deserialize("""{"list": ["kudos"], "list2": [null]}""") + + deserialize("""{"array": [null], "array2": []}""") + deserialize("""{"array": ["kudos"], "array2": [null]}""") +} + +// EXPECT +// FILE: MainKt.main.stdout +java.lang.NullPointerException: Missing non-null field 'id'. +java.lang.NullPointerException: Missing non-null field 'name'. +java.lang.NullPointerException: Missing non-null field 'id'. +java.lang.NullPointerException: Missing non-null field 'name'. +[User(id=10, name=Bob, age=10)] +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosCollection. +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosList. +java.lang.NullPointerException: Element cannot be null for com.kanyun.kudos.collections.KudosSet. +java.lang.NullPointerException: Missing non-null field 'projectDesc'. +[null] +java.lang.NullPointerException: Missing non-null field 'id'. +User(id=10, name=Bob, age=10) +java.lang.NullPointerException: Element must not be null in List 'list'. +Collections(list=[kudos], list2=[null], list3=null) +java.lang.NullPointerException: Element must not be null in array 'array'. +Arrays(array=[kudos], array2=[null], array3=null) \ No newline at end of file diff --git a/kudos-compiler/testData/jsonReader/simple.kt b/kudos-compiler/testData/jsonReader/simple.kt index 76b66f4..01192d3 100644 --- a/kudos-compiler/testData/jsonReader/simple.kt +++ b/kudos-compiler/testData/jsonReader/simple.kt @@ -21,6 +21,10 @@ class Desc(val descDetail: String) : KudosValidator, KudosJsonAdapter { jsonReader.beginObject() while (jsonReader.hasNext()) { val tmp0 = jsonReader.nextName() + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.skipValue() + continue + } when { tmp0 == "descDetail" -> { .descDetail = jsonReader.nextString() @@ -50,6 +54,10 @@ class Project(val projectName: String, val projectId: Int, val tags: List { .projectName = jsonReader.nextString() diff --git a/kudos-runtime/src/main/java/com/kanyun/kudos/validator/KudosValidator.kt b/kudos-runtime/src/main/java/com/kanyun/kudos/validator/KudosValidator.kt index 1c1b4af..1846a62 100644 --- a/kudos-runtime/src/main/java/com/kanyun/kudos/validator/KudosValidator.kt +++ b/kudos-runtime/src/main/java/com/kanyun/kudos/validator/KudosValidator.kt @@ -24,7 +24,7 @@ interface KudosValidator { } fun validateField(name: String, fieldStatus: Map) { - if (fieldStatus[name] == false) { + if (fieldStatus[name] != true) { throw NullPointerException("Missing non-null field '$name'.") } }