我正在尝试创建一个用于创建JSONObjects的DSL。这是一个构建器类和一个示例用法:
import org.json.JSONObject
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
val builder = JsonObjectBuilder()
builder.build()
return builder.json
}
class JsonObjectBuilder {
val json = JSONObject()
infix fun <T> String.To(value: T) {
json.put(this, value)
}
}
fun main(args: Array<String>) {
val jsonObject =
json {
"name" To "ilkin"
"age" To 37
"male" To true
"contact" To json {
"city" To "istanbul"
"email" To "[email protected]"
}
}
println(jsonObject)
}
上面代码的输出是:
{"contact":{"city":"istanbul","email":"[email protected]"},"name":"ilkin","age":37,"male":true}
它按预期工作。但是每次创建json对象时它都会创建一个额外的JsonObjectBuilder实例。是否可以编写DSL来创建json对象而无需额外的垃圾?
您可以使用Deque作为堆栈,使用单个JSONObject
跟踪您当前的JsonObjectBuilder
上下文:
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
return JsonObjectBuilder().json(build)
}
class JsonObjectBuilder {
private val deque: Deque<JSONObject> = ArrayDeque()
fun json(build: JsonObjectBuilder.() -> Unit): JSONObject {
deque.push(JSONObject())
this.build()
return deque.pop()
}
infix fun <T> String.To(value: T) {
deque.peek().put(this, value)
}
}
fun main(args: Array<String>) {
val jsonObject =
json {
"name" To "ilkin"
"age" To 37
"male" To true
"contact" To json {
"city" To "istanbul"
"email" To "[email protected]"
}
}
println(jsonObject)
}
示例输出:
{"contact":{"city":"istanbul","email":"[email protected]"},"name":"ilkin","age":37,"male":true}
在单个json
上跨多个线程调用build
和JsonObjectBuilder
会有问题,但这不应该是您的用例的问题。
你需要DSL吗?你失去了强制执行String
密钥的能力,但香草Kotlin并不是那么糟糕:)
JSONObject(mapOf(
"name" to "ilkin",
"age" to 37,
"male" to true,
"contact" to mapOf(
"city" to "istanbul",
"email" to "[email protected]"
)
))
我不确定我是否正确地得到了问题。你不想要一个建设者?
import org.json.JSONArray
import org.json.JSONObject
class Json() {
private val json = JSONObject()
constructor(init: Json.() -> Unit) : this() {
this.init()
}
infix fun String.to(value: Json) {
json.put(this, value.json)
}
infix fun <T> String.to(value: T) {
json.put(this, value)
}
override fun toString(): String {
return json.toString()
}
}
fun main(args: Array<String>) {
val json = Json {
"name" to "Roy"
"body" to Json {
"height" to 173
"weight" to 80
}
"cars" to JSONArray().apply {
put("Tesla")
put("Porsche")
put("BMW")
put("Ferrari")
}
}
println(json)
}
你会得到
{
"name": "Roy",
"body": {
"weight": 80,
"height": 173
},
"cars": [
"Tesla",
"Porsche",
"BMW",
"Ferrari"
]
}
是的,如果您不需要节点的任何中间表示,并且上下文始终相同(递归调用彼此没有区别),则可能。这可以通过立即写入输出来完成。
但是,这会严重增加代码复杂性,因为您必须立即处理DSL调用而不将其存储在任何位置(同样,为了避免冗余对象)。
示例(请参阅其演示here):
class JsonContext internal constructor() {
internal val output = StringBuilder()
private var indentation = 4
private fun StringBuilder.indent() = apply {
for (i in 1..indentation)
append(' ')
}
private var needsSeparator = false
private fun StringBuilder.separator() = apply {
if (needsSeparator) append(",\n")
}
infix fun String.to(value: Any) {
output.separator().indent().append("\"$this\": \"$value\"")
needsSeparator = true
}
infix fun String.toJson(block: JsonContext.() -> Unit) {
output.separator().indent().append("\"$this\": {\n")
indentation += 4
needsSeparator = false
block(this@JsonContext)
needsSeparator = true
indentation -= 4
output.append("\n").indent().append("}")
}
}
fun json(block: JsonContext.() -> Unit) = JsonContext().run {
block()
"{\n" + output.toString() + "\n}"
}
val j = json {
"a" to 1
"b" to "abc"
"c" toJson {
"d" to 123
"e" toJson {
"f" to "g"
}
}
}
如果您不需要缩进但只需要有效的JSON,这可以很容易地简化。
您可以使json { }
和.toJson { }
函数inline
摆脱lambda类,因此您实现几乎零对象开销(一个JsonContext
和StringBuilder
及其缓冲区仍然分配),但这将要求您更改的可见性修饰符这些函数使用的成员:公共内联函数只能访问public
或@PublishedApi internal
成员。
找到另一个解决方您可以继承JSONObject
类而无需创建其他对象。
class Json() : JSONObject() {
constructor(init: Json.() -> Unit) : this() {
this.init()
}
infix fun <T> String.To(value: T) {
put(this, value)
}
}
fun main(args: Array<String>) {
val jsonObject =
Json {
"name" To "ilkin"
"age" To 37
"male" To true
"contact" To Json {
"city" To "istanbul"
"email" To "[email protected]"
}
}
println(jsonObject)
}
代码的输出将是相同的。
{"contact":{"city":"istanbul","email":"[email protected]"},"name":"ilkin","age":37,"male":true}
UPD:如果你使用gson库,你可以看看这个awesome library。它不会产生任何垃圾,源代码易于阅读和理解。