1. 概述

YAML(YAML Ain’t Markup Language)是一种常用于配置文件的可读性数据格式。官方 Kotlin 序列化库 kotlinx-serialization 并未直接支持 YAML 格式。不过社区提供了如 kamlYamlKt 这类库,它们基于 kotlinx-serialization 实现了对 YAML 的支持。

本文将重点讲解如何在 Kotlin 中 反序列化 YAML 内容,包括标量值、列表、映射以及自定义对象的解析。


2. 依赖配置

首先,我们需要在项目中引入相关依赖。

kaml 依赖:

<dependency>
    <groupId>com.charleskorn.kaml</groupId>
    <artifactId>kaml-jvm</artifactId>
    <version>0.58.0</version>
</dependency>

YamlKt 依赖:

<dependency>
    <groupId>net.mamoe.yamlkt</groupId>
    <artifactId>yamlkt-jvm</artifactId>
    <version>0.13.0</version>
</dependency>

此外,还需要在 pom.xml 中启用 Kotlin 序列化插件:

<build>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <version>${kotlin.version}</version>
            <configuration>
                <compilerPlugins>
                    <plugin>kotlinx-serialization</plugin>
                </compilerPlugins>
            </configuration>
        </plugin>
    </plugins>
</build>

注意:确保所有库版本兼容,避免运行时异常。


3. 读取标量值(Scalar Values)

YAML 中最简单的数据类型是标量值,包括字符串、数字、布尔值和空值。

3.1. 使用 kaml

初始化 Yaml 实例:

val yamlDefault = com.charleskorn.kaml.Yaml.default

使用 decodeFromString() 解析字符串:

val scalarStringYamlStr = "Hello!"
val parsedData: String = yamlDefault.decodeFromString(scalarStringYamlStr)
assertEquals("Hello!", parsedData)

或使用 parseToYamlNode() 提取 YamlScalar

val parsedData: YamlScalar = yamlDefault.parseToYamlNode(scalarStringYamlStr).yamlScalar
assertEquals("Hello!", parsedData.content)

3.2. 使用 YamlKt

初始化默认配置:

val yamlDefault = net.mamoe.yamlkt.Yaml.Default

同样使用 decodeFromString()

val parsedData: String = yamlDefault.decodeFromString(scalarStringYamlStr)
assertEquals("Hello!", parsedData)

⚠️ 注意:两个库的 Yaml 类实现不同,不要混淆使用。


4. 读取列表(Lists)

YAML 支持块风格(block style)和流风格(flow style)两种列表格式。

4.1. 块风格 vs 流风格

val numberListYamlStr =
    """
      - 123
      - 456
    """
val numberListYamlStr = "[123,456]"

建议使用块风格处理长列表,流风格适用于短列表。

4.2. 使用 kaml

val parsedData: List<Int> = yamlDefault.decodeFromString(numberListYamlStr)
assertEquals(listOf(123, 456), parsedData)

也可以解析为 YamlList

val parsedData: YamlList = yamlDefault.parseToYamlNode(numberListYamlStr).yamlList
assertEquals(123, parsedData[0].yamlScalar.content.toInt())

4.3. 使用 YamlKt

支持 decodeListFromString() 方法:

val parsedData: List<Any?> = yamlDefault.decodeListFromString(numberListYamlStr)
assertEquals(listOf(123, 456), parsedData)

5. 读取映射(Maps)

YAML 中的映射是键值对结构。

5.1. 使用 kaml

解析为 YamlMap

val mapWithScalarValuesYamlStr =
    """
    k1: v1
    k2: 123
    k3: false
    k4: 12.3
    k5: 1.2e1
    k6: ~
    """

val yamlMapNode: YamlMap = yamlDefault.parseToYamlNode(mapWithScalarValuesYamlStr).yamlMap

验证值:

assertEquals("v1", yamlMapNode.getScalar("k1")!!.content)
assertEquals(123, yamlMapNode.getScalar("k2")!!.toInt())
assertTrue(yamlMapNode.get<YamlNode>("k6")!! is YamlNull)

5.2. 使用 YamlKt

可直接解析为 YamlMap

val parsedData: YamlMap = yamlDefault.decodeFromString(mapWithScalarValuesYamlStr)
assertEquals("v1", parsedData.getString("k1"))

也可解析为 Kotlin Map

val parsedData: Map<String?, Any?> = yamlDefault.decodeMapFromString(mapWithScalarValuesYamlStr)
assertEquals(12.3, parsedData["k4"])

6. 使用自定义 Schema 解析

6.1. YAML 文档和数据类定义

示例 users.yaml

users:
  - name: Alice
    age: 30
    address:
      city: New York
      country: USA
  - name: Bob
    age: 35
    address:
      city: London
      country: UK

定义数据类:

@Serializable
data class Address(val city: String, val country: String)

@Serializable
data class User(val name: String, val age: Int, val address: Address)

@Serializable
data class Users(val users: List<User>)

6.2. 验证函数 verifyUsers()

fun verifyUsers(users: Users) {
    assertNotNull(users)
    val user1 = users.users[0]
    assertEquals("Alice", user1.name)
    assertEquals("USA", user1.address.country)
}

6.3. 使用 kaml 解析

fun getUsersUsingUsersSerializer(fileContent: String): Users {
    val yaml = Yaml(configuration = YamlConfiguration(strictMode = false))
    return yaml.decodeFromString(fileContent)
}

调用验证:

val users = getUsersUsingUsersSerializer(fileContent)
verifyUsers(users)

6.4. 使用 YamlKt 解析

fun getUsersUsingUsersSerializer(fileContent: String): Users {
    return Yaml.Default.decodeFromString(Users.serializer(), fileContent)
}

验证结果:

val users = getUsersUsingUsersSerializer(fileContent)
verifyUsers(users)

7. 总结

本文介绍了如何使用 kamlYamlKt 两个库在 Kotlin 中读取 YAML 内容,涵盖了:

  • 标量值、列表、映射的解析
  • 自定义数据类的反序列化
  • 不同库的 API 使用方式

两个库各有优势,选择时可根据项目需求和社区活跃度决定。

完整代码可在 GitHub 仓库 找到。

建议:使用时注意版本兼容性,并合理使用类型安全处理方式,避免运行时异常。


原始标题:Guide to Reading YAML Content in Kotlin