Skip to content

Commit 63d6f8b

Browse files
committed
Replace Klaxon with kotlinx-serialization in JSON operations
1 parent b9d4f0c commit 63d6f8b

File tree

10 files changed

+178
-157
lines changed

10 files changed

+178
-157
lines changed

build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ plugins {
1414
with(libs.plugins) {
1515
alias(kotlin.jvm)
1616
alias(publisher)
17-
alias(serialization)
17+
alias(serialization) apply false
1818
alias(jupyter.api) apply false
1919
alias(dokka)
2020
alias(kover)
@@ -71,7 +71,7 @@ private fun String.findVersion(): Version {
7171
// these names of outdated dependencies will not show up in the table output
7272
val dependencyUpdateExclusions = listOf(
7373
// 5.6 requires Java 11
74-
libs.klaxon.get().name,
74+
// libs.serialization.get().name,
7575
// TODO Requires more work to be updated to 1.7.0+, https://github.com/Kotlin/dataframe/issues/594
7676
libs.plugins.kover.get().pluginId,
7777
// TODO Updating requires major changes all across the project, https://github.com/Kotlin/dataframe/issues/364

core/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ dependencies {
6666
implementation(libs.kotlin.stdlib.jdk8)
6767

6868
api(libs.commonsCsv)
69-
implementation(libs.klaxon)
69+
implementation(libs.serialization.core)
70+
implementation(libs.serialization.json)
71+
7072
implementation(libs.fuel)
7173

7274
api(libs.kotlin.datetimeJvm)

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/json.kt

Lines changed: 122 additions & 105 deletions
Large diffs are not rendered by default.

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/JupyterHtmlRenderer.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jetbrains.kotlinx.dataframe.jupyter
22

3-
import com.beust.klaxon.json
3+
import kotlinx.serialization.ExperimentalSerializationApi
4+
import kotlinx.serialization.json.*
45
import org.jetbrains.kotlinx.dataframe.api.rows
56
import org.jetbrains.kotlinx.dataframe.api.toDataFrame
67
import org.jetbrains.kotlinx.dataframe.io.DataFrameHtmlData
@@ -28,6 +29,7 @@ internal class JupyterHtmlRenderer(
2829
val builder: JupyterIntegration.Builder,
2930
)
3031

32+
@OptIn(ExperimentalSerializationApi::class)
3133
internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
3234
noinline getFooter: (T) -> String,
3335
crossinline modifyConfig: T.(DisplayConfiguration) -> DisplayConfiguration = { it },
@@ -60,14 +62,12 @@ internal inline fun <reified T : Any> JupyterHtmlRenderer.render(
6062
val staticHtml = df.toStaticHtml(reifiedDisplayConfiguration, DefaultCellRenderer).toJupyterHtmlData()
6163

6264
if (notebook.kernelVersion >= KotlinKernelVersion.from(MIN_KERNEL_VERSION_FOR_NEW_TABLES_UI)!!) {
63-
val jsonEncodedDf = json {
64-
obj(
65-
"nrow" to df.size.nrow,
66-
"ncol" to df.size.ncol,
67-
"columns" to df.columnNames(),
68-
"kotlin_dataframe" to encodeFrame(df.rows().take(limit).toDataFrame()),
69-
)
70-
}.toJsonString()
65+
val jsonEncodedDf = buildJsonObject {
66+
put("nrow", df.size.nrow)
67+
put("ncol", df.size.ncol)
68+
putJsonArray("columns") { addAll(df.columnNames()) }
69+
put("kotlin_dataframe", encodeFrame(df.rows().take(limit).toDataFrame()),)
70+
}.toString()
7171
notebook.renderAsIFrameAsNeeded(html, staticHtml, jsonEncodedDf)
7272
} else {
7373
notebook.renderHtmlAsIFrameIfNeeded(html)

core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/json.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ class JsonTests {
949949
@Test
950950
fun `nulls in columns should be encoded explicitly`() {
951951
val df = dataFrameOf("a", "b")("1", null, "2", 12)
952-
df.toJson(canonical = true) shouldContain "\"b\":null"
952+
df.toJson() shouldContain "\"b\":null"
953+
// df.toJson(canonical = true) shouldContain "\"b\":null"
953954
}
954955
}

core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/RenderingTests.kt

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package org.jetbrains.kotlinx.dataframe.jupyter
22

3-
import com.beust.klaxon.JsonArray
4-
import com.beust.klaxon.JsonObject
5-
import com.beust.klaxon.Parser
63
import io.kotest.assertions.throwables.shouldNotThrow
74
import io.kotest.matchers.comparables.shouldBeGreaterThan
85
import io.kotest.matchers.comparables.shouldBeLessThan
96
import io.kotest.matchers.shouldBe
107
import io.kotest.matchers.string.shouldContain
118
import io.kotest.matchers.string.shouldNotContain
9+
import kotlinx.serialization.json.*
1210
import org.intellij.lang.annotations.Language
1311
import org.jetbrains.kotlinx.jupyter.api.MimeTypedResult
1412
import org.jetbrains.kotlinx.jupyter.testkit.JupyterReplTestCase
@@ -94,9 +92,9 @@ class RenderingTests : JupyterReplTestCase() {
9492

9593
assertDataFrameDimensions(json, 30, 1)
9694

97-
val rows = json.array<JsonArray<*>>("kotlin_dataframe")!!
98-
rows.getObj(0).int("id") shouldBe 21
99-
rows.getObj(rows.lastIndex).int("id") shouldBe 50
95+
val rows = json["kotlin_dataframe"]!!.jsonArray
96+
rows.getObj(0)["id"]?.jsonPrimitive?.int shouldBe 21
97+
rows.getObj(rows.lastIndex)["id"]?.jsonPrimitive?.int shouldBe 50
10098
}
10199

102100
/**
@@ -111,16 +109,15 @@ class RenderingTests : JupyterReplTestCase() {
111109
}
112110

113111
private fun assertDataFrameDimensions(json: JsonObject, expectedRows: Int, expectedColumns: Int) {
114-
json.int("nrow") shouldBe expectedRows
115-
json.int("ncol") shouldBe expectedColumns
112+
json["nrow"]?.jsonPrimitive?.int shouldBe expectedRows
113+
json["ncol"]?.jsonPrimitive?.int shouldBe expectedColumns
116114
}
117115

118116
private fun parseDataframeJson(result: MimeTypedResult): JsonObject {
119-
val parser = Parser.default()
120-
return parser.parse(StringBuilder(result["application/kotlindataframe+json"]!!)) as JsonObject
117+
return Json.decodeFromString<JsonObject>(result["application/kotlindataframe+json"]!!)
121118
}
122119

123-
private fun JsonArray<*>.getObj(index: Int) = this.get(index) as JsonObject
120+
private fun JsonArray.getObj(index: Int) = this[index] as JsonObject
124121

125122
@Test
126123
fun `test kotlin notebook plugin utils sort by one column asc`() {
@@ -138,10 +135,10 @@ class RenderingTests : JupyterReplTestCase() {
138135

139136
@Suppress("UNCHECKED_CAST")
140137
private fun assertSortedById(json: JsonObject, desc: Boolean) {
141-
val rows = json["kotlin_dataframe"] as JsonArray<JsonObject>
138+
val rows = json["kotlin_dataframe"]!!.jsonArray as List<JsonObject>
142139
var previousId = if (desc) 101 else 0
143-
rows.forEach { row ->
144-
val currentId = row.int("id")!!
140+
rows.forEach { row: JsonObject ->
141+
val currentId = row["id"]!!.jsonPrimitive.int
145142
if (desc) currentId shouldBeLessThan previousId else currentId shouldBeGreaterThan previousId
146143
previousId = currentId
147144
}
@@ -177,25 +174,25 @@ class RenderingTests : JupyterReplTestCase() {
177174

178175
assertDataFrameDimensions(json, 100, 2)
179176

180-
val rows = json["kotlin_dataframe"] as JsonArray<JsonObject>
177+
val rows = json["kotlin_dataframe"]!!.jsonArray as List<JsonObject>
181178
assertSortedByCategory(rows)
182179
assertSortedById(rows)
183180
}
184181

185-
private fun assertSortedByCategory(rows: JsonArray<JsonObject>) {
182+
private fun assertSortedByCategory(rows: List<JsonObject>) {
186183
rows.forEachIndexed { i, row ->
187-
val currentCategory = row.string("category")
184+
val currentCategory = row["category"]!!.jsonPrimitive.content
188185
if (i < 50) currentCategory shouldBe "odd"
189186
else currentCategory shouldBe "even"
190187
}
191188
}
192189

193-
private fun assertSortedById(rows: JsonArray<JsonObject>) {
190+
private fun assertSortedById(rows: List<JsonObject>) {
194191
var previousCategory = "odd"
195192
var previousId = 0
196193
for (row in rows) {
197-
val currentCategory = row.string("category")!!
198-
val currentId = row.int("id")!!
194+
val currentCategory = row["category"]!!.jsonPrimitive.content
195+
val currentId = row["id"]!!.jsonPrimitive.int
199196

200197
if (previousCategory == "odd" && currentCategory == "even") {
201198
previousId shouldBeGreaterThan currentId
@@ -220,9 +217,9 @@ class RenderingTests : JupyterReplTestCase() {
220217

221218
assertDataFrameDimensions(json, 2, 2)
222219

223-
val rows = json.array<JsonArray<*>>("kotlin_dataframe")!!
224-
rows.getObj(0).array<JsonObject>("group1")!!.size shouldBe 50
225-
rows.getObj(1).array<JsonObject>("group1")!!.size shouldBe 50
220+
val rows = json["kotlin_dataframe"]!!.jsonArray
221+
rows.getObj(0).get("group1")!!.jsonArray.size shouldBe 50
222+
rows.getObj(1).get("group1")!!.jsonArray.size shouldBe 50
226223
}
227224

228225
// Regression KTNB-424

gradle/libs.versions.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ kover = "0.6.1"
2626

2727
commonsCsv = "1.10.0"
2828
commonsCompress = "1.26.0"
29-
klaxon = "5.5" # 5.6 requires Java 11
29+
serialization = "1.6.2"
3030
fuel = "2.3.1"
3131
poi = "5.2.5"
3232
mariadb = "3.3.2"
@@ -69,7 +69,10 @@ kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", vers
6969
kotlin-scriptingJvm = { group = "org.jetbrains.kotlin", name = "kotlin-scripting-jvm", version.ref = "kotlin" }
7070
commonsCsv = { group = "org.apache.commons", name = "commons-csv", version.ref = "commonsCsv" }
7171
commonsCompress = { group = "org.apache.commons", name = "commons-compress", version.ref = "commonsCompress" }
72-
klaxon = { group = "com.beust", name = "klaxon", version.ref = "klaxon" }
72+
# Serialization
73+
serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "serialization" }
74+
serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
75+
7376
fuel = { group = "com.github.kittinunf.fuel", name = "fuel", version.ref = "fuel" }
7477
poi = { group = "org.apache.poi", name = "poi", version.ref = "poi" }
7578
mariadb = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version.ref = "mariadb" }

plugins/dataframe-gradle-plugin/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ dependencies {
2424

2525
implementation(libs.kotlin.gradle.plugin.api)
2626
implementation(libs.kotlin.gradle.plugin)
27-
implementation(libs.klaxon)
27+
implementation(libs.serialization.core)
28+
implementation(libs.serialization.json)
2829
implementation(libs.ksp.gradle)
2930
implementation(libs.ksp.api)
3031

plugins/dataframe-gradle-plugin/src/test/kotlin/org/jetbrains/dataframe/gradle/DataFrameReadTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package org.jetbrains.dataframe.gradle
22

3-
import com.beust.klaxon.KlaxonException
43
import io.kotest.assertions.asClue
54
import io.kotest.assertions.throwables.shouldNotThrowAny
65
import io.kotest.assertions.throwables.shouldThrow
76
import io.kotest.assertions.throwables.shouldThrowAny
87
import io.kotest.matchers.shouldBe
8+
import kotlinx.serialization.SerializationException
99
import org.jetbrains.kotlinx.dataframe.DataFrame
1010
import org.jetbrains.kotlinx.dataframe.io.read
1111
import org.jetbrains.kotlinx.dataframe.io.readSqlTable
@@ -33,7 +33,7 @@ class DataFrameReadTest {
3333
fun `file with invalid json`() {
3434
val temp = Files.createTempDirectory("").toFile()
3535
val invalidJson = File(temp, "test.json").also { it.writeText(".") }
36-
shouldThrow<KlaxonException> {
36+
shouldThrow<IllegalArgumentException> {
3737
DataFrame.read(invalidJson)
3838
}
3939
}
@@ -74,7 +74,7 @@ class DataFrameReadTest {
7474
@Test
7575
fun `URL with invalid JSON`() {
7676
useHostedJson("<invalid json>") { url ->
77-
shouldThrow<KlaxonException> {
77+
shouldThrow<SerializationException> {
7878
DataFrame.read(url).also { println(it) }
7979
}
8080
}

tests/src/test/kotlin/org/jetbrains/kotlinx/dataframe/samples/api/Write.kt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,16 @@ class Write : TestBase() {
6060
val jsonStr = df.toJson(prettyPrint = true)
6161
// SampleEnd
6262
jsonStr shouldStartWith """
63-
[{
64-
"name": {
65-
"firstName": "Alice",
66-
"lastName": "Cooper"
67-
},
68-
"age": 15,
69-
"city": "London",
70-
"weight": 54,
71-
"isHappy": true
72-
}
63+
[
64+
{
65+
"name": {
66+
"firstName": "Alice",
67+
"lastName": "Cooper"
68+
},
69+
"age": 15,
70+
"city": "London",
71+
"weight": 54,
72+
"isHappy": true
7373
""".rejoinWithSystemLineSeparator()
7474
}
7575

0 commit comments

Comments
 (0)