Skip to content

Commit

Permalink
Fix: serializing unknown types using JS release
Browse files Browse the repository at this point in the history
Closes #473
  • Loading branch information
Whathecode committed Apr 14, 2024
1 parent 68f5345 commit 2b239d7
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dk.cachet.carp.common.infrastructure.serialization

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

// Get the unknown JSON object.
check( value is UnknownPolymorphicWrapper )
val unknown = Json.parseToJsonElement( value.jsonSource ) as JsonObject
val unknownTypeFields = unknown.filter { it.key != classDiscriminator }

// Create a serial descriptor which contains all elements of the unknown JSON object, except type discriminator.
// The encoder is still in polymorphic writing mode, and when encoding a structure will automatically add a
// class discriminator field using the serial name of the descriptor.
val unknownType = checkNotNull( unknown[ classDiscriminator ]?.jsonPrimitive?.content )
val jsonSerializer = JsonElement.serializer()
val overrideDescriptor = buildClassSerialDescriptor( unknownType )
{
unknownTypeFields.keys.forEach { element( it, jsonSerializer.descriptor ) }
}

// HACK: Modify kotlinx.serialization internals to ensure the encoder is not in polymorphic mode.
// Otherwise, `encoder.encodeJsonElement` encodes type information, but this is already represented in the wrapped unknown object.
AccessInternals.setField( encoder, "polymorphicDiscriminator", null )

// Output the originally wrapped JSON.
encoder.encodeJsonElement( unknown )
// Write the JSON object.
encoder.encodeStructure( overrideDescriptor )
{
var id = 0
for ( field in unknownTypeFields.values )
encodeSerializableElement( overrideDescriptor, id++, JsonElement.serializer(), field )
}
}

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

return createWrapper( className, jsonSource, decoder.json )
}
Expand Down
2 changes: 1 addition & 1 deletion typescript-declarations/tests/carp-protocols-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe( "carp-protocols-core", () => {
expect( parsed ).is.instanceOf( StudyProtocolSnapshot )
} )

it.skip( "can deserialize and serialize unknown types", () => {
it( "can deserialize and serialize unknown types", () => {
const snapshotWithUnknownTypes = serializedSnapshot.replace(
"dk.cachet.carp.common.infrastructure.test.StubTaskConfiguration",
"com.unknown.CustomTaskConfiguration"
Expand Down

0 comments on commit 2b239d7

Please sign in to comment.