You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中实现Notion API响应的类型安全解析?

Kotlin处理Notion API未知类型的序列化方案

针对你遇到的Kotlin多态序列化时未知判别式(比如last_edited_time)导致崩溃的问题,有两种实用的处理方式,适配不同需求:

1. 保留未知类型数据:用默认类接收

这种方式会把所有未定义的类型统一存入一个默认类,既避免崩溃,又能保留原始数据方便后续处理。

实现步骤

首先定义密封基类和已知类型,再创建一个专门处理未知类型的类,最后在多态序列化配置中指定默认序列化器:

import kotlinx.serialization.*
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement

// 基类:配置多态序列化,指定未知类型的默认处理类
@Serializable
@Polymorphic(defaultSerializer = UnknownNotionProperty.serializer())
sealed class NotionProperty {
    abstract val type: String
}

// 已知类型示例:标题属性(根据Notion API实际结构调整字段)
@Serializable
@SerialName("title")
data class TitleProperty(
    override val type: String = "title",
    val title: Map<String, Any>
) : NotionProperty()

// 已知类型示例:富文本属性
@Serializable
@SerialName("rich_text")
data class RichTextProperty(
    override val type: String = "rich_text",
    val rich_text: Map<String, Any>
) : NotionProperty()

// 未知类型的默认处理类:捕获所有未定义的type和对应字段
@Serializable
data class UnknownNotionProperty(
    override val type: String,
    @SerialName(value = "*") // 接收所有额外字段
    val extraFields: Map<String, JsonElement>
) : NotionProperty()

序列化配置与解析

配置Json序列化器时开启忽略未知字段,避免单个属性内的未知字段导致问题:

val notionJson = Json {
    ignoreUnknownKeys = true
    coerceInputValues = true // 兼容缺失的可选字段
}

// 解析示例
val apiResponse = """[{"type":"title","title":{}},{"type":"last_edited_time","last_edited_time":{}}]"""
val properties = notionJson.decodeFromString<List<NotionProperty>>(apiResponse)
// 解析结果包含TitleProperty和UnknownNotionProperty实例

2. 直接忽略未知类型:过滤掉未定义的属性

如果不需要保留未知类型的数据,可以自定义序列化器,遇到未知判别式时返回null,再过滤掉结果中的null值:

自定义多态序列化器

import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

object FilterUnknownPropertySerializer : PolymorphicSerializer<NotionProperty>(NotionProperty::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<NotionProperty>? {
        val type = element.jsonObject["type"]?.jsonPrimitive?.content ?: return null
        // 仅返回已知类型的序列化器,未知类型返回null
        return super.selectDeserializer(element)
    }
}

// 更新基类的序列化器配置
@Serializable(with = FilterUnknownPropertySerializer::class)
sealed class NotionProperty {
    abstract val type: String
}

解析并过滤

// 解析时允许null,之后过滤掉未知类型对应的null
val properties = notionJson.decodeFromString<List<NotionProperty?>>(apiResponse)
    .filterNotNull()
// 结果仅包含已知类型的属性(如TitleProperty)

补充:Jackson序列化的处理方式

如果项目用的是Jackson而非Kotlinx Serialization,可通过以下配置实现:

import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes(
    JsonSubTypes.Type(value = TitleProperty::class, name = "title"),
    JsonSubTypes.Type(value = RichTextProperty::class, name = "rich_text")
)
// 指定未知类型的默认实现类
@JsonTypeInfo(defaultImpl = UnknownNotionProperty::class)
sealed class NotionProperty {
    abstract val type: String
}

内容的提问来源于stack exchange,提问作者nook

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 16:20:06