我正在尝试创建一个DSL来创建JSONObjects。下面是一个builder类和一个示例用法:
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 "xxx@yyy.com"
}
}
println(jsonObject)
}
以上代码的输出为:
{"contact":{"city":"istanbul","email":"xxx@yyy.com"},"name":"ilkin","age":37,"male":true}
它按预期工作。但是它在每次创建json对象时都会创建一个额外的JsonObjectBuilder示例。有没有可能编写一个DSL来创建json对象而不产生额外的垃圾?
6条答案
按热度按时间50pmv0ei1#
您可以使用Deque作为堆栈,通过单个
JsonObjectBuilder
跟踪当前的JSONObject
上下文:输出示例:
在单个
JsonObjectBuilder
上跨多个线程调用json
和build
可能会有问题,但这对您的用例来说应该不是问题。z9gpfhce2#
你需要DSL吗?你失去了强制
String
密钥的能力,但是香草Kotlin并没有那么糟糕:)pkln4tw63#
2023年1月11日更新:
将
infix fun String.to(json: Json -> Unit)
替换为infix fun String.to(json: Json.() -> Unit)
,infix fun String.to(json: Json.() -> Unit)
使用Json块作为接收器,并在创建Json对象后调用。因此,无需在Json对象中添加Json键。我不确定我是否理解错了这个问题。你不想要一个建筑工人?
你会得到
mcvgt66p4#
是的,如果你不需要任何节点的中间表示,并且上下文总是相同的(递归调用彼此没有区别),这是可能的。这可以通过立即写输出来完成。
但是,这严重增加了代码的复杂性,因为您必须立即处理DSL调用,而不将它们存储在任何地方(同样,为了避免冗余对象)。
示例(请参见此处的演示):
不过,如果您不需要缩进,而只需要有效的JSON,这可以很容易地简化。
你可以让
json { }
和.toJson { }
函数inline
甚至摆脱lambda类,这样你就几乎实现了零对象开销(一个JsonContext
和StringBuilder
及其缓冲区仍然被分配),但是这需要你改变这些函数使用的成员的可见性修饰符:公共内联函数只能访问public
或@PublishedApi internal
成员。tpxzln5u5#
找到了另一个解决方案。你可以直接继承
JSONObject
类而不需要创建其他对象。代码的输出将是相同的。
UPD:如果你使用gson库,你可以看看这个awesome library。它不会产生任何垃圾,源代码很容易阅读和理解。
wpx232ag6#
您可以使用https://github.com/holgerbrandl/jsonbuilder之类的库来构建json
免责声明:我是这个库的作者。