1
1
package org.jetbrains.kotlinx.dataframe.impl.api
2
2
3
+ import io.github.oshai.kotlinlogging.KotlinLogging
3
4
import kotlinx.datetime.Instant
4
5
import kotlinx.datetime.LocalDate
5
6
import kotlinx.datetime.LocalDateTime
@@ -10,9 +11,11 @@ import kotlinx.datetime.toKotlinLocalDate
10
11
import kotlinx.datetime.toKotlinLocalDateTime
11
12
import kotlinx.datetime.toKotlinLocalTime
12
13
import org.jetbrains.kotlinx.dataframe.AnyFrame
14
+ import org.jetbrains.kotlinx.dataframe.AnyRow
13
15
import org.jetbrains.kotlinx.dataframe.ColumnsSelector
14
16
import org.jetbrains.kotlinx.dataframe.DataColumn
15
17
import org.jetbrains.kotlinx.dataframe.DataFrame
18
+ import org.jetbrains.kotlinx.dataframe.DataRow
16
19
import org.jetbrains.kotlinx.dataframe.api.GlobalParserOptions
17
20
import org.jetbrains.kotlinx.dataframe.api.ParserOptions
18
21
import org.jetbrains.kotlinx.dataframe.api.asColumnGroup
@@ -30,7 +33,6 @@ import org.jetbrains.kotlinx.dataframe.columns.size
30
33
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
31
34
import org.jetbrains.kotlinx.dataframe.hasNulls
32
35
import org.jetbrains.kotlinx.dataframe.impl.api.Parsers.resetToDefault
33
- import org.jetbrains.kotlinx.dataframe.impl.api.Parsers.stringParser
34
36
import org.jetbrains.kotlinx.dataframe.impl.canParse
35
37
import org.jetbrains.kotlinx.dataframe.impl.catchSilent
36
38
import org.jetbrains.kotlinx.dataframe.impl.createStarProjectedType
@@ -60,6 +62,8 @@ import java.time.LocalDate as JavaLocalDate
60
62
import java.time.LocalDateTime as JavaLocalDateTime
61
63
import java.time.LocalTime as JavaLocalTime
62
64
65
+ private val logger = KotlinLogging .logger { }
66
+
63
67
internal interface StringParser <T > {
64
68
fun toConverter (options : ParserOptions ? ): TypeConverter
65
69
@@ -210,7 +214,7 @@ internal object Parsers : GlobalParserOptions {
210
214
.parseOrNull(this )
211
215
?.toInstantUsingOffset()
212
216
}
213
- // fallback on the java instant to catch things like "2022-01-23T04:29:60", a.k.a. leap seconds
217
+ // fallback on the java instant to catch things like "2022-01-23T04:29:60", a.k.a. leap seconds
214
218
? : toJavaInstantOrNull()?.toKotlinInstant()
215
219
216
220
private fun String.toJavaInstantOrNull (): JavaInstant ? =
@@ -405,23 +409,73 @@ internal object Parsers : GlobalParserOptions {
405
409
stringParser<BigDecimal > { it.toBigDecimalOrNull() },
406
410
407
411
// JSON array as DataFrame<*> TODO
408
- // stringParser<AnyFrame>(catch = true) {
409
- // val trimmed = it.trim()
410
- // if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
411
- // DataFrame.readJsonStr(it)
412
- // } else {
413
- // null
414
- // }
415
- // },
412
+ stringParser<AnyFrame >(catch = true ) {
413
+ val trimmed = it.trim()
414
+ 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." }
439
+ null
440
+ }
441
+ } else {
442
+ null
443
+ }
444
+ },
416
445
// JSON object as DataRow<*> TODO
417
- // stringParser<AnyRow>(catch = true) {
418
- // val trimmed = it.trim()
419
- // if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
420
- // DataRow.readJsonStr(it)
421
- // } else {
422
- // null
423
- // }
424
- // },
446
+ stringParser<AnyRow >(catch = true ) {
447
+ val trimmed = it.trim()
448
+ 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." }
473
+ null
474
+ }
475
+ } else {
476
+ null
477
+ }
478
+ },
425
479
// Char
426
480
stringParser<Char > { it.singleOrNull() },
427
481
// No parser found, return as String
0 commit comments