2 votes

Désérialiser un type générique en utilisant ReferenceTypeDeserializer avec Jackson et Spring

Je pense que je rate quelque chose d'évident ici, mais je n'arrive pas à désérialiser un simple conteneur générique en utilisant Spring/Kotlin/Jackson.

Le type de données en question est très simple :

@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>()
    // …
}

Le désérialiseur s'étend ReferenceTypeDeserializer tout comme la fonction OptionalDeserializer .

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

J'ai supposé que Jackson remplirait les arguments de constructeur pour PatchableDeserializer ici. Or, cela ne semble pas être le cas :

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: {}

Je suppose que Jackson fournit la valeur de javaType car je n'ai aucun moyen de le savoir au moment de la compilation.

Voici le code que j'utilise pour tester, qui génère l'exception ci-dessus :

@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())
    }
}

Qu'est-ce que je rate ? Par ailleurs, j'ai eu beaucoup de mal à trouver en ligne des informations sur la manière de désérialiser les types génériques. Si quelqu'un a des indications sur la manière d'écrire des désérialiseurs personnalisés pour les types génériques, je lui en serai très reconnaissant.

0voto

J'ai fini par implémenter une interface différente pour mon désérialiseur.

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()
}

Cela fonctionne comme prévu, mais Jackson a besoin d'informations contextuelles dans l'analyseur syntaxique pour que cela fonctionne, c'est-à-dire que le code de test ci-dessus ne fonctionne pas. Il fonctionne cependant, si vous spécifiez explicitement un DTO à désérialiser, s'il a les annotations de type correctes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X