Skip to content

Commit 993e6d3

Browse files
committed
moved reflection-based json parser to lazy value
1 parent fe617a4 commit 993e6d3

File tree

6 files changed

+105
-61
lines changed

6 files changed

+105
-61
lines changed

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/columns/ColumnGroup.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import org.jetbrains.kotlinx.dataframe.annotations.HasSchema
99
import org.jetbrains.kotlinx.dataframe.api.ColumnsSelectionDsl
1010
import org.jetbrains.kotlinx.dataframe.api.asColumnGroup
1111
import org.jetbrains.kotlinx.dataframe.api.columnGroup
12-
import org.jetbrains.kotlinx.dataframe.impl.schema.extractSchema
13-
import org.jetbrains.kotlinx.dataframe.schema.DataFrameSchema
1412
import kotlin.reflect.KProperty
1513

1614
/**

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/Utils.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,7 @@ internal fun Iterable<KClass<*>>.commonType(nullable: Boolean, upperBound: KType
152152

153153
// helper overload for friend modules
154154
@JvmName("commonTypeOverload")
155-
internal fun commonType(types: Iterable<KType?>, useStar: Boolean = true) =
156-
types.commonType(useStar)
155+
internal fun commonType(types: Iterable<KType?>, useStar: Boolean = true) = types.commonType(useStar)
157156

158157
/**
159158
* Returns the common supertype of the given types.

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/parse.kt

Lines changed: 103 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import org.jetbrains.kotlinx.dataframe.api.isColumnGroup
2626
import org.jetbrains.kotlinx.dataframe.api.isFrameColumn
2727
import org.jetbrains.kotlinx.dataframe.api.isSubtypeOf
2828
import org.jetbrains.kotlinx.dataframe.api.map
29-
import org.jetbrains.kotlinx.dataframe.api.parser
3029
import org.jetbrains.kotlinx.dataframe.api.to
3130
import org.jetbrains.kotlinx.dataframe.columns.TypeSuggestion
3231
import org.jetbrains.kotlinx.dataframe.columns.size
@@ -214,7 +213,7 @@ internal object Parsers : GlobalParserOptions {
214213
.parseOrNull(this)
215214
?.toInstantUsingOffset()
216215
}
217-
// fallback on the java instant to catch things like "2022-01-23T04:29:60", a.k.a. leap seconds
216+
// fallback on the java instant to catch things like "2022-01-23T04:29:60", a.k.a. leap seconds
218217
?: toJavaInstantOrNull()?.toKotlinInstant()
219218

220219
private fun String.toJavaInstantOrNull(): JavaInstant? =
@@ -338,6 +337,94 @@ internal object Parsers : GlobalParserOptions {
338337
parser
339338
}
340339

340+
// TODO rewrite using parser service later https://github.com/Kotlin/dataframe/issues/962
341+
// null when dataframe-json is not present
342+
private val readJsonStrAnyFrame: ((text: String) -> AnyFrame)? by lazy {
343+
try {
344+
val klass = Class.forName("org.jetbrains.kotlinx.dataframe.io.JsonKt")
345+
val typeClashTactic = Class.forName("org.jetbrains.kotlinx.dataframe.io.JSON\$TypeClashTactic")
346+
val readJsonStr = klass.getMethod(
347+
"readJsonStr",
348+
// this =
349+
DataFrame.Companion::class.java,
350+
// text =
351+
String::class.java,
352+
// header =
353+
List::class.java,
354+
// keyValuePaths =
355+
List::class.java,
356+
// typeClashTactic =
357+
typeClashTactic,
358+
// unifyNumbers =
359+
Boolean::class.java,
360+
)
361+
362+
return@lazy { text: String ->
363+
readJsonStr.invoke(
364+
null,
365+
// this =
366+
DataFrame.Companion,
367+
// text =
368+
text,
369+
// header =
370+
emptyList<Any>(),
371+
// keyValuePaths =
372+
emptyList<Any>(),
373+
// typeClashTactic =
374+
typeClashTactic.enumConstants[0],
375+
// unifyNumbers =
376+
true,
377+
) as AnyFrame
378+
}
379+
} catch (_: ClassNotFoundException) {
380+
return@lazy null
381+
}
382+
}
383+
384+
// TODO rewrite using parser service later https://github.com/Kotlin/dataframe/issues/962
385+
// null when dataframe-json is not present
386+
private val readJsonStrAnyRow: ((text: String) -> AnyRow)? by lazy {
387+
try {
388+
val klass = Class.forName("org.jetbrains.kotlinx.dataframe.io.JsonKt")
389+
val typeClashTactic = Class.forName("org.jetbrains.kotlinx.dataframe.io.JSON\$TypeClashTactic")
390+
val readJsonStr = klass.getMethod(
391+
"readJsonStr",
392+
// this =
393+
DataRow.Companion::class.java,
394+
// text =
395+
String::class.java,
396+
// header =
397+
List::class.java,
398+
// keyValuePaths =
399+
List::class.java,
400+
// typeClashTactic =
401+
typeClashTactic,
402+
// unifyNumbers =
403+
Boolean::class.java,
404+
)
405+
406+
return@lazy { text: String ->
407+
readJsonStr.invoke(
408+
null,
409+
// this =
410+
DataRow.Companion,
411+
// text =
412+
text,
413+
// header =
414+
emptyList<Any>(),
415+
// keyValuePaths =
416+
emptyList<Any>(),
417+
// typeClashTactic =
418+
typeClashTactic.enumConstants[0],
419+
// unifyNumbers =
420+
true,
421+
) as AnyRow
422+
}
423+
} catch (_: ClassNotFoundException) {
424+
return@lazy null
425+
}
426+
}
427+
341428
internal val parsersOrder = listOf(
342429
// Int
343430
stringParser<Int> { it.toIntOrNull() },
@@ -407,70 +494,33 @@ internal object Parsers : GlobalParserOptions {
407494
stringParser<BigInteger> { it.toBigIntegerOrNull() },
408495
// BigDecimal
409496
stringParser<BigDecimal> { it.toBigDecimalOrNull() },
410-
411-
// JSON array as DataFrame<*> TODO
497+
// JSON array as DataFrame<*>
412498
stringParser<AnyFrame>(catch = true) {
413499
val trimmed = it.trim()
414500
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
415-
try {
416-
val klass = Class.forName("org.jetbrains.kotlinx.dataframe.io.JsonKt")
417-
val typeClashTactic = Class.forName("org.jetbrains.kotlinx.dataframe.io.JSON\$TypeClashTactic")
418-
val readJsonStr = klass.getMethod(
419-
"readJsonStr",
420-
/* this = */DataFrame.Companion::class.java,
421-
/* text = */ String::class.java,
422-
/* header = */ List::class.java,
423-
/* keyValuePaths = */ List::class.java,
424-
/* typeClashTactic = */ typeClashTactic,
425-
/* unifyNumbers = */ Boolean::class.java,
426-
)
427-
428-
readJsonStr.invoke(
429-
null,
430-
/* this = */ DataFrame.Companion,
431-
/* text = */ trimmed,
432-
/* header = */ emptyList<Any>(),
433-
/* keyValuePaths = */ emptyList<Any>(),
434-
/* typeClashTactic = */ typeClashTactic.enumConstants[0],
435-
/* unifyNumbers = */ true,
436-
) as AnyFrame
437-
} catch (_: ClassNotFoundException) {
438-
logger.warn { "parse() encountered a string that looks like a JSON array, but the dataframe-json dependency was not detected. Skipping for now." }
501+
if (readJsonStrAnyFrame == null) {
502+
logger.warn {
503+
"parse() encountered a string that looks like a JSON array, but the dataframe-json dependency was not detected. Skipping for now."
504+
}
439505
null
506+
} else {
507+
readJsonStrAnyFrame!!(trimmed)
440508
}
441509
} else {
442510
null
443511
}
444512
},
445-
// JSON object as DataRow<*> TODO
513+
// JSON object as DataRow<*>
446514
stringParser<AnyRow>(catch = true) {
447515
val trimmed = it.trim()
448516
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
449-
try {
450-
val klass = Class.forName("org.jetbrains.kotlinx.dataframe.io.JsonKt")
451-
val typeClashTactic = Class.forName("org.jetbrains.kotlinx.dataframe.io.JSON\$TypeClashTactic")
452-
val readJsonStr = klass.getMethod(
453-
"readJsonStr",
454-
/* this = */DataRow.Companion::class.java,
455-
/* text = */ String::class.java,
456-
/* header = */ List::class.java,
457-
/* keyValuePaths = */ List::class.java,
458-
/* typeClashTactic = */ typeClashTactic,
459-
/* unifyNumbers = */ Boolean::class.java,
460-
)
461-
462-
readJsonStr.invoke(
463-
null,
464-
/* this = */ DataRow.Companion,
465-
/* text = */ trimmed,
466-
/* header = */ emptyList<Any>(),
467-
/* keyValuePaths = */ emptyList<Any>(),
468-
/* typeClashTactic = */ typeClashTactic.enumConstants[0],
469-
/* unifyNumbers = */ true,
470-
) as AnyRow
471-
} catch (_: ClassNotFoundException) {
472-
logger.warn { "parse() encountered a string that looks like a JSON object, but the dataframe-json dependency was not detected. Skipping for now." }
517+
if (readJsonStrAnyRow == null) {
518+
logger.warn {
519+
"parse() encountered a string that looks like a JSON object, but the dataframe-json dependency was not detected. Skipping for now."
520+
}
473521
null
522+
} else {
523+
readJsonStrAnyRow!!(trimmed)
474524
}
475525
} else {
476526
null

core/src/test/kotlin/org/jetbrains/kotlinx/dataframe/Utils.kt

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

3-
import kotlinx.serialization.json.Json
4-
import kotlinx.serialization.json.JsonObject
5-
import kotlinx.serialization.json.jsonObject
63
import org.jetbrains.kotlinx.dataframe.api.print
74
import org.jetbrains.kotlinx.dataframe.api.schema
85
import org.jetbrains.kotlinx.dataframe.io.renderToString

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,4 +1160,5 @@ fun <T : DataFrame<*>> T.alsoDebug(println: String? = null, rowsLimit: Int = 20)
11601160

11611161
internal val nothingType: KType = typeOf<List<Nothing>>().arguments.first().type!!
11621162
internal val nullableNothingType: KType = typeOf<List<Nothing?>>().arguments.first().type!!
1163+
11631164
internal fun nothingType(nullable: Boolean): KType = if (nullable) nullableNothingType else nothingType

settings.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,3 @@ include("dataframe-openapi-generator")
5050
include("dataframe-geo")
5151
include("plugins:public-api-modifier")
5252
include("dataframe-compiler-plugin-core")
53-

0 commit comments

Comments
 (0)