Skip to content

Commit f4c3994

Browse files
committed
Fix: serializing unknown types using JS release
Also had to update kotlinx serialization typescript wrapper due to new method call which was added in `UnknownPolymorphicSerializer` and caused the exported names to change. Closes #473
1 parent 257749e commit f4c3994

File tree

3 files changed

+23
-11
lines changed

3 files changed

+23
-11
lines changed

carp.common/src/commonMain/kotlin/dk/cachet/carp/common/infrastructure/serialization/UnknownPolymorphicSerializer.kt

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dk.cachet.carp.common.infrastructure.serialization
22

3-
import dk.cachet.carp.common.infrastructure.reflect.AccessInternals
43
import dk.cachet.carp.common.infrastructure.reflect.reflectIfAvailable
54
import kotlinx.serialization.*
65
import kotlinx.serialization.descriptors.*
@@ -58,18 +57,30 @@ abstract class UnknownPolymorphicSerializer<P : Any, W : P>(
5857
{
5958
throw unsupportedException
6059
}
61-
getClassDiscriminator( encoder.json ) // Throws error in case array polymorphism is used.
60+
val classDiscriminator = getClassDiscriminator( encoder.json ) // Throws error in case of array polymorphism.
6261

6362
// Get the unknown JSON object.
6463
check( value is UnknownPolymorphicWrapper )
6564
val unknown = Json.parseToJsonElement( value.jsonSource ) as JsonObject
65+
val unknownTypeFields = unknown.filter { it.key != classDiscriminator }
66+
67+
// Create a serial descriptor which contains all elements of the unknown JSON object, except type discriminator.
68+
// If the encoder is in polymorphic writing mode, a class discriminator field will automatically be added using
69+
// the serial name of the descriptor.
70+
val unknownType = checkNotNull( unknown[ classDiscriminator ]?.jsonPrimitive?.content )
71+
val jsonSerializer = JsonElement.serializer()
72+
val overrideDescriptor = buildClassSerialDescriptor( unknownType )
73+
{
74+
unknownTypeFields.keys.forEach { element( it, jsonSerializer.descriptor ) }
75+
}
6676

67-
// HACK: Modify kotlinx.serialization internals to ensure the encoder is not in polymorphic mode.
68-
// Otherwise, `encoder.encodeJsonElement` encodes type information, but this is already represented in the wrapped unknown object.
69-
AccessInternals.setField( encoder, "polymorphicDiscriminator", null )
70-
71-
// Output the originally wrapped JSON.
72-
encoder.encodeJsonElement( unknown )
77+
// Write the JSON object.
78+
encoder.encodeStructure( overrideDescriptor )
79+
{
80+
var id = 0
81+
for ( field in unknownTypeFields.values )
82+
encodeSerializableElement( overrideDescriptor, id++, JsonElement.serializer(), field )
83+
}
7384
}
7485

7586
override fun deserialize( decoder: Decoder ): P
@@ -85,7 +96,8 @@ abstract class UnknownPolymorphicSerializer<P : Any, W : P>(
8596
// Get raw JSON for the unknown type.
8697
val jsonElement = decoder.decodeJsonElement()
8798
val jsonSource = jsonElement.toString()
88-
val className = jsonElement.jsonObject[ classDiscriminator ]!!.jsonPrimitive.content
99+
val className = requireNotNull( jsonElement.jsonObject[ classDiscriminator ]?.jsonPrimitive?.content )
100+
{ "Can't deserialize type which was serialized non-polymorphically." }
89101

90102
return createWrapper( className, jsonSource, decoder.json )
91103
}

typescript-declarations/carp-kotlinx-serialization/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import extendJson from "@cachet/kotlinx-serialization-kotlinx-serialization-json
77
// Facade with better method names and type conversions for internal types.
88
export namespace kotlinx.serialization
99
{
10-
export function getSerializer( type: any ) { return type.Companion.t16() }
10+
export function getSerializer( type: any ) { return type.Companion.m16() }
1111
}
1212
export namespace kotlinx.serialization.json
1313
{

typescript-declarations/tests/carp-protocols-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe( "carp-protocols-core", () => {
2727
expect( parsed ).is.instanceOf( StudyProtocolSnapshot )
2828
} )
2929

30-
it.skip( "can deserialize and serialize unknown types", () => {
30+
it( "can deserialize and serialize unknown types", () => {
3131
const snapshotWithUnknownTypes = serializedSnapshot.replace(
3232
"dk.cachet.carp.common.infrastructure.test.StubTaskConfiguration",
3333
"com.unknown.CustomTaskConfiguration"

0 commit comments

Comments
 (0)