Skip to content

Commit 4643405

Browse files
committed
add support for kotlinx serialization SerialName
1 parent df39cde commit 4643405

File tree

10 files changed

+156
-62
lines changed

10 files changed

+156
-62
lines changed

create-plugin/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
implementation(libs.bundles.jackson)
1616
implementation(libs.kotlinReflect)
1717
implementation(libs.moshi)
18+
implementation(libs.serialization)
1819

1920
testImplementation(libs.classGraph)
2021
testImplementation(libs.compilerTest)

create-plugin/src/main/kotlin/io/github/tabilzad/ktor/k2/ClassDescriptorVisitorK2.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import io.github.tabilzad.ktor.OpenApiSpec.ObjectType
55
import io.github.tabilzad.ktor.PluginConfiguration
66
import io.github.tabilzad.ktor.annotations.KtorDescription
77
import io.github.tabilzad.ktor.annotations.KtorFieldDescription
8-
import io.github.tabilzad.ktor.k2.MoshiJsonNameResolver.getMoshiJsonName
8+
import io.github.tabilzad.ktor.k2.JsonNameResolver.getCustomNameFromAnnotation
99
import io.github.tabilzad.ktor.names
1010
import io.github.tabilzad.ktor.visitors.KtorDescriptionBag
1111
import io.github.tabilzad.ktor.visitors.toSwaggerType
@@ -340,7 +340,7 @@ internal class ClassDescriptorVisitorK2(
340340
}
341341

342342
private fun FirProperty.findName(): String {
343-
return getMoshiJsonName(this, session) ?: name.asString()
343+
return getCustomNameFromAnnotation(this, session) ?: name.asString()
344344
}
345345

346346
}

create-plugin/src/main/kotlin/io/github/tabilzad/ktor/k2/ClassIds.kt

