我正在尝试创建一个用于创建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 "xxx@yyy.com"
}
}
println(jsonObject)
}
上述代码的输出是:
{"contact":{"city":"istanbul","email":"xxx@yyy.com"},"name":"ilkin","age":37,"male":true}
它按预期工作。但它每次创建一个json对象时都会创建一个额外的JsonObjectBuilder实例。是否可以编写DSL来创建json对象而无需额外的垃圾?
答案 0 :(得分:16)
您可以使用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 "xxx@yyy.com"
}
}
println(jsonObject)
}
示例输出:
{"contact":{"city":"istanbul","email":"xxx@yyy.com"},"name":"ilkin","age":37,"male":true}
在单个json
上的多个主题上调用build
和JsonObjectBuilder
会有问题,但这不应该是您的用例的问题。
答案 1 :(得分:10)
你需要DSL吗?你失去了执行String
密钥的能力,但香草Kotlin并没有那么糟糕:)
JSONObject(mapOf(
"name" to "ilkin",
"age" to 37,
"male" to true,
"contact" to mapOf(
"city" to "istanbul",
"email" to "xxx@yyy.com"
)
))
答案 2 :(得分:4)
我不确定我是否正确地提出了问题。你不想要一个建设者?
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"
]
}
答案 3 :(得分:1)
是的,如果您不需要节点的任何中间表示,并且上下文始终相同(递归调用彼此没有区别),则可能。这可以通过立即写入输出来完成。
但是,这会严重增加代码复杂性,因为您必须立即处理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
和{ {1}}仍然分配了缓冲区,但这需要您更改这些函数使用的成员的可见性修饰符:公共内联函数只能访问StringBuilder
或public
成员。
答案 4 :(得分:1)
找到另一个解决方案。您可以继承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 "xxx@yyy.com"
}
}
println(jsonObject)
}
代码的输出将是相同的。
{"contact":{"city":"istanbul","email":"xxx@yyy.com"},"name":"ilkin","age":37,"male":true}
UPD :如果您使用gson库,可以查看此awesome library。它不会产生任何垃圾,源代码易于阅读和理解。
答案 5 :(得分:0)
您可以使用诸如https://github.com/holgerbrandl/jsonbuilder之类的库来构建json
val myJson = json {
"size" to 0
"array" to arrayOf(1,2,3)
"aggs" to {
"num_destinations" to {
"cardinality" to {
"field" to "DestCountry"
}
}
}
}
免责声明:我是图书馆的作者。