-
Notifications
You must be signed in to change notification settings - Fork 624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add concerte type for JsonPrimitives #1298
Comments
It's not that easy. See, in |
It would be great if you could put that into the Docs, while its annoying to have to deal with it myself its a reasonable argument as to why something i expected to be serializable (a class from std lib) is not. |
I asked a similar question in the Kotlin Slack. @sandwwraith: While your explanation makes complete sense, I still wish there was an easier way that didn't involve guesswork (e.g., value classes for I'll include my 'good enough' attempt at this, for posterity. Be warned that this is far from perfect, and doesn't handle all cases ( private val INT_REGEX = Regex("""^-?\d+$""")
private val DOUBLE_REGEX = Regex("""^-?\d+\.\d+(?:E-?\d+)?$""")
val JsonPrimitive.valueOrNull: Any?
get() = when {
this is JsonNull -> null
this.isString -> this.content
else -> this.content.toBooleanStrictOrNull()
?: when {
INT_REGEX.matches(this.content) -> this.int
DOUBLE_REGEX.matches(this.content) -> this.double
else -> throw IllegalArgumentException("Unknown type for JSON value: ${this.content}")
}
} Basic unit tests (click to expand)Written using Kotest runner & property testing. class JsonTest : DescribeSpec({
context("JsonPrimitive") {
it("returns null") {
val value = JsonPrimitive(null as String?).valueOrNull
value.shouldBeNull()
}
it("returns String") {
checkAll<String> { str ->
val value = JsonPrimitive(str).valueOrNull
value.shouldNotBeNull()
value.shouldBeTypeOf<String>()
value shouldBe str
}
}
it("returns Boolean") {
checkAll<Boolean> { bool ->
val value = JsonPrimitive(bool).valueOrNull
value.shouldNotBeNull()
value.shouldBeTypeOf<Boolean>()
value shouldBe bool
}
}
it("returns Int") {
checkAll<Int> { int ->
val value = JsonPrimitive(int).valueOrNull
value.shouldNotBeNull()
value.shouldBeTypeOf<Int>()
value shouldBe int
}
}
it("returns Double") {
checkAll<Double> { double ->
if (double.isNaN() || double.isInfinite()) return@checkAll
val value = JsonPrimitive(double).valueOrNull
value.shouldNotBeNull()
value.shouldBeTypeOf<Double>()
value shouldBe double
}
}
}) |
Kotlin has sealed class JsonData {}
object JsonNull: JsonData()
data class JsonNumber(val value: Number): JsonData()
data class JsonBoolean(val value: Boolean): JsonData()
data class JsonString(val value: String): JsonData()
data class JsonArray(val value: List<JsonData>): JsonData()
data class JsonObject(val value: Map<String, JsonData>): JsonData() The way this is currently implemented is a bit strange: On the one hand the constructors of Also note that this strategy has unnecessary overheads in terms of memory and runtime. Storing for instance a raw boolean as kotlinx.serialization/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt Lines 180 to 229 in 4c30fcf
|
You're completely right. Lines 74 to 82 in 1b2344f
|
However, it is still possible to handle booleans there separately, so users can easily differentiate whether it is String, Boolean, Null, or Number. While we still do not aim to provide a number-parsing strategy, differentiating of booleans may help in some use-cases |
That was exactly what I was thinking now as well. Basically, the technique of postponing the value extraction only makes sense for |
Just ran into this problem. I am parsing user input in the form of This is a solved problem in Gson and Jackson but I was hoping to use a Kotlin 1st solution for sealed classes etc… Guess not. |
The server does return vulnerabilities which do not have a severity value in the dedicated property. The unspecified `databaseSpecific` property often times holds a primitive `severity` property with values such as `[HIGH, MEDIUM, LOW]`. Make use of these values as a fallback as these to provide more indication than a `null` value. Note: The data model of 'osv/client' currently uses subtypes of JsonElement to expose a couple of unspecified JSON objects as properties. Accessing these requires the client code to add 'kotlinx.serialization' as dependency which is not nice. A solution to that would be to use "raw" string values containing the JSON, which is unfortunately not yet possible but may become so in the future, see [1][2][3]. So, for now add 'kotlinx.serialization' as dependency to the advisor in order to access the property and leave a FIXME comment as reminder. [1] Kotlin/kotlinx.serialization#1298 [2] Kotlin/kotlinx.serialization#1405 [3] Kotlin/kotlinx.serialization#1058 Signed-off-by: Frank Viernau <[email protected]>
The server does return vulnerabilities which do not have a severity value in the dedicated property. The unspecified `databaseSpecific` property often times holds a primitive `severity` property with values such as `[HIGH, MEDIUM, LOW]`. Make use of these values as a fallback as these to provide more indication than a `null` value. Note: The data model of 'osv/client' currently uses subtypes of JsonElement to expose a couple of unspecified JSON objects as properties. Accessing these requires the client code to add 'kotlinx.serialization' as dependency which is not nice. A solution to that would be to use "raw" string values containing the JSON, which is unfortunately not yet possible but may become so in the future, see [1][2][3]. So, for now add 'kotlinx.serialization' as dependency to the advisor in order to access the property and leave a FIXME comment as reminder. [1] Kotlin/kotlinx.serialization#1298 [2] Kotlin/kotlinx.serialization#1405 [3] Kotlin/kotlinx.serialization#1058 Signed-off-by: Frank Viernau <[email protected]>
The server does return vulnerabilities which do not have a severity value in the dedicated property. The unspecified `databaseSpecific` property often times holds a primitive `severity` property with values such as `[HIGH, MEDIUM, LOW]`. Make use of these values as a fallback as these to provide more indication than a `null` value. Note: The data model of 'osv/client' currently uses subtypes of JsonElement to expose a couple of unspecified JSON objects as properties. Accessing these requires the client code to add 'kotlinx.serialization' as dependency which is not nice. A solution to that would be to use "raw" string values containing the JSON, which is unfortunately not yet possible but may become so in the future, see [1][2][3]. So, for now add 'kotlinx.serialization' as dependency to the advisor in order to access the property and leave a FIXME comment as reminder. [1] Kotlin/kotlinx.serialization#1298 [2] Kotlin/kotlinx.serialization#1405 [3] Kotlin/kotlinx.serialization#1058 Signed-off-by: Frank Viernau <[email protected]>
The server does return vulnerabilities which do not have a severity value in the dedicated property. The unspecified `databaseSpecific` property often times holds a primitive `severity` property with values such as `[HIGH, MEDIUM, LOW]`. Make use of these values as a fallback as these to provide more indication than a `null` value. Note: The data model of 'osv/client' currently uses subtypes of JsonElement to expose a couple of unspecified JSON objects as properties. Accessing these requires the client code to add 'kotlinx.serialization' as dependency which is not nice. A solution to that would be to use "raw" string values containing the JSON, which is unfortunately not yet possible but may become so in the future, see [1][2][3]. So, for now add 'kotlinx.serialization' as dependency to the advisor in order to access the property and leave a FIXME comment as reminder. [1] Kotlin/kotlinx.serialization#1298 [2] Kotlin/kotlinx.serialization#1405 [3] Kotlin/kotlinx.serialization#1058 Signed-off-by: Frank Viernau <[email protected]>
The server does return vulnerabilities which do not have a severity value in the dedicated property. The unspecified `databaseSpecific` property often times holds a primitive `severity` property with values such as `[HIGH, MEDIUM, LOW]`. Make use of these values as a fallback as these to provide more indication than a `null` value. Note: The data model of 'osv/client' currently uses subtypes of JsonElement to expose a couple of unspecified JSON objects as properties. Accessing these requires the client code to add 'kotlinx.serialization' as dependency which is not nice. A solution to that would be to use "raw" string values containing the JSON, which is unfortunately not yet possible but may become so in the future, see [1][2][3]. So, for now add 'kotlinx.serialization' as dependency to the advisor in order to access the property and leave a FIXME comment as reminder. [1] Kotlin/kotlinx.serialization#1298 [2] Kotlin/kotlinx.serialization#1405 [3] Kotlin/kotlinx.serialization#1058 Signed-off-by: Frank Viernau <[email protected]>
The server does return vulnerabilities which do not have a severity value in the dedicated property. The unspecified `databaseSpecific` property often times holds a primitive `severity` property with values such as `[HIGH, MEDIUM, LOW]`. Make use of these values as a fallback as these to provide more indication than a `null` value. Note: The data model of 'osv/client' currently uses subtypes of JsonElement to expose a couple of unspecified JSON objects as properties. Accessing these requires the client code to add 'kotlinx.serialization' as dependency which is not nice. A solution to that would be to use "raw" string values containing the JSON, which is unfortunately not yet possible but may become so in the future, see [1][2][3]. So, for now add 'kotlinx.serialization' as dependency to the advisor in order to access the property and leave a FIXME comment as reminder. [1] Kotlin/kotlinx.serialization#1298 [2] Kotlin/kotlinx.serialization#1405 [3] Kotlin/kotlinx.serialization#1058 Signed-off-by: Frank Viernau <[email protected]>
To add another use case, I'm converting between JsonObject and a map of AWS SDK AttributeValue. That also only has a number type ( Notice the hacky fun JsonObject.toAWSStructure(): Map<String, AttributeValue> =
entries.associate { (k, v) -> k to v.toAWSStructure() }
fun JsonElement.toAWSStructure(): AttributeValue = when (this) {
is JsonArray -> AttributeValue.fromL(this.jsonArray.map { it.toAWSStructure() })
is JsonObject -> AttributeValue.fromM(this.toAWSStructure())
is JsonNull -> AttributeValue.fromNul(true)
is JsonPrimitive -> {
val primitive = this.jsonPrimitive
when {
primitive.isString -> AttributeValue.fromS(this.content)
primitive.booleanOrNull != null -> AttributeValue.fromBool(primitive.boolean)
else -> AttributeValue.fromN(primitive.content)
}
}
} Completing the type system would make it more concise and safe: fun JsonElement.toAWSStructure(): AttributeValue = when (this) {
is JsonObject -> AttributeValue.fromM(this.toAWSStructure())
is JsonArray -> AttributeValue.fromL(this.jsonArray.map { it.toAWSStructure() })
is JsonNull -> AttributeValue.fromNul(true)
is JsonString -> AttributeValue.fromS(this.content)
is JsonBool -> AttributeValue.fromBool(primitive.boolean)
is JsonNumber -> AttributeValue.fromBool(primitive.content)
} |
Found myself in the same boat, here is an alternate way to go around it when (val value = this[key]) {
is JsonArray -> { // handle arrays }
is JsonObject -> { //handle objects }
is JsonNull -> null
is JsonPrimitive -> {
if( value.isString ) {
value.content
} else {
value.booleanOrNull ?:
value.intOrNull ?:
value.longOrNull ?:
value.doubleOrNull ?:
value.content
}
}
else -> value.toString()
}
|
Also related: #2375 |
What is your use-case and why do you need this feature?
I found its very complex when I try to parse the json string when the type is unknown, for example, I cannot decode json to
Map<String, Any?>
exceptMap<String, JsonElement>
, because I shouldn't exposeJsonElement
to caller, so I try to parse them manually and I cannot get real type ofJsonPrimitive
, the Api only provide some converter method andisString
to determine whether it is aString
, even if I can detect and parse the string to real type just like Int, Float, Boolean ... it's not very convenient.Describe the solution you'd like
add a way to determine the real type of
JsonPrimitive
or just parse them as Any like other json parse library.The text was updated successfully, but these errors were encountered: