Драйвер Java ArangoDB с классами данных Kotlin - PullRequest
0 голосов
/ 01 июля 2018

Хорошо, у драйвера Java Arongo DB нет проблем с хранением классов данных 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)
}

StackTrace:

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, но давай! Должен быть лучший способ, или я определенно что-то упустил.

Ответы [ 4 ]

0 голосов
/ 06 июля 2018

Вы должны сделать изменяемые поля в вашем классе данных, используя var вместо val . Val означает, что поля являются окончательными.

Далее вам нужен конструктор без параметров. Вы можете добиться этого, установив значения по умолчанию для полей в конструкторе или установив их по умолчанию в 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
)
0 голосов
/ 02 июля 2018

В качестве обходного пути для проекта vert.x с зависимостью com.fasterxml.jackson.module:jackson-module-kotlin вы можете добавить собственную встроенную функцию расширения с типом reified, чтобы она в общем случае извлекала хэш-карту документа, а затем позволяла модулю Джексона 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
  • Похоже, у Джексона все еще есть проблемы с анонимными классами

Есть какие-нибудь идеи о том, как его улучшить или как сократить издержки на конверсию?

0 голосов
/ 03 июля 2018

Драйвер Java ArangoDB поддерживает альтернативный сериализатор для де-сериализации документов, ребер и результатов запросов. Одна реализация - VelocyJack , основанная на работе Джексона с 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();
0 голосов
/ 01 июля 2018

ArangoDB использует собственную платформу сериализации - VelocyPack - для сериализации и десериализации классов. Как вы можете видеть в коде (и в предоставленной вами трассировке стека), этой платформе необходим конструктор без параметров для создания экземпляра десериализованного класса, которого нет у классов данных Kotlin. Насколько я знаю, у VelocyPack нет модуля для работы с классами данных Kotlin (как это делает Джексон), поэтому единственным вариантом будет создание настраиваемого десериализатора для вашего класса и его регистрация - это возможно с помощью VelocyPack (см. Документацию ). ), поэтому я предполагаю, что это также возможно в ArrangoDB.

Редактировать: Класс ArrangoDB.Builder имеет метод registerDeserializer(final Class<T> clazz, final VPackDeserializer<T> deserializer), который, как я полагаю, можно использовать для регистрации пользовательских десериализаторов JSON для VelocyPack.

...