Skip to content

Commit b13c06c

Browse files
authored
fix: migrate from Gson to kotlinx.serialization for backup export issue
2 parents 6d94a7a + f7f48b6 commit b13c06c

8 files changed

Lines changed: 37 additions & 31 deletions

File tree

app/build.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ plugins {
55
id("org.jetbrains.kotlin.plugin.compose")
66
id("dagger.hilt.android.plugin")
77
alias(libs.plugins.ktlint)
8+
alias(libs.plugins.kotlin.serialization)
89
}
910

1011
ktlint {
@@ -20,8 +21,8 @@ android {
2021
applicationId = "com.opennotes"
2122
minSdk = 26
2223
targetSdk = 36
23-
versionCode = 13
24-
versionName = "1.3.9"
24+
versionCode = 14
25+
versionName = "1.4.0"
2526

2627
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2728
vectorDrawables {
@@ -108,7 +109,7 @@ dependencies {
108109
ksp(libs.room.compiler)
109110
implementation(libs.coroutines.core)
110111
implementation(libs.coroutines.android)
111-
implementation(libs.gson)
112+
implementation(libs.kotlinx.serialization.json)
112113
implementation(libs.core.splashscreen)
113114
implementation(libs.biometric)
114115
implementation(libs.glance.appwidget)

app/proguard-rules.pro

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,3 @@
77
-keepnames class com.google.gson.Gson
88
-keepnames class com.google.gson.JsonDeserializer
99
-keepnames class com.google.gson.JsonSerializer
10-
11-
-keep class com.opennotes.featureNode.domain.model.** { *; }
12-
13-
-keep class com.opennotes.featureNode.data.repository.GsonJsonHandler { *; }
14-
-keep interface com.opennotes.featureNode.data.repository.JsonHandler { *; }

app/src/main/java/com/opennotes/di/AppModule.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import androidx.room.Room
2323
import com.opennotes.notes.data.datasource.NoteDatabase
2424
import com.opennotes.notes.data.repository.AndroidFileHandler
2525
import com.opennotes.notes.data.repository.FileHandler
26-
import com.opennotes.notes.data.repository.GsonJsonHandler
2726
import com.opennotes.notes.data.repository.JsonHandler
27+
import com.opennotes.notes.data.repository.KotlinxJsonHandler
2828
import com.opennotes.notes.data.repository.NoteRepositoryImpl
2929
import com.opennotes.notes.domain.repository.NoteRepository
3030
import com.opennotes.notes.domain.usecase.AddNote
@@ -64,10 +64,10 @@ object AppModule {
6464
@Singleton
6565
fun provideFileHandler(app: Application): FileHandler = AndroidFileHandler(app)
6666

67-
// CORRECTED: Provides the concrete GsonJsonHandler implementation
67+
// CORRECTED: Provides the concrete KotlinxJsonHandler implementation
6868
@Provides
6969
@Singleton
70-
fun provideJsonHandler(): JsonHandler = GsonJsonHandler()
70+
fun provideJsonHandler(): JsonHandler = KotlinxJsonHandler()
7171

7272
@Provides
7373
@Singleton

app/src/main/java/com/opennotes/notes/data/repository/JsonHandler.kt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,25 @@
1818

1919
package com.opennotes.notes.data.repository
2020

21-
import com.google.gson.GsonBuilder
22-
23-
// In your data/repository directory
21+
import com.opennotes.notes.domain.model.Note
22+
import kotlinx.serialization.encodeToString
23+
import kotlinx.serialization.json.*
2424

2525
interface JsonHandler {
26-
fun <T> toJson(data: T): String
26+
fun serializeNotes(notes: List<Note>): String
2727

28-
fun fromJsonToNoteMapList(json: String): List<Map<String, Any?>>
28+
fun parseToJsonArray(json: String): JsonArray
2929
}
3030

31-
class GsonJsonHandler : JsonHandler {
32-
private val gson = GsonBuilder().create()
31+
class KotlinxJsonHandler : JsonHandler {
32+
private val jsonFormat =
33+
Json {
34+
ignoreUnknownKeys = true
35+
encodeDefaults = true
36+
isLenient = true
37+
}
3338

34-
override fun <T> toJson(data: T): String = gson.toJson(data)
39+
override fun serializeNotes(notes: List<Note>): String = jsonFormat.encodeToString(notes)
3540

36-
override fun fromJsonToNoteMapList(json: String): List<Map<String, Any?>> {
37-
val noteListType = object : com.google.gson.reflect.TypeToken<List<Map<String, Any?>>>() {}.type
38-
return gson.fromJson(json, noteListType)
39-
}
41+
override fun parseToJsonArray(json: String): JsonArray = jsonFormat.parseToJsonElement(json).jsonArray
4042
}

app/src/main/java/com/opennotes/notes/domain/model/Note.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818

1919
package com.opennotes.notes.domain.model
2020

21+
import kotlinx.serialization.Serializable
22+
23+
@Serializable
2124
data class Note(
2225
val title: String,
2326
val content: String,

app/src/main/java/com/opennotes/notes/domain/usecase/ExportUseCases.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class ExportUseCases(
4040
return ExportResult.Error("No notes to export")
4141
}
4242

43-
val notesJson = jsonHandler.toJson(allNotes)
43+
val notesJson = jsonHandler.serializeNotes(allNotes)
4444

4545
val isSaved = fileHandler.exportBackupToZip(targetUriString, notesJson)
4646

app/src/main/java/com/opennotes/notes/domain/usecase/ImportUseCases.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.opennotes.notes.data.repository.JsonHandler
2323
import com.opennotes.notes.domain.model.Note
2424
import com.opennotes.notes.domain.repository.NoteRepository
2525
import com.opennotes.notes.domain.util.ImportResult
26+
import kotlinx.serialization.json.*
2627

2728
class ImportUseCases(
2829
private val repository: NoteRepository,
@@ -43,9 +44,9 @@ class ImportUseCases(
4344
}
4445

4546
// Deserialize with validation - catch invalid JSON first
46-
val rawNotes: List<Map<String, Any?>> =
47+
val rawNotes =
4748
try {
48-
jsonHandler.fromJsonToNoteMapList(notesJson)
49+
jsonHandler.parseToJsonArray(notesJson)
4950
} catch (e: Exception) {
5051
return ImportResult.Error("Invalid JSON format: ${e.message}")
5152
}
@@ -56,11 +57,12 @@ class ImportUseCases(
5657

5758
// Validate and construct trusted Note objects
5859
val validNotes =
59-
rawNotes.mapNotNull { rawNote ->
60-
val title = (rawNote["title"] as? String)?.trim()
61-
val content = (rawNote["content"] as? String)?.trim()
62-
val timestamp = (rawNote["timestamp"] as? Number)?.toLong()
63-
val color = (rawNote["color"] as? Number)?.toInt()
60+
rawNotes.mapNotNull { element ->
61+
val rawNote = element as? JsonObject ?: return@mapNotNull null
62+
val title = rawNote["title"]?.jsonPrimitive?.contentOrNull?.trim()
63+
val content = rawNote["content"]?.jsonPrimitive?.contentOrNull?.trim()
64+
val timestamp = rawNote["timestamp"]?.jsonPrimitive?.longOrNull
65+
val color = rawNote["color"]?.jsonPrimitive?.intOrNull
6466

6567
// Only create Note if all required fields are present and valid
6668
if ((title?.isNotBlank() == true || content?.isNotBlank() == true) &&

gradle/libs.versions.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ ktlint = "14.2.0"
2323
material3 = "1.4.0-alpha02"
2424
coil = "2.6.0"
2525
work = "2.9.0"
26+
kotlinx-serialization-json = "1.7.3"
2627

2728
[libraries]
2829
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
@@ -63,6 +64,7 @@ espresso = { module = "androidx.test.espresso:espresso-core", version.ref = "esp
6364
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
6465
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
6566
work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "work" }
67+
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization-json" }
6668

6769
[plugins]
6870
android-application = { id = "com.android.application", version = "8.9.1" }
@@ -71,3 +73,4 @@ ksp = { id = "com.google.devtools.ksp", version = "2.1.0-1.0.29" }
7173
compose = { id = "org.jetbrains.kotlin.plugin.compose", version = "2.1.0" }
7274
hilt = { id = "com.google.dagger.hilt.android", version = "2.51.1" }
7375
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
76+
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "2.1.0" }

0 commit comments

Comments
 (0)