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
@@ -25,19 +26,18 @@ import org.jetbrains.kotlinx.dataframe.api.isColumnGroup
25
26
import org.jetbrains.kotlinx.dataframe.api.isFrameColumn
26
27
import org.jetbrains.kotlinx.dataframe.api.isSubtypeOf
27
28
import org.jetbrains.kotlinx.dataframe.api.map
28
- import org.jetbrains.kotlinx.dataframe.api.parser
29
29
import org.jetbrains.kotlinx.dataframe.api.to
30
30
import org.jetbrains.kotlinx.dataframe.columns.TypeSuggestion
31
31
import org.jetbrains.kotlinx.dataframe.columns.size
32
32
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
33
33
import org.jetbrains.kotlinx.dataframe.hasNulls
34
+ import org.jetbrains.kotlinx.dataframe.impl.api.Parsers.resetToDefault
34
35
import org.jetbrains.kotlinx.dataframe.impl.canParse
35
36
import org.jetbrains.kotlinx.dataframe.impl.catchSilent
36
37
import org.jetbrains.kotlinx.dataframe.impl.createStarProjectedType
37
38
import org.jetbrains.kotlinx.dataframe.impl.io.FastDoubleParser
38
39
import org.jetbrains.kotlinx.dataframe.impl.javaDurationCanParse
39
40
import org.jetbrains.kotlinx.dataframe.io.isUrl
40
- import org.jetbrains.kotlinx.dataframe.io.readJsonStr
41
41
import org.jetbrains.kotlinx.dataframe.values
42
42
import java.math.BigDecimal
43
43
import java.math.BigInteger
@@ -61,6 +61,8 @@ import java.time.LocalDate as JavaLocalDate
61
61
import java.time.LocalDateTime as JavaLocalDateTime
62
62
import java.time.LocalTime as JavaLocalTime
63
63
64
+ private val logger = KotlinLogging .logger { }
65
+
64
66
internal interface StringParser <T > {
65
67
fun toConverter (options : ParserOptions ? ): TypeConverter
66
68
@@ -335,6 +337,94 @@ internal object Parsers : GlobalParserOptions {
335
337
parser
336
338
}
337
339
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
+
338
428
internal val parsersOrder = listOf (
339
429
// Int
340
430
stringParser<Int > { it.toIntOrNull() },
@@ -408,7 +498,14 @@ internal object Parsers : GlobalParserOptions {
408
498
stringParser<AnyFrame >(catch = true ) {
409
499
val trimmed = it.trim()
410
500
if (trimmed.startsWith(" [" ) && trimmed.endsWith(" ]" )) {
411
- DataFrame .readJsonStr(it)
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
+ }
505
+ null
506
+ } else {
507
+ readJsonStrAnyFrame!! (trimmed)
508
+ }
412
509
} else {
413
510
null
414
511
}
@@ -417,7 +514,14 @@ internal object Parsers : GlobalParserOptions {
417
514
stringParser<AnyRow >(catch = true ) {
418
515
val trimmed = it.trim()
419
516
if (trimmed.startsWith(" {" ) && trimmed.endsWith(" }" )) {
420
- DataRow .readJsonStr(it)
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
+ }
521
+ null
522
+ } else {
523
+ readJsonStrAnyRow!! (trimmed)
524
+ }
421
525
} else {
422
526
null
423
527
}
0 commit comments