Kotlin:将JSON部分反序列化为嵌套的子对象

pn9klfpd  于 2023-03-13  发布在  Kotlin
关注(0)|答案(1)|浏览(230)

是否可以反序列化JSON结构,以便将该结构的各个部分收集到嵌套的子对象中?
给定这个JSON结构

{
  "root_field1": "This field will be in root",
  "root_field2": "This field will be in root",
  "child_field1": "This field will be in a child object",
  "child_field2": 123
}

是否可以使用JSONTransformSerializer(或其他方法)将上述json反序列化为:

@Serializable
data class Root(
  @SerialName("root_field1")
  val field1: String,
  @SerialName("root_field2")
  val field2: String,
  val child: Child
)

@Serializable
data class Child(
  @SerialName("child_field1")
  val field1: String,
  @SerialName("child_field2")
  val field2: Int
)

我尝试在Root上使用JsonTransformingSerializer,但是由于找不到child元素,这只会导致异常。
我还尝试将child设置为@Transient,希望这能让我绕过这个问题,但是JsonTransformingSerializer仍然需要KSerializer作为其底层类的输入,所以这不起作用。

cgh8pdjw

cgh8pdjw1#

事实证明,通过以下面的方式组合JsonTransformingSerializer<Root>和自定义KSerializer<Root>(我以前没有想到过),这是很容易做到的:

object RootTransformingSerializer : JsonTransformingSerializer<Root>(RootSerializer) {

   private val childSet: Set<String> = setOf("child_field1", "child_field2")

   override fun transformDeserialize(element: JsonElement): JsonElement {
       val child: MutableMap<String, JsonElement> = mutableMapOf()
       return buildJsonObject() {
          if (element !is JsonObject) {
                return element
          }
          element.forEach { entry ->
             if (entry.key in childSet) {
               child.put(entry.key, entry.value)
             } else {
               put(entry.key, entry.value)
             }
          }
          put("child", JsonObject(child))
       }
   }
}

自定义KSerializer将如下所示

import kotlinx.serialization.descriptors.element

object RootSerializer : KSerializer<Root> {
 
   override val descriptor: SerialDescriptor
        get() = buildClassSerialDescriptor("Root") {
           element<String>("root_field1")
           element<String>("root_field2")
           element<Child>("child")
        }

   override fun deserialize(decoder: Decoder): Commit {
        return decoder.decodeStructure(descriptor) {
           val rootField1 = decodeStringElement(descriptor, 0)
           val rootField2 = decodeStringElement(descriptor, 1)
           val child = decodeSerializableElement(descriptor, 2, Child.serializer())
           Root(rootField1, rootField2, child)
        }
}

相关问题