11package org.axonframework.extensions.kotlin.serialization
22
33import kotlinx.serialization.ExperimentalSerializationApi
4- import kotlinx.serialization.InternalSerializationApi
54import kotlinx.serialization.KSerializer
65import kotlinx.serialization.builtins.MapSerializer
76import kotlinx.serialization.builtins.serializer
87import kotlinx.serialization.descriptors.SerialDescriptor
8+ import kotlinx.serialization.descriptors.mapSerialDescriptor
99import kotlinx.serialization.encoding.Decoder
1010import kotlinx.serialization.encoding.Encoder
1111import kotlinx.serialization.json.*
12- import kotlinx.serialization.serializer
1312import org.axonframework.messaging.MetaData
1413
15- /* *
16- * A serializer for Axon's MetaData class that works with any encoder format.
17- * MetaData is essentially a map that can contain primitive values and custom objects.
18- *
19- * This serializer uses a two-step approach:
20- * 1. First, we convert the MetaData to a serializable representation using JSON as an intermediate format
21- * 2. Then we use the target encoder to write the final output
22- *
23- * This approach allows us to properly handle any type of value in the MetaData, including custom objects.
24- *
25- * @since 4.11.1
26- */
2714object MetaDataSerializer : KSerializer<MetaData> {
28- // We use Json as an intermediary to convert arbitrary objects to a serializable form
2915 private val json = Json { encodeDefaults = true ; ignoreUnknownKeys = true }
3016
31- // Map with String keys and JsonElement values (which can represent any value)
32- private val mapSerializer = MapSerializer (String .serializer(), JsonElement .serializer())
17+ private val mapSerializer = MapSerializer (
18+ keySerializer = String .serializer(),
19+ valueSerializer = JsonElement .serializer()
20+ )
3321
34- override val descriptor: SerialDescriptor = mapSerializer.descriptor
22+ @OptIn(ExperimentalSerializationApi ::class )
23+ override val descriptor: SerialDescriptor = mapSerialDescriptor(
24+ String .serializer().descriptor,
25+ JsonElement .serializer().descriptor
26+ )
3527
3628 override fun serialize (encoder : Encoder , value : MetaData ) {
37- // Convert each value to JsonElement, which can represent any serializable value
38- val jsonMap = value.mapValues { (_, value) ->
39- convertToJsonElement(value)
29+ val jsonMap = value.entries.associate { (key, rawValue) ->
30+ key to toJsonElement(rawValue)
4031 }
41-
42- // Serialize the map of JsonElements
43- mapSerializer.serialize(encoder, jsonMap)
32+ encoder.encodeSerializableValue(mapSerializer, jsonMap)
4433 }
4534
4635 override fun deserialize (decoder : Decoder ): MetaData {
47- // First deserialize to a Map<String, JsonElement>
48- val jsonMap = mapSerializer.deserialize(decoder)
49-
50- // Then convert each JsonElement back to its original type
51- val resultMap = jsonMap.mapValues { (_, jsonElement) ->
52- convertFromJsonElement(jsonElement)
53- }
54-
55- return MetaData .from(resultMap)
36+ val map = decoder.decodeSerializableValue(mapSerializer)
37+ val restored = map.mapValues { (_, jsonElement) -> fromJsonElement(jsonElement) }
38+ return MetaData (restored)
5639 }
5740
58- /* *
59- * Convert any value to a JsonElement
60- */
61- private fun convertToJsonElement (value : Any? ): JsonElement {
62- return when (value) {
63- null -> JsonNull
64- is JsonElement -> value
65- is Number -> JsonPrimitive (value)
66- is Boolean -> JsonPrimitive (value)
67- is String -> JsonPrimitive (value)
68- is Enum <* > -> JsonPrimitive (value.name)
69- // For custom objects, use Json to encode them to a JsonElement
70- else -> try {
71- json.encodeToJsonElement(value)
72- } catch (e: Exception ) {
73- // If serialization fails, fall back to string representation
74- JsonPrimitive (value.toString())
41+ private fun toJsonElement (value : Any? ): JsonElement = when (value) {
42+ null -> JsonNull
43+ is String -> JsonPrimitive (value)
44+ is Int -> JsonPrimitive (value)
45+ is Long -> JsonPrimitive (value)
46+ is Double -> JsonPrimitive (value)
47+ is Float -> JsonPrimitive (value)
48+ is Boolean -> JsonPrimitive (value)
49+ is List <* > -> JsonArray (value.map { toJsonElement(it) })
50+ is Map <* , * > -> JsonObject (
51+ value.entries.associate { (k, v) ->
52+ k.toString() to toJsonElement(v)
7553 }
76- }
54+ )
55+ else -> JsonPrimitive (value.toString()) // Fallback toString()
7756 }
7857
79- /* *
80- * Convert a JsonElement back to its original type
81- */
82- private fun convertFromJsonElement (element : JsonElement ): Any? {
83- return when (element) {
84- is JsonNull -> null
85- is JsonPrimitive -> when {
86- element.isString -> element.content
87- element.booleanOrNull != null -> element.boolean
88- element.longOrNull != null -> element.long
89- element.doubleOrNull != null -> element.double
90- else -> element.content
91- }
92- is JsonArray -> element.map { convertFromJsonElement(it) }
93- is JsonObject -> element.mapValues { (_, value) -> convertFromJsonElement(value) }
94- }
95- }
96-
97- /* *
98- * Extension method to simplify JSON encoding of arbitrary values
99- */
100- @OptIn(InternalSerializationApi ::class , ExperimentalSerializationApi ::class )
101- private fun Json.encodeToJsonElement (value : Any ): JsonElement {
102- return when (value) {
103- is JsonElement -> value
104- else -> {
105- try {
106- // Try to find a serializer for this type
107- val serializer = serializersModule.getContextual(value::class )
108- ? : value::class .serializer()
109-
110- @Suppress(" UNCHECKED_CAST" )
111- encodeToJsonElement(serializer as KSerializer <Any >, value)
112- } catch (e: Exception ) {
113- // If we can't serialize it properly, convert to string
114- JsonPrimitive (value.toString())
115- }
116- }
58+ private fun fromJsonElement (element : JsonElement ): Any? = when (element) {
59+ is JsonNull -> null
60+ is JsonPrimitive -> when {
61+ element.isString -> element.content
62+ element.booleanOrNull != null -> element.boolean
63+ element.intOrNull != null -> element.int
64+ element.longOrNull != null -> element.long
65+ element.floatOrNull != null -> element.float
66+ element.doubleOrNull != null -> element.double
67+ else -> element.content
11768 }
69+ is JsonObject -> element.mapValues { fromJsonElement(it.value) }
70+ is JsonArray -> element.map { fromJsonElement(it) }
11871 }
11972}
0 commit comments