1. 概述

Klaxon 是一个用于在 Kotlin 中解析 JSON 的开源库,支持多种操作 JSON 的方式,功能强大且使用简单。

在本文中,我们将重点介绍 Klaxon 的主要功能和使用方法,帮助你快速上手并灵活运用在项目中。


2. Maven 依赖

如果你使用的是 Maven 项目,首先需要添加 Klaxon 的依赖:

<dependency>
    <groupId>com.beust</groupId>
    <artifactId>klaxon</artifactId>
    <version>5.6</version>
</dependency>

你可以在 mvnrepository.com 上查看最新版本。


3. API 功能概览

Klaxon 提供了四种主要的 API 来处理 JSON 文档:

  • Object Binding API:将 JSON 映射为 Kotlin 对象,或反之
  • Streaming API:适用于处理大体积 JSON 数据流
  • JSON Path Query API:通过 JSON Path 表达式定位 JSON 中的特定字段
  • Low-Level API:以 Map/List 的方式操作 JSON 数据

下面我们将逐一介绍这些 API 的使用方式。


4. Object Binding API

Object Binding 是最常用的 JSON 操作方式,可以轻松地将 JSON 字符串与 Kotlin 对象之间进行序列化和反序列化。

✅ 示例

我们先定义一个 JSON 字符串:

{
    "name": "HDD"
}

然后定义一个对应的 Kotlin 类:

class Product(val name: String)

接着测试序列化:

@Test
fun givenProduct_whenSerialize_thenGetJsonString() {
    val product = Product("HDD")
    val result = Klaxon().toJsonString(product)

    assertThat(result).isEqualTo("""{"name" : "HDD"}""")
}

再测试反序列化:

@Test
fun givenJsonString_whenDeserialize_thenGetProduct() {
    val result = Klaxon().parse<Product>(
    """
        {
            "name" : "RAM"
        }
    """)

    assertThat(result?.name).isEqualTo("RAM")
}

✅ 使用注解自定义映射

你可以使用 @Json 注解来自定义字段映射规则:

class CustomProduct(
    @Json(name = "productName")
    val name: String,
    @Json(ignored = true)
    val id: Int)

测试:

@Test
fun givenCustomProduct_whenSerialize_thenGetJsonString() {
    val product = CustomProduct("HDD", 1)
    val result = Klaxon().toJsonString(product)

    assertThat(result).isEqualTo("""{"productName" : "HDD"}""")
}

可以看到:

  • name 被序列化为 productName
  • id 被忽略

5. Streaming API

当处理非常大的 JSON 文件时,使用 Streaming API 可以避免一次性加载整个文档,节省内存。

✅ 示例

我们定义一个数据类:

data class ProductData(val name: String, val capacityInGb: Int)

然后使用 JsonReader 流式读取数组:

@Test
fun givenJsonArray_whenStreaming_thenGetProductArray() {
    val jsonArray = """
    [
        { "name" : "HDD", "capacityInGb" : 512 },
        { "name" : "RAM", "capacityInGb" : 16 }
    ]"""
    val expectedArray = arrayListOf(
      ProductData("HDD", 512),
      ProductData("RAM", 16))
    val klaxon = Klaxon()
    val productArray = arrayListOf<ProductData>()
    JsonReader(StringReader(jsonArray)).use { reader ->
        reader.beginArray {
            while (reader.hasNext()) {
                val product = klaxon.parse<ProductData>(reader)
                productArray.add(product!!)
            }
        }
    }

    assertThat(productArray).hasSize(2).isEqualTo(expectedArray)
}

💡 踩坑提醒:记得在 use 中使用 JsonReader,避免资源泄漏。


6. JSON Path Query API

Klaxon 支持 JSON Path 查询,可以用于定位 JSON 中的特定字段。

✅ 示例

我们定义一个 JSON:

{
    "inventory" : {
        "disks" : [
            {
                "type" : "HDD",
                "sizeInGb" : 1000
            },
            {
                "type" : "SDD",
                "sizeInGb" : 512
            }
        ]
    }
}

然后实现 PathMatcher 接口来匹配路径:

val pathMatcher = object : PathMatcher {
    override fun pathMatches(path: String)
      = Pattern.matches(".*inventory.*disks.*type.*", path)

    override fun onMatch(path: String, value: Any) {
        when (path) {
            "$.inventory.disks[0].type"
              -> assertThat(value).isEqualTo("HDD")
            "$.inventory.disks[1].type"
              -> assertThat(value).isEqualTo("SDD")
        }
    }
}

最后执行解析:

@Test
fun givenDiskInventory_whenRegexMatches_thenGetTypes() {
    val jsonString = """..."""
    val pathMatcher = //...
    Klaxon().pathMatcher(pathMatcher)
      .parseJsonObject(StringReader(jsonString))
}

⚠️ 注意:onMatch 中的 value 只能是基础类型,不能是 JsonObjectJsonArray


7. Low-Level API

Low-Level API 允许你以 Map/List 的方式操作 JSON,不需要定义类。

✅ 示例 1:读取 JsonObject

@Test
fun givenJsonString_whenParser_thenGetJsonObject() {
    val jsonString = StringBuilder("""
        {
            "name" : "HDD",
            "capacityInGb" : 512,
            "sizeInInch" : 2.5
        }
    """)
    val parser = Parser.default()
    val json = parser.parse(jsonString) as JsonObject

    assertThat(json)
      .hasSize(3)
      .containsEntry("name", "HDD")
      .containsEntry("capacityInGb", 512)
      .containsEntry("sizeInInch", 2.5)
}

✅ 示例 2:读取 JsonArray

@Test
fun givenJsonStringArray_whenParser_thenGetJsonArray() {
    val jsonString = StringBuilder("""
    [
        { "name" : "SDD" },
        { "madeIn" : "Taiwan" },
        { "warrantyInYears" : 5 }
    ]""")
    val parser = Parser.default()
    val json = parser.parse(jsonString) as JsonArray<JsonObject>

    assertSoftly({
        softly ->
            softly.assertThat(json).hasSize(3)
            softly.assertThat(json[0]["name"]).isEqualTo("SDD")
            softly.assertThat(json[1]["madeIn"]).isEqualTo("Taiwan")
            softly.assertThat(json[2]["warrantyInYears"]).isEqualTo(5)
    })
}

这种方式适合处理结构不固定或动态变化的 JSON 数据。


8. 总结

在本文中,我们介绍了 Klaxon 这个强大的 Kotlin JSON 处理库,涵盖了其四种主要 API:

  • Object Binding:适合常规对象序列化
  • Streaming:适合大文件流式处理
  • JSON Path Query:适合按路径查询字段
  • Low-Level:适合动态或结构不固定的 JSON 操作

你可以根据实际需求选择合适的 API 使用。

如需查看完整示例代码,请访问 GitHub


原始标题:Processing JSON with Kotlin and Klaxson