Json 如何序列化/反序列化Kotlin密封类?
我有一个密封的类:Json 如何序列化/反序列化Kotlin密封类?,json,kotlin,singleton,deserialization,genson,Json,Kotlin,Singleton,Deserialization,Genson,我有一个密封的类: sealed class ViewModel { data class Loaded(val value : String) : ViewModel() object Loading : ViewModel() } import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxm
sealed class ViewModel {
data class Loaded(val value : String) : ViewModel()
object Loading : ViewModel()
}
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
sealed class ViewModel {
data class Loaded(val value: String) : ViewModel()
object Loading : ViewModel()
}
// Custom serializer
class ViewModelDeserializer : JsonDeserializer<ViewModel>() {
override fun deserialize(jp: JsonParser?, p1: DeserializationContext?): ViewModel {
val node: JsonNode? = jp?.getCodec()?.readTree(jp)
val value = node?.get("value")
return if (value != null) ViewModel.Loaded(value.asText()) else ViewModel.Loading
}
}
fun main(args: Array<String>) {
val m = createCustomMapper()
val ser1 = m.writeValueAsString(ViewModel.Loading)
println(ser1)
val ser2 = m.writeValueAsString(ViewModel.Loaded("test"))
println(ser2)
val deserialized1 = m.readValue(ser1, ViewModel::class.java)
val deserialized2 = m.readValue(ser2, ViewModel::class.java)
println(deserialized1)
println(deserialized2)
}
// Using mapper with custom serializer
private fun createCustomMapper(): ObjectMapper {
val m = ObjectMapper()
val sm = SimpleModule()
sm.addDeserializer(ViewModel::class.java, ViewModelDeserializer())
m.registerModule(sm)
return m
}
sealed class Parent(val name: String) {
private companion object {
@JsonCreator
@JvmStatic
fun findBySimpleClassName(simpleName: String): Parent? {
return Parent::class.sealedSubclasses.first {
it.simpleName == simpleName
}.objectInstance
}
}
}
我如何序列化/反序列化ViewModel类的实例,比如从JSON格式到JSON格式
我尝试使用Genson序列化器/反序列化器库-它可以处理Kotlin数据类,也可以支持多态类型(例如,使用一些元数据指定具体类型)
但是,库在Kotlin
对象
类型上失败,因为这些类型是没有公共构造函数的单例。我想我可以编写一个定制的Genson转换器来处理它,但也许有一个更简单的方法来处理它?关于创建定制序列化程序,您可能是对的
我已经尝试使用库和Kotlin对您的类进行序列化和反序列化
以下是Jackson的Maven依赖项:
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
我最终实现了一个定制的转换器加上一个工厂,以正确地将其插入Genson 它使用Genson的元数据约定将对象表示为:
{
"@class": "com.example.ViewModel.Loading"
}
转换器假定设置了useClassMetadata标志,因此序列化只需要标记一个空对象。对于反序列化,它从元数据解析类名,加载它并获取objectInstance
object KotlinObjectConverter : Converter<Any> {
override fun serialize(objectData: Any, writer: ObjectWriter, ctx: Context) {
with(writer) {
// just empty JSON object, class name will be automatically added as metadata
beginObject()
endObject()
}
}
override fun deserialize(reader: ObjectReader, ctx: Context): Any? =
Class.forName(reader.nextObjectMetadata().metadata("class"))
.kotlin.objectInstance
.also { reader.endObject() }
}
使用链式转换器功能的代码可能会更好,但我还没有时间查看它。最近我遇到了类似的问题(尽管使用的是Jackson,而不是Genson) 假设我具备以下条件:
sealed class Parent(val name: String)
object ChildOne : Parent("ValOne")
object ChildTwo : Parent("ValTwo")
然后将JsonCreator
函数添加到密封类:
sealed class ViewModel {
data class Loaded(val value : String) : ViewModel()
object Loading : ViewModel()
}
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
sealed class ViewModel {
data class Loaded(val value: String) : ViewModel()
object Loading : ViewModel()
}
// Custom serializer
class ViewModelDeserializer : JsonDeserializer<ViewModel>() {
override fun deserialize(jp: JsonParser?, p1: DeserializationContext?): ViewModel {
val node: JsonNode? = jp?.getCodec()?.readTree(jp)
val value = node?.get("value")
return if (value != null) ViewModel.Loaded(value.asText()) else ViewModel.Loading
}
}
fun main(args: Array<String>) {
val m = createCustomMapper()
val ser1 = m.writeValueAsString(ViewModel.Loading)
println(ser1)
val ser2 = m.writeValueAsString(ViewModel.Loaded("test"))
println(ser2)
val deserialized1 = m.readValue(ser1, ViewModel::class.java)
val deserialized2 = m.readValue(ser2, ViewModel::class.java)
println(deserialized1)
println(deserialized2)
}
// Using mapper with custom serializer
private fun createCustomMapper(): ObjectMapper {
val m = ObjectMapper()
val sm = SimpleModule()
sm.addDeserializer(ViewModel::class.java, ViewModelDeserializer())
m.registerModule(sm)
return m
}
sealed class Parent(val name: String) {
private companion object {
@JsonCreator
@JvmStatic
fun findBySimpleClassName(simpleName: String): Parent? {
return Parent::class.sealedSubclasses.first {
it.simpleName == simpleName
}.objectInstance
}
}
}
现在,您可以在json属性中使用
ChildOne
或ChildTwo
作为key
进行反序列化。为什么要尝试反序列化singleton?@jrtapsell-因为它不包含任何数据,所以不需要有多个此类实例。使它成为一个常规类是一个解决方法,但它需要equals/hashcode重写,通常感觉不太对。谢谢你的回答。我知道定制序列化程序可以解决这个问题(我甚至认为它可以是一个通用的转换器,而不是绑定到特定的对象
类型)。然而,我想知道是否有现成的解决方案:)@ZbigniewMalinowski我曾试图与Jackson一起找到现成的解决方案,但没有成功——可能有一个更聪明的库(或编码器)。这是目前最好的解决方案。但我猜它不会处理嵌套的密封类?@CarsonHolzheimer我还没有尝试过它,我不明白为什么它不会处理嵌套的密封类。我一定是缺少一些基本的东西。我可以使用这种方法序列化密封类实例,但使用jackson对象映射器反序列化字符串时遇到问题:“无法从对象值反序列化(没有基于委托或属性的创建者)”。你能提供一个将字符串反序列化为对象实例的例子吗?我想我已经没有了,但我会尝试查找它,并更新答案@BastianStein。也许你可以打开另一个问题,在那里发布你的代码。更容易看出你是否遗漏了什么