ResponseTypes.multipleInstancesOf() type mismatch

I have a simple rest endpoint that looks like:

    @GetMapping("/products/admin")
    fun getProductsAdmin(
        @RequestParam(required = false) state: ProductState = ProductState.PENDING)
    : CompletableFuture<List<ProductAdminView>> {
        return queryGateway.query(GetProducts(state),
            ResponseTypes.multipleInstancesOf(ProductAdminView::class.java))
    }

The query handler looks like:

  @QueryHandler
    fun handle(query: GetProducts): List<ProductAdminView> {
        return productRepository.findByState(query.state)
            .map { productDocument ->
                ProductAdminView(
                    id = productDocument.id,
                    name = productDocument.name,
                    description = productDocument.description,
                    price = productDocument.price,
                    inStock = productDocument.inStock,
                    imageUrl = productDocument.imageUrl,
                    state = productDocument.state
                )
            }
    }

This unfortunately results in an exception:

java.lang.IllegalArgumentException: Retrieved response [class java.util.ArrayList] is not convertible to a List of the expected response type [class dev.fidil.monostore.product.ProductAdminView]
	at org.axonframework.messaging.responsetypes.MultipleInstancesResponseType.convert(MultipleInstancesResponseType.java:180) ~[axon-messaging-4.9.2.jar:4.9.2]
	at org.axonframework.messaging.responsetypes.MultipleInstancesResponseType.convert(MultipleInstancesResponseType.java:47) ~[axon-messaging-4.9.2.jar:4.9.2]
	at org.axonframework.messaging.responsetypes.ConvertingResponseMessage.getPayload(ConvertingResponseMessage.java:102) ~[axon-messaging-4.9.2.jar:4.9.2]
	at org.axonframework.queryhandling.DefaultQueryGateway.lambda$query$2(DefaultQueryGateway.java:97) ~[axon-messaging-4.9.2.jar:4.9.2]
	at java.base/java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:718) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na]
	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147) ~[na:na]
	at org.axonframework.axonserver.connector.query.AxonServerQueryBus$ResponseProcessingTask.lambda$run$0(AxonServerQueryBus.java:905) ~[axon-server-connector-4.9.2.jar:4.9.2]
	at org.axonframework.tracing.Span.run(Span.java:101) ~[axon-messaging-4.9.2.jar:4.9.2]
	at org.axonframework.axonserver.connector.query.AxonServerQueryBus$ResponseProcessingTask.run(AxonServerQueryBus.java:904) ~[axon-server-connector-4.9.2.jar:4.9.2]
	at org.axonframework.axonserver.connector.PriorityRunnable.run(PriorityRunnable.java:58) ~[axon-server-connector-4.9.2.jar:4.9.2]
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
	at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

I feel like I’m missing something simple but I cannot see it. It seems like the query handler might be wrapping an extra list around its response somehow. Any help much appreciated.

I am using Axon Framework 4.9.3 with mongodb as read store.

Assuming you use use Jackson for serialisation the query messages and responses, you need something like:

@Bean
@Primary
fun serializer(objectMapper: ObjectMapper): Serializer {
    return JacksonSerializer.builder()
            .objectMapper(objectMapper.copy())
            .defaultTyping()
            .lenientDeserialization()
            .build()
}

For the primary serializer used. Jackson will not add typing info by default, and therefore, it can’t deserialize the elements of the list.