【问题标题】:ArangoDB Java Driver with Kotlin data classes带有 Kotlin 数据类的 ArangoDB Java 驱动程序
【发布时间】:2018-07-01 18:47:41
【问题描述】:

嗯,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 库将它映射回我的数据类,但是来吧!一定有更好的方法,否则我肯定错过了什么。

【问题讨论】:

    标签: java kotlin arangodb


    【解决方案1】:

    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 团队在 Jackson 社区方面取得了进展。但是这个例子对我来说失败了。我在GitHub 上做了一个最小的示例项目来演示我的问题。似乎这个序列化程序打破了 ArgnoDB Java 异步驱动程序的身份验证阶段。
    【解决方案2】:

    ArangoDB 使用自己的序列化框架 - VelocyPack - 来序列化和反序列化类。正如您所看到的in the code(以及您提供的堆栈跟踪),该框架需要无参数构造函数来创建反序列化类的实例,而 Kotlin 的数据类没有。据我所知,VelocyPack 没有用于处理 Kotlin 数据类的模块(就像 Jackson 一样),所以您唯一的选择是为您的类创建自定义反序列化器并注册它 - VelocyPack 可以(请参阅documentation) ,所以我认为在 ArrangoDB 中也是可能的。

    编辑:ArrangoDB.Builder 类具有 registerDeserializer(final Class&lt;T&gt; clazz, final VPackDeserializer&lt;T&gt; deserializer) 方法,我假设它可用于为 VelocyPack 注册自定义 JSON 反序列化器。

    【讨论】:

    • 是的,我在问题之前阅读了文档,因为注册数百个序列化程序并不好玩。只是想要一个像 Jackson Kotlin 模块这样的通用解决方案。无论哪种方式,这是 Vert.x 项目的一部分,所以我已经有一个 com.fasterxml.jackson.module:jackson-module-kotlin 依赖项。
    • 说到这一点,另一种解决方案是对 jackson-module-kotlin 进行逆向工程并为 VelocyPack 编写类似的解决方案
    【解决方案3】:

    作为具有com.fasterxml.jackson.module:jackson-module-kotlin 依赖项的 vert.x 项目的一种解决方法,您可以添加一个具有具体类型的自定义内联扩展函数,以便它通常会提取文档的 hashmap,然后让 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 文档原生属性,例如:_id_key_rev_from_to
    • 似乎杰克逊对anonymous classes 仍有问题

    关于如何改进它或如何减少转换开销的任何想法?

    【讨论】:

      【解决方案4】:

      您必须使用 var 而不是 val 使数据类中的字段可变。 Val 表示字段是最终的。

      接下来你需要一个无参数的构造函数。您可以通过在构造函数中为字段设置默认值或默认设置为 null 来实现此目的。如果您决定将它们设置为 null,则必须通过添加“?”从字段中删除 Null 安全性。数据类型的背后。

      有关删除 Null 安全性的更多信息: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
      )
      

      【讨论】:

        【解决方案5】:

        在你的代码中你应该使用:

        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

        【讨论】:

          【解决方案6】:

          java-velocypack 2.2.0: https://github.com/arangodb/java-velocypack#kotlin-data-classes 起已修复

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多