From 4f7bd89f11ae493b614ce2f0f386cc49a4aec113 Mon Sep 17 00:00:00 2001 From: lidiwei Date: Tue, 10 Jun 2025 12:22:04 +0800 Subject: [PATCH 1/9] Add Enhanced JsonObject deserialization and field reading syntax sugar --- .../src/kotlinx/serialization/json/Json.kt | 3 +++ .../kotlinx/serialization/json/JsonElement.kt | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt index ed63e2ca7..9f6931e6d 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt @@ -179,6 +179,9 @@ public sealed class Json( return decodeFromString(JsonElementSerializer, string) } + public fun parseToJsonObject(@FormatLanguage("json", "", "") string: String): JsonObject = parseToJsonElement(string).jsonObject + public fun parseToJsonArray(@FormatLanguage("json", "", "") string: String): JsonArray = parseToJsonElement(string).jsonArray + /** * Following functions are copied from extensions on StringFormat * to streamline experience for newcomers, since IDE does not star-import kotlinx.serialization.* automatically diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index 202567b77..d9e523f61 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -347,3 +347,23 @@ internal fun unexpectedJson(key: String, expected: String): Nothing = // Use this function to avoid re-wrapping exception into NumberFormatException internal fun JsonPrimitive.parseLongImpl(): Long = StringJsonLexer(content).consumeNumericLiteralFully() + +/** + * Convenience methods to get typed elements from [JsonObject] + */ +public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject +public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray +public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive +public fun JsonObject.getJsonNull(key: String): JsonNull? = this[key]?.jsonNull +public fun JsonObject.getIntOrNull(key: String): Int? = this[key]?.jsonPrimitive?.intOrNull +public fun JsonObject.getLongOrNull(key: String): Long? = this[key]?.jsonPrimitive?.longOrNull +public fun JsonObject.getBooleanOrNull(key: String): Boolean? = this[key]?.jsonPrimitive?.booleanOrNull +public fun JsonObject.getDoubleOrNull(key: String): Double? = this[key]?.jsonPrimitive?.doubleOrNull +public fun JsonObject.getFloatOrNull(key: String): Float? = this[key]?.jsonPrimitive?.floatOrNull +public fun JsonObject.getStringOrNull(key: String): String? = this[key]?.jsonPrimitive?.contentOrNull +public fun JsonObject.getIntOrElse(key: String, default: Int): Int = getIntOrNull(key) ?: default +public fun JsonObject.getLongOrElse(key: String, default: Long): Long = getLongOrNull(key) ?: default +public fun JsonObject.getBooleanOrElse(key: String, default: Boolean): Boolean = getBooleanOrNull(key) ?: default +public fun JsonObject.getDoubleOrElse(key: String, default: Double): Double = getDoubleOrNull(key) ?: default +public fun JsonObject.getFloatOrElse(key: String, default: Float): Float = getFloatOrNull(key) ?: default +public fun JsonObject.getStringOrElse(key: String, default: String): String = getStringOrNull(key) ?: default From 962711c42fe4511436ea2585ab16da4c571cb931 Mon Sep 17 00:00:00 2001 From: lidiwei Date: Tue, 10 Jun 2025 15:27:19 +0800 Subject: [PATCH 2/9] Add JsonObject toBean syntax sugar --- .../commonMain/src/kotlinx/serialization/json/JsonElement.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index d9e523f61..65e6c7c57 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -351,6 +351,8 @@ internal fun JsonPrimitive.parseLongImpl(): Long = StringJsonLexer(content).cons /** * Convenience methods to get typed elements from [JsonObject] */ +public inline fun JsonObject.bean(): T = Json.decodeFromString(this.toString()) +public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.jsonObject?.bean() public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive From d8f8c567619a4eb604fb2453b4a821733228c3e0 Mon Sep 17 00:00:00 2001 From: lidiwei Date: Tue, 10 Jun 2025 16:22:39 +0800 Subject: [PATCH 3/9] Add JsonArray toBean syntax sugar --- .../src/kotlinx/serialization/json/JsonElement.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index 65e6c7c57..3aa03b7d6 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -250,6 +250,12 @@ public val JsonElement.jsonArray: JsonArray public val JsonElement.jsonNull: JsonNull get() = this as? JsonNull ?: error("JsonNull") +/** + * Returns true if current element is [JsonNull] + */ +public val JsonElement?.isJsonNull: Boolean + get() = (this as? JsonNull) != null + /** * Returns content of the current element as int * @throws NumberFormatException if current element is not a valid representation of number @@ -351,8 +357,8 @@ internal fun JsonPrimitive.parseLongImpl(): Long = StringJsonLexer(content).cons /** * Convenience methods to get typed elements from [JsonObject] */ -public inline fun JsonObject.bean(): T = Json.decodeFromString(this.toString()) -public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.jsonObject?.bean() +public inline fun JsonElement.bean(): T = Json.decodeFromString(this.toString()) +public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.bean() public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive From 53ecfc0f483443920232b78f358af58a4780a99b Mon Sep 17 00:00:00 2001 From: lidiwei Date: Thu, 12 Jun 2025 12:32:41 +0800 Subject: [PATCH 4/9] check isString should return false --- .../src/kotlinx/serialization/json/JsonElement.kt | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index 3aa03b7d6..b3f3883ed 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -357,18 +357,17 @@ internal fun JsonPrimitive.parseLongImpl(): Long = StringJsonLexer(content).cons /** * Convenience methods to get typed elements from [JsonObject] */ -public inline fun JsonElement.bean(): T = Json.decodeFromString(this.toString()) -public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.bean() +public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.jsonObject?.let { Json.decodeFromString(it.toString()) } public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive public fun JsonObject.getJsonNull(key: String): JsonNull? = this[key]?.jsonNull -public fun JsonObject.getIntOrNull(key: String): Int? = this[key]?.jsonPrimitive?.intOrNull -public fun JsonObject.getLongOrNull(key: String): Long? = this[key]?.jsonPrimitive?.longOrNull -public fun JsonObject.getBooleanOrNull(key: String): Boolean? = this[key]?.jsonPrimitive?.booleanOrNull -public fun JsonObject.getDoubleOrNull(key: String): Double? = this[key]?.jsonPrimitive?.doubleOrNull -public fun JsonObject.getFloatOrNull(key: String): Float? = this[key]?.jsonPrimitive?.floatOrNull -public fun JsonObject.getStringOrNull(key: String): String? = this[key]?.jsonPrimitive?.contentOrNull +public fun JsonObject.getIntOrNull(key: String): Int? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.intOrNull +public fun JsonObject.getLongOrNull(key: String): Long? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.longOrNull +public fun JsonObject.getBooleanOrNull(key: String): Boolean? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.booleanOrNull +public fun JsonObject.getDoubleOrNull(key: String): Double? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.doubleOrNull +public fun JsonObject.getFloatOrNull(key: String): Float? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.floatOrNull +public fun JsonObject.getStringOrNull(key: String): String? = this[key]?.jsonPrimitive?.takeIf { it.isString }?.contentOrNull public fun JsonObject.getIntOrElse(key: String, default: Int): Int = getIntOrNull(key) ?: default public fun JsonObject.getLongOrElse(key: String, default: Long): Long = getLongOrNull(key) ?: default public fun JsonObject.getBooleanOrElse(key: String, default: Boolean): Boolean = getBooleanOrNull(key) ?: default From 07b6a16edaf77f53a78b1d3709dd41b24b59d25d Mon Sep 17 00:00:00 2001 From: lidiwei Date: Thu, 12 Jun 2025 12:37:42 +0800 Subject: [PATCH 5/9] check isString should return false --- .../commonMain/src/kotlinx/serialization/json/JsonElement.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index b3f3883ed..3f73a191a 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -361,7 +361,7 @@ public inline fun JsonObject.getBeanOrNull(key: String): T? = this[k public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive -public fun JsonObject.getJsonNull(key: String): JsonNull? = this[key]?.jsonNull +public fun JsonObject.isJsonNull(key: String): Boolean = this[key] is JsonNull public fun JsonObject.getIntOrNull(key: String): Int? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.intOrNull public fun JsonObject.getLongOrNull(key: String): Long? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.longOrNull public fun JsonObject.getBooleanOrNull(key: String): Boolean? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.booleanOrNull From 32c42b3d34cd8dbfca186dd7172253d157d0ee4e Mon Sep 17 00:00:00 2001 From: lidiwei Date: Thu, 12 Jun 2025 12:45:32 +0800 Subject: [PATCH 6/9] getBeanOrNull check is JsonObject or JsonArray --- .../commonMain/src/kotlinx/serialization/json/JsonElement.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index 3f73a191a..928a5c7ab 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -357,7 +357,7 @@ internal fun JsonPrimitive.parseLongImpl(): Long = StringJsonLexer(content).cons /** * Convenience methods to get typed elements from [JsonObject] */ -public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.jsonObject?.let { Json.decodeFromString(it.toString()) } +public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.takeIf { it is JsonObject || it is JsonArray }?.let { Json.decodeFromString(it.toString()) } public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive From 4ab3f826670332a144e7d2fa50fef8280f218b71 Mon Sep 17 00:00:00 2001 From: lidiwei Date: Thu, 12 Jun 2025 12:49:40 +0800 Subject: [PATCH 7/9] optimal code --- .../src/kotlinx/serialization/json/JsonElement.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index 928a5c7ab..20ec0daf0 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -250,12 +250,6 @@ public val JsonElement.jsonArray: JsonArray public val JsonElement.jsonNull: JsonNull get() = this as? JsonNull ?: error("JsonNull") -/** - * Returns true if current element is [JsonNull] - */ -public val JsonElement?.isJsonNull: Boolean - get() = (this as? JsonNull) != null - /** * Returns content of the current element as int * @throws NumberFormatException if current element is not a valid representation of number From e3c7fe46a1f08759218dabe0d64c31376c74912d Mon Sep 17 00:00:00 2001 From: lidiwei Date: Tue, 17 Jun 2025 13:16:46 +0800 Subject: [PATCH 8/9] JsonObject API refinement --- .../kotlinx/serialization/json/JsonElement.kt | 104 ++++++++++++++---- 1 file changed, 85 insertions(+), 19 deletions(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt index 20ec0daf0..d15841079 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt @@ -229,6 +229,12 @@ public class JsonArray(private val content: List) : JsonElement(), public val JsonElement.jsonPrimitive: JsonPrimitive get() = this as? JsonPrimitive ?: error("JsonPrimitive") +/** + * Returns content of the current element as JsonPrimitive or `null` if the current element is not a valid representation of JsonPrimitive + */ +public val JsonElement.jsonPrimitiveOrNull: JsonPrimitive? + get() = this as? JsonPrimitive + /** * Convenience method to get current element as [JsonObject] * @throws IllegalArgumentException if current element is not a [JsonObject] @@ -236,6 +242,12 @@ public val JsonElement.jsonPrimitive: JsonPrimitive public val JsonElement.jsonObject: JsonObject get() = this as? JsonObject ?: error("JsonObject") +/** + * Returns content of the current element as JsonObject or `null` if the current element is not a valid representation of JsonObject + */ +public val JsonElement.jsonObjectOrNull: JsonObject? + get() = this as? JsonObject + /** * Convenience method to get current element as [JsonArray] * @throws IllegalArgumentException if current element is not a [JsonArray] @@ -243,6 +255,12 @@ public val JsonElement.jsonObject: JsonObject public val JsonElement.jsonArray: JsonArray get() = this as? JsonArray ?: error("JsonArray") +/** + * Returns content of the current element as JsonArray or `null` if the current element is not a valid representation of JsonArray + */ +public val JsonElement.JsonArrayOrNull: JsonArray? + get() = this as? JsonArray + /** * Convenience method to get current element as [JsonNull] * @throws IllegalArgumentException if current element is not a [JsonNull] @@ -250,6 +268,12 @@ public val JsonElement.jsonArray: JsonArray public val JsonElement.jsonNull: JsonNull get() = this as? JsonNull ?: error("JsonNull") +/** + * Returns content of the current element as JsonNull or `null` if the current element is not a valid representation of JsonNull + */ +public val JsonElement.jsonNullOrNull: JsonNull? + get() = this as? JsonNull + /** * Returns content of the current element as int * @throws NumberFormatException if current element is not a valid representation of number @@ -349,22 +373,64 @@ internal fun unexpectedJson(key: String, expected: String): Nothing = internal fun JsonPrimitive.parseLongImpl(): Long = StringJsonLexer(content).consumeNumericLiteralFully() /** - * Convenience methods to get typed elements from [JsonObject] - */ -public inline fun JsonObject.getBeanOrNull(key: String): T? = this[key]?.takeIf { it is JsonObject || it is JsonArray }?.let { Json.decodeFromString(it.toString()) } -public fun JsonObject.getJsonObject(key: String): JsonObject? = this[key]?.jsonObject -public fun JsonObject.getJsonArray(key: String): JsonArray? = this[key]?.jsonArray -public fun JsonObject.getJsonPrimitive(key: String): JsonPrimitive? = this[key]?.jsonPrimitive -public fun JsonObject.isJsonNull(key: String): Boolean = this[key] is JsonNull -public fun JsonObject.getIntOrNull(key: String): Int? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.intOrNull -public fun JsonObject.getLongOrNull(key: String): Long? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.longOrNull -public fun JsonObject.getBooleanOrNull(key: String): Boolean? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.booleanOrNull -public fun JsonObject.getDoubleOrNull(key: String): Double? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.doubleOrNull -public fun JsonObject.getFloatOrNull(key: String): Float? = this[key]?.jsonPrimitive?.takeIf { !it.isString }?.floatOrNull -public fun JsonObject.getStringOrNull(key: String): String? = this[key]?.jsonPrimitive?.takeIf { it.isString }?.contentOrNull -public fun JsonObject.getIntOrElse(key: String, default: Int): Int = getIntOrNull(key) ?: default -public fun JsonObject.getLongOrElse(key: String, default: Long): Long = getLongOrNull(key) ?: default -public fun JsonObject.getBooleanOrElse(key: String, default: Boolean): Boolean = getBooleanOrNull(key) ?: default -public fun JsonObject.getDoubleOrElse(key: String, default: Double): Double = getDoubleOrNull(key) ?: default -public fun JsonObject.getFloatOrElse(key: String, default: Float): Float = getFloatOrNull(key) ?: default -public fun JsonObject.getStringOrElse(key: String, default: String): String = getStringOrNull(key) ?: default + * Returns the JsonObject value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a JsonObject. + */ +public fun JsonObject.getJsonObjectOrNull(key: String): JsonObject? = + this[key]?.jsonObjectOrNull + +/** + * Returns the JsonArray value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a JsonArray. + */ +public fun JsonObject.getJsonArrayOrNull(key: String): JsonArray? = + this[key]?.JsonArrayOrNull + +/** + * Returns the JsonPrimitive value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a JsonPrimitive. + */ +public fun JsonObject.getJsonPrimitiveOrNull(key: String): JsonPrimitive? = + this[key]?.jsonPrimitiveOrNull + +/** + * Returns the int value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a int. + */ +public fun JsonObject.getIntOrNull(key: String): Int? = + this[key]?.jsonPrimitive?.takeIf { !it.isString }?.intOrNull + +/** + * Returns the long value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a long. + */ +public fun JsonObject.getLongOrNull(key: String): Long? = + this[key]?.jsonPrimitive?.takeIf { !it.isString }?.longOrNull + +/** + * Returns the boolean value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a boolean. + */ +public fun JsonObject.getBooleanOrNull(key: String): Boolean? = + this[key]?.jsonPrimitive?.takeIf { !it.isString }?.booleanOrNull + +/** + * Returns the double value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a double. + */ +public fun JsonObject.getDoubleOrNull(key: String): Double? = + this[key]?.jsonPrimitive?.takeIf { !it.isString }?.doubleOrNull + +/** + * Returns the float value associated with the given [key] in this [JsonObject], + * or null if the key is missing, the value is a string, or cannot be parsed as a float. + */ +public fun JsonObject.getFloatOrNull(key: String): Float? = + this[key]?.jsonPrimitive?.takeIf { !it.isString }?.floatOrNull + +/** + * Returns the string content associated with the given [key] in this [JsonObject], + * or null if the key is missing or the value is not a JSON string. + */ +public fun JsonObject.getStringOrNull(key: String): String? = + this[key]?.jsonPrimitive?.takeIf { it.isString }?.contentOrNull From 771be1ac15220bcf2998b50920e13c56f3044805 Mon Sep 17 00:00:00 2001 From: lidiwei Date: Tue, 17 Jun 2025 13:21:21 +0800 Subject: [PATCH 9/9] JsonObject API refinement 2 --- formats/json/commonMain/src/kotlinx/serialization/json/Json.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt index 9f6931e6d..ed63e2ca7 100644 --- a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt +++ b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt @@ -179,9 +179,6 @@ public sealed class Json( return decodeFromString(JsonElementSerializer, string) } - public fun parseToJsonObject(@FormatLanguage("json", "", "") string: String): JsonObject = parseToJsonElement(string).jsonObject - public fun parseToJsonArray(@FormatLanguage("json", "", "") string: String): JsonArray = parseToJsonElement(string).jsonArray - /** * Following functions are copied from extensions on StringFormat * to streamline experience for newcomers, since IDE does not star-import kotlinx.serialization.* automatically