- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
尝试使用 kotlin 1.4.32/序列化 1.1.0(和 kotlin 1.5.0/序列化 1.2.0)我找不到序列化以下类层次结构的方法
interface Range<T:Comparable<T>>
@Serializable @SerialName("range")
class SimpleRange<T:Comparable<T>>: Range<T>
@Serializable @SerialName("multirange")
class MultiRange<T:Comparable<T>>: Range<T>
我可以序列化 SimpleRange<Double>
(声明为 Range<Double>
)带有一个 SerializersModule 包括
polymorphic(Range::class) {
subclass(SimpleRange.serializer(Double.serializer()))
}
但我找不到一种方法来配置模块,使其可以序列化/反序列化 SingleRange<Double>
或 SingleRange<Int>
或 MultiRange<String>
或者我可以在 SerializersModule 中声明的任何组合。例如,如果我添加 subclass(SimpleRange.serializer(Int.serializer())
到前一个,我得到一个SerializerAlreadyRegisteredException
最佳答案
对于没有通用字段的虚拟类,这应该足够了:
val module = SerializersModule {
polymorphic(Range::class) {
subclass(SimpleRange.serializer(PolymorphicSerializer(Comparable::class)))
subclass(MultiRange.serializer(PolymorphicSerializer(Comparable::class)))
}
}
但是如果你有一个类型为 T : Comparable<T>
的字段在这些类(class)中,那就更棘手了。通常,您需要为 Comparable
声明多态序列化程序。接口(interface)也是,但问题是您不能将“原始类型”( String
、 Int
、 Double
等)注册为多态序列化的子类(kotlinx.serialization 的限制)。在这种情况下,这些类型至关重要。
作为解决方法,您可以执行以下操作:
@ExperimentalSerializationApi
val module = SerializersModule {
contextual(Comparable::class) { it[0] } //use serializer of its first type parameter
}
T
输入 Comparable<T>
(本质上在这种情况下是一样的,但否则你会得到神秘的错误消息 java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
)并用 @Contextual
标记它们注释:@Serializable
@SerialName("range")
class SimpleRange<T : Comparable<T>>(@Contextual val min: Comparable<T>, @Contextual val max: Comparable<T>) : Range<T>
//Will not work due to loss of information about type parameter
polymorphic(Range::class) {
subclass(SimpleRange.serializer(ContextualSerializer(Comparable::class)))
subclass(MultiRange.serializer(ContextualSerializer(Comparable::class)))
}
因此,为了保留类型参数信息,子类序列化器应该通过具有具体化类型参数的辅助函数手动选择:
@Suppress("UNCHECKED_CAST")
inline fun <reified T : Range<K>, reified K : Comparable<K>> rangeSerializer(value: T): KSerializer<T> = when (value) {
is SimpleRange<*> -> serializer<SimpleRange<K>>()
is MultiRange<*> -> serializer<MultiRange<K>>()
else -> throw NotImplementedError() // still required even for sealed interfaces, KT-21908
} as KSerializer<T>
用法:
@ExperimentalSerializationApi
fun main() {
val kotlinx = Json {
serializersModule = module
}
val range1: SimpleRange<Int> = SimpleRange(1, 2)
println(kotlinx.encodeToString(rangeSerializer(range1), range1)) // {"min":1,"max":2}
val range2: SimpleRange<Double> = SimpleRange(1.0, 2.0)
println(kotlinx.encodeToString(rangeSerializer(range2), range2)) // {"min":1.0,"max":2.0}
val range3: Range<Double> = range2
println(kotlinx.encodeToString(rangeSerializer(range3), range3)) // {"min":1.0,"max":2.0}
}
(反序列化)
由于我们在序列化这个数据时放弃了多态序列化,所以没有class discriminator结果 JSON 中的字段以找出 Range
的子类应该被实例化。但即使它在那里,也不够——我们需要 Comparable
的类鉴别器。
因此,我们需要一些启发式方法来实现 content-based polymorphic deserialization .
为了简化这些启发式方法,我建议为此添加专用字段:type
(可以配置 classDiscriminator 属性)和 comparableType
尊敬。因此,应该调整序列化程序:
@ExperimentalSerializationApi
inline fun <reified T : Range<K>, reified K : Comparable<K>> rangeSerializer(value: T): SerializationStrategy<T> =
object : SerializationStrategy<T> {
@Suppress("UNCHECKED_CAST")
private val rangeSerializer = when (value) {
is SimpleRange<*> -> serializer<SimpleRange<K>>()
is MultiRange<*> -> serializer<MultiRange<K>>()
else -> throw NotImplementedError() // still required even for sealed interfaces, KT-21908
} as KSerializer<T>
override val descriptor = rangeSerializer.descriptor
override fun serialize(encoder: Encoder, value: T) = with(encoder as JsonEncoder) {
val jsonElement = json.encodeToJsonElement(rangeSerializer, value)
encodeJsonElement(transformSerialize(jsonElement, json))
}
private fun transformSerialize(element: JsonElement, json: Json) = JsonObject(element.jsonObject.toMutableMap().also {
val typeSerialName = value::class.findAnnotation<SerialName>()?.value ?: value::class.simpleName!!
it[json.configuration.classDiscriminator] = JsonPrimitive(typeSerialName)
it["comparableType"] = JsonPrimitive(K::class.simpleName)
})
}
现在,可以声明尊重的多态反序列化器:
@ExperimentalSerializationApi
@ExperimentalStdlibApi
object RangeDeserializer : DeserializationStrategy<Range<*>> {
private val comparableSerializers = buildMap {
registerSerializerFor<Int>()
registerSerializerFor<Double>()
registerSerializerFor<String>()
}
@InternalSerializationApi
override val descriptor = buildSerialDescriptor("RangeDeserializer", PolymorphicKind.SEALED)
override fun deserialize(decoder: Decoder): Range<*> = with(decoder as JsonDecoder) {
val jsonObject = decodeJsonElement().jsonObject
val deserializer = selectDeserializer(jsonObject, json)
json.decodeFromJsonElement(deserializer, transformDeserialize(jsonObject, json))
}
private fun selectDeserializer(jsonObject: JsonObject, json: Json): DeserializationStrategy<out Range<*>> {
val type = jsonObject[json.configuration.classDiscriminator]!!.jsonPrimitive.content
val comparableType = jsonObject["comparableType"]!!.jsonPrimitive.content
val comparableSerializer = comparableSerializers[comparableType]!!
return when (type) {
"range" -> SimpleRange.serializer(comparableSerializer)
else -> MultiRange.serializer(comparableSerializer)
}
}
//remove extra fields (which we introduced as heuristics for actual serializer selection) before further JSON processing to avoid requiring `ignoreUnknownKeys = true`
private fun transformDeserialize(jsonObject: JsonObject, json: Json) = JsonObject(jsonObject.toMutableMap().also {
it.remove(json.configuration.classDiscriminator)
it.remove("comparableType")
})
}
private inline fun <reified T : Comparable<T>> MutableMap<String, KSerializer<*>>.registerSerializerFor() =
put(T::class.simpleName!!, serializer<T>())
用法:
@ExperimentalSerializationApi
@ExperimentalStdlibApi
fun main() {
val kotlinx = Json {
serializersModule = module
}
val range1: SimpleRange<Int> = SimpleRange(1, 2)
val encoded1 = kotlinx.encodeToString(rangeSerializer(range1), range1)
println(encoded1) // {"min":1,"max":2,"type":"range","comparableType":"Int"}
val range2: SimpleRange<Double> = SimpleRange(1.0, 2.0)
val encoded2 = kotlinx.encodeToString(rangeSerializer(range2), range2)
println(encoded2) // {"min":1.0,"max":2.0,"type":"range","comparableType":"Double"}
val range3: Range<Double> = range2
println(kotlinx.encodeToString(rangeSerializer(range3), range3)) // {"min":1.0,"max":2.0,"type":"range","comparableType":"Double"}
val range1Decoded: Range<*> =
kotlinx.decodeFromString(RangeDeserializer, encoded1) // SimpleRange(min=1, max=2)
val range2Decoded: Range<*> =
kotlinx.decodeFromString(RangeDeserializer, encoded2) // SimpleRange(min=1.0, max=2.0)
}
如果您可以为实际的序列化器选择提出更好的启发式方法(仅基于原始 JSON 的形状,不引入额外的字段),那么您可以保留原始序列化器并享受更简洁的 JSON。
关于通用接口(interface)的 Kotlin 序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67324461/
我正在阅读 Python 文档以真正深入了解 Python 语言,并遇到了 filter 和 map 函数。我以前使用过过滤器,但从未使用过映射,尽管我在 SO 上的各种 Python 问题中都见过这
当我尝试打印 BST 的级别顺序时,这个问题提示了我。 这是一个 Pre-Order Sequence: 4, 1, 2, 3, 5, 6, 7, 8 In_order Sequence : 1, 2
我的代码在 main(序列测试;)的第一行出现错误,指出它是对 sequence::sequence() 的 undefined reference 。我无法更改 main 中的代码。有谁知道我该如何
这可能很简单,但我在通常的 latex 指南中找不到任何相关内容。在这句话中: {\em hello\/} “\/”的目的是什么? 最佳答案 这就是所谓的斜体校正。其目的是确保斜体文本后有适当的间距。
当我从 Postgresql 表中删除所有记录,然后尝试重置序列以在插入时开始一个编号为 1 的新记录时,我得到不同的结果: SELECT setval('tblname_id_seq', (SELE
在版本10.0.3中,MariaDB引入了一种称为序列的存储引擎。 其ad hoc为操作生成整数序列,然后终止。 该序列包含正整数,以降序或升序排列,并使用起始,结束和递增值。 它不允许在多个查询中
如何在 Groovy 中获取给定数字的序列,例如: def number = 169 // need a method in groovy to find the consecutive number
基本上,如果这是 .NET,它看起来像这样: ISomething { string A { get; } int B { get; } } var somethings = new List
说以下代码部分(同一块): A <= 1 A <= 2 变量 A 总是被赋值为 2 吗?还是会出现竞争条件并分配 1 或 2? 我对非阻塞赋值的理解是,由硬件在 future 分配变量 A,因此它可能
在运行 WiX 设置时,我正在寻找操作列表及其顺序。不知何故,官方网站似乎没有提供任何信息。 基本问题是我想正确安排我的自定义操作。通常我需要使用 regsvr32.exe 注册一个 DLL,而这只能
F#初学者在这里 我想创建一个类型,它是具有至少一个元素的另一种具体类型(事件)的序列。任何其他元素都可以在以后随时添加。通常在 C# 中,我会创建一个具有私有(private) List 和公共(p
作为构建过程和不断发展的数据库的一部分,我试图创建一个脚本,该脚本将删除用户的所有表和序列。我不想重新创建用户,因为这将需要比所允许的更多的权限。 我的脚本创建了一个过程来删除表/序列,执行该过程,然
我想恢复两个向量的第一个日期和相同向量的第二个日期之间的日期序列,.... 这是一个例子: dates1 = as.Date(c('2015-10-01', '2015-03-27', '2015-0
这个问题已经有答案了: sql ORDER BY multiple values in specific order? (12 个回答) 已关闭 9 年前。 我有一个 sql 语句,我想要ORDER
我想恢复两个向量的第一个日期和相同向量的第二个日期之间的日期序列,.... 这是一个例子: dates1 = as.Date(c('2015-10-01', '2015-03-27', '2015-0
在用java编写代码时,我需要用“],[”分割字符串。下面是我的代码。 try (BufferedReader reader = new BufferedReader(new InputStreamR
这个问题已经有答案了: Project Euler Question 14 (Collatz Problem) (8 个回答) 已关闭 9 年前。 我正在尝试查找数字的 Collatz 序列。以下
我有一个例程函数process_letter_location(const char& c, string &word)。 在我的 main 中,我声明了一系列字符串变量,如下所示: string s
我需要找到最长的多米诺骨牌链,给定一组 12 个随机挑选的多米诺骨牌。我已经递归地生成了多米诺骨牌的所有可能性(使用 0 到 12 的面值有 91 种可能性)。多米诺骨牌由一 block “砖 blo
我有这个数据结构 Seq,它继承了类 vector 但有一些额外的功能。使用这个数据结构 Seq 我有这个预定义的数据结构: typedef Seq > MxInt2d; 我现在想要一个包含多个 Mx
我是一名优秀的程序员,十分优秀!