@@ -10,23 +10,17 @@ package dev.restate.serde.kotlinx
1010
1111import dev.restate.common.Slice
1212import dev.restate.serde.Serde
13+ import dev.restate.serde.Serde.Schema
1314import dev.restate.serde.SerdeFactory
1415import dev.restate.serde.TypeRef
1516import dev.restate.serde.TypeTag
1617import java.nio.charset.StandardCharsets
1718import kotlin.reflect.KClass
1819import kotlin.reflect.KType
1920import kotlinx.serialization.*
20- import kotlinx.serialization.builtins.*
21- import kotlinx.serialization.descriptors.PrimitiveKind
22- import kotlinx.serialization.descriptors.SerialDescriptor
23- import kotlinx.serialization.descriptors.StructureKind
24- import kotlinx.serialization.encodeToString
21+ import kotlinx.serialization.builtins.nullable
2522import kotlinx.serialization.json.Json
26- import kotlinx.serialization.json.JsonArray
27- import kotlinx.serialization.json.JsonElement
2823import kotlinx.serialization.json.JsonNull
29- import kotlinx.serialization.json.JsonTransformingSerializer
3024import kotlinx.serialization.modules.SerializersModule
3125
3226/* *
@@ -38,7 +32,22 @@ import kotlinx.serialization.modules.SerializersModule
3832 */
3933open class KotlinSerializationSerdeFactory
4034@JvmOverloads
41- constructor (private val json: Json = Json .Default ) : SerdeFactory {
35+ constructor (
36+ private val json: Json = Json .Default ,
37+ private val jsonSchemaFactory: JsonSchemaFactory = DefaultJsonSchemaFactory
38+ ) : SerdeFactory {
39+
40+ /* * Factory to generate json schemas. */
41+ interface JsonSchemaFactory {
42+ fun generateSchema (json : Json , serializer : KSerializer <* >): Schema ?
43+
44+ companion object {
45+ val NOOP =
46+ object : JsonSchemaFactory {
47+ override fun generateSchema (json : Json , serializer : KSerializer <* >): Schema ? = null
48+ }
49+ }
50+ }
4251
4352 @PublishedApi
4453 internal class KtTypeTag <T >(
@@ -61,7 +70,7 @@ constructor(private val json: Json = Json.Default) : SerdeFactory {
6170 }
6271 val serializer: KSerializer <T > =
6372 json.serializersModule.serializer(typeRef.type) as KSerializer <T >
64- return jsonSerde(json, serializer)
73+ return jsonSerde(json, jsonSchemaFactory, serializer)
6574 }
6675
6776 @Suppress(" UNCHECKED_CAST" )
@@ -70,7 +79,7 @@ constructor(private val json: Json = Json.Default) : SerdeFactory {
7079 return UNIT as Serde <T >
7180 }
7281 val serializer: KSerializer <T > = json.serializersModule.serializer(clazz) as KSerializer <T >
73- return jsonSerde(json, serializer)
82+ return jsonSerde(json, jsonSchemaFactory, serializer)
7483 }
7584
7685 @Suppress(" UNCHECKED_CAST" )
@@ -81,7 +90,7 @@ constructor(private val json: Json = Json.Default) : SerdeFactory {
8190 }
8291 val serializer: KSerializer <T > =
8392 json.serializersModule.serializerForKtTypeInfo(ktSerdeInfo) as KSerializer <T >
84- return jsonSerde(json, serializer)
93+ return jsonSerde(json, jsonSchemaFactory, serializer)
8594 }
8695
8796 companion object {
@@ -103,7 +112,13 @@ constructor(private val json: Json = Json.Default) : SerdeFactory {
103112 }
104113
105114 /* * Creates a [Serde] implementation using the `kotlinx.serialization` json module. */
106- fun <T : Any ?> jsonSerde (json : Json = Json .Default , serializer : KSerializer <T >): Serde <T > {
115+ fun <T : Any ?> jsonSerde (
116+ json : Json = Json .Default ,
117+ jsonSchemaFactory : JsonSchemaFactory = DefaultJsonSchemaFactory ,
118+ serializer : KSerializer <T >
119+ ): Serde <T > {
120+ val schema = jsonSchemaFactory.generateSchema(json, serializer)
121+
107122 return object : Serde <T > {
108123 @Suppress(" WRONG_NULLABILITY_FOR_JAVA_OVERRIDE" )
109124 override fun serialize (value : T ? ): Slice {
@@ -123,77 +138,11 @@ constructor(private val json: Json = Json.Default) : SerdeFactory {
123138 return " application/json"
124139 }
125140
126- override fun jsonSchema (): Serde .Schema {
127- val schema: JsonSchema = serializer.descriptor.jsonSchema()
128- return Serde .StringifiedJsonSchema (Json .encodeToString(schema))
141+ override fun jsonSchema (): Schema ? {
142+ return schema
129143 }
130144 }
131145 }
132-
133- @Serializable
134- @PublishedApi
135- internal data class JsonSchema (
136- @Serializable(with = StringListSerializer ::class ) val type : List <String >? = null ,
137- val format : String? = null ,
138- ) {
139- companion object {
140- val INT = JsonSchema (type = listOf (" number" ), format = " int32" )
141-
142- val LONG = JsonSchema (type = listOf (" number" ), format = " int64" )
143-
144- val DOUBLE = JsonSchema (type = listOf (" number" ), format = " double" )
145-
146- val FLOAT = JsonSchema (type = listOf (" number" ), format = " float" )
147-
148- val STRING = JsonSchema (type = listOf (" string" ))
149-
150- val BOOLEAN = JsonSchema (type = listOf (" boolean" ))
151-
152- val OBJECT = JsonSchema (type = listOf (" object" ))
153-
154- val LIST = JsonSchema (type = listOf (" array" ))
155-
156- val ANY = JsonSchema ()
157- }
158- }
159-
160- object StringListSerializer :
161- JsonTransformingSerializer <List <String >>(ListSerializer (String .Companion .serializer())) {
162- override fun transformSerialize (element : JsonElement ): JsonElement {
163- require(element is JsonArray )
164- return element.singleOrNull() ? : element
165- }
166- }
167-
168- /* *
169- * Super simplistic json schema generation. We should replace this with an appropriate library.
170- */
171- @OptIn(ExperimentalSerializationApi ::class )
172- @PublishedApi
173- internal fun SerialDescriptor.jsonSchema (): JsonSchema {
174- var schema =
175- when (this .kind) {
176- PrimitiveKind .BOOLEAN -> JsonSchema .BOOLEAN
177- PrimitiveKind .BYTE -> JsonSchema .INT
178- PrimitiveKind .CHAR -> JsonSchema .STRING
179- PrimitiveKind .DOUBLE -> JsonSchema .DOUBLE
180- PrimitiveKind .FLOAT -> JsonSchema .FLOAT
181- PrimitiveKind .INT -> JsonSchema .INT
182- PrimitiveKind .LONG -> JsonSchema .LONG
183- PrimitiveKind .SHORT -> JsonSchema .INT
184- PrimitiveKind .STRING -> JsonSchema .STRING
185- StructureKind .LIST -> JsonSchema .LIST
186- StructureKind .MAP -> JsonSchema .OBJECT
187- else -> JsonSchema .ANY
188- }
189-
190- // Add nullability constraint
191- if (this .isNullable && schema.type != null ) {
192- schema = schema.copy(type = schema.type.plus(" null" ))
193- }
194-
195- return schema
196- }
197146 }
198147
199148 @InternalSerializationApi
0 commit comments