如何在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
相关产品推荐
相关产品推荐

