From 3b51d8c53217df9b28fa9616a7b8ffa89a46512f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brais=20Gab=C3=ADn?= Date: Tue, 13 Feb 2024 19:06:44 +0100 Subject: [PATCH] Don't expose JsonElement --- .../io/github/detekt/sarif4k/Merging.kt | 7 +-- .../detekt/sarif4k/SarifDataBindings.kt | 16 +++--- .../github/detekt/sarif4k/SarifSerializer.kt | 57 ++++++++++++++++++- .../detekt/sarif4k/SarifSerializerTest.kt | 19 ++----- 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/commonMain/kotlin/io/github/detekt/sarif4k/Merging.kt b/src/commonMain/kotlin/io/github/detekt/sarif4k/Merging.kt index 529efd2..d2da926 100644 --- a/src/commonMain/kotlin/io/github/detekt/sarif4k/Merging.kt +++ b/src/commonMain/kotlin/io/github/detekt/sarif4k/Merging.kt @@ -2,7 +2,6 @@ package io.github.detekt.sarif4k -import kotlinx.serialization.json.JsonArray import kotlin.jvm.JvmName fun SarifSchema210.merge(other: SarifSchema210): SarifSchema210 { @@ -32,12 +31,12 @@ fun SarifSchema210.merge(other: SarifSchema210): SarifSchema210 { fun PropertyBag.merge(other: PropertyBag): PropertyBag { var merge = this + other - val aTags = this["tags"] as? JsonArray - val bTags = other["tags"] as? JsonArray + val aTags = this["tags"] as? Collection<*> + val bTags = other["tags"] as? Collection<*> if (aTags != null && bTags != null) { merge = merge.toMutableMap().also { - it["tags"] = JsonArray((aTags + bTags).distinct()) + it["tags"] = (aTags + bTags).distinct() } } diff --git a/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifDataBindings.kt b/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifDataBindings.kt index 2d8a687..cc75d26 100644 --- a/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifDataBindings.kt +++ b/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifDataBindings.kt @@ -346,24 +346,22 @@ data class Address ( */ @Serializable(with = PropertyBagSerializer::class) @JvmInline -value class PropertyBag(private val value: JsonObject) : Map by value { - constructor(value: Map) : this(JsonObject(value)) +value class PropertyBag(private val value: Map) : Map by value { + /** + * A set of distinct strings that provide additional information. + */ + val tags: List? get() = (value["tags"] as Collection<*>?)?.map { it as String } } -/** - * A set of distinct strings that provide additional information. - */ -val PropertyBag.tags: List? get() = this["tags"]?.let { Json.decodeFromJsonElement(it) } - object PropertyBagSerializer : KSerializer { override val descriptor: SerialDescriptor = JsonObject.serializer().descriptor override fun deserialize(decoder: Decoder): PropertyBag { - return PropertyBag(decoder.decodeSerializableValue(JsonObject.serializer())) + return PropertyBag(decoder.decodeSerializableValue(JsonObject.serializer()).toMap()) } override fun serialize(encoder: Encoder, value: PropertyBag) { - encoder.encodeSerializableValue(JsonObject.serializer(), JsonObject(value)) + encoder.encodeSerializableValue(JsonObject.serializer(), value.toJsonElement()) } } diff --git a/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifSerializer.kt b/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifSerializer.kt index e85f290..2959e3e 100644 --- a/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifSerializer.kt +++ b/src/commonMain/kotlin/io/github/detekt/sarif4k/SarifSerializer.kt @@ -3,6 +3,11 @@ package io.github.detekt.sarif4k import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive object SarifSerializer { private val json = Json { @@ -15,4 +20,54 @@ object SarifSerializer { fun toJson(sarif: SarifSchema210): String = json.encodeToString(sarif) fun fromJson(json: String): SarifSchema210 = Json.decodeFromString(json) -} \ No newline at end of file +} + +// Extracted from https://github.com/Kotlin/kotlinx.serialization/issues/296#issuecomment-1859572541 +private fun Any?.toJsonElement(): JsonElement = when (this) { + null -> JsonNull + is JsonElement -> this + is Map<*, *> -> toJsonElement() + is Collection<*> -> toJsonElement() + is ByteArray -> this.toList().toJsonElement() + is CharArray -> this.toList().toJsonElement() + is ShortArray -> this.toList().toJsonElement() + is IntArray -> this.toList().toJsonElement() + is LongArray -> this.toList().toJsonElement() + is FloatArray -> this.toList().toJsonElement() + is DoubleArray -> this.toList().toJsonElement() + is BooleanArray -> this.toList().toJsonElement() + is Array<*> -> toJsonElement() + is Boolean -> JsonPrimitive(this) + is Number -> JsonPrimitive(this) + is String -> JsonPrimitive(this) + is Enum<*> -> JsonPrimitive(this.toString()) + else -> error("Can't serialize unknown type: $this") +} + +internal fun Map<*, *>.toJsonElement(): JsonObject { + return JsonObject(this.map { (key, value) -> key as String to value.toJsonElement() }.toMap()) +} + +private fun Collection<*>.toJsonElement(): JsonArray { + return JsonArray(this.map { it.toJsonElement() }) +} + +private fun Array<*>.toJsonElement(): JsonArray { + return JsonArray(this.map { it.toJsonElement() }) +} + +private fun JsonElement.toMap(): Any? { + return when (this) { + JsonNull -> null + is JsonArray -> this.map { it.toMap() } + is JsonObject -> this.toMap() + is JsonPrimitive -> if (isString) content else { + content.toBooleanStrictOrNull() ?: content.toDoubleOrNull() ?: content.toLongOrNull() + ?: error("Unknown primitive type") + } + } +} + +internal fun JsonObject.toMap(): Map { + return this.map { (key, value) -> key to value.toMap() }.toMap() +} diff --git a/src/jvmTest/kotlin/io/github/detekt/sarif4k/SarifSerializerTest.kt b/src/jvmTest/kotlin/io/github/detekt/sarif4k/SarifSerializerTest.kt index 77f0e77..e262344 100644 --- a/src/jvmTest/kotlin/io/github/detekt/sarif4k/SarifSerializerTest.kt +++ b/src/jvmTest/kotlin/io/github/detekt/sarif4k/SarifSerializerTest.kt @@ -1,10 +1,5 @@ package io.github.detekt.sarif4k -import kotlinx.serialization.json.add -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonArray -import kotlinx.serialization.json.putJsonObject import org.junit.jupiter.api.Test import java.io.File import kotlin.test.assertEquals @@ -50,14 +45,10 @@ class SarifSerializerTest { ) ), properties = PropertyBag( - buildJsonObject { - putJsonArray("tags") { - add("tag") - } - putJsonObject("foo") { - put("bar", "buz") - } - } + mapOf( + "tags" to listOf("tag"), + "foo" to mapOf("bar" to "buz") + ) ), ) ) @@ -106,4 +97,4 @@ class SarifSerializerTest { ) assertEquals(sarifSchema210, actual) } -} \ No newline at end of file +}