@@ -6,6 +6,7 @@ import io.kotest.matchers.collections.shouldBeIn
6
6
import io.kotest.matchers.shouldBe
7
7
import io.kotest.matchers.string.shouldContain
8
8
import io.kotest.matchers.string.shouldNotContain
9
+ import io.kotest.matchers.string.shouldStartWith
9
10
import io.kotest.matchers.types.instanceOf
10
11
import io.kotest.matchers.types.shouldBeInstanceOf
11
12
import kotlinx.serialization.json.Json
@@ -20,35 +21,43 @@ import org.intellij.lang.annotations.Language
20
21
import org.jetbrains.kotlinx.dataframe.AnyFrame
21
22
import org.jetbrains.kotlinx.dataframe.DataFrame
22
23
import org.jetbrains.kotlinx.dataframe.DataRow
24
+ import org.jetbrains.kotlinx.dataframe.api.FormattedFrame
23
25
import org.jetbrains.kotlinx.dataframe.api.JsonPath
24
26
import org.jetbrains.kotlinx.dataframe.api.allNulls
25
27
import org.jetbrains.kotlinx.dataframe.api.colsOf
26
28
import org.jetbrains.kotlinx.dataframe.api.columnsCount
27
29
import org.jetbrains.kotlinx.dataframe.api.convert
28
30
import org.jetbrains.kotlinx.dataframe.api.dataFrameOf
29
31
import org.jetbrains.kotlinx.dataframe.api.forEach
32
+ import org.jetbrains.kotlinx.dataframe.api.format
30
33
import org.jetbrains.kotlinx.dataframe.api.getColumnGroup
34
+ import org.jetbrains.kotlinx.dataframe.api.getColumns
31
35
import org.jetbrains.kotlinx.dataframe.api.getFrameColumn
32
36
import org.jetbrains.kotlinx.dataframe.api.print
33
37
import org.jetbrains.kotlinx.dataframe.api.schema
34
38
import org.jetbrains.kotlinx.dataframe.api.toFloat
35
39
import org.jetbrains.kotlinx.dataframe.api.toMap
40
+ import org.jetbrains.kotlinx.dataframe.api.with
36
41
import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup
37
42
import org.jetbrains.kotlinx.dataframe.columns.ColumnKind
38
43
import org.jetbrains.kotlinx.dataframe.columns.FrameColumn
39
44
import org.jetbrains.kotlinx.dataframe.columns.ValueColumn
40
45
import org.jetbrains.kotlinx.dataframe.impl.io.SERIALIZATION_VERSION
41
46
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.COLUMNS
42
47
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.DATA
48
+ import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.IS_FORMATTED
43
49
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.KIND
44
50
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.KOTLIN_DATAFRAME
45
51
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.METADATA
46
52
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.NCOL
47
53
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.NROW
54
+ import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.TYPE
55
+ import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.TYPES
48
56
import org.jetbrains.kotlinx.dataframe.impl.io.SerializationKeys.VERSION
49
57
import org.jetbrains.kotlinx.dataframe.impl.io.readJsonImpl
50
58
import org.jetbrains.kotlinx.dataframe.io.JSON.TypeClashTactic.ANY_COLUMNS
51
59
import org.jetbrains.kotlinx.dataframe.io.JSON.TypeClashTactic.ARRAY_AND_VALUE_COLUMNS
60
+ import org.jetbrains.kotlinx.dataframe.jupyter.KotlinNotebookPluginUtils.convertToDataFrame
52
61
import java.net.URL
53
62
import kotlin.reflect.KType
54
63
import kotlin.reflect.typeOf
@@ -1040,6 +1049,35 @@ class JsonTests {
1040
1049
val metadata = json[METADATA ]!! .jsonObject
1041
1050
metadata[NROW ]!! .jsonPrimitive.int shouldBe 1
1042
1051
metadata[NCOL ]!! .jsonPrimitive.int shouldBe 4
1052
+ metadata[IS_FORMATTED ]!! .jsonPrimitive.boolean shouldBe false
1053
+ val columns = metadata[COLUMNS ]!! .jsonArray.map { it.jsonPrimitive.content }
1054
+ columns shouldBe listOf (" id" , " node_id" , " name" , " full_name" )
1055
+
1056
+ val decodedData = json[KOTLIN_DATAFRAME ]!! .jsonArray
1057
+ val decodedDf = DataFrame .readJsonStr(decodedData.toString())
1058
+ decodedDf shouldBe df
1059
+ }
1060
+
1061
+ @Suppress(" USELESS_IS_CHECK" )
1062
+ @Test
1063
+ fun `json with metadata flat formatted table` () {
1064
+ @Language(" json" )
1065
+ val data =
1066
+ """
1067
+ [{"id":3602279,"node_id":"MDEwOlJlcG9zaXRvcnkzNjAyMjc5","name":"kotlin-web-demo","full_name":"JetBrains/kotlin-web-demo"}]
1068
+ """ .trimIndent()
1069
+ // simulating the functions used to define whether the dataframe is formatted
1070
+ val formattedDf = DataFrame .readJsonStr(data).format().with { background(blue) }
1071
+ val df = convertToDataFrame(formattedDf)
1072
+ val jsonStr = df.toJsonWithMetadata(df.rowsCount(), isFormatted = formattedDf is FormattedFrame <* >).trimIndent()
1073
+ val json = parseJsonStr(jsonStr)
1074
+
1075
+ json[VERSION ]!! .jsonPrimitive.content shouldBe SERIALIZATION_VERSION
1076
+
1077
+ val metadata = json[METADATA ]!! .jsonObject
1078
+ metadata[NROW ]!! .jsonPrimitive.int shouldBe 1
1079
+ metadata[NCOL ]!! .jsonPrimitive.int shouldBe 4
1080
+ metadata[IS_FORMATTED ]!! .jsonPrimitive.boolean shouldBe true
1043
1081
val columns = metadata[COLUMNS ]!! .jsonArray.map { it.jsonPrimitive.content }
1044
1082
columns shouldBe listOf (" id" , " node_id" , " name" , " full_name" )
1045
1083
@@ -1079,6 +1117,8 @@ class JsonTests {
1079
1117
val df = DataFrame .readJson(testJson(" repositories" ))
1080
1118
val jsonStr = df.toJsonWithMetadata(df.rowsCount()).trimIndent()
1081
1119
val json = parseJsonStr(jsonStr)
1120
+ json[METADATA ]!! .jsonObject[IS_FORMATTED ]!! .jsonPrimitive.boolean shouldBe false
1121
+
1082
1122
val row = json[KOTLIN_DATAFRAME ]!! .jsonArray[0 ].jsonObject
1083
1123
1084
1124
val contributors = row[" contributors" ]!! .jsonObject
@@ -1101,6 +1141,8 @@ class JsonTests {
1101
1141
val nestedFrameRowLimit = 20
1102
1142
val jsonStr = df.toJsonWithMetadata(df.rowsCount(), nestedFrameRowLimit).trimIndent()
1103
1143
val json = parseJsonStr(jsonStr)
1144
+ json[METADATA ]!! .jsonObject[IS_FORMATTED ]!! .jsonPrimitive.boolean shouldBe false
1145
+
1104
1146
val row = json[KOTLIN_DATAFRAME ]!! .jsonArray[0 ].jsonObject
1105
1147
1106
1148
val contributors = row[" contributors" ]!! .jsonObject
@@ -1114,6 +1156,33 @@ class JsonTests {
1114
1156
decodedData.size shouldBe nestedFrameRowLimit
1115
1157
}
1116
1158
1159
+ @Test
1160
+ fun `json with metadata containing formatted nested frames` () {
1161
+ val df = DataFrame .readJson(testJson(" repositories" ))
1162
+ .convert { frameCols() }.with { it.format().with { background(blue) } }
1163
+
1164
+ // simulating the functions used to define whether the dataframe is formatted
1165
+ val hasFormattedColumns = df.getColumns { colsAtAnyDepth().colsOf<FormattedFrame <* >? > () }.isNotEmpty()
1166
+ val jsonStr = df.toJsonWithMetadata(df.rowsCount(), isFormatted = hasFormattedColumns).trimIndent()
1167
+
1168
+ val json = parseJsonStr(jsonStr)
1169
+ val metadata = json[METADATA ]!! .jsonObject
1170
+ metadata[IS_FORMATTED ]!! .jsonPrimitive.boolean shouldBe true
1171
+ metadata[NCOL ]!! .jsonPrimitive.int shouldBe 1
1172
+ metadata[NROW ]!! .jsonPrimitive.int shouldBe 1
1173
+ metadata[COLUMNS ]!! .jsonArray.single().jsonPrimitive.content shouldBe " contributors"
1174
+ metadata[TYPES ]!! .jsonArray.single().jsonObject.let {
1175
+ it[KIND ]!! .jsonPrimitive.content shouldBe " ValueColumn"
1176
+ it[TYPE ]!! .jsonPrimitive.content shouldStartWith FormattedFrame ::class .qualifiedName!!
1177
+ }
1178
+
1179
+ val row = json[KOTLIN_DATAFRAME ]!! .jsonArray[0 ].jsonObject
1180
+
1181
+ // is read as value column
1182
+ val contributors = row[" contributors" ]!! .jsonPrimitive.content
1183
+ contributors shouldStartWith FormattedFrame ::class .qualifiedName!!
1184
+ }
1185
+
1117
1186
@Test
1118
1187
fun `serialize column with list of objects` () {
1119
1188
val df = dataFrameOf(" col" )(Regex (" .+" ).findAll(" abc" ).toList())
0 commit comments