-
Notifications
You must be signed in to change notification settings - Fork 653
Open
Description
Describe the bug
Have certain data classes which need to be de/serialised and contain JS types (primarily BigInt).
Added a custom serializer but serialisation always throws a ClassCastException.
Happy to contribute a fix, just give me some pointers.
To Reproduce
- Try running following tests written for js bigint
@Serializable
data class ObjWithPrimitiveJsType(@Contextual val value: BigInt)
fun toBigInt(value: Long) = (js("BigInt") as (dynamic) -> BigInt)(value.toString())
class BigIntSerializer : KSerializer<BigInt> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigInt.js", PrimitiveKind.INT)
override fun deserialize(decoder: Decoder): BigInt = toBigInt(decoder.decodeLong())
override fun serialize(encoder: Encoder, value: BigInt) = encoder.encodeLong(value.toString().toLong())
}
class NativeLongTestsJs {
fun `js primitive serializer bug`() {
val data = ObjWithPrimitiveJsType(toBigInt(Random.nextLong()))
val encoded = json.encodeToString(data)
println(encoded)
}
}
Expected behavior
- Value should ser/deser properly, ser throws CCE instead
- The culprit seems to be an overridden serialize method in the generated code which does a type check like:
- Generated code:
serialize_tl1wd4_k$(encoder, value) {
return encoder.encodeLong_3didw_k$(toLong_0(toString_1(value)));
}
serialize_5ase3y_k$(encoder, value) {
return this.serialize_tl1wd4_k$(encoder, value instanceof BigInt ? value : THROW_CCE());
}
- For JS primitive types, instanceof checks fail and one needs to do a "typeof" check instead iirc
- This means kotlinx serialization can never be used to work with JS primitive types
- Especially never for js types, as their BigInt doesn't even return a boxed value. It's just a constructor call that returns the primitive
bigint
value
- Especially never for js types, as their BigInt doesn't even return a boxed value. It's just a constructor call that returns the primitive
Workarounds considered
- Use a wrapper type for all primitive types, kinda dirty but solves the problem. Boilerplate for js devs who consume it.
- Use "new Number" type constructors for creating primitive values, like:
val jsNewNumber = js( "function(x) { return new Number(x); }" ) as (dynamic) -> JsNumber
- This is not a solution as these objects can be passed directly from js apps in my case
- And there I cannot control how these values are created (via constructor or notation)
Ideal Solution
- Some way to mark a serializer js primitive so that it either avoids the instanceof checks or uses typeof check
Environment
- Kotlin version: 2.1.20
- Library version: 1.8.1
- Kotlin platforms: JS for above issue (actual code is targets js/android/ios)
- Gradle version: 8.7
- IDE version (if bug is related to the IDE) [e.g. IntellijIDEA 2019.1, Android Studio 3.4]
- Other relevant context [e.g. OS version, JRE version, ... ]
rnett
Metadata
Metadata
Assignees
Labels
No labels