Разверните корневое значение с помощью kotlinx.seralization

Пожалуйста, помогите мне с моей проблемой. Мне нужно десериализовать этот файл, используя 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
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Самое простое решение — объявить класс и для корневого элемента. Затем вы можете получить доступ к вложенной структуре данных.

Что-то вроде следующего должно работать:

@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 22.08.2024 11:25

@TenebrisUltorem Понятно. Насколько мне известно, в самом Kaml нет встроенного решения, но я скорректировал свой ответ дополнительным подходом, который может быть вам полезен, поскольку он снимает требование создания класса-оболочки для каждого типа, который вы хотите поддерживать.

Endzeit 22.08.2024 19:54

Другие вопросы по теме

Способ запуска сценария bash перед перехватом перед развертыванием с помощью безагентного сервера в Azure Devops
Azure Pipelines — обработка одинаковых имен переменных в разных группах переменных
Можно ли создавать образы докеров с несколькими архитектурами через конвейер?
Автоматическое преобразование значения Azure DevOps в целочисленный тип в yaml
Привести к сбою конвейера, если в журнале появляется определенное предложение. Найдите подстроку, но игнорируйте определенную подстроку
Сделать конвейер сбоем, если в журнале появляется определенное предложение
Ошибка конвейера Azure DevOps: не найден агент с версией выше 2.220.0
Можете ли вы условно использовать «netsh http add urlacl» в YAML-файле конвейера Azure?
Срок службы .NET Core SDK/среды выполнения 2.2 и 3.0 истек (EOL) и удален из всех размещенных агентов
Мой конвейер Azure не запускается, несмотря на выполнение условий