如何使用来自 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)