Migrated to KotlinX Serialization library for JSON parsing

This commit is contained in:
Arty Bishop committed 2026-04-24 17:18:06 +01:00
1 parent 99158e74fa
commit fb2cd85b11
9 files changed
+56 -57

No files matched your search

@@ -33,7 +33,7 @@ internal class CoreDataPlugin : Plugin<Project> {
implementation(libs.androidx.room) implementation(libs.androidx.room)
implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.runtime)
ksp(libs.androidx.room.compiler) ksp(libs.androidx.room.compiler)
implementation(libs.other.coroutines) implementation(libs.kotlin.coroutines)
implementation(libs.other.okhttp) implementation(libs.other.okhttp)
} }
} }
@@ -25,10 +25,11 @@ import org.gradle.kotlin.dsl.dependencies
internal class CoreDomainPlugin : Plugin<Project> { internal class CoreDomainPlugin : Plugin<Project> {
override fun apply(target: Project) = with(target) { override fun apply(target: Project) = with(target) {
applyPlugin(libs.plugins.kotlin.jvm) applyPlugin(libs.plugins.kotlin.jvm)
applyPlugin(libs.plugins.kotlin.serialization)
setupKotlin() setupKotlin()
dependencies { dependencies {
implementation(libs.other.coroutines) implementation(libs.kotlin.coroutines)
implementation(libs.other.json) implementation(libs.kotlin.serialization)
} }
} }
} }
+1
View File
@@ -4,6 +4,7 @@ plugins {
alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.google.ksp) apply false alias(libs.plugins.google.ksp) apply false
alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
} }
tasks.register("clean", Delete::class.java) { tasks.register("clean", Delete::class.java) {
@@ -25,11 +25,11 @@ data class SatRadio(
@PrimaryKey val uuid: String, @PrimaryKey val uuid: String,
val info: String, val info: String,
val isAlive: Boolean, val isAlive: Boolean,
var downlinkLow: Long?, val downlinkLow: Long?,
var downlinkHigh: Long?, val downlinkHigh: Long?,
val downlinkMode: String?, val downlinkMode: String?,
var uplinkLow: Long?, val uplinkLow: Long?,
var uplinkHigh: Long?, val uplinkHigh: Long?,
val uplinkMode: String?, val uplinkMode: String?,
val isInverted: Boolean, val isInverted: Boolean,
val catnum: Int? val catnum: Int?
@@ -17,16 +17,20 @@
*/ */
package com.rtbishop.look4sat.core.domain.model package com.rtbishop.look4sat.core.domain.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class SatRadio( data class SatRadio(
val uuid: String, @SerialName("uuid") val uuid: String,
val info: String, @SerialName("description") val info: String,
val isAlive: Boolean, @SerialName("alive") val isAlive: Boolean,
var downlinkLow: Long?, @SerialName("downlink_low") val downlinkLow: Long?,
var downlinkHigh: Long?, @SerialName("downlink_high") val downlinkHigh: Long?,
val downlinkMode: String?, @SerialName("mode") val downlinkMode: String?,
var uplinkLow: Long?, @SerialName("uplink_low") val uplinkLow: Long?,
var uplinkHigh: Long?, @SerialName("uplink_high") val uplinkHigh: Long?,
val uplinkMode: String?, @SerialName("uplink_mode") val uplinkMode: String?,
val isInverted: Boolean, @SerialName("invert") val isInverted: Boolean,
val catnum: Int? @SerialName("norad_cat_id") val catnum: Int?
) )
@@ -21,13 +21,19 @@ import com.rtbishop.look4sat.core.domain.model.SatRadio
import com.rtbishop.look4sat.core.domain.predict.OrbitalData import com.rtbishop.look4sat.core.domain.predict.OrbitalData
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.json.JSONArray import kotlinx.serialization.json.Json
import org.json.JSONObject import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.decodeFromJsonElement
import java.io.InputStream import java.io.InputStream
import kotlin.math.pow import kotlin.math.pow
class DataParser(private val dispatcher: CoroutineDispatcher) { class DataParser(private val dispatcher: CoroutineDispatcher) {
private val json = Json {
ignoreUnknownKeys = true
coerceInputValues = true
}
suspend fun parseCSVStream(stream: InputStream): List<OrbitalData> = withContext(dispatcher) { suspend fun parseCSVStream(stream: InputStream): List<OrbitalData> = withContext(dispatcher) {
stream.bufferedReader().useLines { lines -> stream.bufferedReader().useLines { lines ->
lines.drop(1).mapNotNull { parseCSV(it.split(",")) }.toList() lines.drop(1).mapNotNull { parseCSV(it.split(",")) }.toList()
@@ -43,8 +49,12 @@ class DataParser(private val dispatcher: CoroutineDispatcher) {
suspend fun parseJSONStream(stream: InputStream): List<SatRadio> = withContext(dispatcher) { suspend fun parseJSONStream(stream: InputStream): List<SatRadio> = withContext(dispatcher) {
runCatching { runCatching {
val jsonArray = JSONArray(stream.bufferedReader().readText()) val root = json.parseToJsonElement(stream.bufferedReader().readText())
(0 until jsonArray.length()).mapNotNull { parseJSON(jsonArray.getJSONObject(it)) } (root as? JsonArray)?.mapNotNull { element ->
runCatching { json.decodeFromJsonElement<SatRadio>(element) }
.onFailure { println("JSON parsing exception: $it") }
.getOrNull()
} ?: emptyList()
}.getOrDefault(emptyList()) }.getOrDefault(emptyList())
} }
@@ -95,26 +105,6 @@ class DataParser(private val dispatcher: CoroutineDispatcher) {
) )
}.onFailure { println("TLE parsing exception: $it") }.getOrNull() }.onFailure { println("TLE parsing exception: $it") }.getOrNull()
private fun parseJSON(json: JSONObject): SatRadio? = runCatching {
SatRadio(
uuid = json.getString("uuid"),
info = json.getString("description"),
isAlive = json.getBoolean("alive"),
downlinkLow = json.optLongOrNull("downlink_low"),
downlinkHigh = json.optLongOrNull("downlink_high"),
downlinkMode = json.optStringOrNull("mode"),
uplinkLow = json.optLongOrNull("uplink_low"),
uplinkHigh = json.optLongOrNull("uplink_high"),
uplinkMode = json.optStringOrNull("uplink_mode"),
isInverted = json.getBoolean("invert"),
catnum = json.optIntOrNull("norad_cat_id")
)
}.onFailure { println("JSON parsing exception: $it") }.getOrNull()
private fun JSONObject.optStringOrNull(key: String): String? = if (isNull(key)) null else getString(key)
private fun JSONObject.optLongOrNull(key: String): Long? = if (isNull(key)) null else getLong(key)
private fun JSONObject.optIntOrNull(key: String): Int? = if (isNull(key)) null else getInt(key)
private fun getDayOfYear(year: Int, month: Int, dayOfMonth: Int): Int { private fun getDayOfYear(year: Int, month: Int, dayOfMonth: Int): Int {
val daysInMonth = intArrayOf(31, if (isLeapYear(year)) 29 else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) val daysInMonth = intArrayOf(31, if (isLeapYear(year)) 29 else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
return daysInMonth.take(month - 1).sum() + dayOfMonth return daysInMonth.take(month - 1).sum() + dayOfMonth
@@ -2,9 +2,9 @@ package com.rtbishop.look4sat.core.domain
import com.rtbishop.look4sat.core.domain.model.SatRadio import com.rtbishop.look4sat.core.domain.model.SatRadio
import com.rtbishop.look4sat.core.domain.utility.TransponderMapper import com.rtbishop.look4sat.core.domain.utility.TransponderMapper
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
class TransponderMapperTest { class TransponderMapperTest {
@@ -149,8 +149,8 @@ private fun RadarScreen(
@Composable @Composable
private fun RadarCard(uiState: RadarState, modifier: Modifier = Modifier) { private fun RadarCard(uiState: RadarState, modifier: Modifier = Modifier) {
val isEclipsed = uiState.orbitalPos?.eclipsed == true val satellitePos = uiState.orbitalPos
val borderModifier = if (isEclipsed) { val borderModifier = if (satellitePos?.aboveHorizon == true && satellitePos.eclipsed) {
val infiniteTransition = rememberInfiniteTransition(label = "eclipsedBorder") val infiniteTransition = rememberInfiniteTransition(label = "eclipsedBorder")
val borderAlpha by infiniteTransition.animateFloat( val borderAlpha by infiniteTransition.animateFloat(
initialValue = 1.0f, initialValue = 1.0f,
+14 -11
View File
@@ -12,9 +12,7 @@ jdkVersion = "17"
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
packageName = "com.rtbishop.look4sat" packageName = "com.rtbishop.look4sat"
android-gradle-plugin = "9.1.1" android-gradle-plugin = "9.2.0"
google-ksp = "2.3.6"
kotlin = "2.3.20"
androidx-core-ktx = "1.18.0" androidx-core-ktx = "1.18.0"
androidx-core-splashscreen = "1.2.0" androidx-core-splashscreen = "1.2.0"
@@ -25,8 +23,12 @@ compose-activity = "1.13.0"
compose-lifecycle = "2.10.0" compose-lifecycle = "2.10.0"
compose-navigation = "2.9.7" compose-navigation = "2.9.7"
other-coroutines = "1.10.2" google-ksp = "2.3.6"
other-json = "20251224"
kotlin = "2.3.20"
kotlin-coroutines = "1.10.2"
kotlin-serialization = "1.11.0"
other-okhttp = "5.3.2" other-okhttp = "5.3.2"
other-osmdroid = "6.1.20" other-osmdroid = "6.1.20"
@@ -64,18 +66,18 @@ compose-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose",
compose-debug-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } compose-debug-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
compose-debug-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } compose-debug-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } #noinspection UnusedVersionCatalogEntry
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
#noinspection UnusedVersionCatalogEntry
kotlin-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlin-serialization" }
#noinspection UnusedVersionCatalogEntry
other-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "other-coroutines" }
#noinspection UnusedVersionCatalogEntry
other-json = { module = "org.json:json", version.ref = "other-json" }
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
other-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "other-okhttp" } other-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "other-okhttp" }
#noinspection UnusedVersionCatalogEntry #noinspection UnusedVersionCatalogEntry
other-osmdroid = { module = "org.osmdroid:osmdroid-android", version.ref = "other-osmdroid" } other-osmdroid = { module = "org.osmdroid:osmdroid-android", version.ref = "other-osmdroid" }
test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "other-coroutines" } test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlin-coroutines" }
test-junit4 = { module = "junit:junit", version.ref = "test-junit4" } test-junit4 = { module = "junit:junit", version.ref = "test-junit4" }
androidTest-junit = { module = "androidx.test.ext:junit", version.ref = "androidTest-junit" } androidTest-junit = { module = "androidx.test.ext:junit", version.ref = "androidTest-junit" }
@@ -87,6 +89,7 @@ android-library = { id = "com.android.library", version.ref = "android-gradle-pl
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" } google-ksp = { id = "com.google.devtools.ksp", version.ref = "google-ksp" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
# plugins defined by this project # plugins defined by this project
convention-applicationPlugin = { id = "com.rtbishop.look4sat.convention.applicationPlugin" } convention-applicationPlugin = { id = "com.rtbishop.look4sat.convention.applicationPlugin" }
convention-coreDataPlugin = { id = "com.rtbishop.look4sat.convention.coreDataPlugin" } convention-coreDataPlugin = { id = "com.rtbishop.look4sat.convention.coreDataPlugin" }