带有Kotlin数据类的ArangoDB Java驱动程序

quhf5bfb  于 2022-12-09  发布在  Go
关注(0)|答案(6)|浏览(192)

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库将其Map回我的数据类,但拜托!一定有更好的方法,否则我肯定错过了什么。

2jcobegt

2jcobegt1#

ArangoDB Java驱动程序支持替代的序列化器来反序列化文档、边缘和查询结果。一个实现是VelcyJack,它基于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();
ig9co6j1

ig9co6j12#

ArangoDB使用它自己的序列化框架-VelcyPack-来序列化和反序列化类。(以及在您提供的堆栈跟踪中)该框架需要无参数构造函数来创建反序列化类的示例,而Kotlin的数据类并不具备这一点。(就像Jackson做的那样),所以您唯一的选择就是为您的类创建自定义反序列化器并注册它--使用VelcyPack(请参见文档)可以实现这一点,所以我假设在ArrangoDB中也可以实现这一点。
编辑:类ArrangoDB.Builder有方法registerDeserializer(final Class<T> clazz, final VPackDeserializer<T> deserializer),我假设它可以用来为VelcyPack注册自定义JSON反序列化器。

jyztefdp

jyztefdp3#

作为带有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")

已知问题:

  • 不考虑ArangoDB文档的本机属性,如:第一个月
  • 看来Jackson对anonymous classes还是有意见

有什么想法如何改进它或如何减少转换开销?

ee7vknir

ee7vknir4#

您必须使用var而不是瓦尔来使数据类中的字段可变。Val表示字段是最终的。
接下来,您需要一个无参数的构造函数。您可以通过在构造函数中为字段设置默认值或默认设置为空值来实现这一点。如果您决定将它们设置为空值,则必须通过在数据类型后面添加“?”来删除字段的空值安全性。
有关删除Null Safety的更多信息:https://kotlinlang.org/docs/reference/null-safety.html
具有默认值的最终数据类:

data class Foo(
    var topic: String = "",
    var answer: Int = ""
)

具有null的最终数据类:

data class Foo(
    var topic: String? = null,
    var answer: Int? = null
)
daolsyd0

daolsyd05#

在代码中,应用途:

arango.setSerializer(VelocyJack...

而不是:

arango.serializer(VelocyJack...

否则只能将其用于序列化而不能用于反序列化。
我创建了一个pull请求,您可以使用Kotlinno-arg maven插件作为解决方法,如下所示:https://github.com/slavaatsig/arangodb-jackson-dataformat-velocypack-issue/pull/1/files
即使JacksonKotlinModule与数据类一起工作(正如我在这里验证的: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

2w2cym1i

2w2cym1i6#

java-velocypack 2.2.0:https://github.com/arangodb/java-velocypack#kotlin-data-classes

相关问题