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

