microsoft / microsoft/durabletask-java

Customizable Serialization for Activity Return Values in Durable Functions

未关闭
#206 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

P2
主要语言
Java
星标
29
派生
18
平均合并
1 天 10 小时
30 天内合并 PR
2

描述

What’s the problem?

Hey👋,

I'm working with Durable Functions in Java, and I’ve encountered an issue with how activity return values are serialized. Specifically:

  • Activity functions return POJOs annotated with Jackson and Gson and having subTypes, which is deserialized using Gson when entering the activity but none is used for serializing return value.
  • This creates inconsistencies.
  • There's no way to customize the serialization mechanism for Durable Functions activities.
  • Raises an Exception of InvalidTypeIdException because the type is missing at com.microsoft.durabletask.JacksonDataConverter#deserialize.

How to reproduce it?

Here’s a minimal Durable Function example where an Activity returns a POJO, but the Azure Functions Java Worker does not deserialize it properly:

1️⃣ POJO with Jackson & Gson annotations and Subtypes
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import com.google.gson.*;
import java.io.IOException;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
    @JsonSubTypes.Type(value = AdminUser.class, name = "admin"),
    @JsonSubTypes.Type(value = RegularUser.class, name = "regular")
})
@JsonAdapter(UserTypeAdapter.class)
public abstract class User {
    private String id;

    private String name;

    public User() {}

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() { return id; }
    public String getName() { return name; }
}

public class AdminUser extends User {
    private int adminLevel;

    public AdminUser() {}
    public AdminUser(String id, String name, int adminLevel) {
        super(id, name);
        this.adminLevel = adminLevel;
    }
}

public class RegularUser extends User {
    private String membershipStatus;

    public RegularUser() {}
    public RegularUser(String id, String name, String membershipStatus) {
        super(id, name);
        this.membershipStatus = membershipStatus;
    }
}

public class UserTypeAdapter extends TypeAdapter<User> {
    @Override
    public void write(JsonWriter out, User user) throws IOException {
        out.beginObject();
        out.name("type").value(user instanceof AdminUser ? "admin" : "regular");
        out.name("id").value(user.getId());
        out.name("name").value(user.getName());
        if (user instanceof AdminUser) {
            out.name("adminLevel").value(((AdminUser) user).adminLevel);
        } else if (user instanceof RegularUser) {
            out.name("membershipStatus").value(((RegularUser) user).membershipStatus);
        }
        out.endObject();
    }

    @Override
    public User read(JsonReader in) throws IOException {
        in.beginObject();
        String type = "";
        String id = "";
        String name = "";
        int adminLevel = 0;
        String membershipStatus = "";
        
        while (in.hasNext()) {
            String fieldName = in.nextName();
            switch (fieldName) {
                case "type":
                    type = in.nextString();
                    break;
                case "id":
                    id = in.nextString();
                    break;
                case "name":
                    name = in.nextString();
                    break;
                case "adminLevel":
                    adminLevel = in.nextInt();
                    break;
                case "membershipStatus":
                    membershipStatus = in.nextString();
                    break;
                default:
                    in.skipValue();
                    break;
            }
        }
        in.endObject();
        
        return "admin".equals(type) ? new AdminUser(id, name, adminLevel) : new RegularUser(id, name, membershipStatus);
    }
}

What do you want to happen?

Supporting GSON for serialization.

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从 com.microsoft.durabletask.JacksonDataConverter 开始,追踪 activity 返回值的序列化以及后续的反序列化过程。使用提供的多态 User 示例来理解失败原因,然后确定可配置的 Gson serializer 应如何集成。完成标准是:使用 Gson 注解的 activity POJO 能够完成往返处理,且不会出现因类型缺失导致的 InvalidTypeIdException。

由索引模型根据 Issue 内容生成。

评估

技术栈
java
领域
backend
Issue 类型
功能
难度
4/5
预计耗时
3-5 天
活跃度
停滞
描述清晰度
基本清楚
新手友好度
35/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。