Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serializer<KType>() throws SerializationException when used with sealed class on JS #1116

Closed
rnett opened this issue Oct 5, 2020 · 16 comments

Comments

@rnett
Copy link

rnett commented Oct 5, 2020

I have a sealed class Trait in common code:

@Serializable
sealed class Trait() {
    abstract val name: String
    ...
}

I want to get it's serializer via serializer<Trait>(). On JVM this works fine and I get a SealedClassSerializer. On JS (IR) I get:

"SerializationException: Serializer for class 'Trait' is not found.
Mark the class as @Serializable or provide the serializer explicitly.
On Kotlin/JS explicitly declared serializer should be used for interfaces and enums without @Serializable annotation
    at platformSpecificSerializerNotRegistered (http://localhost:8080/static/spellbook.js:159984:11)
    at serializer (http://localhost:8080/static/spellbook.js:155641:7)
    at main (http://localhost:8080/static/spellbook.js:188469:25)
    at Object.<anonymous> (http://localhost:8080/static/spellbook.js:195282:3)
    at http://localhost:8080/static/spellbook.js:136257:37
    at Object.<anonymous> (http://localhost:8080/static/spellbook.js:136260:2)
    at Object.H:\Google Drive\My Stuff\spellbook\build\js\packages\spellbook\kotlin\spellbook.js (http://localhost:8080/static/spellbook.js:195286:30)
    at __webpack_require__ (http://localhost:8080/static/spellbook.js:30:30)
    at Object.0 (http://localhost:8080/static/spellbook.js:106:18)
    at __webpack_require__ (http://localhost:8080/static/spellbook.js:30:30)
    at http://localhost:8080/static/spellbook.js:94:18
    at http://localhost:8080/static/spellbook.js:97:10
    at webpackUniversalModuleDefinition (http://localhost:8080/static/spellbook.js:9:23)
    at http://localhost:8080/static/spellbook.js:10:3"

This is with 1.0.0-RC2.

@rnett
Copy link
Author

rnett commented Oct 6, 2020

More info: it is definitely related to resolving the serializer. Using Trait.serializer() directly works just fine, it is only when I try to get it via serializer<Trait>() or use it in another serializable class I get this error. Even adding it to the serialization module explicitly using something like:

SerializersModule {
    polymorphic(Trait::class, Trait.serializer())
}

doesn't help, it causes Serializer for Trait can't be registered as a subclass for polymorphic serialization because its kind SEALED is not concrete. To work with multiple hierarchies, register it as a base class. even though I am passing it as the base class serializer.

Full stack trace:

"IllegalArgumentException: Serializer for Trait can't be registered as a subclass for polymorphic serialization because its kind SEALED is not concrete. To work with multiple hierarchies, register it as a base class.
    at checkKind_0 (http://localhost:8080/static/spellbook.js:147781:13)
    at PolymorphismValidator.polymorphic_1 (http://localhost:8080/static/spellbook.js:147759:5)
    at SerialModuleImpl.dumpTo_0 (http://localhost:8080/static/spellbook.js:145927:19)
    at validateConfiguration (http://localhost:8080/static/spellbook.js:146289:40)
    at new JsonImpl (http://localhost:8080/static/spellbook.js:146263:5)
    at Json_1 (http://localhost:8080/static/spellbook.js:146195:12)
    at Json$default (http://localhost:8080/static/spellbook.js:146199:12)
    at main (http://localhost:8080/static/spellbook.js:174473:18)
    at Object.<anonymous> (http://localhost:8080/static/spellbook.js:181357:3)
    at http://localhost:8080/static/spellbook.js:122208:37
    at Object.<anonymous> (http://localhost:8080/static/spellbook.js:122211:2)
    at Object.H:\Google Drive\My Stuff\spellbook\build\js\packages\spellbook\kotlin\spellbook.js (http://localhost:8080/static/spellbook.js:181361:30)
    at __webpack_require__ (http://localhost:8080/static/spellbook.js:30:30)
    at Object.0 (http://localhost:8080/static/spellbook.js:106:18)
    at __webpack_require__ (http://localhost:8080/static/spellbook.js:30:30)
    at http://localhost:8080/static/spellbook.js:94:18
    at http://localhost:8080/static/spellbook.js:97:10
    at webpackUniversalModuleDefinition (http://localhost:8080/static/spellbook.js:9:23)
    at http://localhost:8080/static/spellbook.js:10:3"

Specifying the subclasses explicitly without a base serializer does work. E.g.:

polymorphic(Trait::class){
    subclass(Trait.Attack::class, Trait.Attack.serializer())
    subclass(Trait.Other::class, Trait.Other.serializer())
}

Although note that this only fixes serialization/deserialization of the class, serializer<KType>() still won't work.

@sandwwraith
Copy link
Member

Yes, that's unfortunately a current limitation on Kotlin/JS and Kotlin/Native — serializer<T>() does not work with sealed/abstract classes

@rnett
Copy link
Author

rnett commented Oct 6, 2020

Is this something that's planned to be fixed by 1.0 or soonish, or is it a longer term issue?

@sandwwraith
Copy link
Member

It is likely won't be fixed in 1.0 but we have it on our radar for future releases

@theromis
Copy link

@sandwwraith my kotlin/js serializer just hangs:

@Serializable(with = LonjeDateSerializer::class)
actual class LonjeDate {
    actual val format: String
        get() {
            return "format"
        }
}

@Serializer(LonjeDate::class)
actual object LonjeDateSerializer : KSerializer<LonjeDate> {
    override val descriptor: SerialDescriptor =
        PrimitiveSerialDescriptor("kotlinx.serialization.LonjeDateSerializer", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: LonjeDate) {
    }

    override fun deserialize(decoder: Decoder): LonjeDate {
        return LonjeDate()
    }
}

is it known issue?
Asked question here #1086

@rnett
Copy link
Author

rnett commented Mar 25, 2021

As a workaround, it seems to work as long as the sealed class has type parameters.

@rjaros
Copy link

rjaros commented Apr 28, 2021

Is this issue fixed in 1.2.0?

@sandwwraith
Copy link
Member

Nope, it will likely be fixed in Kotlin 1.5.20

@nschulzke
Copy link

What version will this be fixed in for Kotlin/JS? I'm seeing it right now in Kotlin 1.5.21 and Kotlinx.Serialization 1.2.1.

@sandwwraith
Copy link
Member

@nschulzke Do you use JS IR?

@nschulzke
Copy link

Yes, I'm using IR.

@sandwwraith
Copy link
Member

Can you post your reproducer, please?

@nschulzke
Copy link

Ah, wait, I thought this issue would resolve the error "Serializer for ___ can't be registered as a subclass for polymorphic serialization because its kind SEALED is not concrete." It looks like that error was related, but not changed here.

I'll ask a question on the Kotlin Slack and maybe file a different issue.

Sorry for the confusion!

@bcwhite-code
Copy link

I'm encountering the same issue with the IR compiler. If I switch to LEGACY, it works fine. Doing the test via JVM is also fine.

build.gradle.kts:

plugins {
    kotlin("multiplatform") version "1.7.+"
    kotlin("plugin.serialization") version "1.7.+"
    application
}

AccessibleRegionsRequest.kt:

import kotlinx.serialization.Serializable
@Serializable
data class AccessibleRegionsRequest(
    val worldId: String,
)

AccessibleRegionsTest.kt:

class AccessibleRegionsTest {
    @Test
    fun testFakeCall() {
        val req = AccessibleRegionsRequest(
            worldId = "foo-bar",
        )
        val reqs = Json.encodeToString(req)
        assertEquals("""{"worldId":"foo-bar"}""", reqs)
        val req2 = Json.decodeFromString<AccessibleRegionsRequest>(reqs)
        assertEquals(req, req2)
    }
}

Test Output:

SerializationException: Serializer for class 'AccessibleRegionsRequest' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
On Kotlin/JS explicitly declared serializer should be used for interfaces and enums without @Serializable annotation
SerializationException: Serializer for class 'AccessibleRegionsRequest' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
On Kotlin/JS explicitly declared serializer should be used for interfaces and enums without @Serializable annotation
	at <global>.platformSpecificSerializerNotRegistered(C:/Users/bcwhite/AppData/Local/Temp/_karma_webpack_539458/commons.js:40973)
	at <global>.serializer(C:/Users/bcwhite/AppData/Local/Temp/_karma_webpack_539458/commons.js:35040)
	at com.riverworth.deity.common.api.AccessibleRegionsTest.testFakeCall_flu7nw(C:/Users/bcwhite/AppData/Local/Temp/_karma_webpack_539458/commons.js:5455)
	at <global>.test_fun$AccessibleRegionsTest_test_fun$testFakeCall_test_fun_9hdub3(C:/Users/bcwhite/AppData/Local/Temp/_karma_webpack_539458/commons.js:5486)
	at Context.<anonymous>(C:/Users/bcwhite/AppData/Local/Temp/_karma_webpack_539458/commons.js:47604)

@Coneys
Copy link

Coneys commented Feb 20, 2023

Any update about this? I have the same issue with IR backend, on Legacy it works fine

@bcwhite-code
Copy link

I think it'll need to be re-opened as a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants