Upcaster "Cannot build a converter to convert from [B to org.dom4j.Document"

I’m writing my first event upcaster in a Spring Boot 2.3.4 app using Axon framework 4.2.1, but when my upcaster calls upcastPayload() I get:

org.axonframework.serialization.CannotConvertBetweenTypesException: Cannot build a converter to convert from [B to org.dom4j.Document
	at org.axonframework.serialization.ChainedConverter.calculateChain(ChainedConverter.java:59)

The app uses the XStreamSerializer.

My upcaster:

import org.axonframework.serialization.SimpleSerializedType;
import org.axonframework.serialization.upcasting.event.IntermediateEventRepresentation;
import org.axonframework.serialization.upcasting.event.SingleEventUpcaster;
import org.dom4j.Document;
[...]

public class MyEventUpcaster extends SingleEventUpcaster {

  private SimpleSerializedType oldEventType = new SimpleSerializedType(MyEvent.class.getTypeName(), null);
  private SimpleSerializedType newEventType = new SimpleSerializedType(MyEvent.class.getTypeName(), "2");

  @Override
  protected boolean canUpcast(IntermediateEventRepresentation intermediateEventRepresentation) {
    return intermediateEventRepresentation.getType().equals(oldEventType);
  }

  @Override
  protected IntermediateEventRepresentation doUpcast(IntermediateEventRepresentation intermediateEventRepresentation) {
    return intermediateEventRepresentation.upcastPayload(newEventType, Document.class, this::upcastEvent);
  }

  private Document upcastEvent(Document oldEvent) {
    // separate for debugging, but never reached
    [...]
  }
}

I’m under the impression from what I’ve read that there’s built-in conversion for org.dom4j.Document?

Hi @Lee_C,

I’m under the impression from what I’ve read that there’s built-in conversion for org.dom4j.Document?

I am under the exact same impression, and actually it definitely should do this automatically.
It is indeed assuming all events which are being upcasted have originally been serialized by the XStreamSerializer, and that the XStreamSerializer is also used during the upcasting process. If you have configured it as such however, this should work out of the box.

What you could do to thoroughly ensure the XStreamSerializer is used, is by defining it on all levels through the Configurer. To put that in a sample:

Configurer configurer = ...;
Serializer serializer = XStreamSerializer.defaultSerializer();
configurer.configureEventSerializer(config -> serializer);

The same can be achieved in a Spring Boot app by doing the following:

@Bean
@Qualifier("eventSerializer")
public Serializer eventSerializer() {
    return XStreamSerializer.defaultSerializer();
}

If the error still persists, it might be worth checking whether distinct dom4j version are being pulled by Axon and your application.

If none of the above helps, I’d be hard pressed to know where to look further really.
So if it does persist, I’d like to ask you to provide or build a simple sample applications which consistently provides this failure behavior.

Let’s resolve this problem for you @Lee_C!

Cheers,
Steven

Sorry. As it turns out our application uses JacksonSerializer for events, with XStream for other objects.

Then the problem is rather straightforward there @Lee_C. The EventStore contains events in XStream serialized format (Document) whereas the event Serializer is configured as the JacksonSerializer.

The JacksonSerializer simply cannot deserialize XStream’s Document format, as you might have assumed. As a solution, you can do a couple of things:

  1. Switch your serializer back to XStream.
  2. Adjust the old events to Jackson serialized format. This requires a simple Axon application which reads from one event store using the XStreamSerializer and writes to another with the JacksonSerializer.
  3. Define a custom Serializer which wraps a Jackson and XStream serializer. As you want it to be Jackson (as that’s what you’ve configured right now), it should default to Jackson. If it fails reading with that serializer showing the above shared exception, that’s when you switch to the XStream serializer for reading.

Hope this clarifies your options @Lee_C!

Thanks for the detailed reply. As soon as I realized that I had drawn an incorrect conclusion as to what serializer we were using, I changed the expectedRepresentationType passed to IntermediateEventRepresentation.upcastPayload() from Document to String, wrote an upcastFunction that converted a string representation of the old event to a string representation of the new event, and everything else fell into place.
Sorry I wasn’t explicit about that in my last message.
Lee

1 Like

No worries @Lee_C.
Just happy to hear the issue has been resolved.

Thanks for sharing your approach as well.
That’ll likely serve other attendees of the forum too.