kotlin序列化用于通用对象...?
我有一个实现通用对象并试图序列化 /进行序列化的好主意,并收到了此错误:
Serializer for class 'DetailsRequest' is not found.
Mark the class as @Serializable or provide the serializer explicitly.
我认为@Serializer注释将实现这一目标...?
数据类的实现及其自定义序列化器(可能是过度杀伤)如下:
@Serializable(with=DetailsRequestSerializer::class)
data class DetailsRequest<out T>(val details: T)
object DetailsRequestSerializer : KSerializer<DetailsRequest<*>> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor(
"DetailsRequest") {
when (this::class) {
String::class -> element<String>("details")
Long::class -> element<Long>("details")
Int::class -> element<Int>("details")
Double::class -> element<Double>("details")
Float::class -> element<Float>("details")
else -> element<String>("details")
}
}
override fun serialize(
encoder: Encoder,
value: DetailsRequest<*>
) {
value.details.let {
encoder.encodeStructure(descriptor) {
when (value::class) {
String::class -> encodeStringElement(descriptor, 0, value.details as String)
Long::class -> encodeLongElement(descriptor, 0, value.details as Long)
Int::class -> encodeIntElement(descriptor, 0, value.details as Int)
Double::class -> encodeDoubleElement(descriptor, 0, value.details as Double)
Float::class -> encodeFloatElement(descriptor, 0, value.details as Float)
else -> encodeStringElement(descriptor, 0, value.details as String)
}
}
}
}
override fun deserialize(decoder: Decoder): DetailsRequest<*> {
return when (this::class) {
String::class -> DetailsRequest(decoder.decodeString())
Long::class -> DetailsRequest(decoder.decodeLong())
Int::class -> DetailsRequest(decoder.decodeInt())
Double::class -> DetailsRequest(decoder.decodeDouble())
Float::class -> DetailsRequest(decoder.decodeFloat())
else -> DetailsRequest(decoder.decodeString())
}
}
}
我编写了本单元测试:
class PlaygroundTest {
private val json = Json {
encodeDefaults = true
isLenient = true
allowSpecialFloatingPointValues = true
allowStructuredMapKeys = true
prettyPrint = true
useArrayPolymorphism = false
ignoreUnknownKeys = true
}
@Test
fun `Details Integration Tests`() {
val input = DetailsRequest(details = "New Record")
val value = json.encodeToString(input)
println("value=[$value]")
val output = value.convertToDataClass<DetailsRequest<String>>()
println("output=[$output]")
}
@OptIn(InternalSerializationApi::class)
internal inline fun <reified R : Any> String.convertToDataClass() =
json.decodeFromString(R::class.serializer(), this)
}
奖励积分:这是一个Kotlin Multiplatform项目,我认为这不会影响这种情况。
这种情况甚至可能吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您询问的有关好消息的问题
:这种情况是完全可能的,您的代码几乎是正确的。但是,Kotlin的内置序列化对获得连续化器有些奇怪,因此起初您的代码片段不起作用,这有点令人惊讶。
答案在 [1] :
具有 代码>详细信息request :: class.serializer()无法使用。
幸运的是,
kotlinx.serialization
向我们提供另一种方法 [2] to获取一个任意类型的序列化器:在您的情况下,您可以这样使用它:
请注意,
json.decodefromstring
自动使用serializer()自动使用json.decodefromstring
。 ,在这一点上,您甚至可以考虑完全删除转换todataclass方法。)
您没有询问
是否使用该更改来运行代码的问题,您会遇到不同的错误:
这仅仅是因为您的实现Deserializer的中有点不合时宜 - 它试图将delesialize详细信息作为单个字符串(例如
“新记录”
),而不是按照您序列化的方式(例如{例如
{ “详细信息”:“新记录”}
)。对此问题的完整说明将需要比此答案合理的空间更多的空间,但是我将您引用了非常完整的(如果有点长) - 序列化指南在这里。您可能需要滚动并阅读其余的“自定义序列化器”部分,以确保您也了解这一切。
更快的解决方案
(如果您将
标记详细信息
)使用 @serializable (没有参数的,则参数),然后删除整个efoce> efoce> efacterrequestSerialializer
对象,代码将正常运行良好,序列化并进行序列化,如人们所期望的。@serializable
注释(没有参数的)告诉编译器自动化序列化器,在这种情况下,它成功合理地进行了序列化器。
除非您有特定的序列化需要此默认序列化器无法实现,否则我强烈建议您简单地使用默认序列化器。它更干净,工作要少得多。 :-)
[1] 您必须向下滚动一点; Dokka不允许我链接到功能的特定声明。相关位一直处于底部。
[2] 是的,它在文档中的同一页面上。只是不要滚动这次。 : - )
The problem you asked about
Good news: this situation is totally possible, and your code is very nearly correct. Kotlin's builtin serialization is a bit wonky about getting serializers, though, so it is a bit surprising at first that your code snippet doesn't work.
The answer lies in a bit of almost fine print in the documentation[1]:
Since
DetailsRequest<out T>
has that genericout T
, this means that accessing its serializer throughDetailsRequest::class.serializer()
won't work.Fortunately,
kotlinx.serialization
provides us a different method[2] to get a serializer for an arbitrary type:In your case, you could use it like this:
Note that there's an overload of
json.decodeFromString
that uses serializer() automatically: all you really need is(In fact, at this point, you may even consider removing the convertToDataClass method entirely.)
The problem you didn't ask about
If you run the code with that change, you'll get a different error:
This is simply because your implementation of the deserializer is a bit off—it's trying to deserialize DetailsRequest as a single string (such as
"New Record"
) rather than a full JSON object the way you serialized it (such as{ "details": "New Record" }
).A full explanation of the issue here would take a bit more space than is reasonable for this answer, but I refer you to the very complete—if a bit long—serialization guide here. You may need to scroll up and read the rest of the Custom Serializers section to ensure you understand it all as well.
A quicker solution
If you mark
DetailsRequest
with simply@Serializable
(without thewith
argument) and remove the entireDetailsRequestSerializer
object, the code will run fine and serialize and deserialize as one would expect. The@Serializable
annotation (without awith
argument) tells the compiler to autogenerate a serializer, which it does successfully and reasonably in this case.Unless you have a specific serialization need that this default serializer does not fulfill, I would strongly recommend simply using the default one. It's much cleaner and a lot less work. :-)
[1] You'll have to scroll down a bit; Dokka doesn't allow me to link to a specific declaration of a function. The relevant bit is all the way at the bottom.
[2] Yes, it's on the same page in the documentation. Just don't scroll down this time. :-)