Skip to content
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

How about this JsonPrimitive Builder #2893

Open
SMFDrummer opened this issue Jan 5, 2025 · 2 comments
Open

How about this JsonPrimitive Builder #2893

SMFDrummer opened this issue Jan 5, 2025 · 2 comments

Comments

@SMFDrummer
Copy link

When I was coding with buildJsonObject and buildJsonArray Dsl s, I'm very exciting. But JsonPrimitive have no dsl, when I want to get a JsonPrimitive I must use JsonPrimitive() constructor. For a single value, such as 1 in Int, true in Boolean etc. JsonPrimitive is much more longer than the value, this is too fragmented, like little mouse with a big fat tail. So I make this dsl, this may look better. Could you check my code if it meets the basic specifications and suggest and improve my code? Thanks for you.

import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonPrimitive

class JsonPrimitiveBuilder @PublishedApi internal constructor() {
    var value: JsonPrimitive = JsonNull

    operator fun <T> T.unaryPlus() where T : Any? {
        value = when (this) {
            null -> JsonNull
            is Int -> JsonPrimitive(this)
            is Long -> JsonPrimitive(this)
            is Double -> JsonPrimitive(this)
            is Float -> JsonPrimitive(this.toDouble())
            is String -> JsonPrimitive(this)
            is Boolean -> JsonPrimitive(this)
            is java.math.BigInteger -> JsonPrimitive(this.toString())
            is java.math.BigDecimal -> JsonPrimitive(this.toString())
            else -> throw IllegalArgumentException("Unsupported value: $this")
        }
    }
}

inline fun primitive(block: JsonPrimitiveBuilder.() -> Any?): JsonPrimitive {
    val builder = JsonPrimitiveBuilder()
    builder.apply { +block() }
    return builder.value
}

Then use like this:

fun main() {
    println(primitive { 123 })              // Int
    println(primitive { 123L })             // Long
    println(primitive { 123.45 })           // Double
    println(primitive { 123.45f })          // Float
    println(primitive { "Hello, DSL!" })    // String
    println(primitive { true })             // Boolean
    println(primitive { null })             // null
    println(primitive { java.math.BigInteger("12345678901234567890") }) // BigInteger
    println(primitive { java.math.BigDecimal("1234567890.1234567890") }) // BigDecimal
}

Looking forward to your reply!

@sandwwraith
Copy link
Member

Hi, thanks for the suggestion. However, I'm missing the point here. You've said that JsonPrimitive(123) is too long, but primitive { 123 } is not much shorter. Are there any other supposed usages?

@SMFDrummer
Copy link
Author

Thank you for your reply. This question is actually quite a headache for me
Because for constructing JSON, we can use the first to second function method or directly use the constructor dsl to automatically infer the type and convert it to JsonPrimitive, if this is serialization. But when JSON contains a value that needs to be correctly serialized by both integers and strings, a problem arises.
For example, I have a data class like this:

@Serializable
data class User(
    @Required val userId: JsonPrimitive,
    val userNick: String? = null,
    val token: String? = null,
    val phone: String? = null,
    val password: String? = null,
)

The reason for setting userId to JsonPrimitive is because (this is a game account): userId in the game has three types: Int, Long, and String. When parsing from my JSON file, I also need to deserialize them completely.
However, for some times, such as code testing, I need to actively assign values and construct User instances. At this time, I will write a large number of JsonPrimitive() constructors (of course, sometimes constructors are also written in some buildJsonObject and buildJsonArray dsls). In order to make it start with a lowercase letter and compensate for the lack of dsl in JsonPrimitive, I wrote a dsl. But as you said, it doesn't look shorter, and I haven't come up with a better solution yet.
If I had to force myself to think, I could only come up with this one:

object JsonDsl {
    operator fun Int.unaryPlus() = JsonPrimitive(this)
    operator fun Long.unaryPlus() = JsonPrimitive(this)
    operator fun String.unaryPlus() = JsonPrimitive(this)
}

// use this
import JsonDsl.*

val intPrimitive = +123
val longPrimitive = +1234567890123L
val stringPrimitive = +"example"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants