Skip to content

Upcaster#594

Closed
m1l4n54v1c wants to merge 4 commits intorestatedev:mainfrom
m1l4n54v1c:upcaster
Closed

Upcaster#594
m1l4n54v1c wants to merge 4 commits intorestatedev:mainfrom
m1l4n54v1c:upcaster

Conversation

@m1l4n54v1c
Copy link

  • Enable schema evolution and backward compatibility by allowing payload upcasting at the state machine boundary.
  • Provide Upcasters with enough context (such as the core message type and selected header keys) to make informed transformation decisions.
  • Ensure system robustness: failures during upcasting must not break invocation processing.

Upcaster:

public interface Upcaster {
  boolean canUpcast(Slice body, Map<String, String> headers);

  Slice upcast(Slice body, Map<String, String> headers);
}

The upcaster is invoked for relevant protocol messages before handed over to the current state of the StateMachine. Messages carrying any form of payload. It could be that certain messages could be left out.

To help Upcaster implementations, metadata is constructed that includes the name of the core message and, where applicable, relevant information.

Since there is no hierarchy/generalization in messages, DefaultInvocationInputUpcaster is a bit verbose. I'm open to suggestions on how to improve it.

I see this as a conversation starter rather than a final solution. Looking forward to the feedback.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@m1l4n54v1c
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

@slinkydeveloper
Copy link
Contributor

Hi @m1l4n54v1c, I'm trying to understand what is a concrete usage of this interface. Could you provide the example use case you're trying to solve?

Also, if you need to plug into serialization/deserialization, could the SerdeFactory interface work already for you? https://docs.restate.dev/develop/java/serialization#use-another-serialization-library

@m1l4n54v1c
Copy link
Author

m1l4n54v1c commented Mar 4, 2026

Hi @slinkydeveloper I've just pushed an example to this PR. Thanks for getting time to take a look at this.

Address example demonstrates upcasting legacy XML AddressV1 data to JSON AddressV2 with a default country = "Serbia".

Why Upcaster (not just Serde)

  • Serde (Jackson) handles how to (de)serialize known JSON types, but it can’t ingest legacy XML bytes or fix schema gaps before type resolution.
  • Upcaster runs earlier, rewriting raw payloads (XML → JSON) and adding/renaming fields so normal JSON deserialization succeeds everywhere.

Hope this clarifies. Please let me know what you think :)

@slinkydeveloper
Copy link
Contributor

Upcaster runs earlier, rewriting raw payloads (XML → JSON) and adding/renaming fields so normal JSON deserialization succeeds everywhere.

Both these things are standard practices that you can do within a custom Serde implementation, that ultimately wraps the Jackson serde or similar. The Serde::deserialize would just need to:

  • Check if the payload is xml, if so convert it to json
  • Do all the transformations you want on the json itself
  • Then give the json payload to the builtin JacksonSerde created by JacksonSerdeFactory

Serde (Jackson) handles how to (de)serialize known JSON types, but it can’t ingest legacy XML bytes or fix schema gaps before type resolution.

I'm not sure what you mean by "before type resolution" here. Whatever type the Serde "materializes" (that is, the T in the Serde<T>) is not necessarily the type you need to use to perform the (de)serialization in the XML land/JSON land. With your custom serde you can decouple the two.

@m1l4n54v1c
Copy link
Author

Ah, you are right! I missed that deserialize takes a Slice. In my head, it was something like ObjectMapper, or a specific serialized type. Hence, I thought that serialization kicks too late in the process. We can close this PR.

@m1l4n54v1c m1l4n54v1c closed this Mar 5, 2026
@github-actions github-actions bot locked and limited conversation to collaborators Mar 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants