[英]ArangoDB Java Driver with Kotlin data classes
好吧,Arongo DB Java 驱动程序存储 Kotlin 数据类没有问题,但无法将它们加载回来。
展示:
import com.arangodb.ArangoCollection
import com.arangodb.ArangoDB
import com.arangodb.entity.DocumentCreateEntity
fun main(args: Array<String>) {
// Get or recreate collection: "some_collection" in DB "test_db"
val collection: ArangoCollection = with(ArangoDB.Builder().build()!!.db("test_db")) {
if (!exists()) create()
with(collection("some_colelction")) {
if (!exists()) create()
this
}
}
// POJO as Kotlin data class
data class Foo(
val topic: String,
val answer: Int
)
val result: DocumentCreateEntity<Foo> = collection.insertDocument(Foo("The ultimate answer", 42))
// reusult have a key of the new document
// And in ArangoDB Web Interface you can see this document: {"answer":42,"topic":"The ultimate answer"}
// http://localhost:8529/_db/test_db/_admin/aardvark/index.html#collection/some_colelction/documents/
// But it doesn't work backwards
val foo: Foo = collection.getDocument(result.key, Foo::class.java)
}
堆栈跟踪:
Exception in thread "main" com.arangodb.ArangoDBException: com.arangodb.velocypack.exception.VPackParserException: java.lang.InstantiationException: MainKt$main$Foo
at com.arangodb.internal.util.ArangoDeserializerImpl.deserialize(ArangoDeserializerImpl.java:59)
at com.arangodb.internal.util.ArangoUtilImpl.deserialize(ArangoUtilImpl.java:58)
at com.arangodb.internal.ArangoExecutor.createResult(ArangoExecutor.java:112)
at com.arangodb.internal.ArangoExecutorSync$1.deserialize(ArangoExecutorSync.java:56)
at com.arangodb.internal.ArangoExecutorSync.execute(ArangoExecutorSync.java:72)
at com.arangodb.internal.ArangoExecutorSync.execute(ArangoExecutorSync.java:53)
at com.arangodb.internal.ArangoExecutorSync.execute(ArangoExecutorSync.java:49)
at com.arangodb.internal.ArangoCollectionImpl.getDocument(ArangoCollectionImpl.java:134)
at com.arangodb.internal.ArangoCollectionImpl.getDocument(ArangoCollectionImpl.java:126)
at MainKt.main(main.kt:30)
Caused by: com.arangodb.velocypack.exception.VPackParserException: java.lang.InstantiationException: MainKt$main$Foo
at com.arangodb.velocypack.VPack.deserialize(VPack.java:398)
at com.arangodb.internal.util.ArangoDeserializerImpl.deserialize(ArangoDeserializerImpl.java:55)
... 9 more
Caused by: java.lang.InstantiationException: MainKt$main$Foo
at java.lang.Class.newInstance(Class.java:427)
at com.arangodb.velocypack.VPack.createInstance(VPack.java:488)
at com.arangodb.velocypack.VPack.deserializeObject(VPack.java:450)
at com.arangodb.velocypack.VPack.getValue(VPack.java:569)
at com.arangodb.velocypack.VPack.deserialize(VPack.java:396)
... 10 more
Caused by: java.lang.NoSuchMethodException: MainKt$main$Foo.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 14 more
Kotlin 的数据类很好地序列化为预期的 JSON 文档,但似乎 ArangoDB Java 驱动程序无法加载它们。 如果我将文档作为BaseDocument
获取,我可以使用一些 JSON 库将其映射回我的数据类,但是来吧! 必须有更好的方法,否则我肯定错过了一些东西。
ArangoDB Java 驱动程序支持替代序列化程序来对文档、边和查询结果进行反/序列化。 一种实现是VelocyJack ,它基于 Jackson 与jackson-dataformat-velocypack 一起工作。
您应该能够对其进行配置,使其与jackson-kotlin-module一起工作。
VelocyJack velocyJack = new VelocyJack();
velocyJack.configure((mapper) -> {
mapper.registerModule(new KotlinModule())
});
ArangoDB arango = new ArangoDB.Builder().serializer(velocyJack).build();
ArangoDB 使用自己的序列化框架——VelocyPack——来序列化和反序列化类。 正如您在代码(以及您提供的堆栈跟踪)中看到的那样,该框架需要无参数构造函数来创建反序列化类的实例,而 Kotlin 的数据类没有。 据我所知,VelocyPack 没有用于处理 Kotlin 数据类的模块(就像 Jackson 那样),因此您唯一的选择是为您的类创建自定义解串器并注册它 - VelocyPack 可以实现(请参阅文档),所以我认为在 ArrangoDB 中也是可能的。
编辑:类ArrangoDB.Builder
有方法registerDeserializer(final Class<T> clazz, final VPackDeserializer<T> deserializer)
,我认为它可用于为 VelocyPack 注册自定义 JSON 解串器。
作为具有com.fasterxml.jackson.module:jackson-module-kotlin
依赖项的 vert.x 项目的解决方法,您可以添加具有具体化类型的自定义内联扩展函数,以便它通常提取文档的哈希图,然后让 Jackson 的 Kotlin 模块做魔术:
inline fun <reified T> ArangoCollection.getDoc(key: String): T =
JsonObject(getDocument(key, BaseDocument::class.java).properties).mapTo(T::class.java)!!
然后这一行适用于 Kotlin 编译器的类型推断:
val for: Foo = collection.getDoc("document-key")
已知的问题:
_id
、 _key
、 _rev
、 _from
、 _to
关于如何改进它或如何减少转换开销的任何想法?
您必须使用var而不是val使数据类中的字段可变。 Val 表示这些字段是最终的。
接下来你需要一个无参数的构造函数。 您可以通过在构造函数中为字段设置默认值或将它们默认设置为 null 来实现这一点。 如果您决定将它们设置为 null,则必须通过添加“?”来从您的字段中删除 Null 安全性。 后面的数据类型。
有关删除 Null Safety 的更多信息: https : //kotlinlang.org/docs/reference/null-safety.html
具有默认值的最终数据类:
data class Foo(
var topic: String = "",
var answer: Int = ""
)
最终数据类为空:
data class Foo(
var topic: String? = null,
var answer: Int? = null
)
在您的代码中,您应该使用:
arango.setSerializer(VelocyJack...
代替:
arango.serializer(VelocyJack...
否则你只能将它用于序列化而不是反序列化。
我创建了一个拉取请求,您可以将其用作解决方法,使用 kotlin no-arg maven 插件,这里: https : //github.com/slavaatsig/arangodb-jackson-dataformat-velocypack-issue/pull/1/files
即使 jackson KotlinModule 适用于数据类(正如我在此处验证的那样: https : //github.com/rashtao/arangodb-jackson-dataformat-velocypack-issue/blob/dbg_serde/src/main/kotlin/com/example/issue /main.kt#L16-L21 ),驱动程序以某种方式尝试访问无参数构造函数......
有关此问题的进一步更新: https : //github.com/arangodb/arangodb-java-driver/issues/202
从java-velocypack 2.2.0
开始java-velocypack 2.2.0
:https ://github.com/arangodb/java-velocypack#kotlin-data-classes
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.