Пожалуйста, помогите мне с моей проблемой. Мне нужно десериализовать этот файл, используя kotlinx.serialization и kaml.
rootElement:
id: 10
name: Some name
в класс данных
@Serializable
data class SomeData(
val id: Int,
val name: String
)
но я не знаю, как развернуть корневой элемент. В Джексоне есть аннотация @JsonRootElement, но в kotlinx.serialization я не могу найти ничего подобного. Есть ли способ развернуть корневой элемент?
@Serializable
data class SomeData(
val id: Int,
val name: String
)
private val YAML = Yaml(configuration = YamlConfiguration(
encodeDefaults = false,
//no setting here to unwrap root element =(
))
fun main() {
val fileData = File(ClassLoader.getSystemResource("yaml_file.yml").file).readText()
val someData: SomeData = YAML.decodeFromString(fileData)
println(someData)
}
Я пробовал Джексона, но он намного тяжелее и не может десериализовать встроенные классы, когда включен DeserializationFeature.UNWRAP_ROOT_VALUE.
УПД
Я также пытался проанализировать файл, например Map<String, String>, и принять значение по ключу, но kotlinx не может проанализировать объект yaml как sring.
@Serializable
data class SomeData(
val id: Int,
val name: String
)
private val YAML = Yaml(configuration = YamlConfiguration(
encodeDefaults = false,
//no setting here to unwrap root element =(
))
fun main() {
val fileData = File(ClassLoader.getSystemResource("yaml_file.yml").file).readText()
val rootName = "rootElement"
val wrapper: Map<String, String> = YAML.decodeFromString(fileData)
val data: SomeData? = wrapper[rootName]?.let { YAML.decodeFromString(it) }
println(data)
}
Exception in thread "main" InvalidPropertyValueException at rootElement on line 2, column 3: Value for 'rootElement' is invalid: Expected a string, but got a map





Самое простое решение — объявить класс и для корневого элемента. Затем вы можете получить доступ к вложенной структуре данных.
Что-то вроде следующего должно работать:
@Serializable
data class SomeRoot(
val rootElement: SomeData,
)
fun main() {
val fileData: String = ...
val someRoot: SomeRoot = YAML.decodeFromString(fileData)
println(someRoot.rootElement)
}
Если у вас есть поддержка нескольких разных типов данных и вы не хотите создавать класс-оболочку для каждого из них, вы можете сначала декодировать String в YamlNode и использовать его для получения вложенного элемента, который вы впоследствии декодируете. Что-то вроде этого может сработать для вас:
fun main() {
val fileData: String = ...
val data: SomeData = YAML.decodeFromStringUnwrappingRoot(fileData)
println(data)
}
private inline fun <reified T> Yaml.decodeFromStringUnwrappingRoot(string: String): T {
val rootElement: YamlMap = YAML.parseToYamlNode(string).yamlMap
val singleChildElement = rootElement.entries.values.single()
return YAML.decodeFromYamlNode(serializersModule.serializer<T>(), singleChildElement)
}
@TenebrisUltorem Понятно. Насколько мне известно, в самом Kaml нет встроенного решения, но я скорректировал свой ответ дополнительным подходом, который может быть вам полезен, поскольку он снимает требование создания класса-оболочки для каждого типа, который вы хотите поддерживать.
Большое спасибо за ответ! Это решение хорошо работает, если для анализа требуется только один тип, но если имеется много разных файлов с разными типами данных (и именами корневых элементов), нам придется создать класс-оболочку для каждого из них =(