Hi, TLDR we have implemented the following (the comments contain the reasoning). Maybe it was not necessary and there exists a more elegant solution.
/**
* Because Temporal stores data serialised without having access to the class definitions, it stores type
* information based on the declared type. This applies to all types of methods: Workflows, Signals, Queries,
* Activities. In case the objects passed are descendants of that type, this information is lost. Then during
* deserialisation critical errors of type UnknownProperty will cause failure.
* To overcome that problem we need to manually store the exact type and then deserialise to that type. To trick
* the serialisation layer data is first transformed to a Tree which is generic and will be deserialised exactly,
* and then we convert back to the right object by using the type information stored in a separate top-level
* attribute.
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class WrappedPayload implements Serializable {
static ObjectMapper MAPPER = new ObjectMapper(); // must be static
static {
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
MAPPER.registerModule(new JavaTimeModule());
}
String type;
JsonNode data;
public static WrappedPayload of(MessagePayload data) {
return new WrappedPayload(
data.getClass().getTypeName(),
MAPPER.valueToTree(data));
}
public void replace(MessagePayload data) {
if (!data.getClass().getTypeName().equals(type)) {
throw new IllegalArgumentException("The type wrapped is immutable");
}
this.data = MAPPER.valueToTree(data);
}
public MessagePayload getData() {
try {
return (MessagePayload) MAPPER.treeToValue(data, Class.forName(type));
} catch (ClassNotFoundException | JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public String dataAsString() {
return data.toString();
}
}