Skip to content

Commit

Permalink
MOD: jsonReader support Float, Array, Set and Map Type
Browse files Browse the repository at this point in the history
  • Loading branch information
RicardoJiang committed Nov 7, 2023
1 parent cb7bc97 commit 4d44653
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrBranch
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
Expand Down Expand Up @@ -147,7 +146,7 @@ internal class KudosFromJsonFunctionBuilder(
).first()
}

private fun getNextValue(field: IrField): IrCall {
private fun getNextValue(field: IrField): IrExpression {
return if (field.type.isSubtypeOfClass(context.irBuiltIns.stringClass)) {
irCall(getJsonReaderNextSymbol("String")).apply {
dispatchReceiver = irGet(jsonReader)
Expand All @@ -164,13 +163,25 @@ internal class KudosFromJsonFunctionBuilder(
irCall(getJsonReaderNextSymbol("Double")).apply {
dispatchReceiver = irGet(jsonReader)
}
} else if (field.type.isSubtypeOfClass(context.irBuiltIns.floatClass)) {
irCall(
pluginContext.referenceFunctions(
CallableId(FqName("kotlin.text"), Name.identifier("toFloat")),
).first().owner,
).apply {
extensionReceiver = irCall(getJsonReaderNextSymbol("String")).apply {
dispatchReceiver = irGet(jsonReader)
}
}
} else if (field.type.isSubtypeOfClass(context.irBuiltIns.booleanClass)) {
irCall(getJsonReaderNextSymbol("Boolean")).apply {
dispatchReceiver = irGet(jsonReader)
}
} else if (
field.type.isSubtypeOfClass(context.irBuiltIns.listClass) ||
field.type.isSubtypeOfClass(context.irBuiltIns.arrayClass) ||
field.type.isSubtypeOfClass(context.irBuiltIns.setClass) ||
field.type.isSubtypeOfClass(context.irBuiltIns.mapClass) ||
field.type.isSubtypeOfClass(
pluginContext.referenceClass(KUDOS_JSON_ADAPTER_CLASS_ID)!!,
)
Expand All @@ -196,7 +207,7 @@ internal class KudosFromJsonFunctionBuilder(
if (typeArguments.isEmpty()) {
return irCall(
pluginContext.referenceProperties(
CallableId(FqName("kotlin.jvm"), Name.identifier("java")),
CallableId(FqName("kotlin.jvm"), Name.identifier("javaObjectType")),
).first().owner.getter!!,
).apply {
extensionReceiver = kClassReference(type)
Expand Down Expand Up @@ -225,7 +236,7 @@ internal class KudosFromJsonFunctionBuilder(
0,
irCall(
pluginContext.referenceProperties(
CallableId(FqName("kotlin.jvm"), Name.identifier("java")),
CallableId(FqName("kotlin.jvm"), Name.identifier("javaObjectType")),
).first().owner.getter!!,
).apply {
extensionReceiver = kClassReference(type)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ class KudosTests : 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_simple`() = testBase()
}
4 changes: 2 additions & 2 deletions kudos-compiler/testData/jsonReader/deserialize.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// FILE: Main.kt [MainKt#main]
import com.kanyun.kudos.annotations.Kudos
@Kudos
class Desc(val descDetail: String)
class Desc(val descDetail: String, val descId: Int)

@Kudos
class UserLazy(val id: Long, val name: String, val desc: Desc, val tags: List<List<String>>) {
Expand All @@ -21,7 +21,7 @@ class UserLazy(val id: Long, val name: String, val desc: Desc, val tags: List<Li
}

fun main() {
deserialize<UserLazy>("""{"id": 10, "name": "John Claud", "desc": {"descDetail": "desc detail"}, "tags": [["tag1", "tag2"],["abc","def"]] }""")
deserialize<UserLazy>("""{"id": 10, "name": "John Claud", "desc": {"descDetail": "desc detail", "descId": 123}, "tags": [["tag1", "tag2"],["abc","def"]] }""")
}

// EXPECT
Expand Down
20 changes: 20 additions & 0 deletions kudos-compiler/testData/jsonReader/deserializeArrayType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SOURCE
{{deserialize}}
// FILE: Main.kt [MainKt#main]
import com.kanyun.kudos.annotations.Kudos

@Kudos
class User(val ids: Array<Int>) {

override fun toString(): String {
return "User(ids[1]=${ids[1]})"
}
}

fun main() {
deserialize<User>("""{"ids": [123, 456]}""")
}

// EXPECT
// FILE: MainKt.main.stdout
User(ids[1]=456)
20 changes: 20 additions & 0 deletions kudos-compiler/testData/jsonReader/deserializeFloatType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SOURCE
{{deserialize}}
// FILE: Main.kt [MainKt#main]
import com.kanyun.kudos.annotations.Kudos

@Kudos
class User(val doubleId: Double, val floatId: Float) {

override fun toString(): String {
return "User(doubleId=$doubleId, floatId=$floatId)"
}
}

fun main() {
deserialize<User>("""{"doubleId": 10.1234, "floatId": "10.1234"}""")
}

// EXPECT
// FILE: MainKt.main.stdout
User(doubleId=10.1234, floatId=10.1234)
38 changes: 38 additions & 0 deletions kudos-compiler/testData/jsonReader/deserializeMapType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SOURCE
{{deserialize}}
// FILE: Main.kt [MainKt#main]
import com.kanyun.kudos.annotations.Kudos

@Kudos
class User(val id: Int, val tag: String){
override fun toString(): String {
return "User(id=${id}, tag=${tag})"
}
}

@Kudos
class UserMap(val itemMap: Map<String, User>) {

override fun toString(): String {
return "UserMap(user2=${itemMap["user2"]})"
}
}

fun main() {
deserialize<UserMap>("""{
"itemMap": {
"user1": {
"id": 123,
"tag": "tag1"
},
"user2": {
"id": 456,
"tag": "tag2"
}
}
}""")
}

// EXPECT
// FILE: MainKt.main.stdout
UserMap(user2=User(id=456, tag=tag2))
20 changes: 20 additions & 0 deletions kudos-compiler/testData/jsonReader/deserializeSetType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SOURCE
{{deserialize}}
// FILE: Main.kt [MainKt#main]
import com.kanyun.kudos.annotations.Kudos

@Kudos
class User(val ids: Set<Int>) {

override fun toString(): String {
return "User(ids=${ids})"
}
}

fun main() {
deserialize<User>("""{"ids": [123, 456, 123]}""")
}

// EXPECT
// FILE: MainKt.main.stdout
User(ids=[123, 456])
4 changes: 2 additions & 2 deletions kudos-compiler/testData/jsonReader/simple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ class Project : KudosValidator, KudosJsonAdapter<Project> {
when {
EQEQ(arg0 = tmp0, arg1 = "projectName") -> <this>.#projectName = jsonReader.nextString()
EQEQ(arg0 = tmp0, arg1 = "projectId") -> <this>.#projectId = jsonReader.nextInt()
EQEQ(arg0 = tmp0, arg1 = "tags") -> <this>.#tags = parseKudosObject(jsonReader = jsonReader, type = ParameterizedTypeImpl(type = KClass::class.<get-java></* null */>(), typeArguments = arrayOf</* null */>(elements = [KClass::class.<get-java></* null */>()])))
EQEQ(arg0 = tmp0, arg1 = "desc") -> <this>.#desc = parseKudosObject(jsonReader = jsonReader, type = KClass::class.<get-java></* null */>())
EQEQ(arg0 = tmp0, arg1 = "tags") -> <this>.#tags = parseKudosObject(jsonReader = jsonReader, type = ParameterizedTypeImpl(type = KClass::class.<get-javaObjectType></* null */>(), typeArguments = arrayOf</* null */>(elements = [KClass::class.<get-javaObjectType></* null */>()])))
EQEQ(arg0 = tmp0, arg1 = "desc") -> <this>.#desc = parseKudosObject(jsonReader = jsonReader, type = KClass::class.<get-javaObjectType></* null */>())
else -> jsonReader.skipValue()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,31 +41,64 @@ private fun parseKudosList(jsonReader: JsonReader, typeArguments: Array<Type>):
return list
}

private fun parseKudosArray(jsonReader: JsonReader, typeArguments: Array<Type>): Any {
val list = parseKudosList(jsonReader, typeArguments)
val array = java.lang.reflect.Array.newInstance(typeArguments[0] as Class<*>, list.size)
for (i in list.indices) {
java.lang.reflect.Array.set(array, i, list[i])
}
return array
}

private fun parseKudosMap(jsonReader: JsonReader, typeArguments: Array<Type>): Map<String, Any> {
val resultMap = mutableMapOf<String, Any>()
jsonReader.beginObject()
while (jsonReader.hasNext()) {
val key = jsonReader.nextName()
val value = parseKudosObject(jsonReader, typeArguments[1])
resultMap[key] = value
}
jsonReader.endObject()
return resultMap
}

private fun parseKudosObjectInternal(
jsonReader: JsonReader,
type: Type,
typeArguments: Array<Type>,
): Any {
val value = when (type) {
String::class.java -> jsonReader.nextString()
Int::class.java -> jsonReader.nextInt()
Long::class.java -> jsonReader.nextLong()
Double::class.java -> jsonReader.nextDouble()
Boolean::class.java -> jsonReader.nextBoolean()
List::class.java -> parseKudosList(jsonReader, typeArguments)
Array::class.java -> parseKudosList(jsonReader, typeArguments).toTypedArray()
String::class.javaObjectType -> jsonReader.nextString()
Int::class.javaObjectType -> jsonReader.nextInt()
Long::class.javaObjectType -> jsonReader.nextLong()
Double::class.javaObjectType -> jsonReader.nextDouble()
Float::class.javaObjectType -> jsonReader.nextString().toFloat()
Boolean::class.javaObjectType -> jsonReader.nextBoolean()
List::class.javaObjectType -> parseKudosList(jsonReader, typeArguments)
Set::class.javaObjectType -> parseKudosList(jsonReader, typeArguments).toSet()
Map::class.javaObjectType -> parseKudosMap(jsonReader, typeArguments)
else -> {
if (type is Class<*>) {
val adapter = type.getDeclaredConstructor().newInstance()
if (adapter is KudosJsonAdapter<*>) {
adapter.fromJson(jsonReader)!!
} else {
throw IllegalArgumentException("class ${type.name} must implement KudosJsonAdapter")
}
} else {
throw IllegalArgumentException("class ${type.typeName} must implement KudosJsonAdapter")
}
parseKudosObjectSpecial(jsonReader, type, typeArguments)
}
}
return value
}

private fun parseKudosObjectSpecial(
jsonReader: JsonReader,
type: Type,
typeArguments: Array<Type>,
): Any {
return if (type.typeName.endsWith("[]")) {
parseKudosArray(jsonReader, typeArguments)
} else if (type is Class<*>) {
val adapter = type.getDeclaredConstructor().newInstance()
if (adapter is KudosJsonAdapter<*>) {
adapter.fromJson(jsonReader)!!
} else {
throw IllegalArgumentException("class ${type.name} must implement KudosJsonAdapter")
}
} else {
throw IllegalArgumentException("class ${type.typeName} must implement KudosJsonAdapter")
}
}

0 comments on commit 4d44653

Please sign in to comment.