kotlin - 基于 GSON 的 DSL 导致 NPE
问题描述
我一直在尝试创建一个 Kotlin DSL,以使用类似 JSON 的语法创建 GSON JsonObjects。我的构建器看起来像这样
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
class JsonBuilder(builder: JsonBuilder.() -> Unit) {
init {
builder()
}
val result = JsonObject()
infix fun String.to(property: Number) = result.addProperty(this, property)
infix fun String.to(property: Char) = result.addProperty(this, property)
infix fun String.to(property: Boolean) = result.addProperty(this, property)
infix fun String.to(property: String) = result.addProperty(this, property)
infix fun String.to(property: JsonElement) = result.add(this, property)
infix fun String.to(properties: Collection<JsonElement>) {
val arr = JsonArray()
properties.forEach(arr::add)
result.add(this, arr)
}
operator fun String.invoke(builder: JsonObject.() -> Unit) {
val obj = JsonObject()
obj.builder()
result.add(this, obj)
}
}
fun json(builder: JsonBuilder.() -> Unit) = JsonBuilder(builder).result
我的测试看起来像这样
fun main() {
val json = json {
"name" to "value"
"obj" {
"int" to 1
}
"true" to true
}
println(json)
}
但是,在执行时,它会导致 NullPointerException 指向使用的第一个 String 扩展函数,我觉得它的描述性不是很强,因为我没有看到任何可以为空的东西。此外,我看不出它与当然不会导致 NPE 的常规执行有何不同。
val json = JsonObject()
json.addProperty("name", "value")
val obj = JsonObject()
obj.addProperty("int", 1)
json.add("obj", obj)
json.addProperty("true", true)
我的问题是究竟是什么导致了异常(以及如何防止它)。
解决方案
问题是您在结果对象之前指定了初始化程序块,导致在您使用它时它为空 - 这可以通过以下(代码的反编译输出)可视化。
public JsonBuilder(@NotNull Function1 builder) {
Intrinsics.checkParameterIsNotNull(builder, "builder");
super();
builder.invoke(this);
this.result = new JsonObject();
}
因此,解决方案是将结果的声明和初始化移到初始化块之前。
class JsonBuilder(builder: JsonBuilder.() -> Unit) {
val result = JsonObject()
init {
builder()
}
// ...
}
而现在的结果...
{"name":"value","int":1,"obj":{},"true":true}
编辑:您还希望允许与您的 DSL 链接,并修复您当前拥有的错误。
operator fun String.invoke(builder: JsonBuilder.() -> Unit) {
val obj = JsonBuilder(builder).result
result.add(this, obj)
}
这会产生正确的结果
{"name":"value","obj":{"int":1},"true":true}
推荐阅读
- r - Shiny:如何使用 Plotly 在箱形图上添加中线?
- react-native - 渲染子组件或从父组件调用子组件的方法,反之亦然[React Native]
- javascript - 使用 JavaScript 从提供的数据中解析
- javascript - 如何在 REACT 中使用选择表单更改请求 api?
- ssis - ODBC 批量大小
- javascript - 提交表单验证,刷新页面并无法继续
- python - Python 脚本突然无法在 PowerBI 服务上运行
- c# - Textbox.Text 重新排列字符串内容
- php - 将接口传递给另一个接口方法
- sql - 检查元组是否在每个子查询结果中(Oracle SQL)