如何使用来自 JSON 的 kotlin 序列化解析通用密钥
How to parse generic key with kotlin serialization from JSON
我正在努力想出如何像这样正确解析 JSON 的想法:
{
"generic_key": { "version":1, "ttl":42 }
}
预期的 kotlin class 应如下所示:
@Serializable
data class Config(val version: Int, val ttl: Long) {
@Transient
var key: String? = null // <== here comes generic_key
}
更新
我想要实现的是从字符串 JSON 中获取一个 kotlin class 并且我不知道将使用哪个键作为“generic_key”。
更新 2
即使是这样的事情对我来说也没问题:
@Serializable
data class ConfigWrapper(val map: Map<String, Config>)
哪里会有带有来自 jsonObject 的键(例如 generic_key)的单个项目的映射,其余的用 standard/generated Config.serializer.
解析
使用以下数据类
data class Config(
@SerializedName("generic_key" ) var genericKey : GenericKey? = GenericKey()
)
data class GenericKey (
@SerializedName("version" ) var version : Int? = null,
@SerializedName("ttl" ) var ttl : Int? = null
)
如果key是动态的,不同的,map结构应该没问题
@Serializable
data class Config(val version: Int, val ttl: Long)
val result = JsonObject(mapOf("generic_key" to Config(1, 42)))
最后这对我有用,但如果有更直接的解决方案请告诉我。
private val jsonDecoder = Json { ignoreUnknownKeys = true }
private val jsonConfig = "...."
val result = jsonDecoder.parseToJsonElement(jsonConfig)
result.jsonObject.firstNonNullOf { (key, value) ->
config = jsonDecoder.decodeFromJsonElement<Config>(value).also {
it.key = key // this is generic_key (whatever string)
}
}
选项 1. 定义自定义反序列化器,它将使用 plugin-generated 序列化器 Config
class:
object ConfigDeserializer : DeserializationStrategy<Config> {
private val delegateSerializer = MapSerializer(String.serializer(), Config.serializer())
override val descriptor = delegateSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
val map = decoder.decodeSerializableValue(delegateSerializer)
val (k, v) = map.entries.first()
return v.apply { key = k }
}
}
要使用它,您需要手动将其传递给 decodeFromString
方法:
val result: Config = Json.decodeFromString(ConfigDeserializer, jsonString)
选项 2. 为 Config
class 定义代理和自定义序列化程序,它将使用 plugin-generated 序列化程序 ConfigSurrogate
class,这样您就可以拒绝 Config
class 的 plugin-generated 序列化程序,并将此自定义序列化程序连接到 Config
class:
@Serializable
@SerialName("Config")
data class ConfigSurrogate(val version: Int, val ttl: Long)
object ConfigSerializer : KSerializer<Config> {
private val surrogateSerializer = ConfigSurrogate.serializer()
private val delegateSerializer = MapSerializer(String.serializer(), surrogateSerializer)
override val descriptor = delegateSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
val map = decoder.decodeSerializableValue(delegateSerializer)
val (k, v) = map.entries.first()
return Config(v.version, v.ttl).apply { key = k }
}
override fun serialize(encoder: Encoder, value: Config) {
surrogateSerializer.serialize(encoder, ConfigSurrogate(value.version, value.ttl))
}
}
@Serializable(with = ConfigSerializer::class)
data class Config(val version: Int, val ttl: Long) {
// actually, now there is no need for @Transient annotation
var key: String? = null // <== here comes generic_key
}
现在,默认使用自定义序列化程序:
val result: Config = Json.decodeFromString(jsonString)
我正在努力想出如何像这样正确解析 JSON 的想法:
{
"generic_key": { "version":1, "ttl":42 }
}
预期的 kotlin class 应如下所示:
@Serializable
data class Config(val version: Int, val ttl: Long) {
@Transient
var key: String? = null // <== here comes generic_key
}
更新
我想要实现的是从字符串 JSON 中获取一个 kotlin class 并且我不知道将使用哪个键作为“generic_key”。
更新 2
即使是这样的事情对我来说也没问题:
@Serializable
data class ConfigWrapper(val map: Map<String, Config>)
哪里会有带有来自 jsonObject 的键(例如 generic_key)的单个项目的映射,其余的用 standard/generated Config.serializer.
解析使用以下数据类
data class Config(
@SerializedName("generic_key" ) var genericKey : GenericKey? = GenericKey()
)
data class GenericKey (
@SerializedName("version" ) var version : Int? = null,
@SerializedName("ttl" ) var ttl : Int? = null
)
如果key是动态的,不同的,map结构应该没问题
@Serializable
data class Config(val version: Int, val ttl: Long)
val result = JsonObject(mapOf("generic_key" to Config(1, 42)))
最后这对我有用,但如果有更直接的解决方案请告诉我。
private val jsonDecoder = Json { ignoreUnknownKeys = true }
private val jsonConfig = "...."
val result = jsonDecoder.parseToJsonElement(jsonConfig)
result.jsonObject.firstNonNullOf { (key, value) ->
config = jsonDecoder.decodeFromJsonElement<Config>(value).also {
it.key = key // this is generic_key (whatever string)
}
}
选项 1. 定义自定义反序列化器,它将使用 plugin-generated 序列化器 Config
class:
object ConfigDeserializer : DeserializationStrategy<Config> {
private val delegateSerializer = MapSerializer(String.serializer(), Config.serializer())
override val descriptor = delegateSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
val map = decoder.decodeSerializableValue(delegateSerializer)
val (k, v) = map.entries.first()
return v.apply { key = k }
}
}
要使用它,您需要手动将其传递给 decodeFromString
方法:
val result: Config = Json.decodeFromString(ConfigDeserializer, jsonString)
选项 2. 为 Config
class 定义代理和自定义序列化程序,它将使用 plugin-generated 序列化程序 ConfigSurrogate
class,这样您就可以拒绝 Config
class 的 plugin-generated 序列化程序,并将此自定义序列化程序连接到 Config
class:
@Serializable
@SerialName("Config")
data class ConfigSurrogate(val version: Int, val ttl: Long)
object ConfigSerializer : KSerializer<Config> {
private val surrogateSerializer = ConfigSurrogate.serializer()
private val delegateSerializer = MapSerializer(String.serializer(), surrogateSerializer)
override val descriptor = delegateSerializer.descriptor
override fun deserialize(decoder: Decoder): Config {
val map = decoder.decodeSerializableValue(delegateSerializer)
val (k, v) = map.entries.first()
return Config(v.version, v.ttl).apply { key = k }
}
override fun serialize(encoder: Encoder, value: Config) {
surrogateSerializer.serialize(encoder, ConfigSurrogate(value.version, value.ttl))
}
}
@Serializable(with = ConfigSerializer::class)
data class Config(val version: Int, val ttl: Long) {
// actually, now there is no need for @Transient annotation
var key: String? = null // <== here comes generic_key
}
现在,默认使用自定义序列化程序:
val result: Config = Json.decodeFromString(jsonString)