+5-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ object ClassIds {
2828
val KTOR_DSL_ANNOTATION = ClassId(FqName("io.ktor.util"), FqName("KtorDsl"), false)
2929
val TRANSIENT_ANNOTATION = ClassId(FqName("kotlin.jvm"), FqName("Transient"), false)
3030

31-
val MOSHI_JSON_ANNOTATION_FQ_NAME = FqName("com.squareup.moshi.Json")
32-
val MOSHI_JSON_ANNOTATION_NAME_ARGUMENT_IDENTIFIER: Name = Name.identifier("name")
31+
}
32+
33+
enum class SerializationFramework(val fqName: FqName, val identifier: Name) {
34+
MOSHI(FqName("com.squareup.moshi.Json"), Name.identifier("name")),
35+
KOTLINX(FqName("kotlinx.serialization.SerialName"), Name.identifier("value"))
3336
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.github.tabilzad.ktor.k2
2+
3+
import org.jetbrains.kotlin.fir.FirSession
4+
import org.jetbrains.kotlin.fir.declarations.FirProperty
5+
import org.jetbrains.kotlin.fir.declarations.getStringArgument
6+
import org.jetbrains.kotlin.fir.declarations.primaryConstructorIfAny
7+
import org.jetbrains.kotlin.fir.declarations.utils.isData
8+
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
9+
import org.jetbrains.kotlin.fir.resolve.fqName
10+
import org.jetbrains.kotlin.fir.resolve.getContainingClass
11+
12+
object JsonNameResolver {
13+
14+
fun getCustomNameFromAnnotation(
15+
property: FirProperty,
16+
session: FirSession,
17+
): String? = getCustomNameFromPropertyAnnotation(property, session)
18+
?: getMoshiNameFromDataClassConstructorParamAnnotation(property, session)
19+
20+
private fun getCustomNameFromPropertyAnnotation(
21+
property: FirProperty,
22+
session: FirSession,
23+
): String? = property.annotations.getCustomNameFromAnnotation(session)
24+
25+
private fun List<FirAnnotation>?.getCustomNameFromAnnotation(
26+
session: FirSession
27+
): String? = this?.let { annotations ->
28+
SerializationFramework.entries.mapNotNull {
29+
annotations
30+
.find { annotation -> annotation.fqName(session) == it.fqName }
31+
?.getStringArgument(it.identifier, session)
32+
}
33+
}?.firstOrNull()
34+
35+
private fun getMoshiNameFromDataClassConstructorParamAnnotation(
36+
property: FirProperty,
37+
session: FirSession,
38+
): String? = property.getContainingClass(session)
39+
?.takeIf { it.isData }
40+
?.primaryConstructorIfAny(session)
41+
?.valueParameterSymbols
42+
?.find { it.name == property.name }
43+
?.annotations
44+
?.getCustomNameFromAnnotation(session)
45+
}

create-plugin/src/main/kotlin/io/github/tabilzad/ktor/k2/MoshiJsonNameResolver.kt

-49
This file was deleted.

create-plugin/src/test/kotlin/io/github/tabilzad/ktor/K2StabilityTest.kt

+8
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,14 @@ class K2StabilityTest {
279279
result.assertWith(expected)
280280
}
281281

282+
@Test
283+
fun `should handle kotlinx serialization annotated properties and data class constructor parameters`() {
284+
val (source, expected) = loadSourceAndExpected("SerializationAnnotated")
285+
generateCompilerTest(testFile, source, hideTransient = false, hidePrivate = false)
286+
val result = testFile.readText()
287+
result.assertWith(expected)
288+
}
289+
282290
@Test
283291
fun `should resolve request body schema directly from http method parameter if it's not a resource`() {
284292
val (source, expected) = loadSourceAndExpected("RequestBodyParam")

create-plugin/src/test/kotlin/io/github/tabilzad/ktor/TestUtils.kt

+1
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,5 @@ private val deps = arrayOf(
163163
"ktor-http:2.2.4",
164164
"kotlinx-coroutines-core:1.6.4",
165165
"moshi:1.14.0",
166+
"kotlinx-serialization-core:1.7.1"
166167
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"openapi" : "3.1.0",
3+
"info" : {
4+
"title" : "Open API Specification",
5+
"description" : "test",
6+
"version" : "1.0.0"
7+
},
8+
"paths" : {
9+
"/v1/action" : {
10+
"post" : {
11+
"requestBody" : {
12+
"required" : true,
13+
"content" : {
14+
"application/json" : {
15+
"schema" : {
16+
"$ref" : "#/components/schemas/sources.SerialNameAnnotated"
17+
}
18+
}
19+
}
20+
}
21+
}
22+
}
23+
},
24+
"components" : {
25+
"schemas" : {
26+
"sources.SerialNameAnnotated" : {
27+
"type" : "object",
28+
"properties" : {
29+
"notAnnotated" : {
30+
"type" : "string"
31+
},
32+
"serial_annotated_constructor_derived_property" : {
33+
"type" : "string"
34+
},
35+
"serial_annotated_constructor_parameter" : {
36+
"type" : "string"
37+
},
38+
"serial_annotated_lateinit_var" : {
39+
"type" : "string"
40+
},
41+
"serial_annotated_mutable_property" : {
42+
"type" : "string"
43+
}
44+
},
45+
"required" : [ "serial_annotated_constructor_parameter", "notAnnotated", "serial_annotated_constructor_derived_property", "serial_annotated_lateinit_var" ]
46+
}
47+
}
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package sources
2+
3+
import io.github.tabilzad.ktor.annotations.GenerateOpenApi
4+
import io.ktor.server.application.*
5+
import io.ktor.server.request.*
6+
import io.ktor.server.routing.*
7+
import kotlinx.serialization.SerialName
8+
import kotlinx.serialization.Serializable
9+
10+
@Serializable
11+
data class SerialNameAnnotated(
12+
@SerialName("serial_annotated_constructor_parameter")
13+
val serialNameAnnotatedConstructorParameter: String,
14+
val notAnnotated: String,
15+
) {
16+
@SerialName("serial_annotated_constructor_derived_property")
17+
val serialNameAnnotatedConstructorDerivedProperty = notAnnotated + ""
18+
19+
@SerialName("serial_annotated_mutable_property")
20+
var serialNameAnnotatedProperty: String? = null
21+
22+
@SerialName("serial_annotated_lateinit_var")
23+
lateinit var serialNameAnnotatedLateinitVar: String
24+
}
25+
26+
@GenerateOpenApi
27+
fun Application.moduleSerialization() {
28+
routing {
29+
route("/v1") {
30+
post("/action") {
31+
call.receive<SerialNameAnnotated>()
32+
}
33+
}
34+
}
35+
}

gradle/libs.versions.toml

+10-9
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,24 @@ mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.28.0" }
1414
[bundles]
1515
jackson = ["jacksonKotlin", "jacksonYaml"]
1616
ktor = ["ktor", "ktorNetty", "ktorResources"]
17-
kotlinGradle = ["kotlinGradleApi","kotlinGradle"]
17+
kotlinGradle = ["kotlinGradleApi", "kotlinGradle"]
1818

1919
[libraries]
2020
kotlinCompiler = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlinVersion" }
21-
assertJ = {module = "org.assertj:assertj-core", version.ref = "assertJVerison"}
22-
classGraph = {module="io.github.classgraph:classgraph", version.ref="classgraph"}
23-
junit = {module = "org.junit:junit-bom", version = "5.10.0"}
24-
junitJupiter = {module = "org.junit.jupiter:junit-jupiter"}
25-
compilerTest = {module = "dev.zacsweers.kctfork:core", version.ref = "compilerTestingVersion"}
21+
assertJ = { module = "org.assertj:assertj-core", version.ref = "assertJVerison" }
22+
classGraph = { module = "io.github.classgraph:classgraph", version.ref = "classgraph" }
23+
junit = { module = "org.junit:junit-bom", version = "5.10.0" }
24+
junitJupiter = { module = "org.junit.jupiter:junit-jupiter" }
25+
compilerTest = { module = "dev.zacsweers.kctfork:core", version.ref = "compilerTestingVersion" }
2626
jacksonKotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jacksonVersion" }
2727
jacksonYaml = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", version.ref = "jacksonVersion" }
2828
ktor = { module = "io.ktor:ktor", version.ref = "ktorVersion" }
2929
ktorNetty = { module = "io.ktor:ktor-server-netty", version.ref = "ktorVersion" }
3030
ktorResources = { module = "io.ktor:ktor-server-resources", version.ref = "ktorVersion" }
31-
kotlinGradleApi = {module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlinVersion"}
32-
kotlinGradle = {module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion"}
31+
kotlinGradleApi = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin-api", version.ref = "kotlinVersion" }
32+
kotlinGradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" }
3333
kotlinReflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinVersion" }
34-
moshi = { module = "com.squareup.moshi:moshi", version = "1.14.0"}
34+
moshi = { module = "com.squareup.moshi:moshi", version = "1.14.0" }
35+
serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version = "1.7.1" }
3536

3637

0 commit comments

Comments
 (0)