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

如何用kotlinx.serialization和kaml解析YAML时展开根元素?

如何用kotlinx.serialization + kaml展开YAML根元素反序列化?

问题场景

需要将以下YAML内容,通过kotlinx.serialization和kaml反序列化为SomeData数据类:

rootElement:
  id: 10
  name: Some name

目标数据类:

@Serializable
data class SomeData(
    val id: Int,
    val name: String
)

直接解析会失败,因为YAML包含一层根元素rootElement,而kotlinx.serialization没有类似Jackson的@JsonRootElement注解自动展开根元素。尝试过直接解析、用Jackson(体积大且处理inline类有问题)、错误解析为Map<String, String>(报错提示"Expected a string, but got a map"),均无法解决。


解决方案

方法1:临时包装类(最简单直接)

定义仅用于解析的包装类,包含根元素字段,解析后取出内部的SomeData实例:

@Serializable
data class SomeDataWrapper(
    val rootElement: SomeData
)

@Serializable
data class SomeData(
    val id: Int,
    val name: String
)

private val YAML = Yaml(configuration = YamlConfiguration(encodeDefaults = false))

fun main() {
    val fileData = File(ClassLoader.getSystemResource("yaml_file.yml").file).readText()
    val wrapper = YAML.decodeFromString<SomeDataWrapper>(fileData)
    val someData = wrapper.rootElement
    println(someData) // 输出:SomeData(id=10, name=Some name)
}

方法2:自定义序列化器(无需额外包装类)

编写自定义DeserializationStrategy,手动跳过根元素节点,直接解析内部的SomeData:

@Serializable
data class SomeData(
    val id: Int,
    val name: String
)

private val YAML = Yaml(configuration = YamlConfiguration(encodeDefaults = false))

fun main() {
    val fileData = File(ClassLoader.getSystemResource("yaml_file.yml").file).readText()
    
    val someData = YAML.decodeFromString(object : DeserializationStrategy<SomeData> {
        override val descriptor: SerialDescriptor = SomeData.serializer().descriptor
        
        override fun deserialize(decoder: Decoder): SomeData {
            decoder.beginStructure(descriptor).run {
                while (true) {
                    val index = decodeElementIndex(descriptor)
                    if (index == CompositeDecoder.DECODE_DONE) break
                    if (decodeStringElement(descriptor, index) == "rootElement") {
                        return decodeSerializableElement(descriptor, index, SomeData.serializer())
                    }
                }
                throw SerializationException("Root element 'rootElement' not found")
            }
        }
    }, fileData)
    
    println(someData)
}

方法3:解析为通用Map后转换

将YAML解析为Map<String, JsonElement>,取出根元素对应的节点再反序列化为SomeData:

import kotlinx.serialization.json.JsonElement

@Serializable
data class SomeData(
    val id: Int,
    val name: String
)

private val YAML = Yaml(configuration = YamlConfiguration(encodeDefaults = false))

fun main() {
    val fileData = File(ClassLoader.getSystemResource("yaml_file.yml").file).readText()
    val rootName = "rootElement"
    
    val rootMap: Map<String, JsonElement> = YAML.decodeFromString(fileData)
    val dataElement = rootMap[rootName] ?: throw IllegalArgumentException("Root element $rootName not found")
    val someData = YAML.decodeFromJsonElement(SomeData.serializer(), dataElement)
    
    println(someData)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:00:04