Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用于创建json对象的Kotlin DSL(不创建垃圾)_Kotlin - Fatal编程技术网

用于创建json对象的Kotlin DSL(不创建垃圾)

用于创建json对象的Kotlin DSL(不创建垃圾),kotlin,Kotlin,我正在尝试创建一个用于创建JSONObject的DSL。以下是生成器类和示例用法: import org.json.JSONObject fun json(build: JsonObjectBuilder.() -> Unit): JSONObject { val builder = JsonObjectBuilder() builder.build() return builder.json } class JsonObjectBuilder { va

我正在尝试创建一个用于创建JSONObject的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)
}
它按预期工作。但每次创建json对象时,它都会创建一个额外的JsonObjectBuilder实例。是否可以编写一个DSL来创建json对象而无需额外的垃圾?

您可以使用一个堆栈,用一个
JsonObjectBuilder
跟踪当前的
JSONObject
上下文:

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)
}

在一个
JsonObjectBuilder
上跨多个线程调用
json
build
可能会有问题,但这对于您的用例来说不应该是问题。

您需要DSL吗?您将失去强制执行
String
键的能力,但vanilla Kotlin并没有那么糟糕:)


是的,如果您不需要节点的任何中间表示,并且上下文总是相同的(递归调用彼此没有区别),那么这是可能的。这可以通过立即写入输出来完成

然而,这严重增加了代码的复杂性,因为您必须立即处理DSL调用,而不将它们存储在任何地方(同样,为了避免冗余对象)

示例(参见其演示):

如果您不需要缩进,只需要有效的JSON,那么这可以很容易地简化


您可以使用
json{}
.toJson{}
函数
内联
,甚至消除lambda类,从而实现几乎零的对象开销(一个
JsonContext
StringBuilder
及其缓冲区仍然被分配),但这需要您更改这些函数使用的成员的可见性修饰符:公共内联函数只能访问
public
@PublishedApi internal
成员。

我不确定我是否正确回答了这个问题。你不想要一个建筑工人

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)

}

找到了另一个解决方案。您只需继承
JSONObject
类,而无需创建其他对象。


UPD:如果您使用gson库,您可以查看以下内容。它不会产生任何垃圾,源代码易于阅读和理解。

您可以使用类似的库来构建json

val myJson = json {
        "size" to 0
        "array" to arrayOf(1,2,3)
        "aggs" to {
            "num_destinations" to {
                "cardinality" to {
                    "field" to "DestCountry"
                }
            }
        }
    }

免责声明:我是该库的作者。

Kotlin必须创建一个函数对象以传递到
json{…}
,因此“不创建额外对象”问题从一开始就是有缺陷的。JVM在优化短期对象方面非常有效。除非您对代码进行了基准测试,并且100%确定创建
JSONObjectBuilder
实例会限制您的性能,否则我根本不会担心这一点。(个人提示:我会让你的构建器成为一个接口,并将实际实现隐藏在一个私有类中,这样你就不会暴露json字段。)是的,
json{…}
应该是
inline
你是否为这些位创建了一个人工制品?请参阅github.com/holgerbrandl/jsonbuilder,获取一个小型DSL,以使用kotlinI创建json同意,这个版本看起来已经像DSL了。我非常喜欢这个在Java中创建JSON的解决方案,因为它使用Kotlin已经很干净的DSL来创建类似JSON的数据,但它的开销与问题中的示例相同。每个
mapOf
创建一个新的
Map
,然后为每个
JSONObject
(包括顶级对象中嵌套的
JSONObject
)复制并丢弃该映射。我喜欢堆栈,但这不正是一个问题(即每次调用
json()时都会创建一个JSONObjectBuilder和JSONObject)
)?不过,我认为您的思路是正确的-您只需要记住上次调用
JSONObject.put()
的结果,就可以对嵌套字段调用
put()
,而不是创建新的
JSONObject
。我希望这是有道理的!:)上面的示例只创建了一个JSONObject Builder和两个JSONObject,这比我提出的解决方案要好得多。唯一的问题是最终的json字符串的顺序是相反的。@HoundDog,在
org.json.JSONObject
API中,您必须创建一个新的
JSONObject
,以便将其嵌套在另一个对象中,因此我不相信这里有任何其他对象。我希望这有意义@ilkinulas我打印了我的解决方案,结果与您在问题中的示例输出相同。我不相信
JSONObject
会维护键的顺序,尽管从一次运行到另一次运行,您可能会得到不同的键顺序。JSON键没有排序(想想
HashMap
LinkedHashMap
),太棒了!这是一个有趣的解决方案,但是类型在最后的json字符串中丢失了。每个值都转换为字符串。@ilkinulas您可以用
${JSONObject.valueToString(value)}
替换
\“$value\”
,以保留类型。这非常优雅。您的答案看起来不错,但是关于JSONArray呢?我是kotlin的初学者,所以,你能给我解释一下如何处理JSONArray吗kotlin@KishanDonga我不确定你想要实现什么。我更新了我的答案,您也可以分配JSONArray。
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"
        }
    }
}
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"
  ]
}
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}
val myJson = json {
        "size" to 0
        "array" to arrayOf(1,2,3)
        "aggs" to {
            "num_destinations" to {
                "cardinality" to {
                    "field" to "DestCountry"
                }
            }
        }
    }