Десериализовать общий тип, используя ReferenceTypeDeserializer с Jackson & Spring - PullRequest
1 голос
/ 14 марта 2019

Я думаю, что здесь упущено что-то очевидное, но я не могу десериализовать простой универсальный контейнер с помощью Spring / Kotlin / Jackson.

Тип данных, о котором идет речь, очень прост:

@JsonDeserialize(using = PatchableDeserializer::class)
sealed class Patchable<T> {
    class Undefined<T>: Patchable<T>()
    class Null<T>: Patchable<T>()
    data class Present<T>(val content: T): Patchable<T>()
    // …
}

Десериализатор расширяет ReferenceTypeDeserializer, точно так же, как OptionalDeserializer.

class PatchableDeserializer(javaType: JavaType, vi: ValueInstantiator, typeDeser: TypeDeserializer, deser: JsonDeserializer<*> ):
        ReferenceTypeDeserializer<Patchable<*>>(javaType, vi, typeDeser, deser) {
    // …
}

модуля jdk8. Я предполагал, что Джексон заполнит аргументы конструктора для PatchableDeserializer здесь,Однако, похоже, что это не так:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'my.namespace.PatchableDeserializer': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.fasterxml.jackson.databind.JavaType' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

Я бы предположил, что Джексон предоставляет значение javaType, поскольку у меня нет возможности узнать его во время компиляции.

Вот код, который я использую для тестирования, который генерирует указанное выше исключение:

@RunWith(SpringRunner::class)
@JsonTest
class PatchableTest {
    @Autowired
    lateinit var objectMapper: ObjectMapper

    @Test
    fun patchableDeserialisesStringValue() {
        val value: Patchable<String> = objectMapper.readValue("\"null\"", object: TypeReference<Patchable<String>>() {})
        assertTrue(value.isPresent())
        assertEquals("null", value.unsafeGetValue())
    }
}

Чего мне не хватает?Кроме того, мне было очень трудно искать в Интернете некоторую информацию о том, как вообще десериализовать универсальные типы, поэтому, если у кого-то есть указания на то, как писать собственные десериализаторы для универсальных типов, я был бы очень признателен.

1 Ответ

0 голосов
/ 14 марта 2019

В итоге я реализовал другой интерфейс для моего десериализатора.

class PatchableDeserializer(private val valueType: Class<*>?): JsonDeserializer<Patchable<*>>(), ContextualDeserializer {
    override fun createContextual(ctxt: DeserializationContext?, property: BeanProperty?): JsonDeserializer<*> {
        val wrapperType = property?.type

        val rawClass = wrapperType?.containedType(0)?.rawClass
        return PatchableDeserializer(rawClass)
    }

    override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Patchable<*> =
        Patchable.of(p!!.readValueAs(valueType))

    override fun getNullValue(ctxt: DeserializationContext?): Patchable<Any> =
            if (ctxt?.parser?.currentToken == JsonToken.VALUE_NULL)
                Patchable.ofNull()
            else
                Patchable.undefined()
}

Это работает так, как задумано, но Джексону нужна контекстная информация в парсере, чтобы он заработал, т.е. приведенный выше тестовый код не работает.Однако он работает, если вы явно указываете DTO для десериализации, если он имеет правильные аннотации типов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...