如何使用动态对象反序列化JSON?

时间:2019-07-24 08:04:22

标签: json serialization kotlin kotlinx.serialisation

我有一个简单的json,但包含字段具有动态对象。例如,json看起来像

{
    "fixedField1": "value1",
    "dynamicField1": {
        "f1": "abc",
        "f2": 123
    }
}

{
    "fixedField1": "value2",
    "dynamicField1": {
        "g1": "abc",
        "g2": { "h1": "valueh1"}
    }
}

我正在尝试序列化此对象,但不确定如何映射动态字段

@Serializable
data class Response(
  @SerialName("fixedField1")
  val fixedField: String,

  @SerialName("dynamicField1")
  val dynamicField: Map<String, Any> // ???? what should be the type?
)

以上代码失败,并出现以下错误

  

后端内部错误:代码生成期间发生异常原因:   后端(JVM)内部错误:类型为Any的元素的序列化器具有   找不到。

1 个答案:

答案 0 :(得分:0)

当我不得不序列化任意Map<String, Any?>

时,我遇到了类似的问题

到目前为止,我唯一能做到这一点的方法是使用JsonObject / JsonElement API,并将其与@ImplicitReflectionSerializer

组合

主要缺点是使用了反射,该反射仅在JVM中正常工作,对于kotlin-multiplatform来说不是一个好的解决方案。

    @ImplicitReflectionSerializer
    fun Map<*, *>.toJsonObject(): JsonObject = JsonObject(map {
        it.key.toString() to it.value.toJsonElement()
    }.toMap())

    @ImplicitReflectionSerializer
    fun Any?.toJsonElement(): JsonElement = when (this) {
        null -> JsonNull
        is Number -> JsonPrimitive(this)
        is String -> JsonPrimitive(this)
        is Boolean -> JsonPrimitive(this)
        is Map<*, *> -> this.toJsonObject()
        is Iterable<*> -> JsonArray(this.map { it.toJsonElement() })
        is Array<*> -> JsonArray(this.map { it.toJsonElement() })
        else -> {
            //supporting classes that declare serializers
            val jsonParser = Json(JsonConfiguration.Stable)
            val serializer = jsonParser.context.getContextualOrDefault(this)
            jsonParser.toJson(serializer, this)
        }
    }

然后,要序列化,请使用:


val response = mapOf(
    "fixedField1" to "value1",
    "dynamicField1" to mapOf (
        "f1" to "abc",
        "f2" to 123
    )
)


val serialized = Json.stringify(JsonObjectSerializer, response.toJsonObject())

注意

仅当您必须使用Map<String, Any?>

时,才需要基于反射的序列化

如果您可以自由使用自己的DSL构建响应,则可以直接使用json DSL,这与mapOf

非常相似
val response1 = json {
    "fixedField1" to "value1",
    "dynamicField1" to json (
        "f1" to "abc",
        "f2" to 123
    )
}

val serialized1 = Json.stringify(JsonObjectSerializer, response1)

val response 2 = json {
    "fixedField1" to "value2",
    "dynamicField1" to json {
        "g1" to "abc",
        "g2" to json { "h1" to "valueh1"}
    }
}

val serialized2 = Json.stringify(JsonObjectSerializer, response2)

但是,如果您受限于定义数据类型,并进行序列化和反序列化,则可能无法使用json DSL,因此必须使用@Serializer定义以上方法。

在Apache 2许可下,这样的序列化器的示例在这里:ArbitraryMapSerializer.kt

然后,您可以在具有任意Map的类上使用它。在您的示例中为:

@Serializable
data class Response(
  @SerialName("fixedField1")
  val fixedField: String,

  @SerialName("dynamicField1")
  @Serializable(with = ArbitraryMapSerializer::class)
  val dynamicField: Map<String, Any>
)